Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++: Reduce size of edges and nodes in cpp/upcast-array-pointer-arithmetic #11330

Open
wants to merge 1 commit into
base: mathiasvp/replace-ast-with-ir-use-usedataflow
Choose a base branch
from

Conversation

MathiasVP
Copy link
Contributor

@MathiasVP MathiasVP commented Nov 18, 2022

This predicate was generating very large nodes and edges relations on openjdk:

[2022-11-18 13:51:16] Evaluated non-recursive predicate nodes#query#fffffffff@e1f77atd in 575ms (size: 700124).
Evaluated relational algebra for predicate nodes#query#fffffffff@e1f77atd with tuple counts:
        700124   ~1%    {4} r1 = JOIN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT "semmle.label", Lhs.0, Lhs.1, Rhs.1
                    
        700124   ~1%    {3} r2 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT "semmle.label", In.0, In.1
             0   ~0%    {3} r3 = r2 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
             0   ~0%    {4} r4 = SCAN r3 OUTPUT "semmle.label", In.1, In.2, "(no string representation)"
                    
        700124   ~1%    {4} r5 = r1 UNION r4
             0   ~0%    {4} r6 = r5 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
             0   ~0%    {9} r7 = SCAN r6 OUTPUT In.1, In.3, "", 0, 0, 0, 0, "semmle.label", In.2
                    
        700124   ~0%    {3} r8 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT In.0, "semmle.label", In.1
        700124   ~5%    {4} r9 = JOIN r8 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, "semmle.label", Lhs.2, Rhs.1
                    
        700124   ~1%    {3} r10 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT "semmle.label", In.0, In.1
             0   ~0%    {3} r11 = r10 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
             0   ~0%    {4} r12 = SCAN r11 OUTPUT In.1, "semmle.label", In.2, "(no string representation)"
                    
        700124   ~5%    {4} r13 = r9 UNION r12
        700124   ~0%    {9} r14 = JOIN r13 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.0, Lhs.3, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5, "semmle.label", Lhs.2
                    
        700124   ~0%    {9} r15 = r7 UNION r14
                        return r15

[2022-11-18 13:51:36] Evaluated non-recursive predicate edges#query#ffffffffffffff@82c396dj in 20635ms (size: 9587083).
Evaluated relational algebra for predicate edges#query#ffffffffffffff@82c396dj with tuple counts:
        9587083   ~1%    {3} r1 = JOIN DataFlowImpl#9021cc4c::PathGraph::edges#2#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Rhs.1
                     
              0   ~0%    {2} r2 = DataFlowImpl#9021cc4c::PathGraph::edges#2#ff AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.0)
                     
              0   ~0%    {3} r3 = SCAN r2 OUTPUT In.1, In.0, "(no string representation)"
                     
        9587083   ~1%    {3} r4 = r1 UNION r3
                     
        9587083   ~0%    {4} r5 = JOIN r4 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Rhs.1
                     
        9587083   ~0%    {3} r6 = JOIN DataFlowImpl#9021cc4c::PathGraph::edges#2#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Rhs.1
                     
              0   ~0%    {3} r7 = SCAN r2 OUTPUT In.0, In.1, "(no string representation)"
                     
        9587083   ~0%    {3} r8 = r6 UNION r7
              0   ~0%    {3} r9 = r8 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
                     
              0   ~0%    {4} r10 = SCAN r9 OUTPUT In.1, In.0, In.2, "(no string representation)"
                     
        9587083   ~0%    {4} r11 = r5 UNION r10
        9587083   ~0%    {9} r12 = JOIN r11 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2, Lhs.3, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5
                     
        9587083   ~3%    {4} r13 = JOIN r4 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2, Rhs.1
                     
              0   ~0%    {4} r14 = SCAN r9 OUTPUT In.0, In.1, In.2, "(no string representation)"
                     
        9587083   ~3%    {4} r15 = r13 UNION r14
              0   ~0%    {4} r16 = r15 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
              0   ~0%    {9} r17 = SCAN r16 OUTPUT In.0, In.1, In.2, In.3, "", 0, 0, 0, 0
                     
        9587083   ~0%    {9} r18 = r12 UNION r17
        9587083   ~0%    {14} r19 = JOIN r18 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.0, Lhs.2, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5, Lhs.1, Lhs.3, Lhs.4, Lhs.5, Lhs.6, Lhs.7, Lhs.8
                     
              0   ~0%    {2} r20 = DataFlowImpl#9021cc4c::PathGraph::edges#2#ff AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.0)
              0   ~0%    {3} r21 = SCAN r20 OUTPUT In.0, In.1, "(no string representation)"
                     
        9587083   ~0%    {3} r22 = r6 UNION r21
              0   ~0%    {3} r23 = r22 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
              0   ~0%    {4} r24 = SCAN r23 OUTPUT In.0, In.1, In.2, "(no string representation)"
                     
        9587083   ~3%    {4} r25 = r13 UNION r24
              0   ~0%    {4} r26 = r25 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
              0   ~0%    {9} r27 = SCAN r26 OUTPUT In.0, In.1, In.2, In.3, "", 0, 0, 0, 0
                     
        9587083   ~0%    {9} r28 = r12 UNION r27
              0   ~0%    {9} r29 = r28 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.0)
              0   ~0%    {14} r30 = SCAN r29 OUTPUT In.0, In.2, "", 0, 0, 0, 0, In.1, In.3, In.4, In.5, In.6, In.7, In.8
                     
        9587083   ~0%    {14} r31 = r19 UNION r30
                         return r31

