diff options
| author | Jordan Rose <jordan_rose@apple.com> | 2012-08-04 00:25:30 +0000 |
|---|---|---|
| committer | Jordan Rose <jordan_rose@apple.com> | 2012-08-04 00:25:30 +0000 |
| commit | 4aa80e156d5e446eff242556bc0a159bdf8dc2a8 (patch) | |
| tree | eb1a66737acca1ee3dca4a59d2cae71978324e46 /clang | |
| parent | 08af4c84618198679be67d18cbfda82547a07edc (diff) | |
| download | bcm5719-llvm-4aa80e156d5e446eff242556bc0a159bdf8dc2a8.tar.gz bcm5719-llvm-4aa80e156d5e446eff242556bc0a159bdf8dc2a8.zip | |
[analyzer] Don't assume values bound to references are automatically non-null.
While there is no such thing as a "null reference" in the C++ standard,
many implementations of references (including Clang's) do not actually
check that the location bound to them is non-null. Thus unlike a regular
null dereference, this will not cause a problem at runtime until the
reference is actually used. In order to catch these cases, we need to not
prune out paths on which the input pointer is null.
llvm-svn: 161288
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp | 21 | ||||
| -rw-r--r-- | clang/test/Analysis/reference.cpp | 15 |
2 files changed, 32 insertions, 4 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index a94d7a773ec..e98c131ce97 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -233,7 +233,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { - // If we're binding to a reference, check if the value is potentially null. + // If we're binding to a reference, check if the value is known to be null. if (V.isUndef()) return; @@ -265,8 +265,23 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, } } - // From here on out, assume the value is non-null. - C.addTransition(StNonNull); + // Unlike a regular null dereference, initializing a reference with a + // dereferenced null pointer does not actually cause a runtime exception in + // Clang's implementation of references. + // + // int &r = *p; // safe?? + // if (p != NULL) return; // uh-oh + // r = 5; // trap here + // + // The standard says this is invalid as soon as we try to create a "null + // reference" (there is no such thing), but turning this into an assumption + // that 'p' is never null will not match our actual runtime behavior. + // So we do not record this assumption, allowing us to warn on the last line + // of this example. + // + // We do need to add a transition because we may have generated a sink for + // the "implicit" null dereference. + C.addTransition(State, this); } void ento::registerDereferenceChecker(CheckerManager &mgr) { diff --git a/clang/test/Analysis/reference.cpp b/clang/test/Analysis/reference.cpp index c9bfadced79..06e4a50e44c 100644 --- a/clang/test/Analysis/reference.cpp +++ b/clang/test/Analysis/reference.cpp @@ -91,12 +91,25 @@ namespace PR13440 { } } -void testRef() { +void testNullReference() { int *x = 0; int &y = *x; // expected-warning{{Dereference of null pointer}} y = 5; } +void testRetroactiveNullReference(int *x) { + // According to the C++ standard, there is no such thing as a + // "null reference". So the 'if' statement ought to be dead code. + // However, Clang (and other compilers) don't actually check that a pointer + // value is non-null in the implementation of references, so it is possible + // to produce a supposed "null reference" at runtime. The analyzer shoeuld + // still warn when it can prove such errors. + int &y = *x; + if (x != 0) + return; + y = 5; // expected-warning{{Dereference of null pointer}} +} + // ------------------------------------ // False negatives |

