| // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s |
| |
| #include "Inputs/system-header-simulator.h" |
| #include "Inputs/system-header-simulator-for-malloc.h" |
| #include "Inputs/system-header-simulator-for-valist.h" |
| |
| void clang_analyzer_eval(int); |
| void clang_analyzer_dump_int(int); |
| void clang_analyzer_dump_ptr(void*); |
| void clang_analyzer_warnIfReached(); |
| |
| void test_getline_null_lineptr() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char **buffer = NULL; |
| size_t n = 0; |
| getline(buffer, &n, F1); // expected-warning {{Line pointer might be NULL}} |
| fclose(F1); |
| } |
| |
| void test_getline_null_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| getline(&buffer, NULL, F1); // expected-warning {{Size pointer might be NULL}} |
| fclose(F1); |
| } |
| |
| void test_getline_null_buffer_size_gt0() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| size_t n = 8; |
| getline(&buffer, &n, F1); // ok since posix 2018 |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getline_null_buffer_size_gt0_2(size_t n) { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| if (n > 0) { |
| getline(&buffer, &n, F1); // ok since posix 2018 |
| } |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getline_null_buffer_unknown_size(size_t n) { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| |
| getline(&buffer, &n, F1); // ok |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getline_null_buffer_undef_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char *buffer = NULL; |
| size_t n; |
| |
| getline(&buffer, &n, F1); // ok since posix 2018 |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getline_buffer_size_0() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char *buffer = malloc(10); |
| size_t n = 0; |
| if (buffer != NULL) |
| getline(&buffer, &n, F1); // ok, the buffer is enough for 0 character |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getline_buffer_bad_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char *buffer = malloc(10); |
| size_t n = 100; |
| if (buffer != NULL) |
| getline(&buffer, &n, F1); // expected-warning {{The buffer from the first argument is smaller than the size specified by the second parameter}} |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getline_buffer_smaller_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char *buffer = malloc(100); |
| size_t n = 10; |
| if (buffer != NULL) |
| getline(&buffer, &n, F1); // ok, there is enough space for 10 characters |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getline_buffer_undef_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| |
| char *buffer = malloc(100); |
| size_t n; |
| if (buffer != NULL) |
| getline(&buffer, &n, F1); // expected-warning {{The buffer from the first argument is not NULL, but the size specified by the second parameter is undefined}} |
| fclose(F1); |
| free(buffer); |
| } |
| |
| |
| void test_getline_null_buffer() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| size_t n = 0; |
| ssize_t r = getline(&buffer, &n, F1); |
| // getline returns -1 on failure, number of char reads on success (>= 0) |
| if (r < -1) { |
| clang_analyzer_warnIfReached(); // must not happen |
| } else { |
| // The buffer could be allocated both on failure and success |
| clang_analyzer_dump_int(n); // expected-warning {{conj_$}} |
| clang_analyzer_dump_ptr(buffer); // expected-warning {{conj_$}} |
| } |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getdelim_null_size() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| getdelim(&buffer, NULL, ',', F1); // expected-warning {{Size pointer might be NULL}} |
| fclose(F1); |
| } |
| |
| void test_getdelim_null_buffer_size_gt0() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| size_t n = 8; |
| getdelim(&buffer, &n, ';', F1); // ok since posix 2018 |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getdelim_null_buffer_size_gt0_2(size_t n) { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| if (n > 0) { |
| getdelim(&buffer, &n, ' ', F1); // ok since posix 2018 |
| } |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getdelim_null_buffer_unknown_size(size_t n) { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| getdelim(&buffer, &n, '-', F1); // ok |
| fclose(F1); |
| free(buffer); |
| } |
| |
| void test_getdelim_null_buffer() { |
| FILE *F1 = tmpfile(); |
| if (!F1) |
| return; |
| char *buffer = NULL; |
| size_t n = 0; |
| ssize_t r = getdelim(&buffer, &n, '\r', F1); |
| // getdelim returns -1 on failure, number of char reads on success (>= 0) |
| if (r < -1) { |
| clang_analyzer_warnIfReached(); // must not happen |
| } |
| else { |
| // The buffer could be allocated both on failure and success |
| clang_analyzer_dump_int(n); // expected-warning {{conj_$}} |
| clang_analyzer_dump_ptr(buffer); // expected-warning {{conj_$}} |
| } |
| free(buffer); |
| fclose(F1); |
| } |
| |
| void test_getline_while() { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| char *line = NULL; |
| size_t len = 0; |
| ssize_t read; |
| |
| while ((read = getline(&line, &len, file)) != -1) { |
| printf("%s\n", line); |
| } |
| |
| free(line); |
| fclose(file); |
| } |
| |
| void test_getline_return_check() { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| char *line = NULL; |
| size_t len = 0; |
| ssize_t r = getline(&line, &len, file); |
| |
| if (r != -1) { |
| if (line[0] == '\0') {} // ok |
| } |
| free(line); |
| fclose(file); |
| } |
| |
| void test_getline_clear_eof() { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| size_t n = 10; |
| char *buffer = malloc(n); |
| ssize_t read = fread(buffer, n, 1, file); |
| if (feof(file)) { |
| clearerr(file); |
| getline(&buffer, &n, file); // ok |
| } |
| fclose(file); |
| free(buffer); |
| } |
| |
| void test_getline_not_null(char **buffer, size_t *size) { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| getline(buffer, size, file); |
| fclose(file); |
| |
| if (size == NULL || buffer == NULL) { |
| clang_analyzer_warnIfReached(); // must not happen |
| } |
| } |
| |
| void test_getline_size_constraint(size_t size) { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| size_t old_size = size; |
| char *buffer = malloc(10); |
| if (buffer != NULL) { |
| ssize_t r = getline(&buffer, &size, file); |
| if (r >= 0) { |
| // Since buffer has a size of 10, old_size must be less than or equal to 10. |
| // Otherwise, there would be UB. |
| clang_analyzer_eval(old_size <= 10); // expected-warning{{TRUE}} |
| } |
| } |
| fclose(file); |
| free(buffer); |
| } |
| |
| void test_getline_negative_buffer() { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| char *buffer = NULL; |
| size_t n = -1; |
| getline(&buffer, &n, file); // ok since posix 2018 |
| free(buffer); |
| fclose(file); |
| } |
| |
| void test_getline_negative_buffer_2(char *buffer) { |
| FILE *file = fopen("file.txt", "r"); |
| if (file == NULL) { |
| return; |
| } |
| |
| size_t n = -1; |
| (void)getline(&buffer, &n, file); // ok |
| free(buffer); |
| fclose(file); |
| } |