aboutsummaryrefslogtreecommitdiff
path: root/test/Sema/format-strings-scanf.c
blob: d3a03adf61959f358b86be44e631a01fb15364b5 (plain) (blame)
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
// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral %s

// Test that -Wformat=0 works:
// RUN: %clang_cc1 -fsyntax-only -Werror -Wformat=0 %s

#include <stdarg.h>
typedef __typeof(sizeof(int)) size_t;
typedef struct _FILE FILE;
typedef __WCHAR_TYPE__ wchar_t;

int fscanf(FILE * restrict, const char * restrict, ...) ;
int scanf(const char * restrict, ...) ;
int sscanf(const char * restrict, const char * restrict, ...) ;
int my_scanf(const char * restrict, ...) __attribute__((__format__(__scanf__, 1, 2)));

int vscanf(const char * restrict, va_list);
int vfscanf(FILE * restrict, const char * restrict, va_list);
int vsscanf(const char * restrict, const char * restrict, va_list);

void test(const char *s, int *i) {
  scanf(s, i); // expected-warning{{ormat string is not a string literal}}
  scanf("%0d", i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%d%[asdfasdfd", i, s); // expected-warning{{no closing ']' for '%[' in scanf format string}}

  unsigned short s_x;
  scanf ("%" "hu" "\n", &s_x); // no-warning
  scanf("%y", i); // expected-warning{{invalid conversion specifier 'y'}}
  scanf("%%"); // no-warning
  scanf("%%%1$d", i); // no-warning
  scanf("%1$d%%", i); // no-warning
  scanf("%d", i, i); // expected-warning{{data argument not used by format string}}
  scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
  scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
  scanf("%*d%1$d", i); // no-warning

  scanf("%s", (char*)0); // no-warning
  scanf("%s", (volatile char*)0); // no-warning
  scanf("%s", (signed char*)0); // no-warning
  scanf("%s", (unsigned char*)0); // no-warning
  scanf("%hhu", (signed char*)0); // no-warning
}

void bad_length_modifiers(char *s, void *p, wchar_t *ws, long double *ld) {
  scanf("%hhs", "foo"); // expected-warning{{length modifier 'hh' results in undefined behavior or no effect with 's' conversion specifier}}
  scanf("%1$zp", &p); // expected-warning{{length modifier 'z' results in undefined behavior or no effect with 'p' conversion specifier}}
  scanf("%ls", ws); // no-warning
  scanf("%#.2Lf", ld); // expected-warning{{invalid conversion specifier '#'}}
}

// Test that the scanf call site is where the warning is attached.  If the
// format string is somewhere else, point to it in a note.
void pr9751() {
  int *i;
  char str[100];
  const char kFormat1[] = "%00d"; // expected-note{{format string is defined here}}}
  scanf(kFormat1, i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}}
  const char kFormat2[] = "%["; // expected-note{{format string is defined here}}}
  scanf(kFormat2, str); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  scanf("%[", str); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  const char kFormat3[] = "%hu"; // expected-note{{format string is defined here}}}
  scanf(kFormat3, &i); // expected-warning {{format specifies type 'unsigned short *' but the argument}}
  const char kFormat4[] = "%lp"; // expected-note{{format string is defined here}}}
  scanf(kFormat4, &i); // expected-warning {{length modifier 'l' results in undefined behavior or no effect with 'p' conversion specifier}}
}

void test_variants(int *i, const char *s, ...) {
  FILE *f = 0;
  char buf[100];

  fscanf(f, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
  sscanf(buf, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
  my_scanf("%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}

  va_list ap;
  va_start(ap, s);

  vscanf("%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  vfscanf(f, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  vsscanf(buf, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
}

void test_scanlist(int *ip, char *sp, wchar_t *ls) {
  scanf("%[abc]", ip); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}}
  scanf("%h[abc]", sp); // expected-warning{{length modifier 'h' results in undefined behavior or no effect with '[' conversion specifier}}
  scanf("%l[xyx]", ls); // no-warning
  scanf("%ll[xyx]", ls); // expected-warning {{length modifier 'll' results in undefined behavior or no effect with '[' conversion specifier}}

  // PR19559
  scanf("%[]% ]", sp); // no-warning
  scanf("%[^]% ]", sp); // no-warning
  scanf("%[a^]% ]", sp); // expected-warning {{invalid conversion specifier ' '}}
}

void test_alloc_extension(char **sp, wchar_t **lsp, float *fp) {
  /* Make sure "%a" gets parsed as a conversion specifier for float,
   * even when followed by an 's', 'S' or '[', which would cause it to be
   * parsed as a length modifier in C90. */
  scanf("%as", sp); // expected-warning{{format specifies type 'float *' but the argument has type 'char **'}}
  scanf("%aS", lsp); // expected-warning{{format specifies type 'float *' but the argument has type 'wchar_t **'}}
  scanf("%a[bcd]", sp); // expected-warning{{format specifies type 'float *' but the argument has type 'char **'}}

  // Test that the 'm' length modifier is only allowed with s, S, c, C or [.
  // TODO: Warn that 'm' is an extension.
  scanf("%ms", sp); // No warning.
  scanf("%mS", lsp); // No warning.
  scanf("%mc", sp); // No warning.
  scanf("%mC", lsp); // No warning.
  scanf("%m[abc]", sp); // No warning.
  scanf("%md", sp); // expected-warning{{length modifier 'm' results in undefined behavior or no effect with 'd' conversion specifier}}

  // Test argument type check for the 'm' length modifier.
  scanf("%ms", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
  scanf("%mS", fp); // expected-warning-re{{format specifies type 'wchar_t **' (aka '{{[^']+}}') but the argument has type 'float *'}}
  scanf("%mc", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
  scanf("%mC", fp); // expected-warning-re{{format specifies type 'wchar_t **' (aka '{{[^']+}}') but the argument has type 'float *'}}
  scanf("%m[abc]", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
}

void test_quad(int *x, long long *llx) {
  scanf("%qd", x); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
  scanf("%qd", llx); // no-warning
}

void test_writeback(int *x) {
  scanf("%n", (void*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'void *'}}
  scanf("%n %c", x, x); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}}

  scanf("%hhn", (signed char*)0); // no-warning
  scanf("%hhn", (char*)0); // no-warning
  scanf("%hhn", (unsigned char*)0); // no-warning
  scanf("%hhn", (int*)0); // expected-warning{{format specifies type 'signed char *' but the argument has type 'int *'}}

  scanf("%hn", (short*)0); // no-warning
  scanf("%hn", (unsigned short*)0); // no-warning
  scanf("%hn", (int*)0); // expected-warning{{format specifies type 'short *' but the argument has type 'int *'}}

  scanf("%n", (int*)0); // no-warning
  scanf("%n", (unsigned int*)0); // no-warning
  scanf("%n", (char*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}}

  scanf("%ln", (long*)0); // no-warning
  scanf("%ln", (unsigned long*)0); // no-warning
  scanf("%ln", (int*)0); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}

  scanf("%lln", (long long*)0); // no-warning
  scanf("%lln", (unsigned long long*)0); // no-warning
  scanf("%lln", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}

  scanf("%qn", (long long*)0); // no-warning
  scanf("%qn", (unsigned long long*)0); // no-warning
  scanf("%qn", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}

}

void test_qualifiers(const int *cip, volatile int* vip,
                     const char *ccp, volatile char* vcp,
                     const volatile int *cvip) {
  scanf("%d", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
  scanf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
  scanf("%s", ccp); // expected-warning{{format specifies type 'char *' but the argument has type 'const char *'}}
  scanf("%d", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}}

  scanf("%d", vip); // No warning.
  scanf("%n", vip); // No warning.
  scanf("%c", vcp); // No warning.

  typedef int* ip_t;
  typedef const int* cip_t;
  scanf("%d", (ip_t)0); // No warning.
  scanf("%d", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}}
}