385 lines
9.0 KiB
C
385 lines
9.0 KiB
C
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s
|
||
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -DSUPPRESSED=1 %s
|
||
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s
|
||
|
|
||
|
int opaquePropertyCheck(void *object);
|
||
|
int coin();
|
||
|
|
||
|
int *getNull() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int* getPtr();
|
||
|
|
||
|
int *dynCastToInt(void *ptr) {
|
||
|
if (opaquePropertyCheck(ptr))
|
||
|
return (int *)ptr;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int *dynCastOrNull(void *ptr) {
|
||
|
if (!ptr)
|
||
|
return 0;
|
||
|
if (opaquePropertyCheck(ptr))
|
||
|
return (int *)ptr;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void testDynCast(void *p) {
|
||
|
int *casted = dynCastToInt(p);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void testDynCastOrNull(void *p) {
|
||
|
int *casted = dynCastOrNull(p);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
void testBranch(void *p) {
|
||
|
int *casted;
|
||
|
|
||
|
// Although the report will be suppressed on one branch, it should still be
|
||
|
// valid on the other.
|
||
|
if (coin()) {
|
||
|
casted = dynCastToInt(p);
|
||
|
} else {
|
||
|
if (p)
|
||
|
return;
|
||
|
casted = (int *)p;
|
||
|
}
|
||
|
|
||
|
*casted = 1; // expected-warning {{Dereference of null pointer}}
|
||
|
}
|
||
|
|
||
|
void testBranchReversed(void *p) {
|
||
|
int *casted;
|
||
|
|
||
|
// Although the report will be suppressed on one branch, it should still be
|
||
|
// valid on the other.
|
||
|
if (coin()) {
|
||
|
if (p)
|
||
|
return;
|
||
|
casted = (int *)p;
|
||
|
} else {
|
||
|
casted = dynCastToInt(p);
|
||
|
}
|
||
|
|
||
|
*casted = 1; // expected-warning {{Dereference of null pointer}}
|
||
|
}
|
||
|
|
||
|
void testMultipleStore(void *p) {
|
||
|
int *casted = 0;
|
||
|
casted = dynCastToInt(p);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Test that div by zero does not get suppressed. This is a policy choice.
|
||
|
int retZero() {
|
||
|
return 0;
|
||
|
}
|
||
|
int triggerDivZero () {
|
||
|
int y = retZero();
|
||
|
return 5/y; // expected-warning {{Division by zero}}
|
||
|
}
|
||
|
|
||
|
// Treat a function-like macro similarly to an inlined function, so suppress
|
||
|
// warnings along paths resulting from inlined checks.
|
||
|
#define MACRO_WITH_CHECK(a) ( ((a) != 0) ? *a : 17)
|
||
|
void testInlineCheckInMacro(int *p) {
|
||
|
int i = MACRO_WITH_CHECK(p);
|
||
|
(void)i;
|
||
|
|
||
|
*p = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
#define MACRO_WITH_NESTED_CHECK(a) ( { int j = MACRO_WITH_CHECK(a); j; } )
|
||
|
void testInlineCheckInNestedMacro(int *p) {
|
||
|
int i = MACRO_WITH_NESTED_CHECK(p);
|
||
|
(void)i;
|
||
|
|
||
|
*p = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
#define NON_FUNCTION_MACRO_WITH_CHECK ( ((p) != 0) ? *p : 17)
|
||
|
void testNonFunctionMacro(int *p) {
|
||
|
int i = NON_FUNCTION_MACRO_WITH_CHECK ;
|
||
|
(void)i;
|
||
|
|
||
|
*p = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
|
||
|
// This macro will dereference its argument if the argument is NULL.
|
||
|
#define MACRO_WITH_ERROR(a) ( ((a) != 0) ? 0 : *a)
|
||
|
void testErrorInMacro(int *p) {
|
||
|
int i = MACRO_WITH_ERROR(p); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
|
||
|
(void)i;
|
||
|
}
|
||
|
|
||
|
// Here the check (the "if") is not in a macro, so we should still warn.
|
||
|
#define MACRO_IN_GUARD(a) (!(a))
|
||
|
void testMacroUsedAsGuard(int *p) {
|
||
|
if (MACRO_IN_GUARD(p))
|
||
|
*p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
|
||
|
}
|
||
|
|
||
|
// When a nil case split is introduced in a macro and the macro is in a guard,
|
||
|
// we still shouldn't warn.
|
||
|
int isNull(int *p);
|
||
|
int isEqual(int *p, int *q);
|
||
|
#define ISNULL(ptr) ((ptr) == 0 || isNull(ptr))
|
||
|
#define ISEQUAL(a, b) ((int *)(a) == (int *)(b) || (ISNULL(a) && ISNULL(b)) || isEqual(a,b))
|
||
|
#define ISNOTEQUAL(a, b) (!ISEQUAL(a, b))
|
||
|
void testNestedDisjunctiveMacro(int *p, int *q) {
|
||
|
if (ISNOTEQUAL(p,q)) {
|
||
|
*p = 1; // no-warning
|
||
|
*q = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
*p = 1; // no-warning
|
||
|
*q = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
void testNestedDisjunctiveMacro2(int *p, int *q) {
|
||
|
if (ISEQUAL(p,q)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
*p = 1; // no-warning
|
||
|
*q = 1; // no-warning
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Here the check is entirely in non-macro code even though the code itself
|
||
|
// is a macro argument.
|
||
|
#define MACRO_DO_IT(a) (a)
|
||
|
void testErrorInArgument(int *p) {
|
||
|
int i = MACRO_DO_IT((p ? 0 : *p)); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}c
|
||
|
(void)i;
|
||
|
}
|
||
|
|
||
|
// No warning should be emitted if dereference is performed from a different
|
||
|
// macro.
|
||
|
#define MACRO_CHECK(a) if (a) {}
|
||
|
#define MACRO_DEREF(a) (*a)
|
||
|
int testDifferentMacro(int *p) {
|
||
|
MACRO_CHECK(p);
|
||
|
return MACRO_DEREF(p); // no-warning
|
||
|
}
|
||
|
|
||
|
// --------------------------
|
||
|
// "Suppression suppression"
|
||
|
// --------------------------
|
||
|
|
||
|
void testDynCastOrNullOfNull() {
|
||
|
// Don't suppress when one of the arguments is NULL.
|
||
|
int *casted = dynCastOrNull(0);
|
||
|
*casted = 1;
|
||
|
#if !SUPPRESSED || NULL_ARGS
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void testDynCastOfNull() {
|
||
|
// Don't suppress when one of the arguments is NULL.
|
||
|
int *casted = dynCastToInt(0);
|
||
|
*casted = 1;
|
||
|
#if !SUPPRESSED || NULL_ARGS
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int *lookUpInt(int unused) {
|
||
|
if (coin())
|
||
|
return 0;
|
||
|
static int x;
|
||
|
return &x;
|
||
|
}
|
||
|
|
||
|
void testZeroIsNotNull() {
|
||
|
// /Do/ suppress when the argument is 0 (an integer).
|
||
|
int *casted = lookUpInt(0);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void testTrackNull() {
|
||
|
// /Do/ suppress if the null argument came from another call returning null.
|
||
|
int *casted = dynCastOrNull(getNull());
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void testTrackNullVariable() {
|
||
|
// /Do/ suppress if the null argument came from another call returning null.
|
||
|
int *ptr;
|
||
|
ptr = getNull();
|
||
|
int *casted = dynCastOrNull(ptr);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void inlinedIsDifferent(int inlined) {
|
||
|
int i;
|
||
|
|
||
|
// We were erroneously picking up the inner stack frame's initialization,
|
||
|
// even though the error occurs in the outer stack frame!
|
||
|
int *p = inlined ? &i : getNull();
|
||
|
|
||
|
if (!inlined)
|
||
|
inlinedIsDifferent(1);
|
||
|
|
||
|
*p = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void testInlinedIsDifferent() {
|
||
|
// <rdar://problem/13787723>
|
||
|
inlinedIsDifferent(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
// ---------------------------------------
|
||
|
// FALSE NEGATIVES (over-suppression)
|
||
|
// ---------------------------------------
|
||
|
|
||
|
void testNoArguments() {
|
||
|
// In this case the function has no branches, and MUST return null.
|
||
|
int *casted = getNull();
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int *getNullIfNonNull(void *input) {
|
||
|
if (input)
|
||
|
return 0;
|
||
|
static int x;
|
||
|
return &x;
|
||
|
}
|
||
|
|
||
|
void testKnownPath(void *input) {
|
||
|
if (!input)
|
||
|
return;
|
||
|
|
||
|
// In this case we have a known value for the argument, and thus the path
|
||
|
// through the function doesn't ever split.
|
||
|
int *casted = getNullIfNonNull(input);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int *alwaysReturnNull(void *input) {
|
||
|
if (opaquePropertyCheck(input))
|
||
|
return 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void testAlwaysReturnNull(void *input) {
|
||
|
// In this case all paths out of the function return 0, but they are all
|
||
|
// dominated by a branch whose condition we don't know!
|
||
|
int *casted = alwaysReturnNull(input);
|
||
|
*casted = 1;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int derefArg(int *p) {
|
||
|
return *p;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
void ternaryArg(char cond) {
|
||
|
static int x;
|
||
|
derefArg(cond ? &x : getNull());
|
||
|
}
|
||
|
|
||
|
int derefArgCast(char *p) {
|
||
|
return *p;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
void ternaryArgCast(char cond) {
|
||
|
static int x;
|
||
|
derefArgCast((char*)((unsigned)cond ? &x : getNull()));
|
||
|
}
|
||
|
|
||
|
int derefAssignment(int *p) {
|
||
|
return *p;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void ternaryAssignment(char cond) {
|
||
|
static int x;
|
||
|
int *p = cond ? getNull() : getPtr();
|
||
|
derefAssignment(p);
|
||
|
}
|
||
|
|
||
|
int *retNull(char cond) {
|
||
|
static int x;
|
||
|
return cond ? &x : getNull();
|
||
|
}
|
||
|
int ternaryRetNull(char cond) {
|
||
|
int *p = retNull(cond);
|
||
|
return *p;
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Test suppression of nested conditional operators.
|
||
|
int testConditionalOperatorSuppress(int x) {
|
||
|
return *(x ? getNull() : getPtr());
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
int testNestedConditionalOperatorSuppress(int x) {
|
||
|
return *(x ? (x ? getNull() : getPtr()) : getPtr());
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
int testConditionalOperator(int x) {
|
||
|
return *(x ? 0 : getPtr()); // expected-warning {{Dereference of null pointer}}
|
||
|
}
|
||
|
int testNestedConditionalOperator(int x) {
|
||
|
return *(x ? (x ? 0 : getPtr()) : getPtr()); // expected-warning {{Dereference of null pointer}}
|
||
|
}
|
||
|
|
||
|
int testConditionalOperatorSuppressFloatCond(float x) {
|
||
|
return *(x ? getNull() : getPtr());
|
||
|
#ifndef SUPPRESSED
|
||
|
// expected-warning@-2 {{Dereference of null pointer}}
|
||
|
#endif
|
||
|
}
|
||
|
|