From e3021f4a65bdc3cd47264d8f440e5aa428528b7a Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 13:33:14 +0200 Subject: [PATCH 1/3] Java: Untangle code a bit to improve join order. --- .../Likely Bugs/Resource Leaks/CloseType.qll | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll index 41239f249a27..aca95c9bc1f5 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll @@ -212,33 +212,35 @@ private LocalVariableDecl getCloseableVariable(CloseableInitExpr cie) { /** * A variable on which a "close" method is called, implicitly or explicitly, directly or indirectly. */ -private predicate closeCalled(Variable v) { +private predicate closeCalled(LocalScopeVariable v) { // `close()` is implicitly called on variables declared or referenced // in the resources clause of try-with-resource statements. exists(TryStmt try | try.getAResourceVariable() = v) or // Otherwise, there should be an explicit call to a method whose name contains "close". exists(MethodCall e | - v = getCloseableVariable(_) or v instanceof Parameter or v instanceof LocalVariableDecl - | e.getMethod().getName().toLowerCase().matches("%close%") and exists(VarAccess va | va = v.getAnAccess() | e.getQualifier() = va or e.getAnArgument() = va ) - or - // The "close" call could happen indirectly inside a helper method of unknown name. - exists(int i | e.getArgument(i) = v.getAnAccess() | - exists(Parameter p, int j | p.getPosition() = j and p.getCallable() = e.getMethod() | - closeCalled(p) and i = j - or - // The helper method could be iterating over a varargs parameter. - exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() | - closeCalled(for.getVariable().getVariable()) - ) and - p.isVarargs() and - j <= i - ) + ) + or + // The "close" call could happen indirectly inside a helper method of unknown name. + exists(Parameter p | + closeCalled(p) and p.getAnArgument() = v.getAnAccess() and p.getCallable() instanceof Method + ) + or + exists(MethodCall e, int i | e.getArgument(i) = v.getAnAccess() | + exists(Parameter p, int j | + p.getPosition() = j and p.getCallable() = e.getMethod().getSourceDeclaration() + | + // The helper method could be iterating over a varargs parameter. + exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() | + closeCalled(for.getVariable().getVariable()) + ) and + p.isVarargs() and + j <= i ) ) } From 5ca35afb8c6606d633e097ee8f755525d6861b8c Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 13:34:11 +0200 Subject: [PATCH 2/3] Java: Improve joinorder in getErasedRepr. --- .../java/dataflow/internal/DataFlowPrivate.qll | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 164e2d8aa262..8b9087ecbdc5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -348,6 +348,16 @@ predicate expectsContent(Node n, ContentSet c) { FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c) } +pragma[nomagic] +private predicate numericRepresentative(RefType t) { + t.(BoxedType).getPrimitiveType().getName() = "double" +} + +pragma[nomagic] +private predicate booleanRepresentative(RefType t) { + t.(BoxedType).getPrimitiveType().getName() = "boolean" +} + /** * Gets a representative (boxed) type for `t` for the purpose of pruning * possible flow. A single type is used for all numeric types to account for @@ -356,10 +366,10 @@ predicate expectsContent(Node n, ContentSet c) { RefType getErasedRepr(Type t) { exists(Type e | e = t.getErasure() | if e instanceof NumericOrCharType - then result.(BoxedType).getPrimitiveType().getName() = "double" + then numericRepresentative(result) else if e instanceof BooleanType - then result.(BoxedType).getPrimitiveType().getName() = "boolean" + then booleanRepresentative(result) else result = e ) or From 6c8275298b4366c66d5b00e1f3f0a620683cb5ee Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 25 Jul 2025 14:41:06 +0200 Subject: [PATCH 3/3] Java: Improve ObjFlow performance. --- .../lib/semmle/code/java/dispatch/ObjFlow.qll | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll index 12fe1cba5e99..86915f802743 100644 --- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll @@ -214,24 +214,35 @@ private predicate relevantNode(ObjNode n) { exists(ObjNode mid | relevantNode(mid) and objStep(mid, n) and relevantNodeBack(n)) } -pragma[noinline] -private predicate objStepPruned(ObjNode n1, ObjNode n2) { - objStep(n1, n2) and relevantNode(n1) and relevantNode(n2) +private newtype TObjFlowNode = + TObjNode(ObjNode n) { relevantNode(n) } or + TObjType(RefType t) { source(t, _) } + +private predicate objStepPruned(TObjFlowNode node1, TObjFlowNode node2) { + exists(ObjNode n1, ObjNode n2 | + node1 = TObjNode(n1) and + node2 = TObjNode(n2) and + objStep(n1, n2) + ) + or + exists(RefType t, ObjNode n | + node1 = TObjType(t) and + node2 = TObjNode(n) and + source(t, n) + ) } -private predicate stepPlus(Node n1, Node n2) = fastTC(objStepPruned/2)(n1, n2) +private predicate flowSrc(TObjFlowNode src) { src instanceof TObjType } + +private predicate flowSink(TObjFlowNode sink) { exists(ObjNode n | sink = TObjNode(n) and sink(n)) } + +private predicate stepPlus(TObjFlowNode n1, TObjFlowNode n2) = + doublyBoundedFastTC(objStepPruned/2, flowSrc/1, flowSink/1)(n1, n2) /** * Holds if the qualifier `n` of an `Object.toString()` call might have type `t`. */ -pragma[noopt] -private predicate objType(ObjNode n, RefType t) { - exists(ObjNode n2 | - sink(n) and - (stepPlus(n2, n) or n2 = n) and - source(t, n2) - ) -} +private predicate objType(ObjNode n, RefType t) { stepPlus(TObjType(t), TObjNode(n)) } private VirtualMethodCall objectToString(ObjNode n) { result.getQualifier() = n.asExpr() and sink(n)