|
3 | 3 | * @name DCL30-C: Declare objects with appropriate storage durations |
4 | 4 | * @description When pointers to local variables are returned by a function it can lead to referring |
5 | 5 | * to objects outside of their lifetime, which is undefined behaviour. |
6 | | - * @kind problem |
| 6 | + * @kind path-problem |
7 | 7 | * @precision high |
8 | 8 | * @problem.severity error |
9 | 9 | * @tags external/cert/id/dcl30-c |
|
18 | 18 |
|
19 | 19 | import cpp |
20 | 20 | import codingstandards.c.cert |
21 | | -import codingstandards.c.Objects |
22 | | -import semmle.code.cpp.dataflow.DataFlow |
| 21 | +import semmle.code.cpp.ir.IR |
| 22 | +import semmle.code.cpp.ir.dataflow.MustFlow |
| 23 | +import PathGraph |
23 | 24 |
|
24 | | -class Source extends Expr { |
25 | | - ObjectIdentity rootObject; |
| 25 | +/** Holds if `f` appears to intentionally return a stack pointer. */ |
| 26 | +predicate intentionallyReturnsStackPointer(Function f) { |
| 27 | + f.getName().toLowerCase().matches(["%stack%", "%sp%"]) |
| 28 | +} |
| 29 | + |
| 30 | +/** Configuration for detecting stack-allocated memory returned by a function. */ |
| 31 | +class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { |
| 32 | + ReturnStackAllocatedMemoryConfig() { this = "DCL30CReturnStackAllocatedMemoryConfig" } |
26 | 33 |
|
27 | | - Source() { |
28 | | - rootObject.getStorageDuration().isAutomatic() and |
29 | | - this = rootObject.getASubobjectAddressExpr() |
| 34 | + override predicate isSource(Instruction source) { |
| 35 | + exists(Function func | |
| 36 | + not func.hasErrors() and |
| 37 | + not intentionallyReturnsStackPointer(func) and |
| 38 | + func = source.getEnclosingFunction() |
| 39 | + | |
| 40 | + exists(VariableAddressInstruction var | |
| 41 | + var = source and |
| 42 | + var.getAstVariable() instanceof StackVariable and |
| 43 | + not var.getResultType() instanceof PointerToMemberType |
| 44 | + ) |
| 45 | + or |
| 46 | + exists(Call call | |
| 47 | + call.getTarget().hasGlobalName(["alloca", "strdupa", "strndupa", "_alloca", "_malloca"]) and |
| 48 | + source.getUnconvertedResultExpression() = call |
| 49 | + ) |
| 50 | + ) |
30 | 51 | } |
31 | | -} |
32 | 52 |
|
33 | | -class Sink extends DataFlow::Node { |
34 | | - Sink() { |
35 | | - //output parameter |
36 | | - exists(Parameter f | |
37 | | - f.getAnAccess() = this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() and |
38 | | - f.getUnderlyingType() instanceof PointerType |
| 53 | + override predicate isSink(Operand sink) { |
| 54 | + exists(StoreInstruction store | |
| 55 | + store.getDestinationAddress().(VariableAddressInstruction).getIRVariable() instanceof |
| 56 | + IRReturnVariable and |
| 57 | + sink = store.getSourceValueOperand() |
39 | 58 | ) |
| 59 | + } |
| 60 | + |
| 61 | + override predicate allowInterproceduralFlow() { none() } |
| 62 | + |
| 63 | + override predicate isAdditionalFlowStep(Operand node1, Instruction node2) { |
| 64 | + node2.(FieldAddressInstruction).getObjectAddressOperand() = node1 |
40 | 65 | or |
41 | | - //function returns pointer |
42 | | - exists(Function f, ReturnStmt r | |
43 | | - f.getType() instanceof PointerType and |
44 | | - r.getEnclosingFunction() = f and |
45 | | - r.getExpr() = this.asExpr() |
46 | | - ) |
| 66 | + node2.(PointerOffsetInstruction).getLeftOperand() = node1 |
47 | 67 | } |
| 68 | + |
| 69 | + override predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType } |
48 | 70 | } |
49 | 71 |
|
50 | | -from DataFlow::Node src, DataFlow::Node sink |
| 72 | +from |
| 73 | + MustFlowPathNode source, MustFlowPathNode sink, Instruction instr, |
| 74 | + ReturnStackAllocatedMemoryConfig conf |
51 | 75 | where |
52 | | - not isExcluded(sink.asExpr(), |
53 | | - Declarations8Package::appropriateStorageDurationsFunctionReturnQuery()) and |
54 | | - exists(Source s | src.asExpr() = s) and |
55 | | - sink instanceof Sink and |
56 | | - DataFlow::localFlow(src, sink) |
57 | | -select sink, "$@ with automatic storage may be accessible outside of its lifetime.", src, |
58 | | - src.toString() |
| 76 | + conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and |
| 77 | + source.getInstruction() = instr and |
| 78 | + not isExcluded(sink.getInstruction().getAst(), |
| 79 | + Declarations8Package::appropriateStorageDurationsFunctionReturnQuery()) |
| 80 | +select sink.getInstruction(), source, sink, |
| 81 | + "$@ with automatic storage may be accessible outside of its lifetime.", instr.getAst(), |
| 82 | + instr.getAst().toString() |
0 commit comments