476 lines · c
1// RUN: %clang_analyze_cc1 -verify %s \2// RUN: -triple x86_64-linux-gnu \3// RUN: -analyzer-checker=core,unix.Stream,optin.taint \4// RUN: -analyzer-checker=debug.ExprInspection5 6#include "Inputs/system-header-simulator-for-simple-stream.h"7 8#define EOF (-1)9 10void clang_analyzer_dump(int);11void clang_analyzer_dump_char(char);12void clang_analyzer_isTainted(int);13void clang_analyzer_warnIfReached(void);14 15// A stream is only tracked by StreamChecker if it results from a call to "fopen".16// Otherwise, there is no specific modelling of "fread".17void untracked_stream(FILE *fp) {18 char c;19 if (1 == fread(&c, 1, 1, fp)) {20 char p = c; // Unknown value but not garbage and not modeled by checker.21 } else {22 char p = c; // Possibly indeterminate value but not modeled by checker.23 }24}25 26void fgetc_props_taint(void) {27 FILE *fp = fopen("/home/test", "rb+");28 if (fp) {29 int c = fgetc(fp); // c is tainted.30 if (c != EOF) {31 clang_analyzer_isTainted(c); // expected-warning{{YES}}32 }33 fclose(fp);34 }35}36 37void fread_props_taint(void) {38 FILE *fp = fopen("/home/test", "rb+");39 if (fp) {40 char buffer[10];41 int c = fread(buffer, 1, 10, fp); // c is tainted.42 if (c != 10) {43 // If the read failed, then the number of bytes successfully read should be tainted.44 clang_analyzer_isTainted(c); // expected-warning{{YES}}45 }46 fclose(fp);47 }48}49 50void read_one_byte1(void) {51 FILE *fp = fopen("/home/test", "rb+");52 if (fp) {53 char c;54 if (1 == fread(&c, 1, 1, fp)) {55 char p = c; // Unknown value but not garbage.56 clang_analyzer_isTainted(p); // expected-warning{{YES}}57 } else {58 char p = c; // Possibly indeterminate value but not modeled by checker.59 clang_analyzer_isTainted(p); // expected-warning{{YES}}60 }61 fclose(fp);62 }63}64 65void read_one_byte2(char *buffer) {66 FILE *fp = fopen("/home/test", "rb+");67 if (fp) {68 if (1 == fread(buffer, 1, 1, fp)) {69 char p = buffer[0]; // Unknown value but not garbage.70 clang_analyzer_isTainted(p); // expected-warning{{YES}}71 } else {72 char p = buffer[0]; // Possibly indeterminate value but not modeled by checker.73 clang_analyzer_isTainted(p); // expected-warning{{YES}}74 }75 fclose(fp);76 }77}78 79void read_one_byte3(char *buffer) {80 buffer[1] = 10;81 FILE *fp = fopen("/home/test", "rb+");82 if (fp) {83 // buffer[1] is not mutated by fread and remains not tainted.84 fread(buffer, 1, 1, fp);85 char p = buffer[1];86 clang_analyzer_isTainted(p); // expected-warning{{NO}}87 clang_analyzer_dump(buffer[1]); // expected-warning{{10 S32b}}88 fclose(fp);89 }90}91 92void read_many_bytes(char *buffer) {93 FILE *fp = fopen("/home/test", "rb+");94 if (fp) {95 if (42 == fread(buffer, 1, 42, fp)) {96 char p = buffer[0]; // Unknown value but not garbage.97 clang_analyzer_isTainted(p); // expected-warning{{YES}}98 } else {99 char p = buffer[0]; // Possibly indeterminate value but not modeled.100 clang_analyzer_isTainted(p); // expected-warning{{YES}}101 }102 fclose(fp);103 }104}105 106void random_access_read1(int index) {107 FILE *fp = fopen("/home/test", "rb+");108 if (fp) {109 long c[4];110 int success = 2 == fread(c + 1, sizeof(long), 2, fp);111 112 switch (index) {113 case 0:114 // c[0] is not mutated by fread.115 if (success) {116 char p = c[0]; // expected-warning {{Assigned value is uninitialized}} We kept the first byte intact.117 } else {118 char p = c[0]; // expected-warning {{Assigned value is uninitialized}} We kept the first byte intact.119 }120 break;121 122 case 1:123 if (success) {124 // Unknown value but not garbage.125 clang_analyzer_isTainted(c[1]); // expected-warning {{YES}}126 clang_analyzer_dump(c[1]); // expected-warning {{conj_}}127 } else {128 // Possibly indeterminate value but not modeled.129 clang_analyzer_isTainted(c[1]); // expected-warning {{YES}}130 clang_analyzer_dump(c[1]); // expected-warning {{conj_}}131 }132 break;133 134 case 2:135 if (success) {136 long p = c[2]; // Unknown value but not garbage.137 // FIXME: Taint analysis only marks the first byte of a memory region. See getPointeeOf in GenericTaintChecker.cpp.138 clang_analyzer_isTainted(c[2]); // expected-warning {{NO}}139 clang_analyzer_dump(c[2]); // expected-warning {{conj_}}140 } else {141 // Possibly indeterminate value but not modeled.142 clang_analyzer_isTainted(c[2]); // expected-warning {{NO}} // FIXME: See above.143 clang_analyzer_dump(c[2]); // expected-warning {{conj_}}144 }145 break;146 147 case 3:148 // c[3] is not mutated by fread.149 if (success) {150 long p = c[3]; // expected-warning {{Assigned value is uninitialized}}151 } else {152 long p = c[3]; // expected-warning {{Assigned value is uninitialized}}153 }154 break;155 }156 157 fclose(fp);158 }159}160 161void random_access_read2(int b) {162 FILE *fp = fopen("/home/test", "rb+");163 if (fp) {164 int buffer[10];165 int *ptr = buffer + 2;166 if (5 == fread(ptr - 1, sizeof(int), 5, fp)) {167 if (b) {168 int p = buffer[1]; // Unknown value but not garbage.169 clang_analyzer_isTainted(p); // expected-warning {{YES}}170 clang_analyzer_dump(p); // expected-warning {{conj_}}171 } else {172 int p = buffer[0]; // expected-warning {{Assigned value is uninitialized}}173 }174 } else {175 int p = buffer[0]; // expected-warning {{Assigned value is uninitialized}}176 }177 fclose(fp);178 }179}180 181void random_access_read_symbolic_count(size_t count) {182 // Cover a case that used to crash (symbolic count).183 if (count > 2)184 return;185 186 FILE *fp = fopen("/home/test", "rb+");187 if (fp) {188 long c[4];189 fread(c + 1, sizeof(long), count, fp);190 191 // c[0] and c[3] are never mutated by fread, but because "count" is a symbolic value, the checker doesn't know that.192 long p = c[0];193 clang_analyzer_isTainted(p); // expected-warning {{NO}}194 clang_analyzer_dump(p); // expected-warning {{derived_}}195 196 p = c[3];197 clang_analyzer_isTainted(p); // expected-warning {{NO}}198 clang_analyzer_dump(p); // expected-warning {{derived_}}199 200 p = c[1];201 clang_analyzer_isTainted(p); // expected-warning {{YES}}202 clang_analyzer_dump(p); // expected-warning {{derived_}}203 204 fclose(fp);205 }206}207 208void dynamic_random_access_read(int startIndex) {209 FILE *fp = fopen("/home/test", "rb+");210 if (fp) {211 long buffer[10];212 // Cannot reason about index.213 size_t res = fread(buffer + startIndex, sizeof(long), 5, fp);214 long *p = &buffer[startIndex];215 long v = 0;216 217 // If all 5 elements were successfully read, then all 5 elements should be tainted and considered initialized.218 if (5 == res) {219 // FIXME: These should be tainted.220 clang_analyzer_isTainted((v = p[0])); // expected-warning {{NO}}221 clang_analyzer_isTainted((v = p[1])); // expected-warning {{NO}}222 clang_analyzer_isTainted((v = p[2])); // expected-warning {{NO}}223 clang_analyzer_isTainted((v = p[3])); // expected-warning {{NO}}224 clang_analyzer_isTainted((v = p[4])); // expected-warning {{NO}}225 clang_analyzer_dump((v = p[0])); // expected-warning {{conj_}} ok226 clang_analyzer_dump((v = p[1])); // expected-warning {{conj_}} ok227 clang_analyzer_dump((v = p[2])); // expected-warning {{conj_}} ok228 clang_analyzer_dump((v = p[3])); // expected-warning {{conj_}} ok229 clang_analyzer_dump((v = p[4])); // expected-warning {{conj_}} ok230 clang_analyzer_dump((v = p[5])); // expected-warning {{conj_}} FIXME: This should raise an uninit read.231 } else if (res == 4) {232 // If only the first 4 elements were successfully read,233 // then only the first 4 elements should be tainted and considered initialized.234 // FIXME: These should be tainted.235 clang_analyzer_isTainted((v = p[0])); // expected-warning {{NO}}236 clang_analyzer_isTainted((v = p[1])); // expected-warning {{NO}}237 clang_analyzer_isTainted((v = p[2])); // expected-warning {{NO}}238 clang_analyzer_isTainted((v = p[3])); // expected-warning {{NO}}239 clang_analyzer_dump((v = p[0])); // expected-warning {{conj_}} ok240 clang_analyzer_dump((v = p[1])); // expected-warning {{conj_}} ok241 clang_analyzer_dump((v = p[2])); // expected-warning {{conj_}} ok242 clang_analyzer_dump((v = p[3])); // expected-warning {{conj_}} ok243 clang_analyzer_dump((v = p[4])); // expected-warning {{conj_}} FIXME: This should raise an uninit read.244 } else {245 // Neither 5, or 4 elements were successfully read, so we must have read from 0 up to 3 elements.246 // FIXME: These should be tainted.247 clang_analyzer_isTainted((v = p[0])); // expected-warning {{NO}}248 clang_analyzer_isTainted((v = p[1])); // expected-warning {{NO}}249 clang_analyzer_isTainted((v = p[2])); // expected-warning {{NO}}250 clang_analyzer_dump((v = p[0])); // expected-warning {{conj_}} ok251 clang_analyzer_dump((v = p[1])); // expected-warning {{conj_}} ok252 clang_analyzer_dump((v = p[2])); // expected-warning {{conj_}} ok253 clang_analyzer_dump((v = p[3])); // expected-warning {{conj_}} FIXME: This should raise an uninit read.254 }255 fclose(fp);256 }257}258 259struct S {260 int a;261 long b;262};263 264void compound_read1(void) {265 FILE *fp = fopen("/home/test", "rb+");266 if (fp) {267 struct S s; // s.a is not touched by fread.268 if (1 == fread(&s.b, sizeof(s.b), 1, fp)) {269 long p = s.b;270 clang_analyzer_isTainted(p); // expected-warning {{YES}}271 clang_analyzer_dump(p); // expected-warning {{conj_}}272 } else {273 long p = s.b;274 clang_analyzer_isTainted(p); // expected-warning {{YES}}275 clang_analyzer_dump(p); // expected-warning {{conj_}}276 }277 fclose(fp);278 }279}280 281void compound_read2(void) {282 FILE *fp = fopen("/home/test", "rb+");283 if (fp) {284 struct S s; // s.a is not touched by fread.285 if (1 == fread(&s.b, sizeof(s.b), 1, fp)) {286 long p = s.a; // expected-warning {{Assigned value is uninitialized}}287 } else {288 long p = s.a; // expected-warning {{Assigned value is uninitialized}}289 }290 fclose(fp);291 }292}293 294void var_read(void) {295 FILE *fp = fopen("/home/test", "rb+");296 if (fp) {297 int a, b; // 'a' is not touched by fread.298 if (1 == fread(&b, sizeof(b), 1, fp)) {299 long p = a; // expected-warning{{Assigned value is uninitialized}}300 } else {301 long p = a; // expected-warning{{Assigned value is uninitialized}}302 }303 fclose(fp);304 }305}306 307// When reading a lot of data, invalidating all elements is too time-consuming.308// Instead, the knowledge of the whole array is lost.309#define MaxInvalidatedElementRegion 64 // See StreamChecker::evalFreadFwrite in StreamChecker.cpp.310#define PastMaxComplexity MaxInvalidatedElementRegion + 1311void test_large_read(void) {312 int buffer[PastMaxComplexity + 1];313 buffer[PastMaxComplexity] = 42;314 FILE *fp = fopen("/home/test", "rb+");315 if (fp) {316 if (buffer[PastMaxComplexity] != 42) {317 clang_analyzer_warnIfReached(); // Unreachable.318 }319 if (1 == fread(buffer, sizeof(int), PastMaxComplexity, fp)) {320 if (buffer[PastMaxComplexity] != 42) {321 clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}322 }323 }324 fclose(fp);325 }326}327 328void test_small_read(void) {329 int buffer[10];330 buffer[5] = 42;331 FILE *fp = fopen("/home/test", "rb+");332 if (fp) {333 clang_analyzer_dump(buffer[5]); // expected-warning{{42 S32b}}334 if (1 == fread(buffer, sizeof(int), 5, fp)) {335 clang_analyzer_dump(buffer[5]); // expected-warning{{42 S32b}}336 }337 fclose(fp);338 }339}340 341void test_partial_elements_read(void) {342 clang_analyzer_dump(sizeof(int)); // expected-warning {{4 S32b}}343 344 int buffer[100];345 buffer[0] = 1;346 buffer[1] = 2;347 buffer[2] = 3;348 buffer[3] = 4;349 buffer[4] = 5;350 buffer[5] = 6;351 FILE *fp = fopen("/home/test", "rb+");352 if (fp) {353 // 3*5: 15 bytes read; which is not exactly 4 integers, but we still invalidate the first 4 ints.354 if (5 == fread(buffer + 1, 3, 5, fp)) {355 clang_analyzer_dump(buffer[0]); // expected-warning{{1 S32b}}356 clang_analyzer_dump(buffer[1]); // expected-warning{{conj_}}357 clang_analyzer_dump(buffer[2]); // expected-warning{{conj_}}358 clang_analyzer_dump(buffer[3]); // expected-warning{{conj_}}359 clang_analyzer_dump(buffer[4]); // expected-warning{{conj_}}360 clang_analyzer_dump(buffer[5]); // expected-warning{{6 S32b}}361 362 char *c = (char*)buffer;363 clang_analyzer_dump(c[4+12]); // expected-warning{{conj_}} 16th byte of buffer, which is the beginning of the 4th 'int' in the buffer.364 365 // FIXME: The store should have returned a partial binding for the 17th byte of the buffer, which is the 2nd byte of the previous int.366 // This byte should have been initialized by the 'fread' earlier. However, the Store lies to us and says it's uninitialized.367 clang_analyzer_dump(c[4+13]); // expected-warning{{1st function call argument is an uninitialized value}} should be initialized.368 clang_analyzer_dump(c[4+16]); // This should be the first byte that 'fread' leaves uninitialized. This should raise the uninit read diag.369 } else {370 clang_analyzer_dump(buffer[0]); // expected-warning{{1 S32b}} ok371 clang_analyzer_dump(buffer[1]); // expected-warning{{conj_}} ok372 clang_analyzer_dump(buffer[2]); // expected-warning{{conj_}} ok373 clang_analyzer_dump(buffer[3]); // expected-warning{{conj_}} ok374 clang_analyzer_dump(buffer[4]); // expected-warning{{conj_}} ok, but an uninit warning would be also fine.375 clang_analyzer_dump(buffer[5]); // expected-warning{{6 S32b}} ok376 clang_analyzer_dump(buffer[6]); // expected-warning{{1st function call argument is an uninitialized value}} ok377 }378 fclose(fp);379 }380}381 382void test_whole_elements_read(void) {383 clang_analyzer_dump(sizeof(int)); // expected-warning {{4 S32b}}384 385 int buffer[100];386 buffer[0] = 1;387 buffer[15] = 2;388 buffer[16] = 3;389 FILE *fp = fopen("/home/test", "rb+");390 if (fp) {391 // 3*20: 60 bytes read; which is basically 15 integers.392 if (20 == fread(buffer + 1, 3, 20, fp)) {393 clang_analyzer_dump(buffer[0]); // expected-warning{{1 S32b}}394 clang_analyzer_dump(buffer[15]); // expected-warning{{conj_}}395 clang_analyzer_dump(buffer[16]); // expected-warning{{3 S32b}}396 clang_analyzer_dump(buffer[17]); // expected-warning{{1st function call argument is an uninitialized value}}397 } else {398 clang_analyzer_dump(buffer[0]); // expected-warning{{1 S32b}}399 clang_analyzer_dump(buffer[15]); // expected-warning{{conj_}}400 clang_analyzer_dump(buffer[16]); // expected-warning{{3 S32b}}401 clang_analyzer_dump(buffer[17]); // expected-warning{{1st function call argument is an uninitialized value}}402 }403 fclose(fp);404 }405}406 407void test_unaligned_start_read(void) {408 clang_analyzer_dump(sizeof(int)); // expected-warning {{4 S32b}}409 410 int buffer[100];411 buffer[0] = 3;412 buffer[1] = 4;413 buffer[2] = 5;414 char *asChar = (char*)buffer;415 416 FILE *fp = fopen("/home/test", "rb+");417 if (fp) {418 // We have an 'int' binding at offset 0 of value 3.419 // We read 4 bytes at byte offset: 1,2,3,4.420 if (4 == fread(asChar + 1, 1, 4, fp)) {421 clang_analyzer_dump(buffer[0]); // expected-warning{{3 S32b}} FIXME: The int binding should have been partially overwritten by the read call. This definitely should not be 3.422 clang_analyzer_dump(buffer[1]); // expected-warning{{conj_}}423 clang_analyzer_dump(buffer[2]); // expected-warning{{5 S32b}}424 425 clang_analyzer_dump_char(asChar[0]); // expected-warning{{3 S8b}} This is technically true assuming x86 (little-endian) architecture.426 clang_analyzer_dump_char(asChar[1]); // expected-warning{{conj_}} 1427 clang_analyzer_dump_char(asChar[2]); // expected-warning{{conj_}} 2428 clang_analyzer_dump_char(asChar[3]); // expected-warning{{conj_}} 3429 clang_analyzer_dump_char(asChar[4]); // expected-warning{{conj_}} 4430 clang_analyzer_dump_char(asChar[5]); // expected-warning{{1st function call argument is an uninitialized value}}431 } else {432 clang_analyzer_dump(buffer[0]); // expected-warning{{3 S32b}} FIXME: The int binding should have been partially overwritten by the read call. This definitely should not be 3.433 clang_analyzer_dump(buffer[1]); // expected-warning{{conj_}}434 clang_analyzer_dump(buffer[2]); // expected-warning{{5 S32b}}435 436 clang_analyzer_dump_char(asChar[0]); // expected-warning{{3 S8b}} This is technically true assuming x86 (little-endian) architecture.437 clang_analyzer_dump_char(asChar[1]); // expected-warning{{conj_}} 1438 clang_analyzer_dump_char(asChar[2]); // expected-warning{{conj_}} 2439 clang_analyzer_dump_char(asChar[3]); // expected-warning{{conj_}} 3440 clang_analyzer_dump_char(asChar[4]); // expected-warning{{conj_}} 4441 clang_analyzer_dump_char(asChar[5]); // expected-warning{{1st function call argument is an uninitialized value}}442 }443 fclose(fp);444 }445}446 447void no_crash_if_count_is_negative(long l, long r, unsigned char *buffer) {448 FILE *fp = fopen("path", "r");449 if (fp) {450 if (l * r == -1) {451 fread(buffer, 1, l * r, fp); // no-crash452 }453 fclose(fp);454 }455}456 457void no_crash_if_size_is_negative(long l, long r, unsigned char *buffer) {458 FILE *fp = fopen("path", "r");459 if (fp) {460 if (l * r == -1) {461 fread(buffer, l * r, 1, fp); // no-crash462 }463 fclose(fp);464 }465}466 467void no_crash_if_size_and_count_are_negative(long l, long r, unsigned char *buffer) {468 FILE *fp = fopen("path", "r");469 if (fp) {470 if (l * r == -1) {471 fread(buffer, l * r, l * r, fp); // no-crash472 }473 fclose(fp);474 }475}476