Skip to content

Commit b9ccfee

Browse files
committed
Revert VulnerableCalls pragma[nomagic] optimizations
The pragma[nomagic] materializations in VulnerableCalls.qll were added while diagnosing query performance issues. The root cause was a TRAP ID collision bug in the extractor (fixed in 0926f3d), not query inefficiency. Reverting to the simpler implementation: - Removes ~145 lines of pragma[nomagic] predicates - Removes unused externalRefInFunction predicate - Restores straightforward call matching logic This reverts commit 02fd414.
1 parent bc98a0f commit b9ccfee

2 files changed

Lines changed: 25 additions & 179 deletions

File tree

binary/ql/src/VulnerableCalls/VulnerableCalls.qll

Lines changed: 23 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
/**
22
* Provides predicates for finding calls that transitively make a call to a
3-
* known-vulnerable method in CIL (C# IL) and JVM binaries.
4-
*
5-
* Performance optimizations applied:
6-
* - Materialized intermediate predicates with pragma[nomagic] to prevent
7-
* expensive cross-product computations
8-
* - Direct string matching on getExternalName() instead of SSA traversal
9-
* where possible
10-
* - Separated external reference matching from static target matching
11-
* for better join ordering
3+
* known-vulnerable method in CIL (C# IL) binaries.
124
*/
135

146
private import binary
@@ -24,98 +16,19 @@ extensible predicate vulnerableCallModel(
2416
string namespace, string className, string methodName, string id
2517
);
2618

