1 // Check the basic reporting/warning and the application of constraints. 2 // RUN: %clang_analyze_cc1 %s \ 3 // RUN: -analyzer-checker=core \ 4 // RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctions \ 5 // RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \ 6 // RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \ 7 // RUN: -analyzer-checker=debug.ExprInspection \ 8 // RUN: -triple x86_64-unknown-linux-gnu \ 9 // RUN: -verify=report 10 11 // Check the bugpath related to the reports. 12 // RUN: %clang_analyze_cc1 %s \ 13 // RUN: -analyzer-checker=core \ 14 // RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctions \ 15 // RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \ 16 // RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \ 17 // RUN: -analyzer-checker=debug.ExprInspection \ 18 // RUN: -triple x86_64-unknown-linux-gnu \ 19 // RUN: -analyzer-output=text \ 20 // RUN: -verify=bugpath 21 22 #include "Inputs/std-c-library-functions-POSIX.h" 23 24 void clang_analyzer_eval(int); 25 void clang_analyzer_warnIfReached(); 26 27 int glob; 28 29 void test_alnum_concrete(int v) { 30 int ret = isalnum(256); // \ 31 // report-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ 32 // bugpath-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ 33 // bugpath-note{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} 34 (void)ret; 35 } 36 37 void test_alnum_symbolic(int x) { 38 int ret = isalnum(x); // \ 39 // bugpath-note{{Assuming the character is non-alphanumeric}} 40 (void)ret; 41 42 clang_analyzer_eval(EOF <= x && x <= 255); // \ 43 // report-warning{{TRUE}} \ 44 // bugpath-warning{{TRUE}} \ 45 // bugpath-note{{TRUE}} \ 46 // bugpath-note{{Left side of '&&' is true}} \ 47 // bugpath-note{{'x' is <= 255}} 48 } 49 50 void test_alnum_symbolic2(int x) { 51 if (x > 255) { // \ 52 // bugpath-note{{Assuming 'x' is > 255}} \ 53 // bugpath-note{{Taking true branch}} 54 55 int ret = isalnum(x); // \ 56 // report-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ 57 // bugpath-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ 58 // bugpath-note{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} 59 60 (void)ret; 61 } 62 } 63 64 void test_toupper_concrete(int v) { 65 int ret = toupper(256); // \ 66 // report-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ 67 // bugpath-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ 68 // bugpath-note{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} 69 (void)ret; 70 } 71 72 void test_toupper_symbolic(int x) { 73 int ret = toupper(x); 74 (void)ret; 75 76 clang_analyzer_eval(EOF <= x && x <= 255); // \ 77 // report-warning{{TRUE}} \ 78 // bugpath-warning{{TRUE}} \ 79 // bugpath-note{{TRUE}} \ 80 // bugpath-note{{Left side of '&&' is true}} \ 81 // bugpath-note{{'x' is <= 255}} 82 } 83 84 void test_toupper_symbolic2(int x) { 85 if (x > 255) { // \ 86 // bugpath-note{{Assuming 'x' is > 255}} \ 87 // bugpath-note{{Taking true branch}} 88 89 int ret = toupper(x); // \ 90 // report-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ 91 // bugpath-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ 92 // bugpath-note{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} 93 94 (void)ret; 95 } 96 } 97 98 void test_tolower_concrete(int v) { 99 int ret = tolower(256); // \ 100 // report-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ 101 // bugpath-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ 102 // bugpath-note{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} 103 (void)ret; 104 } 105 106 void test_tolower_symbolic(int x) { 107 int ret = tolower(x); 108 (void)ret; 109 110 clang_analyzer_eval(EOF <= x && x <= 255); // \ 111 // report-warning{{TRUE}} \ 112 // bugpath-warning{{TRUE}} \ 113 // bugpath-note{{TRUE}} \ 114 // bugpath-note{{Left side of '&&' is true}} \ 115 // bugpath-note{{'x' is <= 255}} 116 } 117 118 void test_tolower_symbolic2(int x) { 119 if (x > 255) { // \ 120 // bugpath-note{{Assuming 'x' is > 255}} \ 121 // bugpath-note{{Taking true branch}} 122 123 int ret = tolower(x); // \ 124 // report-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ 125 // bugpath-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ 126 // bugpath-note{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} 127 128 (void)ret; 129 } 130 } 131 132 void test_toascii_concrete(int v) { 133 int ret = toascii(256); // \ 134 // report-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ 135 // bugpath-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ 136 // bugpath-note{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} 137 (void)ret; 138 } 139 140 void test_toascii_symbolic(int x) { 141 int ret = toascii(x); 142 (void)ret; 143 144 clang_analyzer_eval(EOF <= x && x <= 255); // \ 145 // report-warning{{TRUE}} \ 146 // bugpath-warning{{TRUE}} \ 147 // bugpath-note{{TRUE}} \ 148 // bugpath-note{{Left side of '&&' is true}} \ 149 // bugpath-note{{'x' is <= 255}} 150 } 151 152 void test_toascii_symbolic2(int x) { 153 if (x > 255) { // \ 154 // bugpath-note{{Assuming 'x' is > 255}} \ 155 // bugpath-note{{Taking true branch}} 156 157 int ret = toascii(x); // \ 158 // report-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ 159 // bugpath-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ 160 // bugpath-note{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} 161 162 (void)ret; 163 } 164 } 165 166 void test_notnull_concrete(FILE *fp) { 167 fread(0, sizeof(int), 10, fp); // \ 168 // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 169 // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 170 // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} 171 } 172 void test_notnull_symbolic(FILE *fp, int *buf) { 173 fread(buf, sizeof(int), 10, fp); // \ 174 // bugpath-note{{'fread' fails}} 175 clang_analyzer_eval(buf != 0); // \ 176 // report-warning{{TRUE}} \ 177 // bugpath-warning{{TRUE}} \ 178 // bugpath-note{{TRUE}} \ 179 // bugpath-note{{'buf' is not equal to null}} 180 } 181 void test_notnull_symbolic2(FILE *fp, int *buf) { 182 if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ 183 // bugpath-note{{Taking true branch}} 184 fread(buf, sizeof(int), 10, fp); // \ 185 // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 186 // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 187 // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} 188 } 189 void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { 190 if (fp) // \ 191 // bugpath-note{{Assuming 'fp' is null}} \ 192 // bugpath-note{{Taking false branch}} 193 return; 194 size_t ret = fread(buf, size, n, fp); // \ 195 // report-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 196 // bugpath-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 197 // bugpath-note{{The 4th argument to 'fread' is NULL but should not be NULL}} 198 clang_analyzer_warnIfReached(); // not reachable 199 } 200 201 // This is one test case for the ARR38-C SEI-CERT rule. 202 void ARR38_C_F(FILE *file) { 203 enum { BUFFER_SIZE = 1024 }; 204 wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}} 205 206 const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}} 207 const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}} 208 209 // The 3rd parameter should be the number of elements to read, not 210 // the size in bytes. 211 fread(wbuf, size, nitems, file); // \ 212 // report-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \ 213 // bugpath-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \ 214 // bugpath-note{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} 215 } 216 217 int __two_constrained_args(int, int); 218 void test_constraints_on_multiple_args(int x, int y) { 219 // State split should not happen here. I.e. x == 1 should not be evaluated 220 // FALSE. 221 __two_constrained_args(x, y); 222 //NOTE! Because of the second `clang_analyzer_eval` call we have two bug 223 clang_analyzer_eval(x == 1); // \ 224 // report-warning{{TRUE}} \ 225 // bugpath-warning{{TRUE}} \ 226 // bugpath-note{{TRUE}} 227 clang_analyzer_eval(y == 1); // \ 228 // report-warning{{TRUE}} \ 229 // bugpath-warning{{TRUE}} \ 230 // bugpath-note{{TRUE}} 231 } 232 233 int __arg_constrained_twice(int); 234 void test_multiple_constraints_on_same_arg(int x) { 235 __arg_constrained_twice(x); 236 clang_analyzer_eval(x < 1 || x > 2); // \ 237 // report-warning{{TRUE}} \ 238 // bugpath-warning{{TRUE}} \ 239 // bugpath-note{{TRUE}} \ 240 // bugpath-note{{Assuming 'x' is < 1}} \ 241 // bugpath-note{{Left side of '||' is true}} 242 } 243 244 int __variadic(void *stream, const char *format, ...); 245 void test_arg_constraint_on_variadic_fun(void) { 246 __variadic(0, "%d%d", 1, 2); // \ 247 // report-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 248 // bugpath-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 249 // bugpath-note{{The 1st argument to '__variadic' is NULL but should not be NULL}} 250 } 251 252 int __buf_size_arg_constraint(const void *, size_t); 253 void test_buf_size_concrete(void) { 254 char buf[3]; // bugpath-note{{'buf' initialized here}} 255 __buf_size_arg_constraint(buf, 4); // \ 256 // report-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \ 257 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \ 258 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} 259 } 260 void test_buf_size_symbolic(int s) { 261 char buf[3]; 262 __buf_size_arg_constraint(buf, s); 263 clang_analyzer_eval(s <= 3); // \ 264 // report-warning{{TRUE}} \ 265 // bugpath-warning{{TRUE}} \ 266 // bugpath-note{{TRUE}} \ 267 // bugpath-note{{'s' is <= 3}} 268 } 269 void test_buf_size_symbolic_and_offset(int s) { 270 char buf[3]; 271 __buf_size_arg_constraint(buf + 1, s); 272 clang_analyzer_eval(s <= 2); // \ 273 // report-warning{{TRUE}} \ 274 // bugpath-warning{{TRUE}} \ 275 // bugpath-note{{TRUE}} \ 276 // bugpath-note{{'s' is <= 2}} 277 } 278 279 int __buf_size_arg_constraint_mul(const void *, size_t, size_t); 280 void test_buf_size_concrete_with_multiplication(void) { 281 short buf[3]; // bugpath-note{{'buf' initialized here}} 282 __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \ 283 // report-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \ 284 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \ 285 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} 286 } 287 void test_buf_size_symbolic_with_multiplication(size_t s) { 288 short buf[3]; 289 __buf_size_arg_constraint_mul(buf, s, sizeof(short)); 290 clang_analyzer_eval(s * sizeof(short) <= 6); // \ 291 // report-warning{{TRUE}} \ 292 // bugpath-warning{{TRUE}} \ 293 // bugpath-note{{TRUE}} 294 } 295 void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) { 296 short buf[3]; 297 __buf_size_arg_constraint_mul(buf + 1, s, sizeof(short)); 298 clang_analyzer_eval(s * sizeof(short) <= 4); // \ 299 // report-warning{{TRUE}} \ 300 // bugpath-warning{{TRUE}} \ 301 // bugpath-note{{TRUE}} 302 } 303 304 // The minimum buffer size for this function is set to 10. 305 int __buf_size_arg_constraint_concrete(const void *); 306 void test_min_buf_size(void) { 307 char buf[9];// bugpath-note{{'buf' initialized here}} 308 __buf_size_arg_constraint_concrete(buf); // \ 309 // report-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \ 310 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \ 311 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} 312 } 313 314 void test_file_fd_at_functions() { 315 (void)linkat(-22, "from", AT_FDCWD, "to", 0); // \ 316 // report-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 317 // bugpath-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 318 // bugpath-note{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} 319 320 // no warning for these functions if the AT_FDCWD value is used 321 (void)linkat(AT_FDCWD, "from", AT_FDCWD, "to", 0); 322 (void)faccessat(AT_FDCWD, "path", 0, 0); 323 (void)symlinkat("oldpath", AT_FDCWD, "newpath"); 324 (void)mkdirat(AT_FDCWD, "path", 0); 325 (void)mknodat(AT_FDCWD, "path", 0, 0); 326 (void)fchmodat(AT_FDCWD, "path", 0, 0); 327 (void)fchownat(AT_FDCWD, "path", 0, 0, 0); 328 (void)linkat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath", 0); 329 (void)unlinkat(AT_FDCWD, "newpath", 0); 330 struct stat St; 331 (void)fstatat(AT_FDCWD, "newpath", &St, 0); 332 char Buf[10]; 333 (void)readlinkat(AT_FDCWD, "newpath", Buf, 10); 334 (void)renameat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath"); 335 } 336