Merge pull request #11202 from s-hadinger/crash_protection

Add crash protection to ext_vsnprintf_P
This commit is contained in:
s-hadinger 2021-03-02 22:11:11 +01:00 committed by GitHub
commit 0ea89b2ed6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 51 additions and 38 deletions

View File

@ -212,6 +212,9 @@ char * copyStr(const char * str) {
return cpy; return cpy;
} }
const char ext_invalid_mem[] PROGMEM = "<--INVALID-->";
const uint32_t min_valid_ptr = 0x3FF00000; // addresses below this line are invalid
int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va) { int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va) {
va_list va_cpy; va_list va_cpy;
va_copy(va_cpy, va); va_copy(va_cpy, va);
@ -222,7 +225,7 @@ int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list
char * fmt = fmt_cpy; char * fmt = fmt_cpy;
const uint32_t ALLOC_SIZE = 12; const uint32_t ALLOC_SIZE = 12;
static char * allocs[ALLOC_SIZE] = {}; // initialized to zeroes static const char * allocs[ALLOC_SIZE] = {}; // initialized to zeroes
uint32_t alloc_idx = 0; uint32_t alloc_idx = 0;
static char hex[20]; // buffer used for 64 bits, favor RAM instead of stack to remove pressure static char hex[20]; // buffer used for 64 bits, favor RAM instead of stack to remove pressure
@ -264,12 +267,13 @@ int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list
fmt++; fmt++;
uint32_t cur_val = va_arg(va, uint32_t); // current value uint32_t cur_val = va_arg(va, uint32_t); // current value
const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack
char * new_val_str = (char*) ""; const char * new_val_str = "";
switch (*fmt) { switch (*fmt) {
case 'H': // Hex, decimals indicates the length, default 2 case 'H': // Hex, decimals indicates the length, default 2
{ {
if (decimals < 0) { decimals = 0; } if (decimals < 0) { decimals = 0; }
if (decimals > 0) { if (cur_val < min_valid_ptr) { new_val_str = ext_invalid_mem; }
else if (decimals > 0) {
char * hex_char = (char*) malloc(decimals*2 + 2); char * hex_char = (char*) malloc(decimals*2 + 2);
ToHex_P((const uint8_t *)cur_val, decimals, hex_char, decimals*2 + 2); ToHex_P((const uint8_t *)cur_val, decimals, hex_char, decimals*2 + 2);
new_val_str = hex_char; new_val_str = hex_char;
@ -280,13 +284,16 @@ int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list
break; break;
case 'B': // Pointer to SBuffer case 'B': // Pointer to SBuffer
{ {
const SBuffer & buf = *(const SBuffer*)cur_val; if (cur_val < min_valid_ptr) { new_val_str = ext_invalid_mem; }
size_t buf_len = (&buf != nullptr) ? buf.len() : 0; else {
if (buf_len) { const SBuffer & buf = *(const SBuffer*)cur_val;
char * hex_char = (char*) malloc(buf_len*2 + 2); size_t buf_len = (&buf != nullptr) ? buf.len() : 0;
ToHex_P(buf.getBuffer(), buf_len, hex_char, buf_len*2 + 2); if (buf_len) {
new_val_str = hex_char; char * hex_char = (char*) malloc(buf_len*2 + 2);
allocs[alloc_idx++] = new_val_str; ToHex_P(buf.getBuffer(), buf_len, hex_char, buf_len*2 + 2);
new_val_str = hex_char;
allocs[alloc_idx++] = new_val_str;
}
} }
} }
break; break;
@ -315,40 +322,46 @@ int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list
// Note: float MUST be passed by address, because C alsays promoted float to double when in vararg // Note: float MUST be passed by address, because C alsays promoted float to double when in vararg
case 'f': // input is `float`, printed to float with 2 decimals case 'f': // input is `float`, printed to float with 2 decimals
{ {
bool truncate = false; if (cur_val < min_valid_ptr) { new_val_str = ext_invalid_mem; }
if (decimals < 0) { else {
decimals = -decimals; bool truncate = false;
truncate = true; if (decimals < 0) {
} decimals = -decimals;
float number = *(float*)cur_val; truncate = true;
if (isnan(number) || isinf(number)) { }
new_val_str = (char*) "null"; float number = *(float*)cur_val;
} else { if (isnan(number) || isinf(number)) {
dtostrf(*(float*)cur_val, (decimals + 2), decimals, hex); new_val_str = "null";
} else {
if (truncate) { dtostrf(*(float*)cur_val, (decimals + 2), decimals, hex);
uint32_t last = strlen(hex) - 1;
// remove trailing zeros if (truncate) {
while (hex[last] == '0') { uint32_t last = strlen(hex) - 1;
hex[last--] = 0; // remove last char // remove trailing zeros
} while (hex[last] == '0') {
// remove trailing dot hex[last--] = 0; // remove last char
if (hex[last] == '.') { }
hex[last] = 0; // remove trailing dot
} if (hex[last] == '.') {
hex[last] = 0;
}
}
new_val_str = copyStr(hex);
allocs[alloc_idx++] = new_val_str;
} }
new_val_str = copyStr(hex);
allocs[alloc_idx++] = new_val_str;
} }
} }
break; break;
// '%_X' outputs a 64 bits unsigned int to uppercase HEX with 16 digits // '%_X' outputs a 64 bits unsigned int to uppercase HEX with 16 digits
case 'X': // input is `uint64_t*`, printed as 16 hex digits (no prefix 0x) case 'X': // input is `uint64_t*`, printed as 16 hex digits (no prefix 0x)
{ {
if ((decimals < 0) || (decimals > 16)) { decimals = 16; } if (cur_val < min_valid_ptr) { new_val_str = ext_invalid_mem; }
U64toHex(*(uint64_t*)cur_val, hex, decimals); else {
new_val_str = copyStr(hex); if ((decimals < 0) || (decimals > 16)) { decimals = 16; }
allocs[alloc_idx++] = new_val_str; U64toHex(*(uint64_t*)cur_val, hex, decimals);
new_val_str = copyStr(hex);
allocs[alloc_idx++] = new_val_str;
}
} }
break; break;
// Trying to do String allocation alternatives, but not as interesting as I thought in the beginning // Trying to do String allocation alternatives, but not as interesting as I thought in the beginning
@ -382,7 +395,7 @@ int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list
// disallocated all temporary strings // disallocated all temporary strings
for (uint32_t i = 0; i < alloc_idx; i++) { for (uint32_t i = 0; i < alloc_idx; i++) {
free(allocs[i]); // it is ok to call free() on nullptr so we don't test for nullptr first free((void*)allocs[i]); // it is ok to call free() on nullptr so we don't test for nullptr first
allocs[i] = nullptr; allocs[i] = nullptr;
} }
free(fmt_cpy); // free the local copy of the format string free(fmt_cpy); // free the local copy of the format string