Repository navigation
Description
A configuration that raises accessPathLimit() above the language default (5) finds longer flows on CodeQL 2.26.2. It no longer does on 2.27.4, and the result is the same as with the default. Lower values still work.
CodeQL CLI release binaries 2.26.2 and 2.27.4 on macOS 26.6 (arm64), with the codeql/java-all and codeql/dataflow packs bundled with each.
Each program NestedN (N = 3 to 7) passes a parameter through N nested java.util.Lists and back into Runtime.exec. Nested6:
public class Nested6 {
void handle(String input) throws java.io.IOException {
java.util.List c0 = new java.util.ArrayList(); c0.add(input);
java.util.List c1 = new java.util.ArrayList(); c1.add(c0);
java.util.List c2 = new java.util.ArrayList(); c2.add(c1);
java.util.List c3 = new java.util.ArrayList(); c3.add(c2);
java.util.List c4 = new java.util.ArrayList(); c4.add(c3);
java.util.List c5 = new java.util.ArrayList(); c5.add(c4);
java.util.List cur = c5;
cur = (java.util.List) cur.get(0);
cur = (java.util.List) cur.get(0);
cur = (java.util.List) cur.get(0);
cur = (java.util.List) cur.get(0);
cur = (java.util.List) cur.get(0);
Runtime.getRuntime().exec((String) cur.get(0));
}
}The query only varies accessPathLimit:
import java
import semmle.code.java.dataflow.TaintTracking
module C implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asParameter().getName() = "input" }
predicate isSink(DataFlow::Node n) {
exists(MethodCall mc | mc.getMethod().hasName("exec") and n.asExpr() = mc.getArgument(0))
}
int accessPathLimit() { result = 8 } // varied below
}
module F = TaintTracking::Global<C>;
from DataFlow::Node s, DataFlow::Node t where F::flow(s, t)
select t, s.getEnclosingCallable().getDeclaringType().getName()Programs found (codeql database create --build-mode=none, then codeql query run):
accessPathLimit() |
2.26.2 (java-all 9.2.2, dataflow 2.1.10) | 2.27.4 (java-all 9.3.4, dataflow 2.1.17) |
|---|---|---|
| default (5) | Nested3-5 | Nested3-5 |
| 3 | Nested3 | Nested3 |
| 4 | Nested3-4 | Nested3-4 |
| 6 | Nested3-6 | Nested3-5 |
| 8 | Nested3-7 | Nested3-5 |
| 10, 20 | Nested3-7 | Nested3-5 |
I ran only 2.26.2 and 2.27.4. From the sources, the change below is not in 2.26.3 or 2.26.4, and it is in 2.27.0.
Where it seems to come from. #22392 ("Track access path lengths in stages 3 and 4") gave the cached access path fronts a length, in shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll:
TFrontHead(Content c, int length) { length in [1 .. accessPathLimit()] }
TApproxFrontHead(ContentApprox c, int length) { length in [1 .. accessPathLimit()] }MakeImplCommon does private import Lang, so this accessPathLimit() is the language default, not the configuration's. DataFlowImpl.qll then uses Config::accessPathLimit(), for example len in [2 .. Config::accessPathLimit()]. A configured limit above the default cannot be represented by these fronts, so the longer paths are pruned.
As far as I can tell, the cap is a side effect rather than a decision. #22392 is about performance and does not mention the per-configuration
limit, and the fronts are cached per language, so they cannot see the configuration. Configurations in this repository only lower the limit (the
reason ConfigSig::accessPathLimit was added in #15867), so the existing tests would not notice the change.
Expected. As before 2.27.0, a configured accessPathLimit() is honoured in both directions. If capping it at the language default is intended, for example to keep the cached fronts small, the ConfigSig::accessPathLimit documentation should say so.
Activity
sam-robson commentedon Nov 6, 2026
Thanks for the report @yuan2li . We'll look into this and get back to you.
aschackmull commentedon Nov 9, 2026
This is correct. I've opened a PR (#23049) to allow configured limits of at least twice the language default, such that configurations can still opt to increase the limit if so desired. It does come with a bit of an up-front materialisation cost, which is why I didn't increase the cap too much. Hopefully 10 should be enough.
yuan2li commentedon Nov 9, 2026
Thanks a lot. I checked the PR (#23049) locally on CodeQL 2.27.4: I applied the diff to a copy of the bundled
codeql/dataflow2.1.17 pack and ran the same configuration with--additional-packs, once with the patched copy and once with an unpatched copy. The test programs pass a tainted value through N nested lists, N = 3 to 12.accessPathLimit()So the configured limit takes effect again, up to the new cap. A value above 10 behaves like 10, without a warning. Once #23049 is merged, I'll update #23047 to say that a configuration can raise the limit up to 10 for Java, so that is documented.
aschackmull commentedon Nov 10, 2026
The PR is now merged.