27-
/*
28-
* ============================================================================
29-
* Materialized helper predicates for performance
30-
* ============================================================================
31-
*/
32-
33-
/**
34-
* Materialized: builds the fully qualified name string for each vulnerable method
35-
* in the model. This enables efficient string comparison.
36-
*/
37-
pragma[nomagic]
38-
private predicate vulnerableMethodFqn(string fqn, string id) {
39-
exists(string namespace, string className, string methodName |
40-
vulnerableCallModel(namespace, className, methodName, id) and
41-
fqn = namespace + "." + className + "." + methodName
42-
)
43-
}
44-
45-
/**
46-
* Materialized: gets ExternalRefInstructions that directly reference vulnerable methods.
47-
* Uses direct string comparison against getExternalName() to avoid expensive
48-
* hasFullyQualifiedName() regex parsing.
49-
*/
50-
pragma[nomagic]
51-
private predicate isVulnerableExternalRef(ExternalRefInstruction eri, string id) {
52-
exists(string fqn |
53-
vulnerableMethodFqn(fqn, id) and
54-
eri.getExternalName() = fqn
55-
)
56-
}
57-
58-
/**
59-
* Materialized: gets Functions that are marked as vulnerable in the model.
60-
* Used for matching calls with static targets.
61-
*/
62-
pragma[nomagic]
63-
private predicate isVulnerableFunction(Function f, string id) {
64-
exists(string namespace, string className, string methodName |
65-
vulnerableCallModel(namespace, className, methodName, id) and
66-
f.hasFullyQualifiedName(namespace, className, methodName)
67-
)
68-
}
69-
70-
/**
71-
* Materialized: maps ExternalRefInstructions to their enclosing functions.
72-
* This avoids repeated getEnclosingFunction() lookups in the recursive predicate.
73-
*/
74-
pragma[nomagic]
75-
private predicate externalRefInFunction(ExternalRefInstruction eri, Function f) {
76-
f = eri.getEnclosingFunction()
77-
}
78-
79-
/**
80-
* Materialized: maps CallInstructions to their enclosing functions.
81-
*/
82-
pragma[nomagic]
83-
private predicate callInFunction(CallInstruction call, Function f) {
84-
f = call.getEnclosingFunction()
85-
}
86-
87-
/**
88-
* Materialized: maps ExternalRefInstructions to their external names.
89-
*/
90-
pragma[nomagic]
91-
private predicate externalRefName(ExternalRefInstruction eri, string name) {
92-
name = eri.getExternalName()
93-
}
94-
95-
/*
96-
* ============================================================================
97-
* Direct vulnerable call detection
98-
* ============================================================================
99-
*/
100-
10119
/**
10220
* A method call that has been marked as vulnerable by a model.
103-
*
104-
* This class matches calls where either:
105-
* 1. The static target is a vulnerable function (internal calls)
106-
* 2. The external reference points to a vulnerable method (external calls)
10721
*/
10822
class VulnerableMethodCall extends CallInstruction {
10923
string vulnerabilityId;
11024

11125
VulnerableMethodCall() {
112-
// Match via static target (more efficient, no SSA traversal)
113-
isVulnerableFunction(this.getStaticTarget(), vulnerabilityId)
114-
or
115-
// Match via external reference for external calls
116-
exists(ExternalRefInstruction eri |
117-
isVulnerableExternalRef(eri, vulnerabilityId) and
118-
this.getTargetOperand().getAnyDef() = eri
26+
exists(string namespace, string className, string methodName |
27+
vulnerableCallModel(namespace, className, methodName, vulnerabilityId) and
28+
this.getTargetOperand()
29+
.getAnyDef()
30+
.(ExternalRefInstruction)
31+
.hasFullyQualifiedName(namespace, className, methodName)
11932
)
12033
}
12134

@@ -131,29 +44,21 @@ VulnerableMethodCall getAVulnerableCallFromModel(string id) { result.getVulnerab
13144
/**
13245
* Gets a method that directly contains a vulnerable call.
13346
*/
134-
pragma[nomagic]
13547
Function getADirectlyVulnerableMethod(string id) {
13648
result = getAVulnerableCallFromModel(id).getEnclosingFunction()
13749
}
13850

139-
/*
140-
* ============================================================================
141-
* C# Iterator/Async State Machine handling
142-
* ============================================================================
143-
*/
144-
14551
/**
14652
* Holds if `stub` is an iterator/async stub method and `stateMachine` is its
14753
* corresponding state machine implementation (the MoveNext method).
148-
*
54+
*
14955
* Iterator/async methods in C# are compiled to:
15056
* 1. A stub method that creates a state machine object
15157
* 2. A nested class with a MoveNext method containing the actual implementation
152-
*
153-
* The pattern is: method `Foo` in class `Bar` creates `Bar.<Foo>d__N`
58+
*
59+
* The pattern is: method `Foo` in class `Bar` creates `Bar.<Foo>d__N`
15460
* and the impl is in `Bar.<Foo>d__N.MoveNext`
15561
*/
156-
pragma[nomagic]
15762
private predicate isStateMachineImplementation(Function stub, Function stateMachine) {
15863
exists(string stubName, Type stubType, string stateMachineTypeName |
15964
stubName = stub.getName() and
@@ -162,7 +67,7 @@ private predicate isStateMachineImplementation(Function stub, Function stateMach
16267
// The state machine type is nested in the same type as the stub
16368
// and named <MethodName>d__N
16469
stateMachineTypeName = stateMachine.getDeclaringType().getName() and
165-
stateMachineTypeName.matches("<" + stubName + ">d__%") and
70+
stateMachineTypeName.matches("<" + stubName + ">d__%" ) and
16671
// The state machine's declaring type's namespace should be the stub's type full name
16772
stateMachine.getDeclaringType().getNamespace() = stubType.getFullName()
16873
)
@@ -175,81 +80,32 @@ Function getStateMachineImplementation(Function stub) {
17580
isStateMachineImplementation(stub, result)
17681
}
17782

178-
/*
179-
* ============================================================================
180-
* Optimized call graph edges for transitive closure
181-
* ============================================================================
182-
*/
183-
184-
/**
185-
* Materialized: holds if function `caller` contains a call to function `callee`
186-
* via a static target (direct call, no SSA traversal needed).
187-
*/
188-
pragma[nomagic]
189-
private predicate callsViaStaticTarget(Function caller, Function callee) {
190-
exists(CallInstruction call |
191-
callInFunction(call, caller) and
192-
callee = call.getStaticTarget()
193-
)
194-
}
195-
196-
/**
197-
* Materialized: holds if function `caller` contains a call to a function
198-
* with fully qualified name `calleeFqn` via an external reference.
199-
*/
200-
pragma[nomagic]
201-
private predicate callsViaExternalRef(Function caller, string calleeFqn) {
202-
exists(CallInstruction call, ExternalRefInstruction eri |
203-
callInFunction(call, caller) and
204-
call.getTargetOperand().getAnyDef() = eri and
205-
externalRefName(eri, calleeFqn)
206-
)
207-
}
208-
209-
/**
210-
* Materialized: maps functions to their fully qualified names for join efficiency.
211-
*/
212-
pragma[nomagic]
213-
private predicate functionFqn(Function f, string fqn) {
214-
fqn = f.getFullyQualifiedName()
215-
}
216-
217-
/*
218-
* ============================================================================
219-
* Transitive vulnerable method detection
220-
* ============================================================================
221-
*/
222-
22383
/**
22484
* Gets a method that transitively calls a vulnerable method.
22585
* This computes the transitive closure of the call graph.
226-
*
86+
*
22787
* Also handles iterator/async methods by linking stub methods to their
22888
* state machine implementations.
229-
*
230-
* Performance notes:
231-
* - Uses materialized helper predicates to avoid repeated expensive operations
232-
* - Separates static target calls from external reference calls for better join ordering
233-
* - The recursion is bounded by the call graph depth
23489
*/
23590
Function getAVulnerableMethod(string id) {
236-
// Base case: direct call to vulnerable method
91+
// Direct call to vulnerable method
23792
result = getADirectlyVulnerableMethod(id)
23893
or
239-
// Transitive case 1: method calls a vulnerable method via static target
240-
exists(Function callee |
94+
// Transitive: method calls another method that is vulnerable (via ExternalRef for external calls)
95+
exists(CallInstruction call, Function callee |
96+
call.getEnclosingFunction() = result and
24197
callee = getAVulnerableMethod(id) and
242-
callsViaStaticTarget(result, callee)
98+
call.getTargetOperand().getAnyDef().(ExternalRefInstruction).getFullyQualifiedName() =
99+
callee.getFullyQualifiedName()
243100
)
244101
or
245-
// Transitive case 2: method calls a vulnerable method via external reference
246-
exists(Function callee, string calleeFqn |
247-
callee = getAVulnerableMethod(id) and
248-
functionFqn(callee, calleeFqn) and
249-
callsViaExternalRef(result, calleeFqn)
102+
// Transitive: method calls another method that is vulnerable (via static target for direct calls)
103+
exists(CallInstruction call |
104+
call.getEnclosingFunction() = result and
105+
call.getStaticTarget() = getAVulnerableMethod(id)
250106
)
251107
or
252-
// Iterator/async: if a state machine's MoveNext is vulnerable,
108+
// Iterator/async: if a state machine's MoveNext is vulnerable,
253109
// the stub method that creates it is also vulnerable
254110
exists(Function stateMachine |
255111
stateMachine = getAVulnerableMethod(id) and
@@ -260,7 +116,6 @@ Function getAVulnerableMethod(string id) {
260116
/**
261117
* Gets a public method that transitively calls a vulnerable method.
262118
*/
263-
pragma[nomagic]
264119
Function getAPublicVulnerableMethod(string id) {
265120
result = getAVulnerableMethod(id) and
266121
result.isPublic()

binary/ql/src/VulnerableCalls/VulnerableCallsSummarize.ql

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ query predicate publicVulnerableCallModel(
3030

3131
/**
3232
* Lists the direct vulnerable call sites with their enclosing method context.
33-
* Handles both external reference calls and static target calls.
3433
*/
3534
query predicate vulnerableCallLocations(
3635
VulnerableMethodCall call,
@@ -41,14 +40,6 @@ query predicate vulnerableCallLocations(
4140
string id
4241
) {
4342
call.getVulnerabilityId() = id and
44-
call.getEnclosingFunction()
45-
.hasFullyQualifiedName(callerNamespace, callerClassName, callerMethodName) and
46-
(
47-
// External call via ExternalRefInstruction
48-
targetFqn = call.getTargetOperand().getAnyDef().(ExternalRefInstruction).getFullyQualifiedName()
49-
or
50-
// Internal call via static target
51-
not exists(call.getTargetOperand().getAnyDef().(ExternalRefInstruction)) and
52-
targetFqn = call.getStaticTarget().getFullyQualifiedName()
53-
)
43+
call.getEnclosingFunction().hasFullyQualifiedName(callerNamespace, callerClassName, callerMethodName) and
44+
targetFqn = call.getTargetOperand().getAnyDef().(ExternalRefInstruction).getFullyQualifiedName()
5445
}

0 commit comments

Comments
 (0)