These are the relations the CLI uses to generate the final paths.

After these have been computed, the body of the select was used to prune out the paths that are okay (by checking that the types match):

[2022-11-18 13:50:58] Evaluated non-recursive predicate #select#cpe#1235#ffff@f7d935aq in 2ms (size: 10).
Evaluated relational algebra for predicate #select#cpe#1235#ffff@f7d935aq with tuple counts:
        9754  ~0%    {4} r1 = SCAN DataFlowImpl#9021cc4c::Configuration::hasFlowPath#2#dispred#fff OUTPUT In.1, In.2, In.2, In.1
        9754  ~2%    {5} r2 = JOIN r1 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0, Lhs.2, Lhs.3
          10  ~0%    {4} r3 = JOIN r2 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4
                     return r3

To improve this situation, this PR (ab)uses flow states to ensure that only the right sinks are matched up with the right source. This results in way smaller edges and nodes:

[2022-11-18 14:21:44] Evaluated non-recursive predicate nodes#query#fffffffff@4b3408m5 in 1ms (size: 14).
Evaluated relational algebra for predicate nodes#query#fffffffff@4b3408m5 with tuple counts:
        14   ~0%    {4} r1 = JOIN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT "semmle.label", Lhs.0, Lhs.1, Rhs.1
                
        14   ~0%    {3} r2 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT "semmle.label", In.0, In.1
         0   ~0%    {3} r3 = r2 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
         0   ~0%    {4} r4 = SCAN r3 OUTPUT "semmle.label", In.1, In.2, "(no string representation)"
                
        14   ~0%    {4} r5 = r1 UNION r4
         0   ~0%    {4} r6 = r5 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
         0   ~0%    {9} r7 = SCAN r6 OUTPUT In.1, In.3, "", 0, 0, 0, 0, "semmle.label", In.2
                
        14   ~0%    {3} r8 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT In.0, "semmle.label", In.1
        14   ~0%    {4} r9 = JOIN r8 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, "semmle.label", Lhs.2, Rhs.1
                
        14   ~0%    {3} r10 = SCAN DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff OUTPUT "semmle.label", In.0, In.1
         0   ~0%    {3} r11 = r10 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
         0   ~0%    {4} r12 = SCAN r11 OUTPUT In.1, "semmle.label", In.2, "(no string representation)"
                
        14   ~0%    {4} r13 = r9 UNION r12
        14   ~0%    {9} r14 = JOIN r13 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.0, Lhs.3, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5, "semmle.label", Lhs.2
                
        14   ~0%    {9} r15 = r7 UNION r14
                    return r15

