1 /* PR tree-optimization/87096 - "Optimised" snprintf is not POSIX conformant
2 Verify that calls to snprintf with size in excess of INT_MAX are not
3 treated as successful.
4 It would be valid for GCC to fold some of these calls to a negative
5 value provided it also arranged to set errno to EOVERFLOW. If that
6 is ever implemented this test will need to be adjusted.
7 { dg-do compile }
8 { dg-options "-O2 -Wall -fdump-tree-optimized -ftrack-macro-expansion=0" } */
9
10 #include "../range.h"
11
12 typedef __builtin_va_list va_list;
13
14 extern int snprintf (char*, size_t, const char*, ...);
15 extern int vsnprintf (char*, size_t, const char*, va_list);
16
17 #define CAT(x, y) x ## y
18 #define CONCAT(x, y) CAT (x, y)
19 #define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
20
21 #define FAIL(name) do { \
22 extern void FAILNAME (name) (void); \
23 FAILNAME (name)(); \
24 } while (0)
25
26 /* Macro to emit a call to function named
27 call_in_true_branch_not_eliminated_on_line_NNN()
28 for each expression that's expected to fold to false but that
29 GCC does not fold. The dg-final scan-tree-dump-time directive
30 at the bottom of the test verifies that no such call appears
31 in output. */
32 #define ELIM(expr) \
33 if ((expr)) FAIL (in_true_branch_not_eliminated); else (void)0
34
35 /* Macro to emit a call to a function named
36 call_made_in_{true,false}_branch_on_line_NNN()
37 for each call that's expected to be retained. The dg-final
38 scan-tree-dump-time directive at the bottom of the test verifies
39 that the expected number of both kinds of calls appears in output
40 (a pair for each line with the invocation of the KEEP() macro. */
41 #define KEEP(expr) \
42 if (expr) \
43 FAIL (made_in_true_branch); \
44 else \
45 FAIL (made_in_false_branch)
46
47 extern void sink (int, ...);
48 #define sink(...) sink (0, __VA_ARGS__)
49
50 #define WARN(N, expr) \
51 do { \
52 char a[N]; \
53 expr; \
54 sink (a); \
55 } while (0)
56
57
58 static const size_t imax = __INT_MAX__;
59 static const size_t imaxp1 = imax + 1;
60
61 #if __PTRDIFF_MAX__ == __INT_MAX__
62 /* Make the test pass on ILP32 the same way it does on LP64. */
63 static const size_t dmax = __PTRDIFF_MAX__ + (size_t)1;
64 #else
65 static const size_t dmax = __PTRDIFF_MAX__;
66 #endif
67 static const size_t dmaxp1 = dmax + 1;
68
69 static const size_t szmax = __SIZE_MAX__;
70 static const size_t szmaxm1 = __SIZE_MAX__ - 1;
71
72
test_size_cst(char ** d)73 void test_size_cst (char **d)
74 {
75 ELIM (0 > snprintf (*d++, imax, "%s", ""));
76
77 KEEP (0 > snprintf (*d++, imaxp1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
78
79 KEEP (0 > snprintf (*d++, dmax, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
80 KEEP (0 > snprintf (*d++, dmaxp1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
81 KEEP (0 > snprintf (*d++, szmaxm1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
82 KEEP (0 > snprintf (*d++, szmax, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
83 }
84
85
test_size_cst_va(char ** d,va_list va)86 void test_size_cst_va (char **d, va_list va)
87 {
88 ELIM (0 > vsnprintf (*d++, imax, " ", va));
89
90 KEEP (0 > vsnprintf (*d++, imaxp1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
91
92 KEEP (0 > vsnprintf (*d++, dmax, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
93 KEEP (0 > vsnprintf (*d++, dmaxp1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
94 KEEP (0 > vsnprintf (*d++, szmaxm1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
95 KEEP (0 > vsnprintf (*d++, szmax, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
96 }
97
98
test_size_range(char ** d)99 void test_size_range (char **d)
100 {
101 size_t r = UR (imax - 1, imax);
102 ELIM (0 > snprintf (*d++, r, "%s", ""));
103
104 r = UR (imax, imax + 1);
105 KEEP (0 > snprintf (*d++, r, "%s", ""));
106
107 r = UR (imaxp1, imaxp1 + 1);
108 KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "specified bound range \\\[\[0-9\]+, \[0-9\]+] exceeds .INT_MAX." } */
109
110 r = UR (dmax, dmaxp1);
111 KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
112
113 r = UR (dmaxp1, dmaxp1 + 1);
114 KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
115
116 r = UR (szmaxm1, szmax);
117 KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
118 }
119
120
test_size_range_va(char ** d,va_list va)121 void test_size_range_va (char **d, va_list va)
122 {
123 size_t r = UR (imax - 1, imax);
124 ELIM (0 > vsnprintf (*d++, r, " ", va));
125
126 r = UR (imax, imax + 1);
127 KEEP (0 > vsnprintf (*d++, r, " ", va));
128
129 r = UR (imaxp1, imaxp1 + 1);
130 KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "specified bound range \\\[\[0-9\]+, \[0-9\]+] exceeds .INT_MAX." } */
131
132 r = UR (dmax, dmaxp1);
133 KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
134
135 r = UR (dmaxp1, dmaxp1 + 1);
136 KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
137
138 r = UR (szmaxm1, szmax);
139 KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
140 }
141
142
test_size_varying(char ** d,size_t n)143 void test_size_varying (char **d, size_t n)
144 {
145 KEEP (0 > snprintf (*d++, n, "%s", ""));
146
147 n += 1;
148 KEEP (0 > snprintf (*d++, n, "%s", ""));
149 }
150
151
test_size_varying_va(char ** d,size_t n,va_list va)152 void test_size_varying_va (char **d, size_t n, va_list va)
153 {
154 KEEP (0 > vsnprintf (*d++, n, " ", va));
155
156 n += 1;
157 KEEP (0 > vsnprintf (*d++, n, " ", va));
158 }
159
160 /* { dg-final { scan-tree-dump-times " = snprintf" 12 "optimized"} }
161 { dg-final { scan-tree-dump-times " = vsnprintf" 12 "optimized"} } */
162