aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-2.c
blob: 60695b88e47b2d34d120d28ee6afe7732c573da7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* { dg-do compile } */
/* { dg-options "-Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */

/* When debugging, define LINE to the line number of the test case to exercise
   and avoid exercising any of the others.  The buffer and objsize macros
   below make use of LINE to avoid warnings for other lines.  */
#ifndef LINE
# define LINE 0
#endif

char buffer [256];
extern char *ptr;

#define buffer(size)							\
  (!LINE || __LINE__ == LINE ? buffer + sizeof buffer - size : ptr)

#define objsize(size)  (!LINE || __LINE__ == LINE ? size : __SIZE_MAX__ / 2)

typedef __SIZE_TYPE__ size_t;

#if !__cplusplus
typedef __WCHAR_TYPE__ wchar_t;
#endif

typedef unsigned char UChar;

#define T(size, fmt, ...)				\
  __builtin_sprintf (buffer (size), fmt, __VA_ARGS__)

__builtin_va_list va;

/* Exercise buffer overflow detection with const string arguments.  */

void test_s_const (void)
{
    /* Wide string literals are handled slightly differently than
       at level 1.  At level 1, each wide character is assumed to
       convert into a single byte.  At level 2, they are assumed
       to convert into at least one byte.  */
  T (0, "%ls",      L"");       /* { dg-warning "nul past the end" } */
  T (1, "%ls",      L"");
  T (1, "%ls",      L"\0");
  T (1, "%1ls",     L"");       /* { dg-warning "nul past the end" } */

  T (0, "%*ls",  0, L"");       /* { dg-warning "nul past the end" } */
  T (1, "%*ls",  0, L"");
  T (1, "%*ls",  0, L"\0");
  T (1, "%*ls",  1, L"");       /* { dg-warning "nul past the end" } */

  /* A wide character converts into between zero and MB_LEN_MAX bytes
     (although individual ASCII characters are assumed to convert into
     1 bt %lc so this could be made smarter.  */
  T (1, "%ls",      L"1");      /* { dg-warning "directive writing up to 6 bytes into a region of size 1" } */
  T (1, "%.0ls",    L"1");
  T (2, "%.0ls",    L"1");
  T (2, "%.1ls",    L"1");
  T (2, "%.2ls",    L"1");      /* { dg-warning "nul past the end" } */
  T (2, "%.3ls",    L"1");      /* { dg-warning "directive writing up to 3 bytes into a region of size 2" } */
  T (2, "%.7ls",    L"1");      /* { dg-warning "directive writing up to 6 bytes into a region of size 2" } */
  T (2, "%.2ls",    L"12");     /* { dg-warning "nul past the end" } */

  /* The "%.2ls" directive below will write at a minimum 1 byte (because
     L"1" is known and can be assumed to convert to at least one multibyte
     character), and at most 2 bytes because of the precision.  Since its
     output is explicitly bounded it is diagnosed.  */
  T (2, "%.2ls",    L"1");      /* { dg-warning "nul past the end" } */
  T (2, "%.*ls", 2, L"1");      /* { dg-warning "nul past the end" } */

  /* The following three are constrained by the precision to at most
     that many bytes of the converted wide string plus a terminating NUL.  */
  T (2, "%.0ls",    L"1");
  T (2, "%.1ls",    L"1");
  T (3, "%.2ls",    L"1");
  T (3, "%.2ls",    L"12");
  T (3, "%.3ls",    L"12");     /* { dg-warning "nul past the end" } */
  T (4, "%.3ls",    L"123");
  T (4, "%.4ls",    L"123");    /* { dg-warning "nul past the end" } */
  T (4, "%.5ls",    L"123");    /* { dg-warning "directive writing up to 5 bytes into a region of size 4" } */
  T (4, "%.6ls",    L"123");    /* { dg-warning "directive writing up to 6 bytes into a region of size 4" } */
}


struct Arrays {
  char a1 [1];
  char a2 [2];
  char a3 [3];
  char a4 [4];
  char a0 [0];
  char ax [];
};

/* Exercise buffer overflow detection with non-const string arguments.  */

void test_s_nonconst (int w, int p, const char *s, const wchar_t *ws,
		      struct Arrays *a)
{
  T (0, "%s",   s);             /* { dg-warning "into a region" } */
  T (1, "%s",   s);             /* { dg-warning "nul past the end" } */
  T (1, "%1s",  s);             /* { dg-warning "writing a terminating nul" } */
  T (1, "%.0s", s);
  T (1, "%.1s", s);             /* { dg-warning "may write a terminating nul" } */
  T (1, "%*s", 0, s);           /* { dg-warning "may write a terminating nul" } */
  T (1, "%*s", 1, s);           /* { dg-warning "writing a terminating nul" } */
  T (1, "%*s", 2, s);           /* { dg-warning "directive writing 2 or more bytes" } */
  T (1, "%*s", 3, s);           /* { dg-warning "directive writing 3 or more bytes" } */

  T (1, "%.*s", 1, s);          /* { dg-warning "may write a terminating nul" } */
  T (1, "%.*s", 2, s);          /* { dg-warning "writing up to 2 bytes" } */
  T (1, "%.*s", 3, s);          /* { dg-warning "writing up to 3 bytes" } */

