Skip to content

Commit d2c946b

Browse files
committed
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 #5422)
1 parent dba7c85 commit d2c946b

2 files changed

Lines changed: 165 additions & 0 deletions

File tree

src/message_test.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@
2222
// static.
2323
#include "message.c"
2424

25+
#ifndef MIN
26+
# define MIN(x,y) ((x) < (y) ? (x) : (y))
27+
#endif
28+
29+
// These formats are not standard in C printf() function.
30+
// Use a global variable rather than a literal format to disable
31+
// -Wformat compiler warnings:
32+
//
33+
// - warning: '0' flag used with ‘%p’ gnu_printf format
34+
// - warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 4 has type ‘char *’
35+
// - warning: unknown conversion type character ‘b’ in format
36+
//
37+
// These formats are in practise only used from vim script printf()
38+
// function and never as literals in C code.
39+
char *fmt_012p = "%012p";
40+
char *fmt_5S = "%5S";
41+
char *fmt_06b = "%06b";
42+
2543
/*
2644
* Test trunc_string().
2745
*/
@@ -93,6 +111,149 @@ test_trunc_string(void)
93111
vim_free(s);
94112
}
95113

114+
/*
115+
* Test vim_snprintf() with a focus on checking that truncation is
116+
* correct when buffer is small, since it cannot be tested from
117+
* vim scrip tests. Check that:
118+
* - no buffer overflows happens (with valgrind or asan)
119+
* - output string is always NUL terminated.
120+
*
121+
* Not all formats of vim_snprintf() are checked here. They are
122+
* checked more exhaustively in Test_printf*() vim script tests.
123+
*/
124+
static void
125+
test_vim_snprintf(void)
126+
{
127+
int n;
128+
size_t bsize;
129+
int bsize_int;
130+
char *ptr = (char *)0x87654321;
131+
132+
// Loop on various buffer sizes to make sure that truncation of
133+
// vim_snprintf() is correct.
134+
for (bsize = 0; bsize < 15; ++bsize)
135+
{
136+
bsize_int = (int)bsize - 1;
137+
138+
// buf is the heap rather than in the stack
139+
// so valgrind can detect buffer overflows if any.
140+
// Use malloc() rather than alloc() as test checks with 0-size
141+
// buffer and its content should then never be used.
142+
char *buf = malloc(bsize);
143+
144+
n = vim_snprintf(buf, bsize, "%d", 1234567);
145+
assert(n == 7);
146+
assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
147+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
148+
149+
n = vim_snprintf(buf, bsize, "%ld", 1234567L);
150+
assert(n == 7);
151+
assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
152+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
153+
154+
n = vim_snprintf(buf, bsize, "%9ld", 1234567L);
155+
assert(n == 9);
156+
assert(bsize == 0 || STRNCMP(buf, " 1234567", bsize_int) == 0);
157+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
158+
159+
n = vim_snprintf(buf, bsize, "%-9ld", 1234567L);
160+
assert(n == 9);
161+
assert(bsize == 0 || STRNCMP(buf, "1234567 ", bsize_int) == 0);
162+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
163+
164+
n = vim_snprintf(buf, bsize, "%x", 0xdeadbeef);
165+
assert(n == 8);
166+
assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0);
167+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
168+
169+
n = vim_snprintf(buf, bsize, fmt_06b, 12);
170+
assert(n == 6);
171+
assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
172+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
173+
174+
#ifdef FEAT_FLOAT
175+
n = vim_snprintf(buf, bsize, "%f", 1.234);
176+
assert(n == 8);
177+
assert(bsize == 0 || STRNCMP(buf, "1.234000", bsize_int) == 0);
178+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
179+
180+
n = vim_snprintf(buf, bsize, "%e", 1.234);
181+
assert(n == 12);
182+
assert(bsize == 0 || STRNCMP(buf, "1.234000e+00", bsize_int) == 0);
183+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
184+
185+
n = vim_snprintf(buf, bsize, "%f", 0.0/0.0);
186+
assert(n == 3);
187+
assert(bsize == 0 || STRNCMP(buf, "nan", bsize_int) == 0);
188+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
189+
190+
n = vim_snprintf(buf, bsize, "%f", 1.0/0.0);
191+
assert(n == 3);
192+
assert(bsize == 0 || STRNCMP(buf, "inf", bsize_int) == 0);
193+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
194+
195+
n = vim_snprintf(buf, bsize, "%f", -1.0/0.0);
196+
assert(n == 4);
197+
assert(bsize == 0 || STRNCMP(buf, "-inf", bsize_int) == 0);
198+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
199+
200+
n = vim_snprintf(buf, bsize, "%f", -0.0);
201+
assert(n == 9);
202+
assert(bsize == 0 || STRNCMP(buf, "-0.000000", bsize_int) == 0);
203+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
204+
#endif
205+
206+
n = vim_snprintf(buf, bsize, "%s", "漢語");
207+
assert(n == 6);
208+
assert(bsize == 0 || STRNCMP(buf, "漢語", bsize_int) == 0);
209+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
210+
211+
n = vim_snprintf(buf, bsize, "%8s", "漢語");
212+
assert(n == 8);
213+
assert(bsize == 0 || STRNCMP(buf, " 漢語", bsize_int) == 0);
214+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
215+
216+
n = vim_snprintf(buf, bsize, "%-8s", "漢語");
217+
assert(n == 8);
218+
assert(bsize == 0 || STRNCMP(buf, "漢語 ", bsize_int) == 0);
219+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
220+
221+
n = vim_snprintf(buf, bsize, "%.3s", "漢語");
222+
assert(n == 3);
223+
assert(bsize == 0 || STRNCMP(buf, "漢", bsize_int) == 0);
224+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
225+
226+
n = vim_snprintf(buf, bsize, fmt_5S, "foo");
227+
assert(n == 5);
228+
assert(bsize == 0 || STRNCMP(buf, " foo", bsize_int) == 0);
229+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
230+
231+
n = vim_snprintf(buf, bsize, "%%%%%%");
232+
assert(n == 3);
233+
assert(bsize == 0 || STRNCMP(buf, "%%%", bsize_int) == 0);
234+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
235+
236+
n = vim_snprintf(buf, bsize, "%c%c", 1, 2);
237+
assert(n == 2);
238+
assert(bsize == 0 || STRNCMP(buf, "\x01\x02", bsize_int) == 0);
239+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
240+
241+
// %p format is not tested in vim script tests Test_printf*()
242+
// as it only makes sense in C code.
243+
n = vim_snprintf(buf, bsize, "%p", ptr);
244+
assert(n == 10);
245+
assert(bsize == 0 || STRNCMP(buf, "0x87654321", bsize_int) == 0);
246+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
247+
248+
n = vim_snprintf(buf, bsize, fmt_012p, ptr);
249+
assert(n == 12);
250+
assert(bsize == 0 || STRNCMP(buf, "0x0087654321", bsize_int) == 0);
251+
assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
252+
253+
free(buf);
254+
}
255+
}
256+
96257
int
97258
main(int argc, char **argv)
98259
{
@@ -104,10 +265,12 @@ main(int argc, char **argv)
104265
set_option_value((char_u *)"encoding", 0, (char_u *)"utf-8", 0);
105266
init_chartab();
106267
test_trunc_string();
268+
test_vim_snprintf();
107269

108270
set_option_value((char_u *)"encoding", 0, (char_u *)"latin1", 0);
109271
init_chartab();
110272
test_trunc_string();
273+
test_vim_snprintf();
111274

112275
return 0;
113276
}

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ static char *(features[]) =
742742

743743
static int included_patches[] =
744744
{ /* Add new patch number below this line */
745+
/**/
746+
66,
745747
/**/
746748
65,
747749
/**/

0 commit comments

Comments
 (0)