|
11 | 11 | * external/cwe/cwe-14
|
12 | 12 | */
|
13 | 13 |
|
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 |
15 | 18 |
|
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 | + } |
22 | 24 | }
|
23 | 25 |
|
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 | + ) |
26 | 38 | 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()) |
32 | 52 | }
|
33 | 53 |
|
34 |
| -from FunctionCall fc, Expr arg |
| 54 | +from LoadInstruction checked, LoadInstruction deref, ValueNumber sourceValue, IRBlock dominator |
35 | 55 | 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