  T (1, "%.0ls",  ws);
  T (1, "%.1ls",  ws);          /* { dg-warning "may write a terminating nul" } */
  T (1, "%ls",    ws);          /* { dg-warning "may write a terminating nul" } */

  /* Verify that the size of the array is used in lieu of its length.  */
  T (1, "%s", a->a1);

  /* In the following test, since the length of the strings isn't known,
     their type (the array) is used to bound the maximum length to 1,
     which means the "%s" directive would not overflow the buffer,
     but it would leave no room for the terminating nul.  */
  T (1, "%s", a->a2);           /* { dg-warning "may write a terminating nul" } */

  /* Unlike in the test above, since the length of the string is bounded
     by the array type to at most 2, the "%s" directive is diagnosed firts,
     preventing the diagnostic about the terminatinb nul.  */
  T (1, "%s", a->a3);           /* { dg-warning "directive writing up to 2 bytes" } */

  /* The length of a zero length array and flexible array member is
     unknown and at leve 2 assumed to be at least 1.  */
  T (1, "%s", a->a0);           /* { dg-warning "may write a terminating nul" } */
  T (1, "%s", a->ax);           /* { dg-warning "may write a terminating nul" } */

  T (2, "%s", a->a0);
  T (2, "%s", a->ax);
}

  /* Exercise buffer overflow detection with non-const integer arguments.  */

void test_hh_nonconst (int w, int p, int x, unsigned y)
{
  T (1, "%hhi",         x);     /* { dg-warning "into a region" } */
  T (2, "%hhi",         x);     /* { dg-warning "into a region" } */
  T (3, "%hhi",         x);     /* { dg-warning "into a region" } */
  T (4, "%hhi",         x);     /* { dg-warning "may write a terminating nul past the end of the destination" } */

  T (1, "%hhi",         y);     /* { dg-warning "between 1 and 4 bytes" } */
  T (2, "%hhi",         y);     /* { dg-warning "into a region" } */
  T (3, "%hhi",         y);     /* { dg-warning "into a region" } */
  T (4, "%hhi",         y);     /* { dg-warning "may write a terminating nul past the end of the destination" } */

  /* Negative precision is treated as if none were specified.  */
  T (1, "%.*hhi",   -1, x);     /* { dg-warning "between 1 and 4 bytes" } */
  T (2, "%.*hhi",   -1, x);     /* { dg-warning "into a region" } */
  T (3, "%.*hhi",   -1, x);     /* { dg-warning "into a region" } */
  T (4, "%.*hhi",   -1, x);     /* { dg-warning "may write a terminating nul past the end of the destination" } */

  /* Zero precision means that zero argument formats as no bytes unless
     length or flags make it otherwise.  */
  T (1, "%.*hhi",    0, x);     /* { dg-warning "writing up to 4 bytes" } */
  T (2, "%.*hhi",    0, x);     /* { dg-warning "writing up to 4 bytes" } */
  T (3, "%.*hhi",    0, x);     /* { dg-warning "writing up to 4 bytes" } */
  T (4, "%.*hhi",    0, x);     /* { dg-warning "may write a terminating nul past the end of the destination" } */

  T (1, "%.*hhi",    0, y);     /* { dg-warning "writing up to 4 bytes" } */
  T (2, "%.*hhi",    0, y);     /* { dg-warning "writing up to 4 bytes" } */
  T (3, "%.*hhi",    0, y);     /* { dg-warning "writing up to 4 bytes" } */
  T (4, "%.*hhi",    0, y);     /* { dg-warning "may write a terminating nul past the end of the destination" } */

  T (1, "%#.*hhi",    0, y);    /* { dg-warning "writing up to 4 bytes" } */
  /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
  T (1, "%+.*hhi",    0, y);    /* { dg-warning "between 1 and 4 bytes" } */
  T (1, "%-.*hhi",    0, y);    /* { dg-warning "writing up to 4 bytes" } */
  T (1, "% .*hhi",    0, y);    /* { dg-warning "between 1 and 4 bytes" } */

  T (1, "%#.*hhi",    1, y);    /* { dg-warning "between 1 and 4 bytes" } */
  /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
  T (1, "%+.*hhi",    1, y);    /* { dg-warning "between 2 and 4 bytes" } */
  T (1, "%-.*hhi",    1, y);    /* { dg-warning "between 1 and 4 bytes" } */
  T (1, "% .*hhi",    1, y);    /* { dg-warning "between 2 and 4 bytes" } */

  T (1, "%#.*hhi",    p, y);    /* { dg-warning "writing up to \[0-9\]+ bytes" } */
  /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
  T (1, "%+.*hhi",    p, y);    /* { dg-warning "writing 1 or more bytes|writing between 1 and \[0-9\]+ bytes" } */
  T (1, "%-.*hhi",    p, y);    /* { dg-warning "writing up to \[0-9\]+ bytes" } */
  T (1, "% .*hhi",    p, y);    /* { dg-warning "writing between 1 and \[0-9\]+ bytes|writing 1 or more bytes" } */

  T (1, "%#.*hhu",    0, y);    /* { dg-warning "writing up to 3 bytes" } */
  /* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
  T (1, "%+.*hhu",    0, y);    /* { dg-warning "writing up to 3 bytes" } */
  /* { dg-warning ".\\+. flag used" "-Wformat" { target *-*-* } .-1 } */
  T (1, "%-.*hhu",    0, y);    /* { dg-warning "writing up to 3 bytes" } */
  T (1, "% .*hhu",    0, y);    /* { dg-warning "writing up to 3 bytes" } */
  /* { dg-warning ". . flag used" "-Wformat" { target *-*-* } .-1 } */
}

