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
146private 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 */
10822class 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]
13547Function 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]
15762private 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 */
23590Function 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]
264119Function getAPublicVulnerableMethod ( string id ) {
265120 result = getAVulnerableMethod ( id ) and
266121 result .isPublic ( )
0 commit comments