Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6d1932

Browse files
committed
Experimental library that tracks the length of memory.
For each pointer, we start tracking (starting from the allocation or an array declaration) 1) how long is the chunk of memory allocated 2) where the current pointer is in this chunk of memory. This information might not always exist, but when it does, it is reliable. Currently only works intraprocedurally.
1 parent 55cd0fa commit e6d1932

4 files changed

Lines changed: 394 additions & 0 deletions

File tree

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
/**
2+
* Provides precise tracking of how big the memory pointed to by pointers is.
3+
* For each pointer, we start tracking (starting from the allocation or an array declaration)
4+
* 1) how long is the chunk of memory allocated
5+
* 2) where the current pointer is in this chunk of memory
6+
* As computing this information is obviously not possible for all pointers,
7+
* we do not guarantee the existence of length/offset information for all pointers.
8+
* However, when it exists it is guaranteed to be accurate.
9+
*
10+
* The length and offset are tracked in a similar way to the Rangeanalysis.
11+
* Each length is a `ValueNumber + delta`, and each Offset is an `Operand + delta`.
12+
* We choose to track a `ValueNumber` for length, because the Rangeanalysis offers
13+
* integer bounds on instructions and operands in terms of `ValueNumber`s,
14+
* and `Operand` for offset because integer bounds on `Operand`s are
15+
* tighter than bounds on `Instruction`s.
16+
*/
17+
18+
import cpp
19+
import semmle.code.cpp.ir.IR
20+
private import semmle.code.cpp.ir.ValueNumbering
21+
private import semmle.code.cpp.ir.internal.CppType
22+
private import semmle.code.cpp.models.interfaces.Allocation
23+
private import semmle.code.cpp.rangeanalysis.RangeUtils
24+
25+
private newtype TLength =
26+
TZeroLength() or
27+
TVNLength(ValueNumber vn) {
28+
exists(Instruction i |
29+
vn.getAnInstruction() = i and
30+
(
31+
i.getResultIRType() instanceof IRSignedIntegerType or
32+
i.getResultIRType() instanceof IRUnsignedIntegerType
33+
)
34+
) and
35+
not vn.getAnInstruction() instanceof ConstantInstruction
36+
}
37+
38+
/**
39+
* Array lengths are represented in a ValueNumber | Zero + delta format.
40+
* This class keeps track of the ValueNumber or Zero.
41+
* The delta is tracked in the predicate `knownArrayLength`.
42+
*/
43+
abstract class Length extends TLength {
44+
abstract string toString();
45+
}
46+
47+
/**
48+
* This length class corresponds to an array having a constant length
49+
* that is tracked by the delta value.
50+
*/
51+
class ZeroLength extends Length, TZeroLength {
52+
override string toString() { result = "ZeroLength" }
53+
}
54+
55+
/**
56+
* This length class corresponds to an array having variable length, i.e. the
57+
* length is tracked by a value number. One example is an array having length
58+
* `count` for an integer variable `count` in the program.
59+
*/
60+
class VNLength extends Length, TVNLength {
61+
ValueNumber vn;
62+
63+
VNLength() { this = TVNLength(vn) }
64+
65+
/** Gets the SSA variable that equals value number bound. */
66+
Instruction getInstruction() { this = TVNLength(valueNumber(result)) }
67+
68+
ValueNumber getValueNumber() { result = vn }
69+
70+
override string toString() { result = "VNLength(" + vn.getExampleInstruction().toString() + ")" }
71+
}
72+
73+
private newtype TOffset =
74+
TZeroOffset() or
75+
TOpOffset(Operand op) {
76+
op.getAnyDef().getResultIRType() instanceof IRSignedIntegerType or
77+
op.getAnyDef().getResultIRType() instanceof IRUnsignedIntegerType
78+
}
79+
80+
/**
81+
* This class describes the offset of a pointer in a chunk of memory.
82+
* It is either an `Operand` or zero, an additional integer delta is added later.
83+
*/
84+
abstract class Offset extends TOffset {
85+
abstract string toString();
86+
}
87+
88+
/**
89+
* This class represents a fixed offset, only specified by a delta.
90+
*/
91+
class ZeroOffset extends Offset, TZeroOffset {
92+
override string toString() { result = "ZeroOffset" }
93+
}
94+
95+
/**
96+
* This class represents an offset of an operand.
97+
*/
98+
class OpOffset extends Offset, TOpOffset {
99+
Operand op;
100+
101+
OpOffset() { this = TOpOffset(op) }
102+
103+
Operand getOperand() { result = op }
104+
105+
override string toString() { result = "OpOffset(" + op.getDef().toString() + ")" }
106+
}
107+
108+
private int getBaseSizeForPointerType(PointerType type) { result = type.getBaseType().getSize() }
109+
110+
/**
111+
* Holds if pointer `prev` that points at offset `prevOffset + prevOffsetDelta`
112+
* steps to `array` that points to `offset + offsetDelta` in one step.
113+
* This predicate does not contain any recursive steps.
114+
*/
115+
bindingset[prevOffsetDelta]
116+
predicate simpleArrayLengthStep(
117+
Instruction array, Offset offset, int offsetDelta, Instruction prev, Offset prevOffset,
118+
int prevOffsetDelta
119+
) {
120+
// array assign
121+
array.(CopyInstruction).getSourceValue() = prev and
122+
offset = prevOffset and
123+
offsetDelta = prevOffsetDelta
124+
or
125+
// pointer add with constant
126+
array.(PointerAddInstruction).getLeft() = prev and
127+
offset = prevOffset and
128+
offsetDelta = prevOffsetDelta + getConstantValue(array.(PointerAddInstruction).getRight())
129+
or
130+
// pointer add with variable
131+
array.(PointerAddInstruction).getLeft() = prev and
132+
prevOffset instanceof ZeroOffset and
133+
offset.(OpOffset).getOperand() = array.(PointerAddInstruction).getRightOperand() and
134+
offsetDelta = prevOffsetDelta and
135+
not exists(getConstantValue(array.(PointerAddInstruction).getRight()))
136+
or
137+
// pointer sub with constant
138+
array.(PointerSubInstruction).getLeft() = prev and
139+
offset = prevOffset and
140+
offsetDelta = prevOffsetDelta - getConstantValue(array.(PointerSubInstruction).getRight())
141+
or
142+
// array to pointer decay
143+
array.(ConvertInstruction).getUnary() = prev and
144+
array.getConvertedResultExpression() instanceof ArrayToPointerConversion and
145+
offset = prevOffset and
146+
offsetDelta = prevOffsetDelta
147+
or
148+
// cast of pointer to pointer with the same element size
149+
exists(PointerType fromTyp, PointerType toTyp |
150+
array.(PtrToPtrCastInstruction).getUnary() = prev and
151+
prev.getResultLanguageType().hasType(fromTyp, false) and
152+
array.getResultLanguageType().hasType(toTyp, false) and
153+
offset = prevOffset and
154+
offsetDelta = prevOffsetDelta and
155+
if fromTyp instanceof VoidPointerType
156+
then getBaseSizeForPointerType(toTyp) = 1
157+
else (
158+
if toTyp instanceof VoidPointerType
159+
then getBaseSizeForPointerType(fromTyp) = 1
160+
else getBaseSizeForPointerType(toTyp) = getBaseSizeForPointerType(fromTyp)
161+
)
162+
)
163+
}
164+
165+
/**
166+
* Parses a `sizeExpr` of malloc into a variable part (`lengthExpr`) and an integer offset (`delta`).
167+
*/
168+
private predicate deconstructMallocSizeExpr(Expr sizeExpr, Expr lengthExpr, int delta) {
169+
sizeExpr instanceof AddExpr and
170+
exists(Expr constantExpr |
171+
lengthExpr = sizeExpr.(AddExpr).getAnOperand() and
172+
constantExpr = sizeExpr.(AddExpr).getAnOperand() and
173+
lengthExpr != constantExpr and
174+
delta = constantExpr.getValue().toInt()
175+
)
176+
or
177+
sizeExpr instanceof SubExpr and
178+
exists(Expr constantExpr |
179+
lengthExpr = sizeExpr.(SubExpr).getAnOperand() and
180+
constantExpr = sizeExpr.(SubExpr).getAnOperand() and
181+
lengthExpr != constantExpr and
182+
delta = -constantExpr.getValue().toInt()
183+
)
184+
}
185+
186+
/**
187+
* Holds if the instruction `array` is a dynamic memory allocation of `length`+`delta` elements.
188+
*/
189+
private predicate allocation(Instruction array, Length length, int delta) {
190+
exists(AllocationExpr alloc, Type ptrTyp |
191+
array.getUnconvertedResultExpression() = alloc and
192+
array.getResultLanguageType().hasType(ptrTyp, false) and
193+
// ensure that we have the same type of the allocation and the pointer
194+
ptrTyp.stripTopLevelSpecifiers().(PointerType).getBaseType().getUnspecifiedType() =
195+
alloc.getAllocatedElementType().getUnspecifiedType() and
196+
// ensure that the size multiplier of the allocation is the same as the
197+
// size of the type we are allocating
198+
alloc.getSizeMult() = getBaseSizeForPointerType(ptrTyp) and
199+
(
200+
length instanceof ZeroLength and
201+
delta = alloc.getSizeExpr().getValue().toInt()
202+
or
203+
not exists(alloc.getSizeExpr().getValue().toInt()) and
204+
(
205+
exists(Expr lengthExpr |
206+
deconstructMallocSizeExpr(alloc.getSizeExpr(), lengthExpr, delta) and
207+
// TODO converted or unconverted here?
208+
length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr
209+
)
210+
or
211+
not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and
212+
// TODO converted or unconverted here?
213+
length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and
214+
delta = 0
215+
)
216+
)
217+
)
218+
}
219+
220+
/**
221+
* Holds if `array` is declared as an array with length `length + lengthDelta`
222+
*/
223+
private predicate arrayDeclaration(Instruction array, Length length, int lengthDelta) {
224+
(
225+
array instanceof VariableAddressInstruction or
226+
array instanceof FieldAddressInstruction
227+
) and
228+
exists(ArrayType type | array.getResultLanguageType().hasType(type, _) |
229+
length instanceof ZeroLength and
230+
lengthDelta = type.getArraySize()
231+
)
232+
}
233+
234+
/**
235+
* Holds if `array` is declared as an array or allocated
236+
* with length `length + lengthDelta`
237+
*/
238+
predicate arrayAllocationOrDeclaration(Instruction array, Length length, int lengthDelta) {
239+
allocation(array, length, lengthDelta)
240+
or
241+
// declaration of variable of array type
242+
arrayDeclaration(array, length, lengthDelta)
243+
}
244+
245+
/**
246+
* Holds if the instruction `array` represents a pointer to a chunk of memory that holds
247+
* `length + lengthDelta` elements, using only local analysis.
248+
* `array` points at `offset + offsetDelta` in the chunk of memory.
249+
* The pointer is in-bounds if `offset < length + lengthDelta` and
250+
* `offset + offsetDelta >= 0` holds.
251+
* The pointer is out-of-bounds if `offset >= length + lengthDelta`
252+
* or `offset + offsetDelta < 0` holds.
253+
* All pointers in this predicate are guaranteed to be non-null,
254+
* but are not guaranteed to be live.
255+
*/
256+
cached
257+
predicate knownArrayLength(
258+
Instruction array, Length length, int lengthDelta, Offset offset, int offsetDelta
259+
) {
260+
arrayAllocationOrDeclaration(array, length, lengthDelta) and
261+
offset instanceof ZeroOffset and
262+
offsetDelta = 0
263+
or
264+
// simple step (no phi nodes)
265+
exists(Instruction prev, Offset prevOffset, int prevOffsetDelta |
266+
knownArrayLength(prev, length, lengthDelta, prevOffset, prevOffsetDelta) and
267+
simpleArrayLengthStep(array, offset, offsetDelta, prev, prevOffset, prevOffsetDelta)
268+
)
269+
or
270+
// merge control flow after phi node - but only if all the bounds agree
271+
forex(Instruction input | array.(PhiInstruction).getAnInput() = input |
272+
knownArrayLength(input, length, lengthDelta, offset, offsetDelta)
273+
)
274+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
| test.cpp:15:8:15:11 | Load: aptr | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
2+
| test.cpp:19:8:19:8 | Load: a | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
3+
| test.cpp:21:8:21:8 | Load: a | VNLength(Chi: ptr) | -1 | ZeroOffset | 0 |
4+
| test.cpp:23:8:23:8 | Load: a | VNLength(Chi: ptr) | 1 | ZeroOffset | 0 |
5+
| test.cpp:25:8:25:8 | Load: a | VNLength(Mul: ... * ...) | 0 | ZeroOffset | 0 |
6+
| test.cpp:27:8:27:8 | Load: c | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
7+
| test.cpp:28:8:28:24 | Convert: (unsigned char *)... | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
8+
| test.cpp:30:8:30:8 | Load: v | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
9+
| test.cpp:34:8:34:12 | Convert: array to pointer conversion | ZeroLength | 100 | ZeroOffset | 0 |
10+
| test.cpp:37:10:37:10 | Load: b | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
11+
| test.cpp:44:8:44:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
12+
| test.cpp:53:10:53:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
13+
| test.cpp:56:10:56:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 3 |
14+
| test.cpp:63:10:63:14 | CopyValue: & ... | VNLength(InitializeParameter: count) | 0 | OpOffset(Load: i) | 0 |
15+
| test.cpp:66:8:66:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
16+
| test.cpp:68:8:68:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 3 |
17+
| test.cpp:70:8:70:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
18+
| test.cpp:72:8:72:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
19+
| test.cpp:74:8:74:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | -10 |
20+
| test.cpp:76:8:76:8 | Load: a | VNLength(InitializeParameter: count) | 1 | ZeroOffset | 0 |
21+
| test.cpp:78:8:78:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Load: count) | 0 |
22+
| test.cpp:80:8:80:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Load: count) | 1 |
23+
| test.cpp:85:8:85:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Add: ... + ...) | 0 |
24+
| test.cpp:87:8:87:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Add: ... + ...) | 1 |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import cpp
2+
import experimental.library.ArrayLengthAnalysis
3+
4+
from Instruction array, Length length, int delta, Offset offset, int offsetDelta
5+
where
6+
knownArrayLength(array, length, delta, offset, offsetDelta) and
7+
array.getAUse() instanceof ArgumentOperand
8+
select array, length, delta, offset, offsetDelta
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
void *malloc(unsigned long);
2+
void sink(...);
3+
4+
typedef struct A {
5+
int a;
6+
int b;
7+
char * c;
8+
} A;
9+
10+
void test1(unsigned int count) {
11+
if (count < 1) {
12+
return;
13+
}
14+
A* aptr = (A *) malloc(sizeof(A) * count);
15+
sink(aptr); // (count, 0, Zero, 0)
16+
unsigned int* ptr = &count;
17+
sink(ptr); // (Zero, 1, Zero, 0) TODO none, as the feature is not implemented
18+
int* a = (int *) malloc(sizeof(int) * count);
19+
sink(a); // (count, 0, Zero, 0)
20+
a = (int *) malloc(sizeof(int) * (count - 1));
21+
sink(a); // (count, -1, Zero, 0)
22+
a = (int *) malloc(sizeof(int) * (count + 1));
23+
sink(a); // (count, 1, Zero, 0)
24+
a = (int *) malloc(sizeof(int) * (2 * count));
25+
sink(a); // (2*count, 0, Zero, 0)
26+
char* c = (char *)malloc(count);
27+
sink(c); // /count, 0, Zero, 0)
28+
sink((unsigned char*)c); // (count, 0, Zero, 0)
29+
void* v = c;
30+
sink(v); // (count, 0, Zero, 0)
31+
v = malloc(count);
32+
sink((char *)v); // none, as we don't track void* allocations
33+
int stack[100];
34+
sink(stack); // (Zero, 100, Zero, 0)
35+
for(unsigned int i = 0; i < count; ++i) {
36+
int* b = (int*) malloc(sizeof(int) * count);
37+
sink(b); // (count, 0, Zero, 0)
38+
}
39+
}
40+
41+
void test2(unsigned int count, bool b) {
42+
int* a = (int *) malloc(sizeof(int) * count);
43+
a = a + 2;
44+
sink(a); // (count, 0, Zero, 2)
45+
for(unsigned int i = 2; i < count; ++i) {
46+
sink(a); // none
47+
a++;
48+
sink(a); // none
49+
}
50+
a = (int*) malloc(sizeof(int) * count);
51+
if (b) {
52+
a += 2;
53+
sink(a); // (count, 0, Zero, 2)
54+
} else {
55+
a += 3;
56+
sink(a); // (count, 0, Zero, 2)
57+
}
58+
sink(a); // none
59+
a -= 2;
60+
sink(a); // none
61+
a = (int*) malloc(sizeof(int) * count);
62+
for(unsigned int i = 0; i < count; i++) {
63+
sink(&a[i]); // (count, 0, i, 0)
64+
}
65+
a = (int*) malloc(sizeof(int) * count);
66+
sink(a); // (count, 0, Zero, 0)
67+
a += 3;
68+
sink(a); // (count, 0, Zero, 3)
69+
a -= 1;
70+
sink(a); // (count, 0, Zero, 2)
71+
a -= 2;
72+
sink(a); // (count, 0, Zero, 0)
73+
a -= 10;
74+
sink(a); // (count, 0, Zero, -10)
75+
a = (int*) malloc(sizeof(int) * (count + 1));
76+
sink(a); // (count, 1, Zero, 0)
77+
a += count;
78+
sink(a); // (count, 1, count, 0);
79+
a += 1;
80+
sink(a); // (count, 1, count, 1);
81+
a -= count;
82+
sink(a); // none
83+
a = (int*) malloc(sizeof(int) * (count + 1));
84+
a += count + 1;
85+
sink(a); // TODO, should be (count, 1, count, 1), but is (count, 1, count + 1, 0)
86+
a += 1;
87+
sink(a); // TODO, should be (count, 1, count, 2), but is (count, 1, count + 1, 1)
88+
}

0 commit comments

Comments
 (0)