void test_h_nonconst (int x)
{
  extern UChar uc;

  T (1, "%hi",         uc);     /* { dg-warning "into a region" } */
  T (2, "%hi",         uc);     /* { dg-warning "into a region" } */
  /* Formatting an 8-bit unsigned char as a signed short (or any other
     type with greater precision) can write at most 3 characters.  */
  T (3, "%hi",         uc);     /* { dg-warning "terminating nul past" } */
  T (4, "%hi",         uc);

  /* Verify that the same thing works when the int argument is cast
     to unsigned char.  */
  T (1, "%hi",   (UChar)x);     /* { dg-warning "into a region" } */
  T (2, "%hi",   (UChar)x);     /* { dg-warning "into a region" } */
  T (3, "%hi",   (UChar)x);     /* { dg-warning "may write a terminating nul past the end of the destination" } */
  T (4, "%hi",   (UChar)x);
}

void test_i_nonconst (int x)
{
  extern UChar uc;

  T (1, "%i",          uc);     /* { dg-warning "into a region" } */
  T (2, "%i",          uc);     /* { dg-warning "into a region" } */
  T (3, "%i",          uc);     /* { dg-warning "terminating nul past" } */
  T (4, "%i",          uc);

  T (1, "%i",    (UChar)x);     /* { dg-warning "into a region" } */
  T (2, "%i",    (UChar)x);     /* { dg-warning "into a region" } */
  T (3, "%i",    (UChar)x);     /* { dg-warning "terminating nul past" } */
  T (4, "%i",    (UChar)x);

  /* Verify the same thing using a bit-field.  */
  extern struct {
    unsigned int  b1: 1;
    unsigned int  b2: 2;
    unsigned int  b3: 3;
    unsigned int  b4: 4;
	     int sb4: 4;
    unsigned int  b5: 5;
    unsigned int  b6: 6;
    unsigned int  b7: 7;
    unsigned int  b8: 8;
  } bf, abf[], *pbf;

  T (1, "%i",       bf.b1);     /* { dg-warning "nul past the end" } */
  T (1, "%i",  abf [x].b1);     /* { dg-warning "nul past the end" } */
  T (1, "%i",     pbf->b1);     /* { dg-warning "nul past the end" } */
  /* A one bit bit-field can only be formatted as '0' or '1'.  Similarly,
     two- and three-bit bit-fields can only be formatted as a single
     decimal digit.  */
  T (2, "%i",       bf.b1);
  T (2, "%i",  abf [x].b1);
  T (2, "%i",     pbf->b1);
  T (2, "%i",       bf.b2);
  T (2, "%i",  abf [x].b2);
  T (2, "%i",     pbf->b2);
  T (2, "%i",       bf.b3);
  T (2, "%i",  abf [x].b3);
  T (2, "%i",     pbf->b3);
  /* A four-bit bit-field can be formatted as either one or two digits.  */
  T (2, "%i",       bf.b4);     /* { dg-warning "nul past the end" } */
  T (2, "%i",  abf [x].b4);     /* { dg-warning "nul past the end" } */
  T (2, "%i",     pbf->b4);     /* { dg-warning "nul past the end" } */

  T (3, "%i",       bf.b4);
  T (3, "%i",     pbf->b4);
  T (3, "%i",       bf.b5);
  T (3, "%i",     pbf->b5);
  T (3, "%i",       bf.b6);
  T (3, "%i",     pbf->b6);
  T (3, "%i",       bf.b7);     /* { dg-warning "nul past the end" } */
  T (3, "%i",     pbf->b7);     /* { dg-warning "nul past the end" } */

  T (1, "%i",       bf.b8);     /* { dg-warning "into a region" } */
  T (2, "%i",       bf.b8);     /* { dg-warning "into a region" } */
  /* Formatting an 8-bit unsigned char as a signed short (or any other
     type with greater precision) int can write at most 3 characters.  */
  T (3, "%i",       bf.b8);     /* { dg-warning "terminating nul past" } */
  T (4, "%i",       bf.b8);

  T (1, "%i",       bf.b8);     /* { dg-warning "into a region" } */
  T (2, "%i",       bf.b8);     /* { dg-warning "into a region" } */
  T (3, "%i",       bf.b8);     /* { dg-warning "terminating nul past" } */

  T (2, "%i",      bf.sb4);     /* { dg-warning "terminating nul past" } */
  T (3, "%i",      bf.sb4);
  T (4, "%i",      bf.sb4);
}