You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: test/testnullpointer.cpp
+58-4Lines changed: 58 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -145,7 +145,7 @@ class TestNullPointer : public TestFixture {
145
145
TEST_CASE(nullpointer105); // #13861
146
146
TEST_CASE(nullpointer106); // #13682
147
147
TEST_CASE(nullpointer107); // #13682 (no false positive past unrelated conditions)
148
-
TEST_CASE(nullpointer108); // #13682 (FN: definite null deref missed due to ProgramMemory)
148
+
TEST_CASE(nullpointer109); // #13682 (no FP when guard depends on pointer via conditional modification)
149
149
TEST_CASE(nullpointer_addressOf); // address of
150
150
TEST_CASE(nullpointerSwitch); // #2626
151
151
TEST_CASE(nullpointer_cast); // #4692
@@ -3040,10 +3040,7 @@ class TestNullPointer : public TestFixture {
3040
3040
" p1->g();\n"
3041
3041
"}\n");
3042
3042
ASSERT_EQUALS("", errout_str());
3043
-
}
3044
3043
3045
-
voidnullpointer108() // #13682 - FN: dereference of a definitely-null pointer is missed
3046
-
{
3047
3044
// 'if (ok) return;' means the surviving path has ok==false, i.e. p==nullptr, so 'p->g()'
3048
3045
// dereferences a null pointer. ProgramMemory cannot evaluate the cached 'ok' (== (p != nullptr))
3049
3046
// during forward analysis, so the conditionReferencesValue() guard stops the analysis here and the
@@ -3061,6 +3058,63 @@ class TestNullPointer : public TestFixture {
3061
3058
"[test.cpp:4:9] -> [test.cpp:8:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p. [nullPointerRedundantCheck]\n",
3062
3059
"",
3063
3060
errout_str());
3061
+
3062
+
// 'q' aliases 'p' (symbolic q==p), so the guard 'if (q)' constrains 'p'. Modifying 'q' via sink()
3063
+
// drops the symbolic relationship; conditionReferencesValue() only inspects symbolic values, so it
3064
+
// no longer sees that the guard relates to 'p', stops the analysis, and the possible null
3065
+
// dereference of 'p' is missed. A more robust dependency check (not symbolic-only) should warn.
3066
+
check("struct S { void g(); bool f() const; };\n"
3067
+
"void sink(S*&);\n"
3068
+
"void f(S* p) {\n"
3069
+
" S* q = p;\n"
3070
+
" if (p && p->f())\n"
3071
+
" return;\n"
3072
+
" sink(q);\n"
3073
+
" if (q)\n"
3074
+
" return;\n"
3075
+
" p->g();\n"
3076
+
"}\n");
3077
+
TODO_ASSERT_EQUALS(
3078
+
"[test.cpp:5:9] -> [test.cpp:10:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p. [nullPointerRedundantCheck]\n",
3079
+
"",
3080
+
errout_str());
3081
+
}
3082
+
3083
+
voidnullpointer109() // #13682 - no false positive when a guard depends on the pointer through a conditional modification
3084
+
{
3085
+
// These are dereferences that are actually safe, but the dependency that makes them safe is hidden
3086
+
// behind a conditional modification. ProgramMemory tracks 'q'/'ok' and would normally evaluate the
3087
+
// guard, but a conditional modification ('if (c) ...') makes it stop tracking the value, so the guard
3088
+
// can no longer be evaluated during forward analysis. Without conditionReferencesValue() the possible
3089
+
// null value would flow past the guard and produce a false positive; these must stay warning-free.
0 commit comments