To: vim_dev@googlegroups.com Subject: Patch 8.2.0066 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0066 Problem: Some corners of vim_snprintf() are not tested. Solution: Add a test in C. (Dominique Pelle, closes #5422) Files: src/message_test.c *** ../vim-8.2.0065/src/message_test.c 2019-12-30 17:55:30.460513283 +0100 --- src/message_test.c 2019-12-31 19:24:11.385712386 +0100 *************** *** 22,27 **** --- 22,45 ---- // static. #include "message.c" + #ifndef MIN + # define MIN(x,y) ((x) < (y) ? (x) : (y)) + #endif + + // These formats are not standard in C printf() function. + // Use a global variable rather than a literal format to disable + // -Wformat compiler warnings: + // + // - warning: '0' flag used with ‘%p’ gnu_printf format + // - warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 4 has type ‘char *’ + // - warning: unknown conversion type character ‘b’ in format + // + // These formats are in practise only used from vim script printf() + // function and never as literals in C code. + char *fmt_012p = "%012p"; + char *fmt_5S = "%5S"; + char *fmt_06b = "%06b"; + /* * Test trunc_string(). */ *************** *** 93,98 **** --- 111,259 ---- vim_free(s); } + /* + * Test vim_snprintf() with a focus on checking that truncation is + * correct when buffer is small, since it cannot be tested from + * vim scrip tests. Check that: + * - no buffer overflows happens (with valgrind or asan) + * - output string is always NUL terminated. + * + * Not all formats of vim_snprintf() are checked here. They are + * checked more exhaustively in Test_printf*() vim script tests. + */ + static void + test_vim_snprintf(void) + { + int n; + size_t bsize; + int bsize_int; + char *ptr = (char *)0x87654321; + + // Loop on various buffer sizes to make sure that truncation of + // vim_snprintf() is correct. + for (bsize = 0; bsize < 15; ++bsize) + { + bsize_int = (int)bsize - 1; + + // buf is the heap rather than in the stack + // so valgrind can detect buffer overflows if any. + // Use malloc() rather than alloc() as test checks with 0-size + // buffer and its content should then never be used. + char *buf = malloc(bsize); + + n = vim_snprintf(buf, bsize, "%d", 1234567); + assert(n == 7); + assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%ld", 1234567L); + assert(n == 7); + assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%9ld", 1234567L); + assert(n == 9); + assert(bsize == 0 || STRNCMP(buf, " 1234567", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%-9ld", 1234567L); + assert(n == 9); + assert(bsize == 0 || STRNCMP(buf, "1234567 ", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%x", 0xdeadbeef); + assert(n == 8); + assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, fmt_06b, 12); + assert(n == 6); + assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + #ifdef FEAT_FLOAT + n = vim_snprintf(buf, bsize, "%f", 1.234); + assert(n == 8); + assert(bsize == 0 || STRNCMP(buf, "1.234000", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%e", 1.234); + assert(n == 12); + assert(bsize == 0 || STRNCMP(buf, "1.234000e+00", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%f", 0.0/0.0); + assert(n == 3); + assert(bsize == 0 || STRNCMP(buf, "nan", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%f", 1.0/0.0); + assert(n == 3); + assert(bsize == 0 || STRNCMP(buf, "inf", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%f", -1.0/0.0); + assert(n == 4); + assert(bsize == 0 || STRNCMP(buf, "-inf", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%f", -0.0); + assert(n == 9); + assert(bsize == 0 || STRNCMP(buf, "-0.000000", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + #endif + + n = vim_snprintf(buf, bsize, "%s", "漢語"); + assert(n == 6); + assert(bsize == 0 || STRNCMP(buf, "漢語", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%8s", "漢語"); + assert(n == 8); + assert(bsize == 0 || STRNCMP(buf, " 漢語", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%-8s", "漢語"); + assert(n == 8); + assert(bsize == 0 || STRNCMP(buf, "漢語 ", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%.3s", "漢語"); + assert(n == 3); + assert(bsize == 0 || STRNCMP(buf, "漢", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, fmt_5S, "foo"); + assert(n == 5); + assert(bsize == 0 || STRNCMP(buf, " foo", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%%%%%%"); + assert(n == 3); + assert(bsize == 0 || STRNCMP(buf, "%%%", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, "%c%c", 1, 2); + assert(n == 2); + assert(bsize == 0 || STRNCMP(buf, "\x01\x02", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + // %p format is not tested in vim script tests Test_printf*() + // as it only makes sense in C code. + n = vim_snprintf(buf, bsize, "%p", ptr); + assert(n == 10); + assert(bsize == 0 || STRNCMP(buf, "0x87654321", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + n = vim_snprintf(buf, bsize, fmt_012p, ptr); + assert(n == 12); + assert(bsize == 0 || STRNCMP(buf, "0x0087654321", bsize_int) == 0); + assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0'); + + free(buf); + } + } + int main(int argc, char **argv) { *************** *** 104,113 **** --- 265,276 ---- set_option_value((char_u *)"encoding", 0, (char_u *)"utf-8", 0); init_chartab(); test_trunc_string(); + test_vim_snprintf(); set_option_value((char_u *)"encoding", 0, (char_u *)"latin1", 0); init_chartab(); test_trunc_string(); + test_vim_snprintf(); return 0; } *** ../vim-8.2.0065/src/version.c 2019-12-30 22:33:14.137783570 +0100 --- src/version.c 2019-12-31 19:16:55.166610195 +0100 *************** *** 744,745 **** --- 744,747 ---- { /* Add new patch number below this line */ + /**/ + 66, /**/ -- There are three kinds of people: Those who can count & those who can't. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///