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