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