[BUGFIX][IOTUC-18] Library fixes

This commit adds mostly integer (and buffer) overflow checks for the
current buffer index (`result` variable).
pull/11051/head
Bogdan Marinescu 2018-03-22 18:47:39 +02:00
parent de7024f7ab
commit a5f07166f9
1 changed files with 256 additions and 250 deletions

View File

@ -17,6 +17,7 @@
#include "mbed_printf_implementation.h" #include "mbed_printf_implementation.h"
#include <stdbool.h> #include <stdbool.h>
#include <limits.h>
/***************************/ /***************************/
/* MBED */ /* MBED */
@ -126,9 +127,9 @@ typedef enum {
static void mbed_minimal_formatted_string_signed(char* buffer, size_t length, int* result, MBED_SIGNED_STORAGE value); static void mbed_minimal_formatted_string_signed(char* buffer, size_t length, int* result, MBED_SIGNED_STORAGE value);
static void mbed_minimal_formatted_string_unsigned(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value); static void mbed_minimal_formatted_string_unsigned(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value);
static void mbed_minimal_formatted_string_hexadecimal(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value); static void mbed_minimal_formatted_string_hexadecimal(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value);
static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, void* value); static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, const void* value);
static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, char character); static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, char character);
static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string); static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, const char* string);
/** /**
* @brief Print signed integer. * @brief Print signed integer.
@ -141,7 +142,7 @@ static void mbed_minimal_formatted_string_string(char* buffer, size_t length, in
static void mbed_minimal_formatted_string_signed(char* buffer, size_t length, int* result, MBED_SIGNED_STORAGE value) static void mbed_minimal_formatted_string_signed(char* buffer, size_t length, int* result, MBED_SIGNED_STORAGE value)
{ {
/* only continue if buffer can fit at least 1 characters */ /* only continue if buffer can fit at least 1 characters */
if ((size_t)(*result + 1) <= length) if ((*result >= 0) && (*result <= INT_MAX - 1) && ((size_t)*result + 1 <= length))
{ {
MBED_UNSIGNED_STORAGE new_value = 0; MBED_UNSIGNED_STORAGE new_value = 0;
@ -184,7 +185,7 @@ static void mbed_minimal_formatted_string_signed(char* buffer, size_t length, in
static void mbed_minimal_formatted_string_unsigned(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value) static void mbed_minimal_formatted_string_unsigned(char* buffer, size_t length, int* result, MBED_UNSIGNED_STORAGE value)
{ {
/* only continue if buffer can fit at least 1 characters */ /* only continue if buffer can fit at least 1 characters */
if ((size_t)(*result + 1) <= length) if ((*result >= 0) && (*result <= INT_MAX - 1) && ((size_t)*result + 1 <= length))
{ {
/* treat 0 as a corner case */ /* treat 0 as a corner case */
if (value == 0) if (value == 0)
@ -218,7 +219,7 @@ static void mbed_minimal_formatted_string_unsigned(char* buffer, size_t length,
} }
/* write scratch pad to buffer or output */ /* write scratch pad to buffer or output */
for ( ; ((size_t)*result < length) && (index > 0); index--) for ( ; (*result <= INT_MAX- 1) && ((size_t)*result < length) && (index > 0); index--)
{ {
if (buffer) if (buffer)
{ {
@ -248,7 +249,7 @@ static void mbed_minimal_formatted_string_hexadecimal(char* buffer, size_t lengt
bool print_leading_zero = false; bool print_leading_zero = false;
/* only continue each loop if buffer can fit at least 2 characters */ /* only continue each loop if buffer can fit at least 2 characters */
for (int index = 7; (((size_t)(*result + 2) <= length)) && (index >= 0); index--) for (int index = 7; (*result >= 0) && (*result <= INT_MAX - 2) && ((size_t)*result + 2 <= length) && (index >= 0); index--)
{ {
/* get most significant byte */ /* get most significant byte */
uint8_t output = value >> (8 * index); uint8_t output = value >> (8 * index);
@ -290,10 +291,11 @@ static void mbed_minimal_formatted_string_hexadecimal(char* buffer, size_t lengt
* @param result The current output location. * @param result The current output location.
* @param[in] value The pointer to be printed. * @param[in] value The pointer to be printed.
*/ */
static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, void* value) static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, const void* value)
{ {
/* only continue if buffer can fit '0x' and twice the size of a void* */ /* only continue if buffer can fit '0x' and twice the size of a void* */
if ((*result + 2 + 2 * sizeof(void*)) <= length) size_t needed = 2 + 2 * sizeof(void*);
if ((*result >= 0) && ((size_t)*result <= INT_MAX - needed) && ((size_t)*result + needed <= length))
{ {
/* write leading 0x */ /* write leading 0x */
if (buffer) if (buffer)
@ -326,7 +328,7 @@ static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t leng
static void mbed_minimal_formatted_string_double(char* buffer, size_t length, int* result, double value) static void mbed_minimal_formatted_string_double(char* buffer, size_t length, int* result, double value)
{ {
/* only continue if buffer can fit at least 1 characters */ /* only continue if buffer can fit at least 1 characters */
if ((size_t)(*result + 1) <= length) if ((*result >= 0) && (*result <= INT_MAX - 1) && ((size_t)*result + 1 <= length))
{ {
/* get integer part */ /* get integer part */
MBED_SIGNED_STORAGE integer = value; MBED_SIGNED_STORAGE integer = value;
@ -385,7 +387,7 @@ static void mbed_minimal_formatted_string_double(char* buffer, size_t length, in
static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, char character) static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, char character)
{ {
/* only continue if the buffer can fit 1 character */ /* only continue if the buffer can fit 1 character */
if ((size_t)(*result + 1) <= length) if ((*result >= 0) && (*result <= INT_MAX - 1) && ((size_t)*result + 1 <= length))
{ {
/* write character */ /* write character */
if (buffer) if (buffer)
@ -422,10 +424,10 @@ static void mbed_minimal_formatted_string_character(char* buffer, size_t length,
* @param result The current output location. * @param result The current output location.
* @param[in] value The string to be printed. * @param[in] value The string to be printed.
*/ */
static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string) static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, const char* string)
{ {
/* only continue if the buffer can fit at least 1 character */ /* only continue if the buffer can fit at least 1 character */
if ((size_t)(*result + 1) <= length) if ((*result >= 0) && (*result <= INT_MAX - 1) && ((size_t)*result + 1 <= length))
{ {
/* count characters in string */ /* count characters in string */
size_t remaining = length - *result; size_t remaining = length - *result;
@ -440,6 +442,11 @@ static void mbed_minimal_formatted_string_string(char* buffer, size_t length, in
/* copy string to buffer */ /* copy string to buffer */
if (buffer) if (buffer)
{ {
/* ensure that the value of "result" doesn't overflow */
if (string_length + *result > INT_MAX)
{
string_length = (size_t)INT_MAX - *result;
}
for (size_t index = 0; index < string_length; index++) for (size_t index = 0; index < string_length; index++)
{ {
buffer[*result + index] = string[index]; buffer[*result + index] = string[index];
@ -476,277 +483,276 @@ int mbed_minimal_formatted_string(char* buffer, size_t length, const char* forma
int result = 0; int result = 0;
/* if writing to buffer, reserve space for NULL termination */ /* ensure that function wasn't called with an empty buffer, or with
if (buffer) a buffer size that is larger than the maximum 'int' value */
if (length > 0 && length <= INT_MAX)
{ {
length--; /* parse string */
} for (size_t index = 0; format[index] != '\0'; index++)
/* parse string */
for (size_t index = 0; format[index] != '\0'; index++)
{
/* format specifier begin */
if (format[index] == '%')
{ {
size_t next_index = index + 1; /* format specifier begin */
if (format[index] == '%')
/* while there is room in buffer and format string is not empty */
while ((next_index < length) && (format[next_index] != '\0') &&
/* skip all flags and precision modifiers */
(((format[next_index] >= '0') && (format[next_index] <= '9')) ||
(format[next_index] == '-') ||
(format[next_index] == '+') ||
(format[next_index] == '#') ||
(format[next_index] == '.')))
{ {
/* skip to next character */ size_t next_index = index + 1;
next_index++;
}
/* look for length modifier, default to NONE */ /* while format string is not empty */
length_t length_modifier = LENGTH_NONE; while ((format[next_index] != '\0') &&
/* skip all flags and precision modifiers */
/* look for two character length modifier */ (((format[next_index] >= '0') && (format[next_index] <= '9')) ||
if (format[next_index + 1] != '\0') (format[next_index] == '-') ||
{ (format[next_index] == '+') ||
if ((format[next_index] == 'h') && (format[next_index + 1] == 'h')) (format[next_index] == '#') ||
{ (format[next_index] == '.')))
length_modifier = LENGTH_HH;
}
else if ((format[next_index] == 'l') && (format[next_index + 1] == 'l'))
{
length_modifier = LENGTH_LL;
}
/* increment next_index if length modifier was found */
if (length_modifier != LENGTH_NONE)
{
next_index += 2;
}
}
/* look for one character length modifier if two character search failed */
if ((length_modifier == LENGTH_NONE) && (format[next_index] != '\0'))
{
if (format[next_index] == 'h')
{
length_modifier = LENGTH_H;
}
else if (format[next_index] == 'l')
{
length_modifier = LENGTH_L;
}
else if (format[next_index] == 'j')
{
length_modifier = LENGTH_J;
}
else if (format[next_index] == 'z')
{
length_modifier = LENGTH_Z;
}
else if (format[next_index] == 't')
{
length_modifier = LENGTH_T;
}
else if (format[next_index] == 'L')
{
length_modifier = LENGTH_CAPITAL_L;
}
/* increment next_index if length modifier was found */
if (length_modifier != LENGTH_NONE)
{ {
/* skip to next character */
next_index++; next_index++;
} }
}
/* read out character - this is a supported format character, '\0', or a not suported character */ /* look for length modifier, default to NONE */
char next = format[next_index]; length_t length_modifier = LENGTH_NONE;
/* signed integer */ /* look for two character length modifier */
if ((next == 'd') || (next == 'i')) if (format[next_index + 1] != '\0')
{
MBED_SIGNED_STORAGE value = 0;
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(long long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(int) > sizeof(MBED_SIGNED_NATIVE_TYPE))))
{ {
/* use 64 bit storage type for readout */ if ((format[next_index] == 'h') && (format[next_index + 1] == 'h'))
value = va_arg(arguments, MBED_SIGNED_STORAGE); {
} length_modifier = LENGTH_HH;
else }
#endif else if ((format[next_index] == 'l') && (format[next_index + 1] == 'l'))
{ {
/* use native storage type (which can be 32 or 64 bit) */ length_modifier = LENGTH_LL;
value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE); }
/* increment next_index if length modifier was found */
if (length_modifier != LENGTH_NONE)
{
next_index += 2;
}
} }
/* constrict value based on lenght modifier */ /* look for one character length modifier if two character search failed */
switch (length_modifier) if ((length_modifier == LENGTH_NONE) && (format[next_index] != '\0'))
{ {
case LENGTH_NONE: if (format[next_index] == 'h')
value = (int) value; {
break; length_modifier = LENGTH_H;
case LENGTH_HH: }
value = (signed char) value; else if (format[next_index] == 'l')
break; {
case LENGTH_H: length_modifier = LENGTH_L;
value = (short int) value; }
break; else if (format[next_index] == 'j')
case LENGTH_L: {
value = (long int) value; length_modifier = LENGTH_J;
break; }
case LENGTH_LL: else if (format[next_index] == 'z')
value = (long long int) value; {
break; length_modifier = LENGTH_Z;
case LENGTH_J: }
value = (intmax_t) value; else if (format[next_index] == 't')
break; {
case LENGTH_T: length_modifier = LENGTH_T;
value = (ptrdiff_t) value; }
break; else if (format[next_index] == 'L')
default: {
break; length_modifier = LENGTH_CAPITAL_L;
}
/* increment next_index if length modifier was found */
if (length_modifier != LENGTH_NONE)
{
next_index++;
}
} }
index = next_index; /* read out character - this is a supported format character, '\0', or a not suported character */
char next = format[next_index];
mbed_minimal_formatted_string_signed(buffer, length, &result, value); /* signed integer */
} if ((next == 'd') || (next == 'i'))
/* unsigned integer */
else if ((next == 'u') || (next == 'x') || (next == 'X'))
{
MBED_UNSIGNED_STORAGE value = 0;
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(unsigned long long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(unsigned long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(unsigned int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))))
{ {
/* use 64 bit storage type for readout */ MBED_SIGNED_STORAGE value = 0;
value = va_arg(arguments, MBED_UNSIGNED_STORAGE);
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(long long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(int) > sizeof(MBED_SIGNED_NATIVE_TYPE))))
{
/* use 64 bit storage type for readout */
value = va_arg(arguments, MBED_SIGNED_STORAGE);
}
else
#endif
{
/* use native storage type (which can be 32 or 64 bit) */
value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
}
/* constrict value based on lenght modifier */
switch (length_modifier)
{
case LENGTH_NONE:
value = (int) value;
break;
case LENGTH_HH:
value = (signed char) value;
break;
case LENGTH_H:
value = (short int) value;
break;
case LENGTH_L:
value = (long int) value;
break;
case LENGTH_LL:
value = (long long int) value;
break;
case LENGTH_J:
value = (intmax_t) value;
break;
case LENGTH_T:
value = (ptrdiff_t) value;
break;
default:
break;
}
index = next_index;
mbed_minimal_formatted_string_signed(buffer, length, &result, value);
} }
else /* unsigned integer */
#endif else if ((next == 'u') || (next == 'x') || (next == 'X'))
{ {
/* use native storage type (which can be 32 or 64 bit) */ MBED_UNSIGNED_STORAGE value = 0;
value = va_arg(arguments, MBED_UNSIGNED_NATIVE_TYPE);
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(unsigned long long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(unsigned long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(unsigned int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))))
{
/* use 64 bit storage type for readout */
value = va_arg(arguments, MBED_UNSIGNED_STORAGE);
}
else
#endif
{
/* use native storage type (which can be 32 or 64 bit) */
value = va_arg(arguments, MBED_UNSIGNED_NATIVE_TYPE);
}
/* constrict value based on lenght modifier */
switch (length_modifier)
{
case LENGTH_NONE:
value = (unsigned int) value;
break;
case LENGTH_HH:
value = (unsigned char) value;
break;
case LENGTH_H:
value = (unsigned short int) value;
break;
case LENGTH_L:
value = (unsigned long int) value;
break;
case LENGTH_LL:
value = (unsigned long long int) value;
break;
case LENGTH_J:
value = (uintmax_t) value;
break;
case LENGTH_Z:
value = (size_t) value;
break;
case LENGTH_T:
value = (ptrdiff_t) value;
break;
default:
break;
}
index = next_index;
/* write unsigned or hexadecimal */
if (next == 'u')
{
mbed_minimal_formatted_string_unsigned(buffer, length, &result, value);
}
else
{
mbed_minimal_formatted_string_hexadecimal(buffer, length, &result, value);
}
} }
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
/* constrict value based on lenght modifier */ /* treat all floating points the same */
switch (length_modifier) else if ((next == 'f') || (next == 'F') || (next == 'g') || (next == 'G'))
{ {
case LENGTH_NONE: double value = va_arg(arguments, double);
value = (unsigned int) value; index = next_index;
break;
case LENGTH_HH: mbed_minimal_formatted_string_double(buffer, length, &result, value);
value = (unsigned char) value;
break;
case LENGTH_H:
value = (unsigned short int) value;
break;
case LENGTH_L:
value = (unsigned long int) value;
break;
case LENGTH_LL:
value = (unsigned long long int) value;
break;
case LENGTH_J:
value = (uintmax_t) value;
break;
case LENGTH_Z:
value = (size_t) value;
break;
case LENGTH_T:
value = (ptrdiff_t) value;
break;
default:
break;
} }
#endif
index = next_index; /* character */
else if (next == 'c')
/* write unsigned or hexadecimal */
if (next == 'u')
{ {
mbed_minimal_formatted_string_unsigned(buffer, length, &result, value); char value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
index = next_index;
mbed_minimal_formatted_string_character(buffer, length, &result, value);
}
/* string */
else if (next == 's')
{
char* value = va_arg(arguments, char*);
index = next_index;
mbed_minimal_formatted_string_string(buffer, length, &result, value);
}
/* pointer */
else if (next == 'p')
{
void* value = va_arg(arguments, void*);
index = next_index;
mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value);
} }
else else
{ {
mbed_minimal_formatted_string_hexadecimal(buffer, length, &result, value); /* write all characters between format beginning and unrecognied modifier */
while (index < next_index)
{
mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
index++;
}
/* if this is not the end of the string, write unrecognized modifier */
if (next != '\0')
{
mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
}
else
{
/* break out of for loop */
break;
}
} }
} }
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
/* treat all floating points the same */
else if ((next == 'f') || (next == 'F') || (next == 'g') || (next == 'G'))
{
double value = va_arg(arguments, double);
index = next_index;
mbed_minimal_formatted_string_double(buffer, length, &result, value);
}
#endif
/* character */
else if (next == 'c')
{
char value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
index = next_index;
mbed_minimal_formatted_string_character(buffer, length, &result, value);
}
/* string */
else if (next == 's')
{
char* value = va_arg(arguments, char*);
index = next_index;
mbed_minimal_formatted_string_string(buffer, length, &result, value);
}
/* pointer */
else if (next == 'p')
{
void* value = va_arg(arguments, void*);
index = next_index;
mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value);
}
else else
/* not a format specifier */
{ {
/* write all characters between format beginning and unrecognied modifier */ /* write normal character */
while (index < next_index) mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
{
mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
index++;
}
/* if this is not the end of the string, write unrecognized modifier */
if (next != '\0')
{
mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
}
else
{
/* break out of for loop */
break;
}
} }
} }
else
/* not a format specifier */
{
/* write normal character */
mbed_minimal_formatted_string_character(buffer, length, &result, format[index]);
}
}
/* if writing to buffer, NULL terminate string in reserved space*/ /* if writing to buffer, NULL terminate string in reserved space*/
if (buffer) if (buffer && ((size_t)result < length))
{ {
buffer[result] = '\0'; buffer[result] = '\0';
}
} }
return result; return result;