Skip to content

Commit c6cac23

Browse files
committed
Implementation fixes
The printf(3) man page says "The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit, then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available." The last part of this spec (returning the number of characters which would have been written to the string if enough space had been available) was not implemented in minimal-printf. This PR changes that by redirecting all the processed characters through the "minimal_printf_putchar" helper function. This function will not write to the buffer if there's no space left, but it will always increment the number of written characters, in order to match the above description. minimal_printf_putchar also contains checks for overflows, so these checks are no longer needed in the rest of the code. Other changes: - In some cases, mbed_minimal_formatted_string didn't put the string terminator in the output buffer. This PR ensures that this always happens. - Fixed a bug in printed hexadecimal numbers introduced by a previous commit. - Added buffer overflow tests for snprintf.
1 parent 362dd3f commit c6cac23

File tree

2 files changed

+246
-213
lines changed

2 files changed

+246
-213
lines changed

features/minimal-printf/TESTS/minimal-printf/compliance/main.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ static control_t test_snprintf_x(const size_t call_count)
618618
return CaseNext;
619619
}
620620

621+
#if MBED_CONF_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
621622
static control_t test_printf_f(const size_t call_count)
622623
{
623624
int result_baseline;
@@ -672,6 +673,99 @@ static control_t test_snprintf_f(const size_t call_count)
672673

673674
return CaseNext;
674675
}
676+
#endif
677+
678+
679+
/* Generic buffer overflow test function.
680+
* Template parameters:
681+
* 'T' is the type being tested
682+
* 'buf_size' is the buffer size used in tests
683+
* Function parameters:
684+
* 'fmt' is the format to use for sprintf
685+
* 'data' is the data that will be printed
686+
*/
687+
template<typename T, size_t buf_size>
688+
static control_t test_snprintf_buffer_overflow_generic(const char *fmt, T data)
689+
{
690+
char buffer_baseline[buf_size];
691+
char buffer_minimal[buf_size];
692+
int result_baseline;
693+
int result_minimal;
694+
695+
/* empty buffer test */
696+
result_minimal = mbed_snprintf(buffer_minimal, 0, fmt, data);
697+
result_baseline = snprintf(buffer_baseline, 0, fmt, data);
698+
TEST_ASSERT_EQUAL_INT(result_baseline, result_minimal);
699+
700+
/* buffer isn't large enough, output needs to be truncated */
701+
result_minimal = mbed_snprintf(buffer_minimal, buf_size - 2, fmt, data);
702+
result_baseline = snprintf(buffer_baseline, buf_size - 2, fmt, data);
703+
TEST_ASSERT_EQUAL_STRING(buffer_baseline, buffer_minimal);
704+
TEST_ASSERT_EQUAL_INT(result_baseline, result_minimal);
705+
706+
/* buffer is one byte shorter than needed, string terminator must
707+
be written and output must be truncated */
708+
result_minimal = mbed_snprintf(buffer_minimal, buf_size - 1, fmt, data);
709+
result_baseline = snprintf(buffer_baseline, buf_size - 1, fmt, data);
710+
TEST_ASSERT_EQUAL_STRING(buffer_baseline, buffer_minimal);
711+
TEST_ASSERT_EQUAL_INT(result_baseline, result_minimal);
712+
713+
/* buffer is just long enough */
714+
result_minimal = mbed_snprintf(buffer_minimal, buf_size, fmt, data);
715+
result_baseline = snprintf(buffer_baseline, buf_size, fmt, data);
716+
TEST_ASSERT_EQUAL_STRING(buffer_baseline, buffer_minimal);
717+
TEST_ASSERT_EQUAL_INT(result_baseline, result_minimal);
718+
719+
return CaseNext;
720+
}
721+
722+
/* Based on the generic buffer overflow function above, create tests for
723+
each relevant data type. In each case, the buffer for printing will only
724+
be large enough to fit the printed data. */
725+
static control_t test_snprintf_buffer_overflow_d(const size_t call_count)
726+
{
727+
return test_snprintf_buffer_overflow_generic<int, sizeof("d: -1024")>("d: %d", -1024);
728+
}
729+
730+
static control_t test_snprintf_buffer_overflow_ld(const size_t call_count)
731+
{
732+
return test_snprintf_buffer_overflow_generic<long, sizeof("ld: -1048576")>("ld: %ld", -1048576L);
733+
}
734+
735+
static control_t test_snprintf_buffer_overflow_lld(const size_t call_count)
736+
{
737+
return test_snprintf_buffer_overflow_generic<long long, sizeof("lld: -1099511627776")>("lld: %lld", -1099511627776LL);
738+
}
739+
740+
static control_t test_snprintf_buffer_overflow_u(const size_t call_count)
741+
{
742+
return test_snprintf_buffer_overflow_generic<unsigned int, sizeof("u: 1024")>("u: %u", 1024);
743+
}
744+
745+
static control_t test_snprintf_buffer_overflow_lu(const size_t call_count)
746+
{
747+
return test_snprintf_buffer_overflow_generic<unsigned long, sizeof("lu: 1048576")>("lu: %lu", 1048576UL);
748+
}
749+
750+
static control_t test_snprintf_buffer_overflow_llu(const size_t call_count)
751+
{
752+
return test_snprintf_buffer_overflow_generic<unsigned long long, sizeof("llu: 1099511627776")>("llu: %llu", 1099511627776ULL);
753+
}
754+
755+
static control_t test_snprintf_buffer_overflow_x(const size_t call_count)
756+
{
757+
return test_snprintf_buffer_overflow_generic<unsigned int, sizeof("x: 0x400")>("x: 0x%x", 0x400);
758+
}
759+
760+
static control_t test_snprintf_buffer_overflow_lx(const size_t call_count)
761+
{
762+
return test_snprintf_buffer_overflow_generic<unsigned long, sizeof("lx: 0x100000")>("lx: 0x%lx", 0x100000UL);
763+
}
764+
765+
static control_t test_snprintf_buffer_overflow_llx(const size_t call_count)
766+
{
767+
return test_snprintf_buffer_overflow_generic<unsigned long long, sizeof("llx: 0x10000000000")>("llx: 0x%llx", 0x10000000000ULL);
768+
}
675769

676770
utest::v1::status_t greentea_setup(const size_t number_of_cases)
677771
{
@@ -690,6 +784,15 @@ Case cases[] = {
690784
Case("printf %f", test_printf_f),
691785
Case("snprintf %f", test_snprintf_f),
692786
#endif
787+
Case("snprintf buffer overflow %d", test_snprintf_buffer_overflow_d),
788+
Case("snprintf buffer overflow %ld", test_snprintf_buffer_overflow_ld),
789+
Case("snprintf buffer overflow %lld", test_snprintf_buffer_overflow_lld),
790+
Case("snprintf buffer overflow %u", test_snprintf_buffer_overflow_u),
791+
Case("snprintf buffer overflow %lu", test_snprintf_buffer_overflow_lu),
792+
Case("snprintf buffer overflow %llu", test_snprintf_buffer_overflow_llu),
793+
Case("snprintf buffer overflow %x", test_snprintf_buffer_overflow_x),
794+
Case("snprintf buffer overflow %lx", test_snprintf_buffer_overflow_lx),
795+
Case("snprintf buffer overflow %llx", test_snprintf_buffer_overflow_llx),
693796
};
694797

695798
Specification specification(greentea_setup, cases, greentea_test_teardown_handler);

0 commit comments

Comments
 (0)