Skip to content

Commit

Permalink
[clang][analyzer] Improve test and documentation in cstring NotNullTe…
Browse files Browse the repository at this point in the history
…rminated checker (llvm#112019)

CStringChecker has a sub-checker alpha.unix.cstring.NotNullTerminated
which checks for invalid objects passed to string functions. The checker
and its name are not exact and more functions could be checked, this
change only adds some tests and improves documentation.
  • Loading branch information
balazske authored Oct 16, 2024
1 parent 4ddea29 commit 9df8d8d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 10 deletions.
17 changes: 14 additions & 3 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3371,12 +3371,23 @@ Checks for overlap in two buffer arguments. Applies to: ``memcpy, mempcpy, wmem
alpha.unix.cstring.NotNullTerminated (C)
""""""""""""""""""""""""""""""""""""""""
Check for arguments which are not null-terminated strings; applies to: ``strlen, strnlen, strcpy, strncpy, strcat, strncat, wcslen, wcsnlen``.
Check for arguments which are not null-terminated strings;
applies to the ``strlen``, ``strcpy``, ``strcat``, ``strcmp`` family of functions.
Only very fundamental cases are detected where the passed memory block is
absolutely different from a null-terminated string. This checker does not
find if a memory buffer is passed where the terminating zero character
is missing.
.. code-block:: c
void test() {
int y = strlen((char *)&test); // warn
void test1() {
int l = strlen((char *)&test); // warn
}
void test2() {
label:
int l = strlen((char *)&&label); // warn
}
.. _alpha-unix-cstring-OutOfBounds:
Expand Down
46 changes: 40 additions & 6 deletions clang/test/Analysis/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ void strcpy_fn_const(char *x) {
strcpy(x, (const char*)&strcpy_fn); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}}
}

void strcpy_fn_dst(const char *x) {
strcpy((char*)&strcpy_fn, x); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}}
}

extern int globalInt;
void strcpy_effects(char *x, char *y) {
char a = x[0];
Expand Down Expand Up @@ -469,8 +473,22 @@ void strcat_null_src(char *x) {
strcat(x, NULL); // expected-warning{{Null pointer passed as 2nd argument to string concatenation function}}
}

void strcat_fn(char *x) {
strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to string concatenation function is the address of the function 'strcat_fn', which is not a null-terminated string}}
void strcat_fn_dst(const char *x) {
strcat((char*)&strcat_fn_dst, x); // expected-warning{{Argument to string concatenation function is the address of the function 'strcat_fn_dst', which is not a null-terminated string}}
}

void strcat_fn_src(char *x) {
strcat(x, (char*)&strcat_fn_src); // expected-warning{{Argument to string concatenation function is the address of the function 'strcat_fn_src', which is not a null-terminated string}}
}

void strcat_label_dst(const char *x) {
label:
strcat((char*)&&label, x); // expected-warning{{Argument to string concatenation function is the address of the label 'label', which is not a null-terminated string}}
}

void strcat_label_src(char *x) {
label:
strcat(x, (char*)&&label); // expected-warning{{Argument to string concatenation function is the address of the label 'label', which is not a null-terminated string}}
}

void strcat_effects(char *y) {
Expand Down Expand Up @@ -568,8 +586,12 @@ void strncpy_null_src(char *x) {
strncpy(x, NULL, 5); // expected-warning{{Null pointer passed as 2nd argument to string copy function}}
}

void strncpy_fn(char *x) {
strncpy(x, (char*)&strcpy_fn, 5); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}}
void strncpy_fn_src(char *x) {
strncpy(x, (char*)&strncpy_fn_src, 5); // expected-warning{{Argument to string copy function is the address of the function 'strncpy_fn_src', which is not a null-terminated string}}
}

void strncpy_fn_dst(const char *x) {
strncpy((char*)&strncpy_fn_dst, x, 5); // expected-warning{{Argument to string copy function is the address of the function 'strncpy_fn_dst', which is not a null-terminated string}}
}

void strncpy_effects(char *x, char *y) {
Expand Down Expand Up @@ -680,8 +702,12 @@ void strncat_null_src(char *x) {
strncat(x, NULL, 4); // expected-warning{{Null pointer passed as 2nd argument to string concatenation function}}
}

void strncat_fn(char *x) {
strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to string concatenation function is the address of the function 'strncat_fn', which is not a null-terminated string}}
void strncat_fn_src(char *x) {
strncat(x, (char*)&strncat_fn_src, 4); // expected-warning{{Argument to string concatenation function is the address of the function 'strncat_fn_src', which is not a null-terminated string}}
}

void strncat_fn_dst(const char *x) {
strncat((char*)&strncat_fn_dst, x, 4); // expected-warning{{Argument to string concatenation function is the address of the function 'strncat_fn_dst', which is not a null-terminated string}}
}

void strncat_effects(char *y) {
Expand Down Expand Up @@ -921,6 +947,14 @@ int strcmp_null_argument(char *a) {
return strcmp(a, b); // expected-warning{{Null pointer passed as 2nd argument to string comparison function}}
}

void strcmp_fn_r(char *x) {
strcmp(x, (char*)&strcmp_null_argument); // expected-warning{{Argument to string comparison function is the address of the function 'strcmp_null_argument', which is not a null-terminated string}}
}

void strcmp_fn_l(char *x) {
strcmp((char*)&strcmp_null_argument, x); // expected-warning{{Argument to string comparison function is the address of the function 'strcmp_null_argument', which is not a null-terminated string}}
}

//===----------------------------------------------------------------------===
// strncmp()
//===----------------------------------------------------------------------===
Expand Down
10 changes: 9 additions & 1 deletion clang/test/Analysis/string.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection -verify %s

// Test functions that are called "memcpy" but aren't the memcpy
// we're looking for. Unfortunately, this test cannot be put into
// a namespace. The out-of-class weird memcpy needs to be recognized
// as a normal C function for the test to make sense.
typedef __typeof(sizeof(int)) size_t;
void *memcpy(void *, const void *, size_t);
size_t strlen(const char *s);

int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
Expand Down Expand Up @@ -45,3 +46,10 @@ void log(const char* fmt, const Args&... args) {
void test_gh_74269_no_crash() {
log("%d", 1);
}

struct TestNotNullTerm {
void test1() {
TestNotNullTerm * const &x = this;
strlen((char *)&x); // expected-warning{{Argument to string length function is not a null-terminated string}}
}
};

0 comments on commit 9df8d8d

Please sign in to comment.