changeset 19011:61d94accf16f v8.2.0066

patch 8.2.0066: some corners of vim_snprintf() are not tested Commit: https://github.com/vim/vim/commit/d2c946bacfedf4e506a4c6771758beddb87df3af Author: Bram Moolenaar <Bram@vim.org> Date: Tue Dec 31 19:24:51 2019 +0100 patch 8.2.0066: some corners of vim_snprintf() are not tested Problem: Some corners of vim_snprintf() are not tested. Solution: Add a test in C. (Dominique Pelle, closes https://github.com/vim/vim/issues/5422)
author Bram Moolenaar <Bram@vim.org>
date Tue, 31 Dec 2019 19:30:04 +0100
parents 18dd6ecc211f
children 5eb079827641
files src/message_test.c src/version.c
diffstat 2 files changed, 165 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/message_test.c
+++ b/src/message_test.c
@@ -22,6 +22,24 @@
 // 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,6 +111,149 @@ test_trunc_string(void)
     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,10 +265,12 @@ main(int argc, char **argv)
     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;
 }
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    66,
+/**/
     65,
 /**/
     64,