diff --git a/rust/ql/test/library-tests/type-inference/CONSISTENCY/ExtractionConsistency.expected b/rust/ql/test/library-tests/type-inference/CONSISTENCY/ExtractionConsistency.expected new file mode 100644 index 000000000000..0b4e0c12d0c8 --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/CONSISTENCY/ExtractionConsistency.expected @@ -0,0 +1,2 @@ +extractionWarning +| loop/main.rs:1:1:1:1 | semantic analyzer unavailable (not included as a module) | diff --git a/rust/ql/test/library-tests/type-inference/loop/main.rs b/rust/ql/test/library-tests/type-inference/loop/main.rs new file mode 100644 index 000000000000..da1d19e3f62d --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/loop/main.rs @@ -0,0 +1,14 @@ +// The code in this file is not valid Rust code, but it is used to test that +// our type inference implementation does not run into an infinite loop. + +struct S(T); + +trait T1: T2> { + fn foo(self) {} +} + +trait T2: T1> { + fn bar(self) { + self.foo() + } +} diff --git a/rust/ql/test/library-tests/type-inference/loop/options.yml b/rust/ql/test/library-tests/type-inference/loop/options.yml new file mode 100644 index 000000000000..cf148dd35f86 --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/loop/options.yml @@ -0,0 +1 @@ +qltest_cargo_check: false diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index 6f63ae4382e3..5974daaa4142 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -1,4 +1,42 @@ inferType +| loop/main.rs:7:12:7:15 | SelfParam | | loop/main.rs:6:1:8:1 | trait T1 | +| loop/main.rs:7:12:7:15 | SelfParam | T | loop/main.rs:6:10:6:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | | loop/main.rs:6:1:8:1 | trait T1 | +| loop/main.rs:11:12:11:15 | SelfParam | | loop/main.rs:10:1:14:1 | trait T2 | +| loop/main.rs:11:12:11:15 | SelfParam | T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:11:12:11:15 | SelfParam | T.T.T.T.T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | | loop/main.rs:6:1:8:1 | trait T1 | +| loop/main.rs:12:9:12:12 | self | | loop/main.rs:10:1:14:1 | trait T2 | +| loop/main.rs:12:9:12:12 | self | T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T.T.T.T | loop/main.rs:4:1:4:15 | struct S | +| loop/main.rs:12:9:12:12 | self | T.T.T.T.T.T.T.T.T.T | loop/main.rs:10:10:10:10 | T | | main.rs:5:19:5:22 | SelfParam | | main.rs:2:5:2:21 | struct Foo | | main.rs:5:33:7:9 | { ... } | | main.rs:2:5:2:21 | struct Foo | | main.rs:6:13:6:16 | self | | main.rs:2:5:2:21 | struct Foo | @@ -783,6 +821,7 @@ inferType | main.rs:582:11:582:20 | ...::Foo {...} | | main.rs:2:5:2:21 | struct Foo | | main.rs:582:23:582:32 | ...::Foo {...} | | main.rs:2:5:2:21 | struct Foo | resolveMethodCallExpr +| loop/main.rs:12:9:12:18 | self.foo(...) | loop/main.rs:7:5:7:19 | fn foo | | main.rs:23:9:23:14 | x.m1(...) | main.rs:5:9:7:9 | fn m1 | | main.rs:24:9:24:14 | y.m2(...) | main.rs:9:9:11:9 | fn m2 | | main.rs:67:26:67:31 | x.m2(...) | main.rs:52:9:54:9 | fn m2 | diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 02c12a1a7817..6b2733085141 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -68,6 +68,15 @@ signature module InputSig1 { predicate typeArgumentParameterPositionMatch( TypeArgumentPosition tapos, TypeParameterPosition tppos ); + + /** + * Gets the limit on the length of type paths. Set to `none()` if there should + * be no limit. + * + * Having a limit can be useful to avoid inifinite recursion on malformed + * programs. + */ + default int getTypePathLimit() { result = 10 } } module Make1 Input1> { @@ -143,6 +152,15 @@ module Make1 Input1> { /** Holds if this type path is empty. */ predicate isEmpty() { this = "" } + /** Gets the length of this path. */ + bindingset[this] + pragma[inline_late] + int length() { + this.isEmpty() and result = 0 + or + result = strictcount(this.indexOf(".")) + 1 + } + /** Gets the path obtained by appending `suffix` onto this path. */ bindingset[suffix, result] bindingset[this, result] @@ -153,7 +171,10 @@ module Make1 Input1> { else if suffix.isEmpty() then result = this - else result = this + "." + suffix + else ( + result = this + "." + suffix and + not result.length() > getTypePathLimit() + ) } /** Holds if this path starts with `tp`, followed by `suffix`. */