It was not possible to disable alpha.unix.cstring.OutOfBounds checker's reports
since unix.Malloc checker always implicitly enabled the filter. Moreover if the
checker was disabled from command line (-analyzer-disable-checker ..) the out
of bounds warnings were nevertheless emitted under different checker names such
as unix.cstring.NullArg, or unix.Malloc.
This patch fixes the case sot that Malloc checker only enables implicitly the
underlying modeling of strcpy, memcpy etc. but not the warning messages that
would have been emmitted by alpha.unix.cstring.OutOfBounds
Patch by: Dániel Krupp
Differential Revision: https://reviews.llvm.org/D48831
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337000
91177308-0d34-0410-b5e6-
96231b3b80d8
ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false);
if (StOutBound && !StInBound) {
// These checks are either enabled by the CString out-of-bounds checker
- // explicitly or the "basic" CStringNullArg checker support that Malloc
- // checker enables.
- assert(Filter.CheckCStringOutOfBounds || Filter.CheckCStringNullArg);
-
+ // explicitly or implicitly by the Malloc checker.
+ // In the latter case we only do modeling but do not emit warning.
+ if (!Filter.CheckCStringOutOfBounds)
+ return nullptr;
// Emit a bug report.
if (warningMsg) {
emitOutOfBoundsBug(C, StOutBound, S, warningMsg);
std::tie(StateWholeReg, StateNotWholeReg) =
State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL));
- // With the semantic of 'memset()', we should convert the CharVal to
+ // With the semantic of 'memset()', we should convert the CharVal to
// unsigned char.
CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);
REGISTER_CHECKER(CStringNotNullTerm)
void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
- registerCStringNullArg(Mgr);
+ Mgr.registerChecker<CStringChecker>();
}
--- /dev/null
+// RUN: rm -f %t
+// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,unix.Malloc,unix.cstring.NullArg -analyzer-disable-checker=alpha.unix.cstring.OutOfBounds -analyzer-output=plist -analyzer-config path-diagnostics-alternate=false -o %t %s
+// RUN: FileCheck -input-file %t %s
+
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void free(void *);
+char *strncpy(char *restrict s1, const char *restrict s2, size_t n);
+
+
+
+void cstringchecker_bounds_nocrash() {
+ char *p = malloc(2);
+ strncpy(p, "AAA", sizeof("AAA")); // we don't expect warning as the checker is disabled
+ free(p);
+}
+
+// CHECK: <key>diagnostics</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </plist>
// or inter-procedural analysis, this is a conservative answer.
int *f3() {
static int *p = 0;
- p = malloc(12);
+ p = malloc(12);
return p; // no-warning
}
// functions or inter-procedural analysis, this is a conservative answer.
static int *p_f4 = 0;
int *f4() {
- p_f4 = malloc(12);
+ p_f4 = malloc(12);
return p_f4; // no-warning
}
if (myValueSize <= sizeof(stackBuffer))
buffer = stackBuffer;
- else
+ else
buffer = malloc(myValueSize);
// do stuff with the buffer
if (myValueSize <= sizeof(stackBuffer))
buffer = stackBuffer;
- else
+ else
buffer = malloc(myValueSize);
// do stuff with the buffer
return;
else
return; // expected-warning {{leak}}
-}\v
+}
// <rdar://problem/11269741> Previously this triggered a false positive
// because malloc() is known to return uninitialized memory and the binding
// of 'o' to 'p->n' was not getting propertly handled. Now we report a leak.
void *smallocNoWarn(size_t size) {
if (size == 0) {
return malloc(1); // this branch is never called
- }
+ }
else {
return malloc(size);
}
free((void *)fnptr); // expected-warning {{Argument to free() is a function pointer}}
}
-// Enabling the malloc checker enables some of the buffer-checking portions
-// of the C-string checker.
-void cstringchecker_bounds_nocrash() {
- char *p = malloc(2);
- strncpy(p, "AAA", sizeof("AAA")); // expected-warning {{Size argument is greater than the length of the destination buffer}}
-
- free(p);
-}
-
void allocateSomeMemory(void *offendingParameter, void **ptr) {
*ptr = malloc(1);
}
void testNoCrashOnOffendingParameter() {
- // "extern" is necessary to avoid unrelated warnings
+ // "extern" is necessary to avoid unrelated warnings
// on passing uninitialized value.
extern void *offendingParameter;
void* ptr;
void clang_analyzer_eval(int);
int scanf(const char *restrict format, ...);
+void *malloc(size_t);
+void free(void *);
//===----------------------------------------------------------------------===
// strlen()
if (a == 0) {
clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
// Make sure clang_analyzer_eval does not invalidate globals.
- clang_analyzer_eval(strlen(global_str) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(global_str) == 0); // expected-warning{{TRUE}}
}
// Call a function with unknown effects, which should invalidate globals.
clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
void strcpy_overflow(char *y) {
char x[4];
if (strlen(y) == 4)
strcpy(x, y); // expected-warning{{String copy function overflows destination buffer}}
}
+#endif
void strcpy_no_overflow(char *y) {
char x[4];
clang_analyzer_eval(a == x[0]); // expected-warning{{UNKNOWN}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
void stpcpy_overflow(char *y) {
char x[4];
if (strlen(y) == 4)
stpcpy(x, y); // expected-warning{{String copy function overflows destination buffer}}
}
+#endif
void stpcpy_no_overflow(char *y) {
char x[4];
clang_analyzer_eval((int)strlen(x) == (orig_len + strlen(y))); // expected-warning{{TRUE}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
void strcat_overflow_0(char *y) {
char x[4] = "12";
if (strlen(y) == 4)
if (strlen(y) == 2)
strcat(x, y); // expected-warning{{String copy function overflows destination buffer}}
}
+#endif
void strcat_no_overflow(char *y) {
char x[5] = "12";
clang_analyzer_eval(a == x[0]); // expected-warning{{UNKNOWN}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
+// Enabling the malloc checker enables some of the buffer-checking portions
+// of the C-string checker.
+void cstringchecker_bounds_nocrash() {
+ char *p = malloc(2);
+ strncpy(p, "AAA", sizeof("AAA")); // expected-warning {{Size argument is greater than the length of the destination buffer}}
+ free(p);
+}
+
void strncpy_overflow(char *y) {
char x[4];
if (strlen(y) == 4)
if (strlen(y) == 3)
strncpy(x, y, n); // expected-warning{{Size argument is greater than the length of the destination buffer}}
}
+#endif
void strncpy_truncate(char *y) {
char x[4];
clang_analyzer_eval(strlen(x) == (orig_len + strlen(y))); // expected-warning{{TRUE}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
void strncat_overflow_0(char *y) {
char x[4] = "12";
if (strlen(y) == 4)
if (strlen(y) == 4)
strncat(x, y, 2); // expected-warning{{Size argument is greater than the free space in the destination buffer}}
}
+#endif
+
void strncat_no_overflow_1(char *y) {
char x[5] = "12";
if (strlen(y) == 2)
clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
+#ifndef SUPPRESS_OUT_OF_BOUND
void strncat_symbolic_src_length(char *src) {
char dst[8] = "1234";
strncat(dst, src, 3);
char dst2[8] = "1234";
strncat(dst2, &src[offset], 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}}
}
+#endif
// There is no strncat_unknown_dst_length because if we can't get a symbolic
// length for the "before" strlen, we won't be able to set one for "after".