Skip to content

Commit 9504455

Browse files
author
Dave Bartolomeo
authored
Merge pull request #10321 from MathiasVP/speedup-using-expired-stack-address-2
C++: Speedup 'cpp/using-expired-stack-address' by avoiding a large ne…
2 parents 9fd9a04 + d6b8f25 commit 9504455

File tree

3 files changed

+102
-12
lines changed

3 files changed

+102
-12
lines changed

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

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,46 @@ TGlobalAddress globalAddress(Instruction instr) {
163163
result = globalAddress(instr.(PointerOffsetInstruction).getLeft())
164164
}
165165

166-
/** Gets a `StoreInstruction` that may be executed after executing `store`. */
167-
pragma[inline]
168-
StoreInstruction getAStoreStrictlyAfter(StoreInstruction store) {
169-
exists(IRBlock block, int index1, int index2 |
170-
block.getInstruction(index1) = store and
171-
block.getInstruction(index2) = result and
172-
index2 > index1
166+
/**
167+
* Gets a first `StoreInstruction` that writes to address `globalAddress` reachable
168+
* from `block`.
169+
*/
170+
StoreInstruction getFirstStore(IRBlock block, TGlobalAddress globalAddress) {
171+
1 = getStoreRank(result, block, globalAddress)
172+
or
173+
not exists(getStoreRank(_, block, globalAddress)) and
174+
result = getFirstStore(block.getASuccessor(), globalAddress)
175+
}
176+
177+
/**
178+
* Gets the rank of `store` in block `block` (i.e., a rank of `1` means that it is the
179+
* first `store` to write to `globalAddress`, a rank of `2` means it's the second, etc.)
180+
*/
181+
int getStoreRank(StoreInstruction store, IRBlock block, TGlobalAddress globalAddress) {
182+
blockStoresToAddress(block, _, store, globalAddress) and
183+
store =
184+
rank[result](StoreInstruction anotherStore, int i |
185+
blockStoresToAddress(_, i, anotherStore, globalAddress)
186+
|
187+
anotherStore order by i
188+
)
189+
}
190+
191+
/**
192+
* Gets a next subsequent `StoreInstruction` to write to `globalAddress`
193+
* after `store` has done so.
194+
*/
195+
StoreInstruction getANextStoreTo(StoreInstruction store, TGlobalAddress globalAddress) {
196+
exists(IRBlock block, int rnk |
197+
rnk = getStoreRank(store, block, globalAddress) and
198+
rnk + 1 = getStoreRank(result, block, globalAddress)
173199
)
174200
or
175-
exists(IRBlock block1, IRBlock block2 |
176-
store.getBlock() = block1 and
177-
result.getBlock() = block2 and
178-
block1.getASuccessor+() = block2
201+
exists(IRBlock block, int rnk, IRBlock succ |
202+
rnk = getStoreRank(store, block, globalAddress) and
203+
not rnk + 1 = getStoreRank(_, block, globalAddress) and
204+
succ = block.getASuccessor() and
205+
result = getFirstStore(succ, globalAddress)
179206
)
180207
}
181208

@@ -192,7 +219,7 @@ predicate stackAddressEscapes(
192219
stackPointerFlowsToUse(store.getSourceValue(), vai)
193220
) and
194221
// Ensure there's no subsequent store that overrides the global address.
195-
not globalAddress = globalAddress(getAStoreStrictlyAfter(store).getDestinationAddress())
222+
not exists(getANextStoreTo(store, globalAddress))
196223
}
197224

198225
predicate blockStoresToAddress(

cpp/ql/test/query-tests/Likely Bugs/Memory Management/UsingExpiredStackAddress/UsingExpiredStackAddress.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ edges
6464
| test.cpp:201:5:201:17 | EnterFunction: maybe_deref_p | test.cpp:201:5:201:17 | VariableAddress: maybe_deref_p |
6565
| test.cpp:210:3:210:9 | Call: call to escape1 | test.cpp:201:5:201:17 | EnterFunction: maybe_deref_p |
6666
| test.cpp:210:3:210:9 | Call: call to escape1 | test.cpp:201:5:201:17 | VariableAddress: maybe_deref_p |
67+
| test.cpp:234:3:234:13 | Store: ... = ... | test.cpp:238:3:238:9 | Call: call to escape2 |
68+
| test.cpp:238:3:238:9 | Call: call to escape2 | test.cpp:239:17:239:17 | Load: p |
69+
| test.cpp:263:3:263:13 | Store: ... = ... | test.cpp:267:3:267:9 | Call: call to escape3 |
70+
| test.cpp:267:3:267:9 | Call: call to escape3 | test.cpp:268:17:268:17 | Load: p |
6771
#select
6872
| test.cpp:15:16:15:16 | Load: p | test.cpp:10:3:10:13 | Store: ... = ... | test.cpp:15:16:15:16 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:9:7:9:7 | x | x | test.cpp:10:3:10:13 | Store: ... = ... | here |
6973
| test.cpp:24:16:24:16 | Load: p | test.cpp:10:3:10:13 | Store: ... = ... | test.cpp:24:16:24:16 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:9:7:9:7 | x | x | test.cpp:10:3:10:13 | Store: ... = ... | here |
@@ -90,3 +94,5 @@ edges
9094
| test.cpp:180:14:180:19 | Load: * ... | test.cpp:154:3:154:22 | Store: ... = ... | test.cpp:180:14:180:19 | Load: * ... | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:133:7:133:8 | b2 | b2 | test.cpp:154:3:154:22 | Store: ... = ... | here |
9195
| test.cpp:181:13:181:20 | Load: access to array | test.cpp:155:3:155:21 | Store: ... = ... | test.cpp:181:13:181:20 | Load: access to array | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:134:7:134:8 | b3 | b3 | test.cpp:155:3:155:21 | Store: ... = ... | here |
9296
| test.cpp:182:14:182:19 | Load: * ... | test.cpp:156:3:156:25 | Store: ... = ... | test.cpp:182:14:182:19 | Load: * ... | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:134:7:134:8 | b3 | b3 | test.cpp:156:3:156:25 | Store: ... = ... | here |
97+
| test.cpp:239:17:239:17 | Load: p | test.cpp:234:3:234:13 | Store: ... = ... | test.cpp:239:17:239:17 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:232:7:232:7 | x | x | test.cpp:234:3:234:13 | Store: ... = ... | here |
98+
| test.cpp:268:17:268:17 | Load: p | test.cpp:263:3:263:13 | Store: ... = ... | test.cpp:268:17:268:17 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:260:7:260:7 | x | x | test.cpp:263:3:263:13 | Store: ... = ... | here |

cpp/ql/test/query-tests/Likely Bugs/Memory Management/UsingExpiredStackAddress/test.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,61 @@ int maybe_deref_p(bool b) {
209209
int field_indirect_maybe_bad(bool b) {
210210
escape1();
211211
return maybe_deref_p(b);
212+
}
213+
214+
// These next tests cover subsequent stores to the same address in the same basic block.
215+
216+
static struct S100 s102;
217+
218+
void not_escape1() {
219+
int x;
220+
s102.p = &x;
221+
s102.p = nullptr;
222+
}
223+
224+
void calls_not_escape1() {
225+
not_escape1();
226+
int x = *s102.p; // GOOD
227+
}
228+
229+
static struct S100 s103;
230+
231+
void escape2() {
232+
int x;
233+
s103.p = nullptr;
234+
s103.p = &x;
235+
}
236+
237+
void calls_escape2() {
238+
escape2();
239+
int x = *s103.p; // BAD
240+
}
241+
242+
bool unknown();
243+
static struct S100 s104;
244+
245+
void not_escape2() {
246+
int x;
247+
s104.p = &x;
248+
if(unknown()) { }
249+
s104.p = nullptr;
250+
}
251+
252+
void calls_not_escape2() {
253+
not_escape2();
254+
int x = *s104.p; // GOOD
255+
}
256+
257+
static struct S100 s105;
258+
259+
void escape3() {
260+
int x;
261+
s105.p = nullptr;
262+
if(unknown()) { }
263+
s105.p = &x;
264+
}
265+
266+
void calls_escape3() {
267+
escape3();
268+
int x = *s105.p; // BAD
212269
}

0 commit comments

Comments
 (0)