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 clang_analyzer_eval(buf != 0); // \ 175 // report-warning{{TRUE}} \ 176 // bugpath-warning{{TRUE}} \ 177 // bugpath-note{{TRUE}} \ 178 // bugpath-note{{'buf' is not equal to null}} 179 } 180 void test_notnull_symbolic2(FILE *fp, int *buf) { 181 if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ 182 // bugpath-note{{Taking true branch}} 183 fread(buf, sizeof(int), 10, fp); // \ 184 // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 185 // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 186 // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} 187 } 188 void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { 189 if (fp) // \ 190 // bugpath-note{{Assuming 'fp' is null}} \ 191 // bugpath-note{{Taking false branch}} 192 return; 193 size_t ret = fread(buf, size, n, fp); // \ 194 // report-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 195 // bugpath-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 196 // bugpath-note{{The 4th argument to 'fread' is NULL but should not be NULL}} 197 clang_analyzer_warnIfReached(); // not reachable 198 } 199 200 // This is one test case for the ARR38-C SEI-CERT rule. 201 void ARR38_C_F(FILE *file) { 202 enum { BUFFER_SIZE = 1024 }; 203 wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}} 204 205 const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}} 206 const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}} 207 208 // The 3rd parameter should be the number of elements to read, not 209 // the size in bytes. 210 fread(wbuf, size, nitems, file); // \ 211 // 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)}} \ 212 // 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)}} \ 213 // 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)}} 214 } 215 216 int __two_constrained_args(int, int); 217 void test_constraints_on_multiple_args(int x, int y) { 218 // State split should not happen here. I.e. x == 1 should not be evaluated 219 // FALSE. 220 __two_constrained_args(x, y); 221 //NOTE! Because of the second `clang_analyzer_eval` call we have two bug 222 clang_analyzer_eval(x == 1); // \ 223 // report-warning{{TRUE}} \ 224 // bugpath-warning{{TRUE}} \ 225 // bugpath-note{{TRUE}} 226 clang_analyzer_eval(y == 1); // \ 227 // report-warning{{TRUE}} \ 228 // bugpath-warning{{TRUE}} \ 229 // bugpath-note{{TRUE}} 230 } 231 232 int __arg_constrained_twice(int); 233 void test_multiple_constraints_on_same_arg(int x) { 234 __arg_constrained_twice(x); 235 clang_analyzer_eval(x < 1 || x > 2); // \ 236 // report-warning{{TRUE}} \ 237 // bugpath-warning{{TRUE}} \ 238 // bugpath-note{{TRUE}} \ 239 // bugpath-note{{Assuming 'x' is < 1}} \ 240 // bugpath-note{{Left side of '||' is true}} 241 } 242 243 int __variadic(void *stream, const char *format, ...); 244 void test_arg_constraint_on_variadic_fun(void) { 245 __variadic(0, "%d%d", 1, 2); // \ 246 // report-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 247 // bugpath-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 248 // bugpath-note{{The 1st argument to '__variadic' is NULL but should not be NULL}} 249 } 250 251 int __buf_size_arg_constraint(const void *, size_t); 252 void test_buf_size_concrete(void) { 253 char buf[3]; // bugpath-note{{'buf' initialized here}} 254 __buf_size_arg_constraint(buf, 4); // \ 255 // 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)}} \ 256 // 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)}} \ 257 // 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)}} 258 } 259 void test_buf_size_symbolic(int s) { 260 char buf[3]; 261 __buf_size_arg_constraint(buf, s); 262 clang_analyzer_eval(s <= 3); // \ 263 // report-warning{{TRUE}} \ 264 // bugpath-warning{{TRUE}} \ 265 // bugpath-note{{TRUE}} \ 266 // bugpath-note{{'s' is <= 3}} 267 } 268 void test_buf_size_symbolic_and_offset(int s) { 269 char buf[3]; 270 __buf_size_arg_constraint(buf + 1, s); 271 clang_analyzer_eval(s <= 2); // \ 272 // report-warning{{TRUE}} \ 273 // bugpath-warning{{TRUE}} \ 274 // bugpath-note{{TRUE}} \ 275 // bugpath-note{{'s' is <= 2}} 276 } 277 278 int __buf_size_arg_constraint_mul(const void *, size_t, size_t); 279 void test_buf_size_concrete_with_multiplication(void) { 280 short buf[3]; // bugpath-note{{'buf' initialized here}} 281 __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \ 282 // 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)}} \ 283 // 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)}} \ 284 // 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)}} 285 } 286 void test_buf_size_symbolic_with_multiplication(size_t s) { 287 short buf[3]; 288 __buf_size_arg_constraint_mul(buf, s, sizeof(short)); 289 clang_analyzer_eval(s * sizeof(short) <= 6); // \ 290 // report-warning{{TRUE}} \ 291 // bugpath-warning{{TRUE}} \ 292 // bugpath-note{{TRUE}} 293 } 294 void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) { 295 short buf[3]; 296 __buf_size_arg_constraint_mul(buf + 1, s, sizeof(short)); 297 clang_analyzer_eval(s * sizeof(short) <= 4); // \ 298 // report-warning{{TRUE}} \ 299 // bugpath-warning{{TRUE}} \ 300 // bugpath-note{{TRUE}} 301 } 302 303 // The minimum buffer size for this function is set to 10. 304 int __buf_size_arg_constraint_concrete(const void *); 305 void test_min_buf_size(void) { 306 char buf[9];// bugpath-note{{'buf' initialized here}} 307 __buf_size_arg_constraint_concrete(buf); // \ 308 // 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}} \ 309 // 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}} \ 310 // 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}} 311 } 312 313 void test_file_fd_at_functions() { 314 (void)linkat(-22, "from", AT_FDCWD, "to", 0); // \ 315 // report-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 316 // bugpath-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 317 // bugpath-note{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} 318 319 // no warning for these functions if the AT_FDCWD value is used 320 (void)linkat(AT_FDCWD, "from", AT_FDCWD, "to", 0); 321 (void)faccessat(AT_FDCWD, "path", 0, 0); 322 (void)symlinkat("oldpath", AT_FDCWD, "newpath"); 323 (void)mkdirat(AT_FDCWD, "path", 0); 324 (void)mknodat(AT_FDCWD, "path", 0, 0); 325 (void)fchmodat(AT_FDCWD, "path", 0, 0); 326 (void)fchownat(AT_FDCWD, "path", 0, 0, 0); 327 (void)linkat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath", 0); 328 (void)unlinkat(AT_FDCWD, "newpath", 0); 329 struct stat St; 330 (void)fstatat(AT_FDCWD, "newpath", &St, 0); 331 char Buf[10]; 332 (void)readlinkat(AT_FDCWD, "newpath", Buf, 10); 333 (void)renameat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath"); 334 } 335