[2022-11-18 14:21:44] Evaluated non-recursive predicate edges#query#ffffffffffffff@2407c139 in 1ms (size: 13).
Evaluated relational algebra for predicate edges#query#ffffffffffffff@2407c139 with tuple counts:
        13   ~0%    {3} r1 = JOIN DataFlowImpl#9021cc4c::PathGraph::edges#2#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Rhs.1
                
         0   ~0%    {2} r2 = DataFlowImpl#9021cc4c::PathGraph::edges#2#ff AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.0)
                
         0   ~0%    {3} r3 = SCAN r2 OUTPUT In.1, In.0, "(no string representation)"
                
        13   ~0%    {3} r4 = r1 UNION r3
                
        13   ~0%    {4} r5 = JOIN r4 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Rhs.1
                
        13   ~0%    {3} r6 = JOIN DataFlowImpl#9021cc4c::PathGraph::edges#2#ff WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Rhs.1
                
         0   ~0%    {3} r7 = SCAN r2 OUTPUT In.0, In.1, "(no string representation)"
                
        13   ~0%    {3} r8 = r6 UNION r7
         0   ~0%    {3} r9 = r8 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
                
         0   ~0%    {4} r10 = SCAN r9 OUTPUT In.1, In.0, In.2, "(no string representation)"
                
        13   ~0%    {4} r11 = r5 UNION r10
        13   ~8%    {9} r12 = JOIN r11 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2, Lhs.3, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5
                
        13   ~0%    {4} r13 = JOIN r4 WITH DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2, Rhs.1
                
         0   ~0%    {4} r14 = SCAN r9 OUTPUT In.0, In.1, In.2, "(no string representation)"
                
        13   ~0%    {4} r15 = r13 UNION r14
         0   ~0%    {4} r16 = r15 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
         0   ~0%    {9} r17 = SCAN r16 OUTPUT In.0, In.1, In.2, In.3, "", 0, 0, 0, 0
                
        13   ~8%    {9} r18 = r12 UNION r17
        13   ~0%    {14} r19 = JOIN r18 WITH DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff ON FIRST 1 OUTPUT Lhs.0, Lhs.2, Rhs.1, Rhs.2, Rhs.3, Rhs.4, Rhs.5, Lhs.1, Lhs.3, Lhs.4, Lhs.5, Lhs.6, Lhs.7, Lhs.8
                
         0   ~0%    {2} r20 = DataFlowImpl#9021cc4c::PathGraph::edges#2#ff AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.0)
         0   ~0%    {3} r21 = SCAN r20 OUTPUT In.0, In.1, "(no string representation)"
                
        13   ~0%    {3} r22 = r6 UNION r21
         0   ~0%    {3} r23 = r22 AND NOT project#DataFlowImpl#9021cc4c::PathNode::toString#0#dispred#ff(Lhs.1)
         0   ~0%    {4} r24 = SCAN r23 OUTPUT In.0, In.1, In.2, "(no string representation)"
                
        13   ~0%    {4} r25 = r13 UNION r24
         0   ~0%    {4} r26 = r25 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.1)
         0   ~0%    {9} r27 = SCAN r26 OUTPUT In.0, In.1, In.2, In.3, "", 0, 0, 0, 0
                
        13   ~8%    {9} r28 = r12 UNION r27
         0   ~0%    {9} r29 = r28 AND NOT project#DataFlowImpl#9021cc4c::PathNode::hasLocationInfo#5#dispred#ffffff(Lhs.0)
         0   ~0%    {14} r30 = SCAN r29 OUTPUT In.0, In.2, "", 0, 0, 0, 0, In.1, In.3, In.4, In.5, In.6, In.7, In.8
                
        13   ~0%    {14} r31 = r19 UNION r30
                    return r31

and since the types now match, the final select ends up like:

