Skip to content

Commit d741e0b

Browse files
authored
Merge pull request #1382 from jbj/redundant-null-check-gvn
Approved by dave-bartolomeo
2 parents 653c8b8 + 4f304fc commit d741e0b

File tree

6 files changed

+56
-48
lines changed

6 files changed

+56
-48
lines changed

cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313

1414
/*
1515
* Note: this query is not assigned a precision yet because we don't want it on
16-
* LGTM until its performance is well understood. It's also lacking qhelp.
16+
* LGTM until its performance is well understood.
1717
*/
1818

1919
import semmle.code.cpp.ir.IR
20+
import semmle.code.cpp.ir.ValueNumbering
2021

2122
class NullInstruction extends ConstantValueInstruction {
2223
NullInstruction() {
@@ -25,17 +26,6 @@ class NullInstruction extends ConstantValueInstruction {
2526
}
2627
}
2728

28-
/**
29-
* An instruction that will never have slicing on its result.
30-
*/
31-
class SingleValuedInstruction extends Instruction {
32-
SingleValuedInstruction() {
33-
this.getResultMemoryAccess() instanceof IndirectMemoryAccess
34-
or
35-
not this.hasMemoryResult()
36-
}
37-
}
38-
3929
predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
4030
bool = any(CompareInstruction cmp |
4131
exists(NullInstruction null |
@@ -56,22 +46,24 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
5646
)
5747
}
5848

59-
predicate candidateResult(LoadInstruction checked, SingleValuedInstruction sourceValue)
49+
pragma[noinline]
50+
predicate candidateResult(LoadInstruction checked, ValueNumber value, IRBlock dominator)
6051
{
6152
explicitNullTestOfInstruction(checked, _) and
6253
not checked.getAST().isInMacroExpansion() and
63-
sourceValue = checked.getSourceValue()
54+
value.getAnInstruction() = checked and
55+
dominator.dominates(checked.getBlock())
6456
}
6557

66-
from LoadInstruction checked, LoadInstruction deref, SingleValuedInstruction sourceValue
58+
from LoadInstruction checked, LoadInstruction deref, ValueNumber sourceValue, IRBlock dominator
6759
where
68-
candidateResult(checked, sourceValue) and
69-
sourceValue = deref.getSourceAddress().(LoadInstruction).getSourceValue() and
60+
candidateResult(checked, sourceValue, dominator) and
61+
sourceValue.getAnInstruction() = deref.getSourceAddress() and
7062
// This also holds if the blocks are equal, meaning that the check could come
7163
// before the deref. That's still not okay because when they're in the same
7264
// basic block then the deref is unavoidable even if the check concluded that
7365
// the pointer was null. To follow this idea to its full generality, we
7466
// should also give an alert when `check` post-dominates `deref`.
75-
deref.getBlock().dominates(checked.getBlock())
67+
deref.getBlock() = dominator
7668
select checked, "This null check is redundant because the value is $@ in any case", deref,
7769
"dereferenced here"

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,10 @@ class ValueNumber extends TValueNumber {
105105
* definition because it accesses the exact same memory.
106106
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
107107
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
108-
*
109-
* This concept should probably be exposed in the public IR API.
110108
*/
111109
private class CongruentCopyInstruction extends CopyInstruction {
112110
CongruentCopyInstruction() {
113-
exists(Instruction def |
114-
def = this.getSourceValue() and
115-
(
116-
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
117-
def.getResultMemoryAccess() instanceof PhiMemoryAccess or
118-
not def.hasMemoryResult()
119-
)
120-
)
111+
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
121112
}
122113
}
123114

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,10 @@ class ValueNumber extends TValueNumber {
105105
* definition because it accesses the exact same memory.
106106
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
107107
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
108-
*
109-
* This concept should probably be exposed in the public IR API.
110108
*/
111109
private class CongruentCopyInstruction extends CopyInstruction {
112110
CongruentCopyInstruction() {
113-
exists(Instruction def |
114-
def = this.getSourceValue() and
115-
(
116-
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
117-
def.getResultMemoryAccess() instanceof PhiMemoryAccess or
118-
not def.hasMemoryResult()
119-
)
120-
)
111+
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
121112
}
122113
}
123114

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,10 @@ class ValueNumber extends TValueNumber {
105105
* definition because it accesses the exact same memory.
106106
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
107107
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
108-
*
109-
* This concept should probably be exposed in the public IR API.
110108
*/
111109
private class CongruentCopyInstruction extends CopyInstruction {
112110
CongruentCopyInstruction() {
113-
exists(Instruction def |
114-
def = this.getSourceValue() and
115-
(
116-
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
117-
def.getResultMemoryAccess() instanceof PhiMemoryAccess or
118-
not def.hasMemoryResult()
119-
)
120-
)
111+
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
121112
}
122113
}
123114

cpp/ql/test/query-tests/Likely Bugs/RedundantNullCheckSimple/RedundantNullCheckSimple.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,44 @@ int test_inverted_logic(int *p) {
6969
return 0;
7070
}
7171
}
72+
73+
void test_indirect_local() {
74+
int a = 0;
75+
int *p = &a;
76+
int **pp = &p;
77+
int x;
78+
x = **pp;
79+
if (*pp == nullptr) { // BAD
80+
return;
81+
}
82+
}
83+
84+
void test_field_local(bool boolvar) {
85+
int a = 0;
86+
struct {
87+
int *p;
88+
} s = { &a };
89+
auto sp = &s;
90+
91+
if (boolvar) {
92+
int x = *sp->p;
93+
if (sp->p == nullptr) { // BAD
94+
return;
95+
}
96+
} else {
97+
int *x = sp->p;
98+
if (sp == nullptr) { // BAD [NOT DETECTED]
99+
return;
100+
}
101+
}
102+
}
103+
104+
struct S {
105+
long **pplong;
106+
107+
void test_phi() {
108+
while (*pplong != nullptr) { // GOOD
109+
pplong++;
110+
}
111+
}
112+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
| RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | dereferenced here |
22
| RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | dereferenced here |
33
| RedundantNullCheckSimple.cpp:48:12:48:12 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:51:10:51:11 | Load: * ... | dereferenced here |
4+
| RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | dereferenced here |
5+
| RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | dereferenced here |

0 commit comments

Comments
 (0)