Skip to content

Commit 81add37

Browse files
committed
[CPP-435] Incremental change to MemsetMayBeDeleted.ql. Not yet usable.
1 parent 1ec307d commit 81add37

File tree

1 file changed

+46
-25
lines changed

1 file changed

+46
-25
lines changed

cpp/ql/src/Likely Bugs/Memory Management/MemsetMayBeDeleted.ql

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,56 @@
1111
* external/cwe/cwe-14
1212
*/
1313

14-
import semmle.code.cpp.dataflow.TaintTracking
14+
import cpp
15+
import semmle.code.cpp.ir.IR
16+
import semmle.code.cpp.ir.ValueNumbering
17+
import semmle.code.cpp.models.implementations.Memset
1518

16-
private predicate memsetName(string fn) {
17-
fn = "memset" or
18-
fn = "bzero" or
19-
fn = "ZeroMemory" or
20-
fn = "FillMemory" or
21-
fn = "__builtin_memset"
19+
class MemsetCallInstruction extends CallInstruction {
20+
MemsetCallInstruction() {
21+
this.getValue() = "0" and
22+
this.getResultType().getUnspecifiedType() instanceof PointerType
23+
}
2224
}
2325

24-
private Expr getVariable(Expr expr) {
25-
expr instanceof CStyleCast and result = getVariable(expr.(CStyleCast).getExpr())
26+
predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
27+
bool = any(CompareInstruction cmp |
28+
exists(NullInstruction null |
29+
cmp.getLeft() = null and cmp.getRight() = checked
30+
or
31+
cmp.getLeft() = checked and cmp.getRight() = null
32+
|
33+
cmp instanceof CompareEQInstruction
34+
or
35+
cmp instanceof CompareNEInstruction
36+
)
37+
)
2638
or
27-
expr instanceof PointerAddExpr and result = getVariable(expr.getChild(0))
28-
or
29-
expr instanceof ArrayToPointerConversion and result = getVariable(expr.getChild(0))
30-
or
31-
result = expr
39+
bool = any(ConvertInstruction convert |
40+
checked = convert.getUnary() and
41+
convert.getResultType() instanceof BoolType and
42+
checked.getResultType() instanceof PointerType
43+
)
44+
}
45+
46+
pragma[noinline]
47+
predicate candidateResult(LoadInstruction checked, ValueNumber value, IRBlock dominator) {
48+
explicitNullTestOfInstruction(checked, _) and
49+
not checked.getAST().isInMacroExpansion() and
50+
value.getAnInstruction() = checked and
51+
dominator.dominates(checked.getBlock())
3252
}
3353

34-
from FunctionCall fc, Expr arg
54+
from LoadInstruction checked, LoadInstruction deref, ValueNumber sourceValue, IRBlock dominator
3555
where
36-
memsetName(fc.getTarget().getName()) and
37-
arg = fc.getArgument(0) and
38-
not exists(Expr succ |
39-
TaintTracking::localTaint(DataFlow::definitionByReferenceNodeFromArgument(arg),
40-
DataFlow::exprNode(succ))
41-
) and
42-
not exists(Parameter parm |
43-
TaintTracking::localTaint(DataFlow::parameterNode(parm), DataFlow::exprNode(arg))
44-
)
45-
select fc, "Call to " + fc.getTarget().getName() + " may be deleted by the compiler."
56+
candidateResult(checked, sourceValue, dominator) and
57+
sourceValue.getAnInstruction() = deref.getSourceAddress() and
58+
// This also holds if the blocks are equal, meaning that the check could come
59+
// before the deref. That's still not okay because when they're in the same
60+
// basic block then the deref is unavoidable even if the check concluded that
61+
// the pointer was null. To follow this idea to its full generality, we
62+
// should also give an alert when `check` post-dominates `deref`.
63+
deref.getBlock() = dominator
64+
select checked, "This null check is redundant because the value is $@ in any case", deref,
65+
"dereferenced here"
66+
select fc, "Call to " + fc.getTarget().getName() + " may be deleted by the compiler."

0 commit comments

Comments
 (0)