[2022-11-18 14:41:49] Evaluated non-recursive predicate #select#cpe#1235#ffff@d790a1nc in 0ms (size: 10).
Evaluated relational algebra for predicate #select#cpe#1235#ffff@d790a1nc with tuple counts:
        10  ~0%    {4} r1 = SCAN DataFlowImpl#9021cc4c::Configuration::hasFlowPath#2#dispred#fff OUTPUT In.1, In.2, In.2, In.1
        10  ~0%    {5} r2 = JOIN r1 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0, Lhs.2, Lhs.3
        10  ~0%    {4} r3 = JOIN r2 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4
                   return r3

Note that we still need the final type check in the body of the select since I don't think that type1.getName() = type2.getName() implies that type1 = type2.

@github-actions github-actions bot added the C++ label Nov 18, 2022
@MathiasVP MathiasVP marked this pull request as ready for review Nov 18, 2022
@MathiasVP MathiasVP requested a review from a team as a code owner Nov 18, 2022
exists(PointerAddExpr pae | pae.getAnOperand() = node.asExpr()) or
exists(ArrayExpr ae | ae.getArrayBase() = node.asExpr())
) and
getFullyConvertedType(node).getName() = state
Copy link
Contributor

@geoffw0 geoffw0 Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we're saying that the sink has to have the same (fully converted, unspecified) type as the source. Why does this help performance? Why does it not affect results?

Copy link
Contributor Author

@MathiasVP MathiasVP Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. Let me answer each question separately:

Why does it not affect results?

Because the query is already requiring that the type of the source and the sink is equivalent. To see this, notice that the body of the select is:

cfg.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
hasFullyConvertedType(source, t) and
hasFullyConvertedType(sink, t)

Why does this help performance?

Consider what would happen without this PR:

  1. We would find all the sources
  2. We would find all the sinks
  3. We compute all the flows between sources and sinks
  4. After computing all these flows, the body of the select clause restricts the alerts to only those where the source type is equivalent to the sink type.

This is what we see in:

[2022-11-18 13:50:58] Evaluated non-recursive predicate #select#cpe#1235#ffff@f7d935aq in 2ms (size: 10).
Evaluated relational algebra for predicate #select#cpe#1235#ffff@f7d935aq with tuple counts:
        9754  ~0%    {4} r1 = SCAN DataFlowImpl#9021cc4c::Configuration::hasFlowPath#2#dispred#fff OUTPUT In.1, In.2, In.2, In.1
        9754  ~2%    {5} r2 = JOIN r1 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0, Lhs.2, Lhs.3
          10  ~0%    {4} r3 = JOIN r2 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4
                     return r3

There are 9754 pairs of sources and sinks, but only 10 of those end up being reported because their types match.

Now, consider what happens with this PR:

  1. We would find all the sources (as before)
  2. We would find all the sinks (as before)
  3. We compute all the flows between sources and sinks (as before). But now there's only a flow between a source and a sink when they have the same (string representation) of their type. This means there are far fewer paths.
  4. After computing these flows, the body of the select clause restricts the alerts to only those where the source type is equivalent to the sink type (as before).

The important part is step 3. We see the effect of the PR in the evaluation of the select body:

[2022-11-18 14:41:49] Evaluated non-recursive predicate #select#cpe#1235#ffff@d790a1nc in 0ms (size: 10).
Evaluated relational algebra for predicate #select#cpe#1235#ffff@d790a1nc with tuple counts:
        10  ~0%    {4} r1 = SCAN DataFlowImpl#9021cc4c::Configuration::hasFlowPath#2#dispred#fff OUTPUT In.1, In.2, In.2, In.1
        10  ~0%    {5} r2 = JOIN r1 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0, Lhs.2, Lhs.3
        10  ~0%    {4} r3 = JOIN r2 WITH CastArrayPointerArithmetic#b621a59c::hasFullyConvertedType#2#ff ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4
                   return r3

@MathiasVP MathiasVP added the no-change-note-required This PR does not need a change note label Nov 18, 2022
@MathiasVP
Copy link
Contributor Author

MathiasVP commented Nov 18, 2022

DCA confirms that we can now analyze openjdk on the use-use flow feature branch without OOM'ing 🎉.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants