From 8488039fb9dc8de56a60b56e56b672a540f67963 Mon Sep 17 00:00:00 2001 From: yoff Date: Sun, 8 Feb 2026 09:32:23 +0100 Subject: [PATCH 1/9] python: add tests for guards compared to booleans --- .../customSanitizer/test_logical.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py index 26e69b8fc050..99b5eafad413 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py +++ b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py @@ -192,6 +192,49 @@ def test_with_exception_neg(): ensure_not_tainted(s) +def test_comparison_with_bool(): + s = TAINTED_STRING + + if is_safe(s) == True: + ensure_not_tainted(s) # $ SPURIOUS: tainted + else: + ensure_tainted(s) # $ tainted + + if is_safe(s) == False: + ensure_tainted(s) # $ tainted + else: + ensure_not_tainted(s) # $ SPURIOUS: tainted + + if is_safe(s) != True: + ensure_tainted(s) # $ tainted + else: + ensure_not_tainted(s) # $ SPURIOUS: tainted + + if is_safe(s) != False: + ensure_not_tainted(s) # $ SPURIOUS: tainted + else: + ensure_tainted(s) # $ tainted + + if is_safe(s) is True: + ensure_not_tainted(s) # $ SPURIOUS: tainted + else: + ensure_tainted(s) # $ tainted + + if is_safe(s) is False: + ensure_tainted(s) # $ tainted + else: + ensure_not_tainted(s) # $ SPURIOUS: tainted + + if is_safe(s) is not True: + ensure_tainted(s) # $ tainted + else: + ensure_not_tainted(s) # $ SPURIOUS: tainted + + if is_safe(s) is not False: + ensure_not_tainted(s) # $ SPURIOUS: tainted + else: + ensure_tainted(s) # $ tainted + # Make tests runable test_basic() @@ -211,3 +254,4 @@ def test_with_exception_neg(): test_with_exception_neg() except: pass +test_comparison_with_bool() From 7351e82c9221d33c29e2eb609f422ff6d5f2e75b Mon Sep 17 00:00:00 2001 From: yoff Date: Sun, 8 Feb 2026 09:35:13 +0100 Subject: [PATCH 2/9] python: handle guards compared to boolean literals --- .../dataflow/new/internal/DataFlowPublic.qll | 30 ++++++++++++++++++- .../customSanitizer/InlineTaintTest.expected | 8 +++++ .../customSanitizer/test_logical.py | 16 +++++----- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index f63d24a300ca..3b3f7f7daebb 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -595,7 +595,8 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { result = conditionBlock.getLastNode() and flipped = false or - // Recursive case: if a guard node is a `not`-expression, + // Recursive cases: + // if a guard node is a `not`-expression, // the operand is also a guard node, but with inverted polarity. exists(UnaryExprNode notNode | result = notNode.getOperand() and @@ -603,6 +604,33 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { | notNode = guardNode(conditionBlock, flipped.booleanNot()) ) + or + // if a guard node is compared to a boolean literal, + // the other operand is also a guard node, + // but with polarity depending on the literal (and on the comparison). + exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean bool | + ( + cmpNode.operands(result, op, b) or + cmpNode.operands(b, op, result) + ) and + not result.getNode() instanceof BooleanLiteral and + ( + // comparing to the boolean + (op instanceof Eq or op instanceof Is) and + // `bool` is the value being compared against, here the value of `b` + b.getNode().(BooleanLiteral).booleanValue() = bool + or + // comparing to the negation of the boolean + (op instanceof NotEq or op instanceof IsNot) and + // again, `bool` is the value being compared against, but here it is the value of `not b` + b.getNode().(BooleanLiteral).booleanValue() = bool.booleanNot() + ) + | + // if `bool` is true, we should preserve `flipped`, otherwise we should flip it + // `flipped xor (not bool)` achieves that. + flipped in [true, false] and + cmpNode = guardNode(conditionBlock, flipped.booleanXor(bool.booleanNot())) + ) } /** diff --git a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected index 86d49b2b249b..89849279d446 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected +++ b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected @@ -22,4 +22,12 @@ isSanitizer | test_logical.py:176:24:176:24 | ControlFlowNode for s | | test_logical.py:185:24:185:24 | ControlFlowNode for s | | test_logical.py:193:24:193:24 | ControlFlowNode for s | +| test_logical.py:199:28:199:28 | ControlFlowNode for s | +| test_logical.py:206:28:206:28 | ControlFlowNode for s | +| test_logical.py:211:28:211:28 | ControlFlowNode for s | +| test_logical.py:214:28:214:28 | ControlFlowNode for s | +| test_logical.py:219:28:219:28 | ControlFlowNode for s | +| test_logical.py:226:28:226:28 | ControlFlowNode for s | +| test_logical.py:231:28:231:28 | ControlFlowNode for s | +| test_logical.py:234:28:234:28 | ControlFlowNode for s | | test_reference.py:31:28:31:28 | ControlFlowNode for s | diff --git a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py index 99b5eafad413..ff215f97f41b 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py +++ b/python/ql/test/library-tests/dataflow/tainttracking/customSanitizer/test_logical.py @@ -196,42 +196,42 @@ def test_comparison_with_bool(): s = TAINTED_STRING if is_safe(s) == True: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted if is_safe(s) == False: ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) if is_safe(s) != True: ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) if is_safe(s) != False: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted if is_safe(s) is True: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted if is_safe(s) is False: ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) if is_safe(s) is not True: ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) if is_safe(s) is not False: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted From 7df44f9418489c0ac38af957466590b48beac32c Mon Sep 17 00:00:00 2001 From: yoff Date: Sun, 8 Feb 2026 09:45:32 +0100 Subject: [PATCH 3/9] python: add change note --- .../2026-02-08-guards-compared-to-boolean-literals.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 python/ql/lib/change-notes/2026-02-08-guards-compared-to-boolean-literals.md diff --git a/python/ql/lib/change-notes/2026-02-08-guards-compared-to-boolean-literals.md b/python/ql/lib/change-notes/2026-02-08-guards-compared-to-boolean-literals.md new file mode 100644 index 000000000000..bf626c2958c5 --- /dev/null +++ b/python/ql/lib/change-notes/2026-02-08-guards-compared-to-boolean-literals.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* When a guard such as `isSafe(x)` is defined, we now also automatically handle `isSafe(x) == true` and `isSafe(x) != false`. From de9b1adf63ec15beb836f3c94a1a218f29db425a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 19 Feb 2026 10:13:35 +0100 Subject: [PATCH 4/9] Rust: Unify logic in `MethodResolution`; remove `TypeQualifierIsInstantiationOfImplSelf` logic --- .../elements/internal/InvocationExprImpl.qll | 14 +- .../typeinference/FunctionOverloading.qll | 6 +- .../internal/typeinference/FunctionType.qll | 24 +- .../internal/typeinference/TypeInference.qll | 437 ++++++++---------- .../type-inference/type-inference.expected | 13 + 5 files changed, 232 insertions(+), 262 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll index d4f68329de94..acf82066f128 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll @@ -8,7 +8,8 @@ module Impl { TPositionalArgumentPosition(int i) { i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1] } or - TSelfArgumentPosition() + TSelfArgumentPosition() or + TTypeQualifierArgumentPosition() /** An argument position in a call. */ class ArgumentPosition extends TArgumentPosition { @@ -16,13 +17,22 @@ module Impl { int asPosition() { this = TPositionalArgumentPosition(result) } /** Holds if this call position is a self argument. */ - predicate isSelf() { this instanceof TSelfArgumentPosition } + predicate isSelf() { this = TSelfArgumentPosition() } + + /** + * Holds if this call position is a type qualifier, that is, not an actual + * argument, but rather an annotation that is needed to resolve the call target, + * just like actual arguments may be need to resolve the call target. + */ + predicate isTypeQualifier() { this = TTypeQualifierArgumentPosition() } /** Gets a string representation of this argument position. */ string toString() { result = this.asPosition().toString() or this.isSelf() and result = "self" + or + this.isTypeQualifier() and result = "type qualifier" } } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll index d96fd892c73e..0f65d21dcf71 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll @@ -86,7 +86,7 @@ predicate traitTypeParameterOccurrence( TypeParameter traitTp ) { f = trait.getAssocItem(functionName) and - traitTp = getAssocFunctionTypeInclNonMethodSelfAt(f, trait, pos, path) and + traitTp = getAssocFunctionTypeAt(f, trait, pos, path) and traitTp = trait.(TraitTypeAbstraction).getATypeParameter() } @@ -124,7 +124,7 @@ private predicate functionResolutionDependsOnArgumentCand( implHasSibling(impl, trait) and traitTypeParameterOccurrence(trait, _, functionName, pos, path, traitTp) and f = impl.getASuccessor(functionName) and - not pos.isSelf() + not pos.isSelfOrTypeQualifier() ) } @@ -141,7 +141,7 @@ pragma[nomagic] private Type getAssocFunctionNonTypeParameterTypeAt( ImplItemNode impl, Function f, FunctionPosition pos, TypePath path ) { - result = getAssocFunctionTypeInclNonMethodSelfAt(f, impl, pos, path) and + result = getAssocFunctionTypeAt(f, impl, pos, path) and not result instanceof TypeParameter } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll index 74d8385bdf20..5eaddc9a7b37 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll @@ -23,6 +23,10 @@ class FunctionPosition extends TFunctionPosition { ArgumentPosition asArgumentPosition() { this = TArgumentFunctionPosition(result) } + predicate isTypeQualifier() { this.asArgumentPosition().isTypeQualifier() } + + predicate isSelfOrTypeQualifier() { this.isSelf() or this.isTypeQualifier() } + predicate isReturn() { this = TReturnFunctionPosition() } /** Gets the corresponding position when `f` is invoked via a function call. */ @@ -82,9 +86,9 @@ private newtype TAssocFunctionType = // through `i`. This ensures that `parent` is either a supertrait of `i` or // `i` in an `impl` block implementing `parent`. (parent = i or BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, _)) and - // We always include the `self` position, even for non-methods, where it is used + // We always include the type qualifer position, even for non-methods, where it is used // to match type qualifiers against the `impl` or trait type, such as in `Vec::new`. - (exists(pos.getTypeMention(f)) or pos.isSelf()) + (exists(pos.getTypeMention(f)) or pos.isTypeQualifier()) } bindingset[abs, constraint, tp] @@ -116,21 +120,9 @@ Type getAssocFunctionTypeAt(Function f, ImplOrTraitItemNode i, FunctionPosition else result = getTraitConstraintTypeAt(i, constraint, tp, suffix) ) ) -} - -/** - * Same as `getAssocFunctionTypeAt`, but also includes types at the `self` position - * for non-methods. - */ -pragma[nomagic] -Type getAssocFunctionTypeInclNonMethodSelfAt( - Function f, ImplOrTraitItemNode i, FunctionPosition pos, TypePath path -) { - result = getAssocFunctionTypeAt(f, i, pos, path) or f = i.getASuccessor(_) and - not f.hasSelfParam() and - pos.isSelf() and + pos.isTypeQualifier() and result = resolveImplOrTraitType(i, path) } @@ -192,7 +184,7 @@ class AssocFunctionType extends MkAssocFunctionType { Type getTypeAt(TypePath path) { exists(Function f, FunctionPosition pos, ImplOrTraitItemNode i, Type t | this.appliesTo(f, i, pos) and - t = getAssocFunctionTypeInclNonMethodSelfAt(f, i, pos, path) + t = getAssocFunctionTypeAt(f, i, pos, path) | not t instanceof SelfTypeParameter and result = t diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 50431c6775b6..70dfe9e90056 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -298,13 +298,11 @@ private class FunctionDeclaration extends Function { } pragma[nomagic] - Type getParameterTypeInclNonMethodSelf( - ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path - ) { + Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) { i = parent and ( not pos.isReturn() and - result = getAssocFunctionTypeInclNonMethodSelfAt(this, i.asSome(), pos, path) + result = getAssocFunctionTypeAt(this, i.asSome(), pos, path) or i.isNone() and result = this.getParam(pos.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path) @@ -315,7 +313,7 @@ private class FunctionDeclaration extends Function { i = parent and ( result = - getAssocFunctionTypeAt(this, i.asSome(), any(FunctionPosition pos | pos.isReturn()), path) + getAssocFunctionTypeAt(this, i.asSome(), any(FunctionPosition ret | ret.isReturn()), path) or i.isNone() and result = getReturnTypeMention(this).getTypeAt(path) @@ -1122,6 +1120,7 @@ private module ContextTyping { ) { exists(FunctionPosition nonRetPos | not nonRetPos.isReturn() and + not nonRetPos.isTypeQualifier() and tp = getAssocFunctionTypeAt(f, i, nonRetPos, _) ) or @@ -1374,23 +1373,21 @@ private module MethodResolution { */ pragma[nomagic] private predicate methodInfo( - Method m, string name, int arity, ImplOrTraitItemNode i, AssocFunctionType selfType, - TypePath strippedTypePath, Type strippedType + Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType ) { - exists(FunctionPosition pos | - assocFunctionInfo(m, name, arity, i, pos, selfType) and - strippedType = selfType.getTypeAt(strippedTypePath) and - isComplexRootStripped(strippedTypePath, strippedType) and - pos.isSelf() - ) + assocFunctionInfo(m, name, arity, i, selfPos, selfType) and + strippedType = selfType.getTypeAt(strippedTypePath) and + isComplexRootStripped(strippedTypePath, strippedType) and + selfPos.isSelfOrTypeQualifier() } pragma[nomagic] private predicate methodInfoTypeParam( - Method m, string name, int arity, ImplOrTraitItemNode i, AssocFunctionType selfType, - TypePath strippedTypePath, TypeParam tp + Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, TypeParam tp ) { - methodInfo(m, name, arity, i, selfType, strippedTypePath, TTypeParamTypeParameter(tp)) + methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, TTypeParamTypeParameter(tp)) } /** @@ -1400,12 +1397,12 @@ private module MethodResolution { */ pragma[inline] private predicate methodInfoNonBlanket( - Method m, string name, int arity, ImplOrTraitItemNode i, AssocFunctionType selfType, - TypePath strippedTypePath, Type strippedType + Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType ) { ( - methodInfo(m, name, arity, i, selfType, strippedTypePath, strippedType) or - methodInfoTypeParam(m, name, arity, i, selfType, strippedTypePath, _) + methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, strippedType) or + methodInfoTypeParam(m, name, arity, selfPos, i, selfType, strippedTypePath, _) ) and not BlanketImplementation::isBlanketLike(i, _, _) } @@ -1420,24 +1417,22 @@ private module MethodResolution { */ pragma[nomagic] private predicate methodInfoBlanketLike( - Method m, string name, int arity, ImplItemNode impl, Trait trait, AssocFunctionType selfType, - TypePath blanketPath, TypeParam blanketTypeParam + Method m, string name, int arity, FunctionPosition selfPos, ImplItemNode impl, Trait trait, + AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam ) { - exists(FunctionPosition pos | - functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath, - blanketTypeParam) and - pos.isSelf() - ) + functionInfoBlanketLike(m, name, arity, impl, trait, selfPos, selfType, blanketPath, + blanketTypeParam) and + selfPos.isSelfOrTypeQualifier() } pragma[nomagic] private predicate methodTraitInfo(string name, int arity, Trait trait) { exists(ImplItemNode i | - methodInfo(_, name, arity, i, _, _, _) and + methodInfo(_, name, arity, _, i, _, _, _) and trait = i.resolveTraitTy() ) or - methodInfo(_, name, arity, trait, _, _, _) + methodInfo(_, name, arity, _, trait, _, _, _) } pragma[nomagic] @@ -1479,12 +1474,12 @@ private module MethodResolution { bindingset[mc, strippedTypePath, strippedType] pragma[inline_late] private predicate methodCallNonBlanketCandidate( - MethodCall mc, Method m, ImplOrTraitItemNode i, AssocFunctionType self, - TypePath strippedTypePath, Type strippedType + MethodCall mc, Method m, FunctionPosition selfPos, ImplOrTraitItemNode i, + AssocFunctionType self, TypePath strippedTypePath, Type strippedType ) { exists(string name, int arity | mc.hasNameAndArity(name, arity) and - methodInfoNonBlanket(m, name, arity, i, self, strippedTypePath, strippedType) + methodInfoNonBlanket(m, name, arity, selfPos, i, self, strippedTypePath, strippedType) | i = any(Impl impl | @@ -1513,12 +1508,12 @@ private module MethodResolution { bindingset[mc] pragma[inline_late] private predicate methodCallBlanketLikeCandidate( - MethodCall mc, Method m, ImplItemNode impl, AssocFunctionType self, TypePath blanketPath, - TypeParam blanketTypeParam + MethodCall mc, Method m, FunctionPosition selfPos, ImplItemNode impl, AssocFunctionType self, + TypePath blanketPath, TypeParam blanketTypeParam ) { exists(string name, int arity | mc.hasNameAndArity(name, arity) and - methodInfoBlanketLike(m, name, arity, impl, _, self, blanketPath, blanketTypeParam) + methodInfoBlanketLike(m, name, arity, selfPos, impl, _, self, blanketPath, blanketTypeParam) | methodCallVisibleImplTraitCandidate(mc, impl) or @@ -1569,20 +1564,20 @@ private module MethodResolution { result = inferType(this.getArg(pos), path) } - private Type getReceiverTypeAt(TypePath path) { - result = this.getArgumentTypeAt(any(ArgumentPosition pos | pos.isSelf()), path) - } - /** * Same as `getACandidateReceiverTypeAt`, but without borrows. */ pragma[nomagic] - Type getACandidateReceiverTypeAtNoBorrow(DerefChain derefChain, TypePath path) { - result = this.getReceiverTypeAt(path) and + Type getACandidateReceiverTypeAtNoBorrow( + FunctionPosition selfPos, DerefChain derefChain, TypePath path + ) { + result = this.getArgumentTypeAt(selfPos.asArgumentPosition(), path) and + selfPos.isSelfOrTypeQualifier() and derefChain.isEmpty() or exists(DerefImplItemNode impl, DerefChain suffix | - result = ImplicitDeref::getDereferencedCandidateReceiverType(this, impl, suffix, path) and + result = + ImplicitDeref::getDereferencedCandidateReceiverType(this, selfPos, impl, suffix, path) and derefChain = DerefChain::cons(impl, suffix) ) } @@ -1597,10 +1592,11 @@ private module MethodResolution { */ pragma[nomagic] private predicate hasIncompatibleTarget( - ImplOrTraitItemNode i, DerefChain derefChain, BorrowKind borrow, Type root + ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, + Type root ) { exists(TypePath path | - ReceiverIsInstantiationOfSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, + ReceiverIsInstantiationOfSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, selfPos, derefChain, borrow), i, _, path) and path.isCons(root.getATypeParameter(), _) ) @@ -1614,13 +1610,13 @@ private module MethodResolution { */ pragma[nomagic] private predicate hasIncompatibleBlanketLikeTarget( - ImplItemNode impl, DerefChain derefChain, BorrowKind borrow + ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow ) { ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, - derefChain, borrow), impl, _, _) + selfPos, derefChain, borrow), impl, _, _) or ReceiverSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkMethodCallCand(this, - derefChain, borrow), impl) + selfPos, derefChain, borrow), impl) } /** @@ -1628,60 +1624,66 @@ private module MethodResolution { */ pragma[nomagic] Type getANonPseudoCandidateReceiverTypeAt( - DerefChain derefChain, BorrowKind borrow, TypePath path + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - result = this.getACandidateReceiverTypeAt(derefChain, borrow, path) and + result = this.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, path) and result != TNeverType() and result != TUnknownType() } pragma[nomagic] private Type getComplexStrippedType( - DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath ) { - result = this.getANonPseudoCandidateReceiverTypeAt(derefChain, borrow, strippedTypePath) and + result = + this.getANonPseudoCandidateReceiverTypeAt(selfPos, derefChain, borrow, strippedTypePath) and isComplexRootStripped(strippedTypePath, result) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleNonBlanketLikeTargetCheck( - DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, Type strippedType + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, + Type strippedType ) { forall(ImplOrTraitItemNode i | - methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType) + methodCallNonBlanketCandidate(this, _, selfPos, i, _, strippedTypePath, strippedType) | - this.hasIncompatibleTarget(i, derefChain, borrow, strippedType) + this.hasIncompatibleTarget(i, selfPos, derefChain, borrow, strippedType) ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleTargetCheck( - DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, Type strippedType + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, + Type strippedType ) { - this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath, + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, strippedType) and - forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) | - this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow) + forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) | + this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleNonBlanketTargetCheck( - DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, Type strippedType + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, + Type strippedType ) { - this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath, + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, strippedType) and forall(ImplItemNode i | - methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation() + methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) and + not i.isBlanketImplementation() | - this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow) + this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) ) } // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetNoBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { ( this.supportsAutoDerefAndBorrow() @@ -1690,12 +1692,14 @@ private module MethodResolution { // `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` derefChain.isEmpty() ) and - strippedType = this.getComplexStrippedType(derefChain, TNoBorrowKind(), strippedTypePath) and + strippedType = + this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetNoBorrowToIndex(derefChain, strippedTypePath, strippedType, n - 1) and + this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, + n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleTargetCheck(derefChain, TNoBorrowKind(), strippedTypePath, t) + this.hasNoCompatibleTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t) ) } @@ -1704,9 +1708,9 @@ private module MethodResolution { * have a matching method target. */ pragma[nomagic] - predicate hasNoCompatibleTargetNoBorrow(DerefChain derefChain) { + predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetNoBorrowToIndex(derefChain, _, strippedType, + this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1714,7 +1718,8 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetNoBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { ( this.supportsAutoDerefAndBorrow() @@ -1723,13 +1728,15 @@ private module MethodResolution { // `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` derefChain.isEmpty() ) and - strippedType = this.getComplexStrippedType(derefChain, TNoBorrowKind(), strippedTypePath) and + strippedType = + this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(derefChain, strippedTypePath, + this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(derefChain, TNoBorrowKind(), strippedTypePath, t) + this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TNoBorrowKind(), + strippedTypePath, t) ) } @@ -1738,9 +1745,11 @@ private module MethodResolution { * a matching non-blanket method target. */ pragma[nomagic] - predicate hasNoCompatibleNonBlanketTargetNoBorrow(DerefChain derefChain) { + predicate hasNoCompatibleNonBlanketTargetNoBorrow( + FunctionPosition selfPos, DerefChain derefChain + ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(derefChain, _, strippedType, + this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1748,17 +1757,18 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetSharedBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { - this.hasNoCompatibleTargetNoBorrow(derefChain) and + this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetSharedBorrowToIndex(derefChain, strippedTypePath, strippedType, - n - 1) and + this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, + strippedType, n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, TSomeBorrowKind(false), + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) } @@ -1768,9 +1778,9 @@ private module MethodResolution { * by a shared borrow, does not have a matching method target. */ pragma[nomagic] - predicate hasNoCompatibleTargetSharedBorrow(DerefChain derefChain) { + predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetSharedBorrowToIndex(derefChain, _, strippedType, + this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1778,16 +1788,18 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetMutBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { - this.hasNoCompatibleTargetSharedBorrow(derefChain) and + this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetMutBorrowToIndex(derefChain, strippedTypePath, strippedType, n - 1) and + this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, + strippedType, n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, TSomeBorrowKind(true), + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) } @@ -1797,9 +1809,9 @@ private module MethodResolution { * by a `mut` borrow, does not have a matching method target. */ pragma[nomagic] - predicate hasNoCompatibleTargetMutBorrow(DerefChain derefChain) { + predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetMutBorrowToIndex(derefChain, _, strippedType, + this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1807,17 +1819,18 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetSharedBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { - this.hasNoCompatibleTargetNoBorrow(derefChain) and + this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(derefChain, strippedTypePath, + this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(derefChain, TSomeBorrowKind(false), + this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) } @@ -1827,27 +1840,30 @@ private module MethodResolution { * by a shared borrow, does not have a matching non-blanket method target. */ pragma[nomagic] - predicate hasNoCompatibleNonBlanketTargetSharedBorrow(DerefChain derefChain) { + predicate hasNoCompatibleNonBlanketTargetSharedBorrow( + FunctionPosition selfPos, DerefChain derefChain + ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, _, + strippedType, getLastLookupTypeIndex(strippedType)) ) } // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetMutBorrowToIndex( - DerefChain derefChain, TypePath strippedTypePath, Type strippedType, int n + FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, + int n ) { - this.hasNoCompatibleNonBlanketTargetSharedBorrow(derefChain) and + this.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(derefChain, strippedTypePath, + this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(derefChain, TSomeBorrowKind(true), + this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) } @@ -1857,9 +1873,11 @@ private module MethodResolution { * by a `mut` borrow, does not have a matching non-blanket method target. */ pragma[nomagic] - predicate hasNoCompatibleNonBlanketTargetMutBorrow(DerefChain derefChain) { + predicate hasNoCompatibleNonBlanketTargetMutBorrow( + FunctionPosition selfPos, DerefChain derefChain + ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(derefChain, _, strippedType, + this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1877,18 +1895,20 @@ private module MethodResolution { * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers */ pragma[nomagic] - Type getACandidateReceiverTypeAt(DerefChain derefChain, BorrowKind borrow, TypePath path) { - result = this.getACandidateReceiverTypeAtNoBorrow(derefChain, path) and + Type getACandidateReceiverTypeAt( + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path + ) { + result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path) and borrow.isNoBorrow() or exists(RefType rt | // first try shared borrow this.supportsAutoDerefAndBorrow() and - this.hasNoCompatibleTargetNoBorrow(derefChain) and + this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and borrow.isSharedBorrow() or // then try mutable borrow - this.hasNoCompatibleTargetSharedBorrow(derefChain) and + this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and borrow.isMutableBorrow() | rt = borrow.getRefType() and @@ -1897,7 +1917,7 @@ private module MethodResolution { result = rt or exists(TypePath suffix | - result = this.getACandidateReceiverTypeAtNoBorrow(derefChain, suffix) and + result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, suffix) and path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix) ) ) @@ -1912,7 +1932,7 @@ private module MethodResolution { pragma[nomagic] Method resolveCallTarget(ImplOrTraitItemNode i, DerefChain derefChain, BorrowKind borrow) { exists(MethodCallCand mcc | - mcc = MkMethodCallCand(this, derefChain, borrow) and + mcc = MkMethodCallCand(this, _, derefChain, borrow) and result = mcc.resolveCallTarget(i) ) } @@ -1924,7 +1944,7 @@ private module MethodResolution { */ predicate argumentHasImplicitDerefChainBorrow(Expr arg, DerefChain derefChain, BorrowKind borrow) { exists(this.resolveCallTarget(_, derefChain, borrow)) and - arg = this.getArg(any(ArgumentPosition pos | pos.isSelf())) and + arg = this.getArg(any(ArgumentPosition apos | apos.isSelf())) and not (derefChain.isEmpty() and borrow.isNoBorrow()) } } @@ -1982,37 +2002,6 @@ private module MethodResolution { forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | i instanceof Method) } - bindingset[this, f] - pragma[inline_late] - private predicate hasTypeQualifiedCandidateFilter(Function f, ImplItemNode impl) { - f = impl.getAnAssocItem() - or - exists(TraitItemNode trait | - f = trait.getAnAssocItem() and - methodCallVisibleTraitCandidate(this, trait) and - impl.resolveTraitTy() = trait - ) - } - - /** - * Holds if this call has a type qualifier, and we are able to resolve, - * using path resolution, the method to a member of `impl` or the trait - * being implemented by `impl` (when this call os of the kind - * `::f()`). - * - * When this is the case, we still want to check that the type qualifier - * is an instance of the type being implemented, which is done in - * `TypeQualifierIsInstantiationOfImplSelfInput`. - */ - pragma[nomagic] - predicate hasTypeQualifiedCandidate(ImplItemNode impl) { - exists(Function f | - exists(getCallExprTypeQualifier(this, _, _)) and - f = CallExprImpl::getResolvedFunction(this) and - this.hasTypeQualifiedCandidateFilter(f, impl) - ) - } - pragma[nomagic] override predicate hasNameAndArity(string name, int arity) { name = CallExprImpl::getFunctionPath(this).getText() and @@ -2026,15 +2015,17 @@ private module MethodResolution { result = super.getSyntacticPositionalArgument(pos.asPosition() + 1) } - // needed for `TypeQualifierIsInstantiationOfImplSelfInput` - Type getTypeAt(TypePath path) { - result = substituteLookupTraits(getCallExprTypeQualifier(this, path, _)) + override Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) { + result = super.getArgumentTypeAt(pos, path) + or + pos.isTypeQualifier() and + result = getCallExprTypeQualifier(this, path, _) } pragma[nomagic] predicate hasNoInherentTarget() { // `_` is fine below, because auto-deref/borrow is not supported - MkMethodCallCand(this, _, _).(MethodCallCand).hasNoInherentTarget() + MkMethodCallCand(this, _, _, _).(MethodCallCand).hasNoInherentTarget() } override predicate supportsAutoDerefAndBorrow() { none() } @@ -2089,9 +2080,9 @@ private module MethodResolution { override predicate argumentHasImplicitDerefChainBorrow( Expr arg, DerefChain derefChain, BorrowKind borrow ) { - exists(ArgumentPosition pos, boolean isMutable | - this.implicitBorrowAt(pos, isMutable) and - arg = this.getArg(pos) and + exists(ArgumentPosition apos, boolean isMutable | + this.implicitBorrowAt(apos, isMutable) and + arg = this.getArg(apos) and derefChain = DerefChain::nil() and borrow = TSomeBorrowKind(isMutable) ) @@ -2109,45 +2100,51 @@ private module MethodResolution { } private newtype TMethodCallCand = - MkMethodCallCand(MethodCall mc, DerefChain derefChain, BorrowKind borrow) { - exists(mc.getACandidateReceiverTypeAt(derefChain, borrow, _)) + MkMethodCallCand( + MethodCall mc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow + ) { + exists(mc.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, _)) } /** A method call with a dereference chain and a potential borrow. */ private class MethodCallCand extends MkMethodCallCand { MethodCall mc_; + FunctionPosition selfPos; DerefChain derefChain; BorrowKind borrow; - MethodCallCand() { this = MkMethodCallCand(mc_, derefChain, borrow) } + MethodCallCand() { this = MkMethodCallCand(mc_, selfPos, derefChain, borrow) } MethodCall getMethodCall() { result = mc_ } Type getTypeAt(TypePath path) { result = - substituteLookupTraits(mc_.getANonPseudoCandidateReceiverTypeAt(derefChain, borrow, path)) + substituteLookupTraits(mc_.getANonPseudoCandidateReceiverTypeAt(selfPos, derefChain, borrow, + path)) } pragma[nomagic] predicate hasNoCompatibleNonBlanketTarget() { - mc_.hasNoCompatibleNonBlanketTargetSharedBorrow(derefChain) and + mc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and borrow.isSharedBorrow() or - mc_.hasNoCompatibleNonBlanketTargetMutBorrow(derefChain) and + mc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPos, derefChain) and borrow.isMutableBorrow() or - mc_.hasNoCompatibleNonBlanketTargetNoBorrow(derefChain) and + mc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPos, derefChain) and borrow.isNoBorrow() } pragma[nomagic] predicate hasSignature( - MethodCall mc, TypePath strippedTypePath, Type strippedType, string name, int arity + MethodCall mc, FunctionPosition selfPos_, TypePath strippedTypePath, Type strippedType, + string name, int arity ) { strippedType = this.getTypeAt(strippedTypePath) and isComplexRootStripped(strippedTypePath, strippedType) and mc = mc_ and - mc.hasNameAndArity(name, arity) + mc.hasNameAndArity(name, arity) and + selfPos = selfPos_ } /** @@ -2168,9 +2165,9 @@ private module MethodResolution { mc_.hasTrait() or exists(TypePath strippedTypePath, Type strippedType, string name, int arity | - this.hasSignature(_, strippedTypePath, strippedType, name, arity) and + this.hasSignature(_, selfPos, strippedTypePath, strippedType, name, arity) and forall(Impl i | - methodInfoNonBlanket(_, name, arity, i, _, strippedTypePath, strippedType) and + methodInfoNonBlanket(_, name, arity, selfPos, i, _, strippedTypePath, strippedType) and not i.hasTrait() | this.hasIncompatibleInherentTarget(i) @@ -2178,18 +2175,9 @@ private module MethodResolution { ) } - pragma[nomagic] - private predicate typeQualifierIsInstantiationOf(ImplItemNode i) { - TypeQualifierIsInstantiationOfImplSelf::isInstantiationOf(mc_, i, _) - } - pragma[nomagic] private predicate argIsInstantiationOf(ImplOrTraitItemNode i, string name, int arity) { - ( - ReceiverIsInstantiationOfSelfParam::argIsInstantiationOf(this, i, _) - or - this.typeQualifierIsInstantiationOf(i) - ) and + ReceiverIsInstantiationOfSelfParam::argIsInstantiationOf(this, i, _) and mc_.hasNameAndArity(name, arity) } @@ -2222,21 +2210,23 @@ private module MethodResolution { */ private module ImplicitDeref { private newtype TMethodCallDerefCand = - MkMethodCallDerefCand(MethodCall mc, DerefChain derefChain) { + MkMethodCallDerefCand(MethodCall mc, FunctionPosition selfPos, DerefChain derefChain) { mc.supportsAutoDerefAndBorrow() and - mc.hasNoCompatibleTargetMutBorrow(derefChain) and - exists(mc.getACandidateReceiverTypeAtNoBorrow(derefChain, TypePath::nil())) + mc.hasNoCompatibleTargetMutBorrow(selfPos, derefChain) and + exists(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) } /** A method call with a dereference chain. */ private class MethodCallDerefCand extends MkMethodCallDerefCand { MethodCall mc; + FunctionPosition selfPos; DerefChain derefChain; - MethodCallDerefCand() { this = MkMethodCallDerefCand(mc, derefChain) } + MethodCallDerefCand() { this = MkMethodCallDerefCand(mc, selfPos, derefChain) } Type getTypeAt(TypePath path) { - result = substituteLookupTraits(mc.getACandidateReceiverTypeAtNoBorrow(derefChain, path)) and + result = + substituteLookupTraits(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path)) and result != TNeverType() and result != TUnknownType() } @@ -2270,10 +2260,11 @@ private module MethodResolution { */ pragma[nomagic] Type getDereferencedCandidateReceiverType( - MethodCall mc, DerefImplItemNode impl, DerefChain derefChain, TypePath path + MethodCall mc, FunctionPosition selfPos, DerefImplItemNode impl, DerefChain derefChain, + TypePath path ) { exists(MethodCallDerefCand mcc, TypePath exprPath | - mcc = MkMethodCallDerefCand(mc, derefChain) and + mcc = MkMethodCallDerefCand(mc, selfPos, derefChain) and MethodCallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(mcc, impl, _, exprPath, result) and exprPath.isCons(getDerefTargetTypeParameter(), path) @@ -2288,9 +2279,9 @@ private module MethodResolution { predicate hasBlanketCandidate( MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam ) { - exists(MethodCall mc, BorrowKind borrow | - mcc = MkMethodCallCand(mc, _, borrow) and - methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and + exists(MethodCall mc, FunctionPosition selfPos, BorrowKind borrow | + mcc = MkMethodCallCand(mc, selfPos, _, borrow) and + methodCallBlanketLikeCandidate(mc, _, selfPos, impl, _, blanketPath, blanketTypeParam) and // Only apply blanket implementations when no other implementations are possible; // this is to account for codebases that use the (unstable) specialization feature // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as @@ -2320,14 +2311,14 @@ private module MethodResolution { MethodCallCand mcc, ImplOrTraitItemNode i, AssocFunctionType selfType ) { exists( - MethodCall mc, Method m, string name, int arity, TypePath strippedTypePath, + MethodCall mc, FunctionPosition selfPos, Method m, TypePath strippedTypePath, Type strippedType | - mcc.hasSignature(mc, strippedTypePath, strippedType, name, arity) + mcc.hasSignature(mc, selfPos, strippedTypePath, strippedType, _, _) | - methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType) + methodCallNonBlanketCandidate(mc, m, selfPos, i, selfType, strippedTypePath, strippedType) or - methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and + methodCallBlanketLikeCandidate(mc, m, selfPos, i, selfType, _, _) and ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i) ) } @@ -2346,7 +2337,7 @@ private module MethodResolution { } predicate relevantConstraint(AssocFunctionType constraint) { - methodInfo(_, _, _, _, constraint, _, _) + methodInfo(_, _, _, _, _, constraint, _, _) } } @@ -2364,57 +2355,22 @@ private module MethodResolution { predicate potentialInstantiationOf( MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint ) { - methodCallBlanketLikeCandidate(mcc.getMethodCall(), _, abs, constraint, _, _) and - if abs.(Impl).hasTrait() - then - // inherent methods take precedence over trait methods, so only allow - // trait methods when there are no matching inherent methods - mcc.hasNoInherentTarget() - else any() + exists(MethodCall mc, FunctionPosition selfPos | + mcc = MkMethodCallCand(mc, selfPos, _, _) and + methodCallBlanketLikeCandidate(mc, _, selfPos, abs, constraint, _, _) and + if abs.(Impl).hasTrait() + then + // inherent methods take precedence over trait methods, so only allow + // trait methods when there are no matching inherent methods + mcc.hasNoInherentTarget() + else any() + ) } } private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam = ArgIsInstantiationOf; - /** - * A configuration for matching the type qualifier of a method call - * against the type being implemented in an `impl` block. For example, - * in `Foo::::m(x)`, we check that the type `Foo` is an - * instance of the type being implemented. - */ - private module TypeQualifierIsInstantiationOfImplSelfInput implements - IsInstantiationOfInputSig - { - pragma[nomagic] - private predicate potentialInstantiationOf0( - MethodCallCallExpr ce, ImplItemNode impl, TypeMention constraint - ) { - ce.hasTypeQualifiedCandidate(impl) and - constraint = impl.getSelfPath() - } - - pragma[nomagic] - predicate potentialInstantiationOf( - MethodCallCallExpr ce, TypeAbstraction abs, TypeMention constraint - ) { - potentialInstantiationOf0(ce, abs, constraint) and - if abs.(Impl).hasTrait() - then - // inherent methods take precedence over trait methods, so only allow - // trait methods when there are no matching inherent methods - ce.hasNoInherentTarget() - else any() - } - - predicate relevantConstraint(TypeMention constraint) { - potentialInstantiationOf0(_, _, constraint) - } - } - - private module TypeQualifierIsInstantiationOfImplSelf = - IsInstantiationOf; - /** * A configuration for anti-matching the type of a receiver against the type of * a `self` parameter in an inherent method. @@ -2492,7 +2448,7 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi } Type getDeclaredType(DeclarationPosition dpos, TypePath path) { - result = m.getParameterTypeInclNonMethodSelf(someParent, dpos, path) + result = m.getParameterType(someParent, dpos, path) or dpos.isReturn() and result = m.getReturnType(someParent, path) @@ -2544,9 +2500,8 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi pragma[nomagic] private Type getInferredSelfType(AccessPosition apos, string derefChainBorrow, TypePath path) { exists(DerefChain derefChain, BorrowKind borrow | - result = this.getACandidateReceiverTypeAt(derefChain, borrow, path) and - derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and - apos.isSelf() + result = this.getACandidateReceiverTypeAt(apos, derefChain, borrow, path) and + derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) ) } @@ -2749,7 +2704,7 @@ private module NonMethodResolution { exists(FunctionPosition pos0, TypePath path0 | traitFunctionResolutionDependsOnArgument0(trait, traitFunction, pos0, impl, implFunction, path0, traitTp0) and - exists(getAssocFunctionTypeInclNonMethodSelfAt(implFunction, impl, pos0, path0)) + exists(getAssocFunctionTypeAt(implFunction, impl, pos0, path0)) ) ) } @@ -2791,7 +2746,7 @@ private module NonMethodResolution { f = impl.getAnAssocItem() and not impl.(Impl).hasTrait() and tp = TTypeParamTypeParameter(impl.resolveSelfTy().getTypeParam(0)) and - pos.isSelf() + pos.isTypeQualifier() } pragma[nomagic] @@ -3053,7 +3008,7 @@ private module NonMethodResolution { isDefaultTypeArg = false or result = getCallExprTypeQualifier(call, path, isDefaultTypeArg) and - pos.isSelf() + pos.isTypeQualifier() } private module NonMethodArgsAreInstantiationsOfBlanketInput implements @@ -3223,7 +3178,7 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { } override Type getParameterType(DeclarationPosition dpos, TypePath path) { - result = f.getParameterTypeInclNonMethodSelf(i, dpos, path) + result = f.getParameterType(i, dpos, path) } override Type getReturnType(TypePath path) { result = f.getReturnType(i, path) } @@ -3267,7 +3222,7 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { pragma[nomagic] Type getInferredType(AccessPosition apos, TypePath path) { - apos.isSelf() and + apos.isTypeQualifier() and result = getCallExprTypeQualifier(this, path, false) or result = inferType(this.getNodeAt(apos), path) @@ -4267,8 +4222,8 @@ private module Debug { Locatable getRelevantLocatable() { exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - filepath.matches("%/sqlx.rs") and - startline = [56 .. 60] + filepath.matches("%/main.rs") and + startline = 103 ) } 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 fa0f9eaa0c28..5d0b167074ae 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -11177,12 +11177,15 @@ inferType | main.rs:2319:18:2319:23 | range1 | Idx | {EXTERNAL LOCATION} | u16 | | main.rs:2319:25:2319:26 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2323:13:2323:17 | vals3 | | {EXTERNAL LOCATION} | Vec | +| main.rs:2323:13:2323:17 | vals3 | A | {EXTERNAL LOCATION} | Global | | main.rs:2323:21:2323:33 | MacroExpr | | {EXTERNAL LOCATION} | Vec | +| main.rs:2323:21:2323:33 | MacroExpr | A | {EXTERNAL LOCATION} | Global | | main.rs:2323:26:2323:26 | 1 | | {EXTERNAL LOCATION} | i32 | | main.rs:2323:29:2323:29 | 2 | | {EXTERNAL LOCATION} | i32 | | main.rs:2323:32:2323:32 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:2324:9:2324:25 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2324:18:2324:22 | vals3 | | {EXTERNAL LOCATION} | Vec | +| main.rs:2324:18:2324:22 | vals3 | A | {EXTERNAL LOCATION} | Global | | main.rs:2324:24:2324:25 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2326:13:2326:18 | vals4a | | {EXTERNAL LOCATION} | Vec | | main.rs:2326:13:2326:18 | vals4a | A | {EXTERNAL LOCATION} | Global | @@ -11272,18 +11275,25 @@ inferType | main.rs:2340:18:2340:22 | vals7 | T | {EXTERNAL LOCATION} | u8 | | main.rs:2340:24:2340:25 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2342:13:2342:19 | matrix1 | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:13:2342:19 | matrix1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:23:2342:50 | MacroExpr | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:23:2342:50 | MacroExpr | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:28:2342:37 | (...) | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:28:2342:37 | (...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:28:2342:37 | MacroExpr | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:28:2342:37 | MacroExpr | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:33:2342:33 | 1 | | {EXTERNAL LOCATION} | i32 | | main.rs:2342:36:2342:36 | 2 | | {EXTERNAL LOCATION} | i32 | | main.rs:2342:40:2342:49 | (...) | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:40:2342:49 | (...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:40:2342:49 | MacroExpr | | {EXTERNAL LOCATION} | Vec | +| main.rs:2342:40:2342:49 | MacroExpr | A | {EXTERNAL LOCATION} | Global | | main.rs:2342:45:2342:45 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:2342:48:2342:48 | 4 | | {EXTERNAL LOCATION} | i32 | | main.rs:2344:13:2344:13 | _ | | {EXTERNAL LOCATION} | () | | main.rs:2344:17:2347:9 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2344:28:2344:34 | matrix1 | | {EXTERNAL LOCATION} | Vec | +| main.rs:2344:28:2344:34 | matrix1 | A | {EXTERNAL LOCATION} | Global | | main.rs:2344:36:2347:9 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:13:2346:13 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2345:29:2346:13 | { ... } | | {EXTERNAL LOCATION} | () | @@ -14500,7 +14510,9 @@ inferType | pattern_matching.rs:788:41:788:45 | tuple | T2 | {EXTERNAL LOCATION} | bool | | pattern_matching.rs:792:35:824:1 | { ... } | | {EXTERNAL LOCATION} | () | | pattern_matching.rs:794:9:794:14 | points | | {EXTERNAL LOCATION} | Vec | +| pattern_matching.rs:794:9:794:14 | points | A | {EXTERNAL LOCATION} | Global | | pattern_matching.rs:794:18:794:65 | MacroExpr | | {EXTERNAL LOCATION} | Vec | +| pattern_matching.rs:794:18:794:65 | MacroExpr | A | {EXTERNAL LOCATION} | Global | | pattern_matching.rs:794:23:794:42 | (...) | | pattern_matching.rs:135:1:140:1 | Point | | pattern_matching.rs:794:23:794:42 | Point {...} | | pattern_matching.rs:135:1:140:1 | Point | | pattern_matching.rs:794:34:794:34 | 1 | | {EXTERNAL LOCATION} | i32 | @@ -14514,6 +14526,7 @@ inferType | pattern_matching.rs:795:17:795:17 | x | | {EXTERNAL LOCATION} | i32 | | pattern_matching.rs:795:20:795:20 | y | | {EXTERNAL LOCATION} | i32 | | pattern_matching.rs:795:27:795:32 | points | | {EXTERNAL LOCATION} | Vec | +| pattern_matching.rs:795:27:795:32 | points | A | {EXTERNAL LOCATION} | Global | | pattern_matching.rs:795:34:799:5 | { ... } | | {EXTERNAL LOCATION} | () | | pattern_matching.rs:796:13:796:18 | loop_x | | {EXTERNAL LOCATION} | i32 | | pattern_matching.rs:796:22:796:22 | x | | {EXTERNAL LOCATION} | i32 | From c4f8748a422489724260928a90e129b67acff1c2 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 25 Feb 2026 18:03:40 +0100 Subject: [PATCH 5/9] Python: simplify barrier guard --- ...ServerSideRequestForgeryCustomizations.qll | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index 3fb260e425d3..999778a6f233 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -177,7 +177,6 @@ module ServerSideRequestForgery { ) } - /** A validation of a URI using the `AntiSSRF` library, considered as a full-ssrf sanitizer. */ private class UriValidator extends FullUrlControlSanitizer { UriValidator() { this = DataFlow::BarrierGuard::getABarrierNode() } } @@ -185,27 +184,14 @@ module ServerSideRequestForgery { import semmle.python.dataflow.new.internal.DataFlowPublic private predicate uri_validator(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { - exists(DataFlow::CallCfgNode call, string funcs | - funcs in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] and - call = API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(funcs).getACall() and + exists(DataFlow::CallCfgNode call, string validator_name | + validator_name in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] and + call = + API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(validator_name).getACall() and call.getArg(0).asCfgNode() = node | - // validator call directly (e.g., if URIValidator.in_domain(...) ) g = call.asCfgNode() and branch = true - or - // validator used in a comparison - exists(Cmpop op, Node n, ControlFlowNode l | - n.getALocalSource() = call and g.(CompareNode).operands(n.asCfgNode(), op, l) - | - // validator == true or validator == false or validator is True or validator is False - (op instanceof Eq or op instanceof Is) and - branch = l.getNode().(BooleanLiteral).booleanValue() - or - // validator != false or validator != true or validator is not True or validator is not False - (op instanceof NotEq or op instanceof IsNot) and - branch = l.getNode().(BooleanLiteral).booleanValue().booleanNot() - ) ) } } From 9b9c9304c7e53e1b5aa38b09a2c68baf03c1fdf1 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 25 Feb 2026 18:16:38 +0100 Subject: [PATCH 6/9] Python: simplify logic, suggested in review --- .../dataflow/new/internal/DataFlowPublic.qll | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 3b3f7f7daebb..89c5c2d8116a 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -608,7 +608,7 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { // if a guard node is compared to a boolean literal, // the other operand is also a guard node, // but with polarity depending on the literal (and on the comparison). - exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean bool | + exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean should_flip | ( cmpNode.operands(result, op, b) or cmpNode.operands(b, op, result) @@ -617,19 +617,19 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { ( // comparing to the boolean (op instanceof Eq or op instanceof Is) and - // `bool` is the value being compared against, here the value of `b` - b.getNode().(BooleanLiteral).booleanValue() = bool + // we shoould flip if the value compared against, here the value of `b`, is false + should_flip = b.getNode().(BooleanLiteral).booleanValue().booleanNot() or // comparing to the negation of the boolean (op instanceof NotEq or op instanceof IsNot) and - // again, `bool` is the value being compared against, but here it is the value of `not b` - b.getNode().(BooleanLiteral).booleanValue() = bool.booleanNot() + // again, we should flip if the value compared against, here the value of `not b`, is false. + // That is, if the value of `b` is true. + should_flip = b.getNode().(BooleanLiteral).booleanValue() ) | - // if `bool` is true, we should preserve `flipped`, otherwise we should flip it - // `flipped xor (not bool)` achieves that. + // we flip `flipped` according to `should_flip` via the formula `flipped xor should_flip`. flipped in [true, false] and - cmpNode = guardNode(conditionBlock, flipped.booleanXor(bool.booleanNot())) + cmpNode = guardNode(conditionBlock, flipped.booleanXor(should_flip)) ) } From cfbae5084561cd80f65233747689106f1e579be2 Mon Sep 17 00:00:00 2001 From: yoff Date: Thu, 26 Feb 2026 13:11:43 +0100 Subject: [PATCH 7/9] Python: convert barrier guard to MaD --- .../python/frameworks/AntiSSRF.model.yml | 6 ++++++ ...ServerSideRequestForgeryCustomizations.qll | 19 +++---------------- 2 files changed, 9 insertions(+), 16 deletions(-) create mode 100644 python/ql/lib/semmle/python/frameworks/AntiSSRF.model.yml diff --git a/python/ql/lib/semmle/python/frameworks/AntiSSRF.model.yml b/python/ql/lib/semmle/python/frameworks/AntiSSRF.model.yml new file mode 100644 index 000000000000..42f483c6970e --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/AntiSSRF.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/python-all + extensible: barrierGuardModel + data: + - ['AntiSSRF', 'Member[URIValidator].Member[in_domain,in_azure_keyvault_domain,in_azure_storage_domain].Argument[0]', "true", 'request-forgery'] diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index 999778a6f233..e3f18170f630 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -10,6 +10,7 @@ private import semmle.python.Concepts private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.BarrierGuards private import semmle.python.ApiGraphs +private import semmle.python.frameworks.data.internal.ApiGraphModels /** * Provides default sources, sinks and sanitizers for detecting @@ -177,21 +178,7 @@ module ServerSideRequestForgery { ) } - private class UriValidator extends FullUrlControlSanitizer { - UriValidator() { this = DataFlow::BarrierGuard::getABarrierNode() } - } - - import semmle.python.dataflow.new.internal.DataFlowPublic - - private predicate uri_validator(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { - exists(DataFlow::CallCfgNode call, string validator_name | - validator_name in ["in_domain", "in_azure_keyvault_domain", "in_azure_storage_domain"] and - call = - API::moduleImport("AntiSSRF").getMember("URIValidator").getMember(validator_name).getACall() and - call.getArg(0).asCfgNode() = node - | - g = call.asCfgNode() and - branch = true - ) + private class ExternalRequestForgerySanitizer extends FullUrlControlSanitizer { + ExternalRequestForgerySanitizer() { ModelOutput::barrierNode(this, "request-forgery") } } } From 89e5a9bd728e4f48ad0a3adc31dd0ad374da6a2f Mon Sep 17 00:00:00 2001 From: yoff Date: Thu, 26 Feb 2026 13:14:26 +0100 Subject: [PATCH 8/9] Update python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll Co-authored-by: Taus --- .../lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 89c5c2d8116a..8612d4a253e0 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -617,7 +617,7 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { ( // comparing to the boolean (op instanceof Eq or op instanceof Is) and - // we shoould flip if the value compared against, here the value of `b`, is false + // we should flip if the value compared against, here the value of `b`, is false should_flip = b.getNode().(BooleanLiteral).booleanValue().booleanNot() or // comparing to the negation of the boolean From 11a726d1b48e15cb8cbcc1fc23fbdfb5715b7fe8 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 26 Feb 2026 14:23:41 +0100 Subject: [PATCH 9/9] Address review comments --- .../codeql/rust/elements/internal/InvocationExprImpl.qll | 8 +++++++- .../codeql/rust/internal/typeinference/FunctionType.qll | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll index acf82066f128..e5dd4cdaee67 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll @@ -22,7 +22,13 @@ module Impl { /** * Holds if this call position is a type qualifier, that is, not an actual * argument, but rather an annotation that is needed to resolve the call target, - * just like actual arguments may be need to resolve the call target. + * just like actual arguments may be needed to resolve the call target. + * + * Example: + * ```rust + * Vec::new(); + * // ^^^^^^^^ type qualifier + * ``` */ predicate isTypeQualifier() { this = TTypeQualifierArgumentPosition() } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll index 5eaddc9a7b37..f8611ce2a3c0 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll @@ -86,7 +86,7 @@ private newtype TAssocFunctionType = // through `i`. This ensures that `parent` is either a supertrait of `i` or // `i` in an `impl` block implementing `parent`. (parent = i or BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, _)) and - // We always include the type qualifer position, even for non-methods, where it is used + // We always include the type qualifier position, even for non-methods, where it is used // to match type qualifiers against the `impl` or trait type, such as in `Vec::new`. (exists(pos.getTypeMention(f)) or pos.isTypeQualifier()) }