Skip to content

Commit 08b8bf7

Browse files
committed
Finalize first working draft for stack / heap arrays
1 parent 7b860d9 commit 08b8bf7

File tree

1 file changed

+95
-76
lines changed

1 file changed

+95
-76
lines changed

cpp/misra/src/rules/RULE-8-7-1/PointerArithmeticFormsAnInvalidPointer.ql

Lines changed: 95 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ import cpp
1616
import codingstandards.cpp.misra
1717
import semmle.code.cpp.dataflow.new.TaintTracking
1818
import semmle.code.cpp.security.BufferAccess
19-
private import semmle.code.cpp.ir.IR // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
20-
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
21-
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
22-
private import semmle.code.cpp.ir.dataflow.FlowSteps // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
23-
private import semmle.code.cpp.ir.dataflow.internal.SsaInternals as Ssa
2419

2520
class ArrayDeclaration extends VariableDeclarationEntry {
2621
int length;
@@ -31,11 +26,6 @@ class ArrayDeclaration extends VariableDeclarationEntry {
3126
* Gets the declared length of this array.
3227
*/
3328
int getLength() { result = length }
34-
35-
/**
36-
* Gets the expression that the variable declared is initialized to, given there is such one.
37-
*/
38-
Expr getInitExpr() { result = this.getVariable().getInitializer().getExpr() }
3929
}
4030

4131
class HeapAllocationFunctionCall extends FunctionCall {
@@ -49,21 +39,27 @@ class HeapAllocationFunctionCall extends FunctionCall {
4939

5040
predicate isReallocCall() { heapAllocFunction.getName() = "realloc" }
5141

52-
abstract Expr getByteArgument();
53-
54-
int getByteLowerBound() { result = lowerBound(this.getByteArgument()) }
42+
abstract int getMinNumBytes();
5543
}
5644

5745
class MallocFunctionCall extends HeapAllocationFunctionCall {
5846
MallocFunctionCall() { this.isMallocCall() }
5947

60-
override Expr getByteArgument() { result = this.getArgument(0) }
48+
override int getMinNumBytes() { result = lowerBound(this.getArgument(0)) }
6149
}
6250

63-
class CallocReallocFunctionCall extends HeapAllocationFunctionCall {
64-
CallocReallocFunctionCall() { this.isCallocCall() or this.isReallocCall() }
51+
class CallocFunctionCall extends HeapAllocationFunctionCall {
52+
CallocFunctionCall() { this.isCallocCall() }
6553

66-
override Expr getByteArgument() { result = this.getArgument(1) }
54+
override int getMinNumBytes() {
55+
result = lowerBound(this.getArgument(0)) * lowerBound(this.getArgument(1))
56+
}
57+
}
58+
59+
class ReallocFunctionCall extends HeapAllocationFunctionCall {
60+
ReallocFunctionCall() { this.isReallocCall() }
61+
62+
override int getMinNumBytes() { result = lowerBound(this.getArgument(1)) }
6763
}
6864

6965
class NarrowedHeapAllocationFunctionCall extends Cast {
@@ -72,8 +68,7 @@ class NarrowedHeapAllocationFunctionCall extends Cast {
7268
NarrowedHeapAllocationFunctionCall() { alloc = this.getExpr() }
7369

7470
int getMinNumElements() {
75-
result =
76-
alloc.getByteLowerBound() / this.getUnderlyingType().(PointerType).getBaseType().getSize()
71+
result = alloc.getMinNumBytes() / this.getUnderlyingType().(PointerType).getBaseType().getSize()
7772
}
7873

7974
HeapAllocationFunctionCall getAllocFunctionCall() { result = alloc }
@@ -112,7 +107,7 @@ class ArrayAllocation extends TArrayAllocation {
112107
}
113108

114109
DataFlow::Node getNode() {
115-
result.asExpr() = this.asStackAllocation().getInitExpr() or
110+
result.asUninitialized() = this.asStackAllocation().getVariable() or
116111
result.asConvertedExpr() = this.asDynamicAllocation()
117112
}
118113
}
@@ -140,8 +135,8 @@ class PointerFormation extends TPointerFormation {
140135
}
141136

142137
Expr asExpr() {
143-
result = this.asArrayExpr() or // This needs to be array base, as we are only dealing with pointers here.
144-
result = this.asPointerArithmetic()
138+
result = this.asArrayExpr() or
139+
/*.getArrayBase()*/ result = this.asPointerArithmetic()
145140
}
146141

147142
DataFlow::Node getNode() { result.asExpr() = this.asExpr() }
@@ -155,47 +150,57 @@ class PointerFormation extends TPointerFormation {
155150
/**
156151
* NOTE
157152
*/
158-
private predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
159-
// Taint can flow through expressions that alter the value but preserve
160-
// more than one bit of it _or_ expressions that follow data through
161-
// pointer indirections.
162-
instrTo.getAnOperand() = opFrom and
163-
(
164-
instrTo instanceof ArithmeticInstruction
153+
module Copied {
154+
import semmle.code.cpp.ir.IR // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
155+
import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
156+
import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
157+
import semmle.code.cpp.ir.dataflow.FlowSteps // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
158+
import semmle.code.cpp.ir.dataflow.internal.SsaInternals as Ssa
159+
160+
predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
161+
// Taint can flow through expressions that alter the value but preserve
162+
// more than one bit of it _or_ expressions that follow data through
163+
// pointer indirections.
164+
instrTo.getAnOperand() = opFrom and
165+
(
166+
instrTo instanceof ArithmeticInstruction
167+
or
168+
instrTo instanceof BitwiseInstruction
169+
or
170+
instrTo instanceof PointerArithmeticInstruction
171+
)
165172
or
166-
instrTo instanceof BitwiseInstruction
173+
// Taint flow from an address to its dereference.
174+
Ssa::isDereference(instrTo, opFrom, _)
167175
or
168-
instrTo instanceof PointerArithmeticInstruction
169-
)
170-
or
171-
// Taint flow from an address to its dereference.
172-
Ssa::isDereference(instrTo, opFrom, _)
173-
or
174-
// Unary instructions tend to preserve enough information in practice that we
175-
// want taint to flow through.
176-
// The exception is `FieldAddressInstruction`. Together with the rules below for
177-
// `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
178-
// could cause flow into one field to come out an unrelated field.
179-
// This would happen across function boundaries, where the IR would not be able to
180-
// match loads to stores.
181-
instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
182-
(
183-
not instrTo instanceof FieldAddressInstruction
176+
// Unary instructions tend to preserve enough information in practice that we
177+
// want taint to flow through.
178+
// The exception is `FieldAddressInstruction`. Together with the rules below for
179+
// `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
180+
// could cause flow into one field to come out an unrelated field.
181+
// This would happen across function boundaries, where the IR would not be able to
182+
// match loads to stores.
183+
instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
184+
(
185+
not instrTo instanceof FieldAddressInstruction
186+
or
187+
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
188+
)
184189
or
185-
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
186-
)
187-
or
188-
// Taint from int to boolean casts. This ensures that we have flow to `!x` in:
189-
// ```cpp
190-
// x = integer_source();
191-
// if(!x) { ... }
192-
// ```
193-
exists(Operand zero |
194-
zero.getDef().(ConstantValueInstruction).getValue() = "0" and
195-
instrTo.(CompareNEInstruction).hasOperands(opFrom, zero)
196-
)
190+
// Taint from int to boolean casts. This ensures that we have flow to `!x` in:
191+
// ```cpp
192+
// x = integer_source();
193+
// if(!x) { ... }
194+
// ```
195+
exists(Operand zero |
196+
zero.getDef().(ConstantValueInstruction).getValue() = "0" and
197+
instrTo.(CompareNEInstruction).hasOperands(opFrom, zero)
198+
)
199+
}
197200
}
198201

202+
import Copied
203+
199204
module TrackArrayConfig implements DataFlow::ConfigSig {
200205
predicate isSource(DataFlow::Node node) {
201206
exists(ArrayAllocation arrayAllocation | node = arrayAllocation.getNode())
@@ -206,44 +211,58 @@ module TrackArrayConfig implements DataFlow::ConfigSig {
206211
}
207212

208213
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
209-
/*
210-
* NOTE
211-
*/
212-
213214
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
214-
or
215-
exists(PointerArithmeticInstruction pai, int indirectionIndex |
216-
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
217-
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
218-
)
219215
}
220216
}
221217

222218
module TrackArray = DataFlow::Global<TrackArrayConfig>;
223219

224-
private predicate arrayIndexExceedsOutOfBounds(
220+
predicate arrayIndexIsNegative(
225221
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode
226222
) {
227223
/* 1. Ensure the array access is reachable from the array declaration. */
228224
TrackArray::flow(arrayDeclarationNode, pointerFormationNode) and
229-
/* 2. Cases where a pointer formation becomes illegal. */
225+
/* 2. An offset cannot be negative. */
230226
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
231227
arrayDeclarationNode = arrayAllocation.getNode() and
232228
pointerFormationNode = pointerFormation.getNode()
233229
|
234-
/* 2-1. An offset cannot be negative. */
235230
pointerFormation.getOffset() < 0
236-
or
237-
/* 2-2. The offset should be at most (number of elements) + 1 = (the declared length). */
238-
arrayAllocation.getLength() < pointerFormation.getOffset()
231+
)
232+
}
233+
234+
predicate arrayIndexExceedsBounds(
235+
DataFlow::Node arrayDeclarationNode, DataFlow::Node pointerFormationNode, int pointerOffset,
236+
int arrayLength
237+
) {
238+
/* 1. Ensure the array access is reachable from the array declaration. */
239+
TrackArray::flow(arrayDeclarationNode, pointerFormationNode) and
240+
/* 2. The offset must be at most (number of elements) + 1 = (the declared length). */
241+
exists(ArrayAllocation arrayAllocation, PointerFormation pointerFormation |
242+
arrayDeclarationNode = arrayAllocation.getNode() and
243+
pointerFormationNode = pointerFormation.getNode() and
244+
pointerOffset = pointerFormation.getOffset() and
245+
arrayLength = arrayAllocation.getLength()
246+
|
247+
arrayLength < pointerOffset
239248
)
240249
}
241250

242251
import TrackArray::PathGraph
243252

244-
from TrackArray::PathNode source, TrackArray::PathNode sink
253+
from TrackArray::PathNode source, TrackArray::PathNode sink, string message
245254
where
246255
not isExcluded(sink.getNode().asExpr(),
247256
Memory1Package::pointerArithmeticFormsAnInvalidPointerQuery()) and
248-
arrayIndexExceedsOutOfBounds(source.getNode(), sink.getNode())
249-
select sink, source, sink, "TODO"
257+
(
258+
exists(int pointerOffset, int arrayLength |
259+
arrayIndexExceedsBounds(source.getNode(), sink.getNode(), pointerOffset, arrayLength) and
260+
message =
261+
"This pointer has offset " + pointerOffset +
262+
" when the minimum possible length of the object is " + arrayLength + "."
263+
)
264+
or
265+
arrayIndexIsNegative(source.getNode(), sink.getNode()) and
266+
message = "This pointer has a negative offset."
267+
)
268+
select sink, source, sink, message

0 commit comments

Comments
 (0)