Skip to content

Commit 9df8d8d

Browse files
authored
[clang][analyzer] Improve test and documentation in cstring NotNullTerminated checker (#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.
1 parent 4ddea29 commit 9df8d8d

File tree

3 files changed

+63
-10
lines changed

3 files changed

+63
-10
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3371,12 +3371,23 @@ Checks for overlap in two buffer arguments. Applies to: ``memcpy, mempcpy, wmem
33713371
33723372
alpha.unix.cstring.NotNullTerminated (C)
33733373
""""""""""""""""""""""""""""""""""""""""
3374-
Check for arguments which are not null-terminated strings; applies to: ``strlen, strnlen, strcpy, strncpy, strcat, strncat, wcslen, wcsnlen``.
3374+
Check for arguments which are not null-terminated strings;
3375+
applies to the ``strlen``, ``strcpy``, ``strcat``, ``strcmp`` family of functions.
3376+
3377+
Only very fundamental cases are detected where the passed memory block is
3378+
absolutely different from a null-terminated string. This checker does not
3379+
find if a memory buffer is passed where the terminating zero character
3380+
is missing.
33753381
33763382
.. code-block:: c
33773383
3378-
void test() {
3379-
int y = strlen((char *)&test); // warn
3384+
void test1() {
3385+
int l = strlen((char *)&test); // warn
3386+
}
3387+
3388+
void test2() {
3389+
label:
3390+
int l = strlen((char *)&&label); // warn
33803391
}
33813392
33823393
.. _alpha-unix-cstring-OutOfBounds:

clang/test/Analysis/string.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ void strcpy_fn_const(char *x) {
361361
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}}
362362
}
363363

364+
void strcpy_fn_dst(const char *x) {
365+
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}}
366+
}
367+
364368
extern int globalInt;
365369
void strcpy_effects(char *x, char *y) {
366370
char a = x[0];
@@ -469,8 +473,22 @@ void strcat_null_src(char *x) {
469473
strcat(x, NULL); // expected-warning{{Null pointer passed as 2nd argument to string concatenation function}}
470474
}
471475

472-
void strcat_fn(char *x) {
473-
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}}
476+
void strcat_fn_dst(const char *x) {
477+
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}}
478+
}
479+
480+
void strcat_fn_src(char *x) {
481+
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}}
482+
}
483+
484+
void strcat_label_dst(const char *x) {
485+
label:
486+
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}}
487+
}
488+
489+
void strcat_label_src(char *x) {
490+
label:
491+
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}}
474492
}
475493

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

571-
void strncpy_fn(char *x) {
572-
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}}
589+
void strncpy_fn_src(char *x) {
590+
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}}
591+
}
592+
593+
void strncpy_fn_dst(const char *x) {
594+
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}}
573595
}
574596

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

683-
void strncat_fn(char *x) {
684-
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}}
705+
void strncat_fn_src(char *x) {
706+
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}}
707+
}
708+
709+
void strncat_fn_dst(const char *x) {
710+
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}}
685711
}
686712

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

950+
void strcmp_fn_r(char *x) {
951+
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}}
952+
}
953+
954+
void strcmp_fn_l(char *x) {
955+
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}}
956+
}
957+
924958
//===----------------------------------------------------------------------===
925959
// strncmp()
926960
//===----------------------------------------------------------------------===

clang/test/Analysis/string.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection -verify %s
22

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

1011
int sprintf(char *str, const char *format, ...);
1112
int snprintf(char *str, size_t size, const char *format, ...);
@@ -45,3 +46,10 @@ void log(const char* fmt, const Args&... args) {
4546
void test_gh_74269_no_crash() {
4647
log("%d", 1);
4748
}
49+
50+
struct TestNotNullTerm {
51+
void test1() {
52+
TestNotNullTerm * const &x = this;
53+
strlen((char *)&x); // expected-warning{{Argument to string length function is not a null-terminated string}}
54+
}
55+
};

0 commit comments

Comments
 (0)