Rust: Restrict type propagation into arguments

This commit is contained in:
Tom Hvitved
2025-10-23 16:02:56 +02:00
parent 72b7dd8955
commit e69ff0d5e8
11 changed files with 242 additions and 603 deletions

View File

@@ -51,6 +51,7 @@ newtype TType =
TSliceType() or
TNeverType() or
TPtrType() or
TContextType() or
TTupleTypeParameter(int arity, int i) { exists(TTuple(arity)) and i in [0 .. arity - 1] } or
TTypeParamTypeParameter(TypeParam t) or
TAssociatedTypeTypeParameter(TypeAlias t) { any(TraitItemNode trait).getAnAssocItem() = t } or
@@ -371,6 +372,30 @@ class PtrType extends Type, TPtrType {
override Location getLocation() { result instanceof EmptyLocation }
}
/**
* A special pseudo type used to indicate that the actual type may have to be
* inferred from a context.
*
* For example, a call like `Default::default()` is assigned this type, which
* means that the actual type is to be inferred from the context in which the call
* occurs.
*
* Context types are not restricted to root types, for example in a call like
* `Vec::new()` we assign this type at the type path corresponding to the type
* parameter of `Vec`.
*
* Context types are used to restrict when type information is allowed to flow
* into call arguments (including method call receivers), in order to avoid
* combinatorial explosions.
*/
class ContextType extends Type, TContextType {
override TypeParameter getPositionalTypeParameter(int i) { none() }
override string toString() { result = "(context typed)" }
override Location getLocation() { result instanceof EmptyLocation }
}
/** A type parameter. */
abstract class TypeParameter extends Type {
override TypeParameter getPositionalTypeParameter(int i) { none() }

View File

@@ -909,6 +909,118 @@ private Type getCallExprTypeQualifier(CallExpr ce, TypePath path) {
)
}
/**
* Provides functionality related to context-based typing of calls.
*/
private module ContextTyping {
/**
* Holds if the return type of the function `f` inside `i` at `path` is type
* parameter `tp`, and `tp` does not appear in the type of any parameter of
* `f`.
*
* In this case, the context in which `f` is called may be needed to infer
* the instantiation of `tp`.
*
* This covers functions like `Default::default` and `Vec::new`.
*/
pragma[nomagic]
private predicate assocFunctionReturnContextTypedAt(
ImplOrTraitItemNode i, Function f, FunctionPosition pos, TypePath path, TypeParameter tp
) {
pos.isReturn() and
tp = getAssocFunctionTypeAt(f, i, pos, path) and
not exists(FunctionPosition nonResPos | not nonResPos.isReturn() |
tp = getAssocFunctionTypeAt(f, i, nonResPos, _)
or
// `Self` types in traits implicitly mention all type parameters of the trait
getAssocFunctionTypeAt(f, i, nonResPos, _) = TSelfTypeParameter(i)
)
}
/**
* A call where the type of the result may have to be inferred from the
* context in which the call appears, for example a call like
* `Default::default()`.
*/
abstract class ContextTypedCallCand extends AstNode {
abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path);
private predicate hasTypeArgument(TypeArgumentPosition apos) {
exists(this.getTypeArgument(apos, _))
}
/**
* Holds if this call resolves to `target` inside `i`, and the return type
* at `pos` and `path` may have to be inferred from the context.
*/
bindingset[this, i, target]
predicate isContextTypedAt(
ImplOrTraitItemNode i, Function target, TypePath path, FunctionPosition pos
) {
exists(TypeParameter tp |
assocFunctionReturnContextTypedAt(i, target, pos, path, tp) and
// check that no explicit type arguments have been supplied for `tp`
not exists(TypeArgumentPosition tapos | this.hasTypeArgument(tapos) |
exists(int j |
j = tapos.asMethodTypeArgumentPosition() and
tp = TTypeParamTypeParameter(target.getGenericParamList().getTypeParam(j))
)
or
TTypeParamTypeParameter(tapos.asTypeParam()) = tp
) and
not (
tp instanceof TSelfTypeParameter and
exists(getCallExprTypeQualifier(this, _))
)
)
}
}
pragma[nomagic]
private predicate isContextTyped(AstNode n, TypePath path) { inferType(n, path) = TContextType() }
pragma[nomagic]
private predicate isContextTyped(AstNode n) { isContextTyped(n, _) }
signature Type inferCallTypeSig(AstNode n, FunctionPosition pos, TypePath path);
/**
* Given a predicate `inferCallType` for inferring the type of a call at a given
* position, this module exposes the predicate `check`, which wraps the input
* predicate and checks that types are only propagated into arguments when they
* are context-typed.
*/
module CheckContextTyping<inferCallTypeSig/3 inferCallType> {
pragma[nomagic]
private Type inferCallTypeFromContextCand(
AstNode n, FunctionPosition pos, TypePath path, TypePath prefix
) {
result = inferCallType(n, pos, path) and
not pos.isReturn() and
isContextTyped(n) and
prefix = path
or
exists(TypePath mid |
result = inferCallTypeFromContextCand(n, pos, path, mid) and
mid.isSnoc(prefix, _)
)
}
pragma[nomagic]
Type check(AstNode n, TypePath path) {
exists(FunctionPosition pos |
result = inferCallType(n, pos, path) and
pos.isReturn()
or
exists(TypePath prefix |
result = inferCallTypeFromContextCand(n, pos, path, prefix) and
isContextTyped(n, prefix)
)
)
}
}
}
/**
* Holds if function `f` with the name `name` and the arity `arity` exists in
* `i`, and the type at position `pos` is `t`.
@@ -1418,20 +1530,20 @@ private module MethodResolution {
* `derefChain` and the Boolean `borrow`.
*/
pragma[nomagic]
Method resolveCallTarget(string derefChain, boolean borrow) {
Method resolveCallTarget(ImplOrTraitItemNode i, string derefChain, boolean borrow) {
exists(MethodCallCand mcc |
mcc = MkMethodCallCand(this, derefChain, borrow) and
result = mcc.resolveCallTarget()
result = mcc.resolveCallTarget(i)
)
}
predicate receiverHasImplicitDeref(AstNode receiver) {
exists(this.resolveCallTarget(".ref", false)) and
exists(this.resolveCallTarget(_, ".ref", false)) and
receiver = this.getArgument(CallImpl::TSelfArgumentPosition())
}
predicate receiverHasImplicitBorrow(AstNode receiver) {
exists(this.resolveCallTarget("", true)) and
exists(this.resolveCallTarget(_, "", true)) and
receiver = this.getArgument(CallImpl::TSelfArgumentPosition())
}
}
@@ -1569,7 +1681,8 @@ private module MethodResolution {
Type getTypeAt(TypePath path) {
result = mc_.getACandidateReceiverTypeAtSubstituteLookupTraits(derefChain, borrow, path) and
not result = TNeverType()
not result = TNeverType() and
not result = TContextType()
}
pragma[nomagic]
@@ -1642,13 +1755,11 @@ private module MethodResolution {
/** Gets a method that matches this method call. */
pragma[nomagic]
Method resolveCallTarget() {
exists(ImplOrTraitItemNode i |
result = this.resolveCallTargetCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(i, _, _, _, _)
)
Method resolveCallTarget(ImplOrTraitItemNode i) {
result = this.resolveCallTargetCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(i, _, _, _, _)
or
MethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, _, result)
MethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
}
predicate hasNoBorrow() { borrow = false }
@@ -1918,14 +2029,14 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi
final private class MethodCallFinal = MethodResolution::MethodCall;
class Access extends MethodCallFinal {
class Access extends MethodCallFinal, ContextTyping::ContextTypedCallCand {
Access() {
// handled in the `OperationMatchingInput` module
not this instanceof Operation
}
pragma[nomagic]
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
exists(TypeMention arg | result = arg.resolveTypeAt(path) |
arg =
this.(MethodCallExpr).getGenericArgList().getTypeArg(apos.asMethodTypeArgumentPosition())
@@ -1971,12 +2082,14 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi
result = this.getInferredNonSelfType(apos, path)
}
Declaration getTarget(string derefChainBorrow) {
Declaration getTarget(ImplOrTraitItemNode i, string derefChainBorrow) {
exists(string derefChain, boolean borrow |
derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and
result = this.resolveCallTarget(derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa
result = this.resolveCallTarget(i, derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa
)
}
Declaration getTarget(string derefChainBorrow) { result = this.getTarget(_, derefChainBorrow) }
}
}
@@ -1989,7 +2102,14 @@ private Type inferMethodCallType0(
) {
exists(TypePath path0 |
n = a.getNodeAt(apos) and
result = MethodCallMatching::inferAccessType(a, derefChainBorrow, apos, path0)
(
result = MethodCallMatching::inferAccessType(a, derefChainBorrow, apos, path0)
or
exists(ImplOrTraitItemNode i |
a.isContextTypedAt(i, a.getTarget(i, derefChainBorrow), path0, apos) and
result = TContextType()
)
)
|
if
// index expression `x[i]` desugars to `*x.index(i)`, so we must account for
@@ -2001,16 +2121,11 @@ private Type inferMethodCallType0(
)
}
/**
* Gets the type of `n` at `path`, where `n` is either a method call or an
* argument/receiver of a method call.
*/
pragma[nomagic]
private Type inferMethodCallType(AstNode n, TypePath path) {
exists(
MethodCallMatchingInput::Access a, MethodCallMatchingInput::AccessPosition apos,
string derefChainBorrow, TypePath path0
|
private Type inferMethodCallType1(
AstNode n, MethodCallMatchingInput::AccessPosition apos, TypePath path
) {
exists(MethodCallMatchingInput::Access a, string derefChainBorrow, TypePath path0 |
result = inferMethodCallType0(a, apos, n, derefChainBorrow, path0)
|
(
@@ -2032,6 +2147,13 @@ private Type inferMethodCallType(AstNode n, TypePath path) {
)
}
/**
* Gets the type of `n` at `path`, where `n` is either a method call or an
* argument/receiver of a method call.
*/
private predicate inferMethodCallType =
ContextTyping::CheckContextTyping<inferMethodCallType1/3>::check/2;
/**
* Provides logic for resolving calls to non-method items. This includes
* "calls" to tuple variants and tuple structs.
@@ -2178,26 +2300,39 @@ private module NonMethodResolution {
trait = this.(Call).getTrait()
}
/**
* Gets the target of this call, which can be resolved using only path resolution.
*/
pragma[nomagic]
private NonMethodFunction resolveCallTargetRec() {
result = this.resolveCallTargetBlanketCand(_) and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
or
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, _, result)
}
pragma[nomagic]
ItemNode resolveCallTargetNonRec() {
ItemNode resolveCallTargetViaPathResolution() {
not this.(Call).hasTrait() and
result = this.getPathResolutionResolved() and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
}
/**
* Gets the target of this call, which can be resolved using type inference.
*/
pragma[nomagic]
NonMethodFunction resolveCallTargetViaTypeInference(ImplOrTraitItemNode i) {
result = this.resolveCallTargetBlanketCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
or
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
}
pragma[inline]
ItemNode resolveCallTarget() {
result = this.resolveCallTargetNonRec()
result = this.resolveCallTargetViaPathResolution()
or
result = this.resolveCallTargetRec()
result = this.resolveCallTargetViaTypeInference(_)
}
pragma[nomagic]
NonMethodFunction resolveTraitFunctionViaPathResolution(TraitItemNode trait) {
this.(Call).hasTrait() and
result = this.getPathResolutionResolved() and
result = trait.getASuccessor(_)
}
}
@@ -2433,9 +2568,9 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
}
}
class Access extends NonMethodResolution::NonMethodCall {
class Access extends NonMethodResolution::NonMethodCall, ContextTyping::ContextTypedCallCand {
pragma[nomagic]
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
result = getCallExprTypeArgument(this, apos).resolveTypeAt(path)
}
@@ -2456,13 +2591,27 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
private module NonMethodCallMatching = Matching<NonMethodCallMatchingInput>;
pragma[nomagic]
private Type inferNonMethodCallType(AstNode n, TypePath path) {
exists(NonMethodCallMatchingInput::Access a, NonMethodCallMatchingInput::AccessPosition apos |
n = a.getNodeAt(apos) and
private Type inferNonMethodCallType0(
AstNode n, NonMethodCallMatchingInput::AccessPosition apos, TypePath path
) {
exists(NonMethodCallMatchingInput::Access a | n = a.getNodeAt(apos) |
result = NonMethodCallMatching::inferAccessType(a, apos, path)
or
exists(ImplOrTraitItemNode i |
a.isContextTypedAt(i,
[
a.resolveCallTargetViaPathResolution().(NonMethodFunction),
a.resolveCallTargetViaTypeInference(i),
a.resolveTraitFunctionViaPathResolution(i)
], path, apos) and
result = TContextType()
)
)
}
private predicate inferNonMethodCallType =
ContextTyping::CheckContextTyping<inferNonMethodCallType0/3>::check/2;
/**
* A matching configuration for resolving types of operations like `a + b`.
*/
@@ -2527,7 +2676,7 @@ private module OperationMatchingInput implements MatchingInputSig {
}
Declaration getTarget() {
result = this.resolveCallTarget(_, _) // mutual recursion
result = this.resolveCallTarget(_, _, _) // mutual recursion
}
}
}
@@ -2535,13 +2684,18 @@ private module OperationMatchingInput implements MatchingInputSig {
private module OperationMatching = Matching<OperationMatchingInput>;
pragma[nomagic]
private Type inferOperationType(AstNode n, TypePath path) {
exists(OperationMatchingInput::Access a, OperationMatchingInput::AccessPosition apos |
private Type inferOperationType0(
AstNode n, OperationMatchingInput::AccessPosition apos, TypePath path
) {
exists(OperationMatchingInput::Access a |
n = a.getNodeAt(apos) and
result = OperationMatching::inferAccessType(a, apos, path)
)
}
private predicate inferOperationType =
ContextTyping::CheckContextTyping<inferOperationType0/3>::check/2;
pragma[nomagic]
private Type getFieldExprLookupType(FieldExpr fe, string name) {
exists(TypePath path |
@@ -3215,7 +3369,7 @@ private module Cached {
/** Gets an item (function or tuple struct/variant) that `call` resolves to, if any. */
cached
Addressable resolveCallTarget(Call call) {
result = call.(MethodResolution::MethodCall).resolveCallTarget(_, _)
result = call.(MethodResolution::MethodCall).resolveCallTarget(_, _, _)
or
result = call.(NonMethodResolution::NonMethodCall).resolveCallTarget()
}

View File

@@ -92,7 +92,8 @@ module SatisfiesBlanketConstraint<
Type getTypeAt(TypePath path) {
result = at.getTypeAt(blanketPath.appendInverse(path)) and
not result = TNeverType()
not result = TNeverType() and
not result = TContextType()
}
string toString() { result = at.toString() + " [blanket at " + blanketPath.toString() + "]" }

View File

@@ -229,7 +229,8 @@ module ArgIsInstantiationOf<
private class ArgSubst extends ArgFinal {
Type getTypeAt(TypePath path) {
result = substituteLookupTraits(super.getTypeAt(path)) and
not result = TNeverType()
not result = TNeverType() and
not result = TContextType()
}
}

View File

@@ -0,0 +1,2 @@
multipleCallTargets
| test.rs:288:7:288:36 | ... .as_str() |

View File

@@ -1,10 +1,12 @@
import rust
import utils.test.InlineExpectationsTest
import codeql.rust.internal.Type
import codeql.rust.internal.TypeInference as TypeInference
import TypeInference
query predicate inferType(AstNode n, TypePath path, Type t) {
t = TypeInference::inferType(n, path) and
t != TContextType() and
n.fromSource() and
not n.isFromMacroExpansion() and
not n instanceof IdentPat and // avoid overlap in the output with the underlying `Name` node

View File

@@ -3,29 +3,10 @@ multipleCallTargets
| mysql.rs:16:26:16:85 | ...::from(...) |
| mysql.rs:18:13:18:66 | ...::from(...) |
| mysql.rs:19:30:19:83 | ...::from(...) |
| mysql.rs:46:45:46:66 | remote_string.as_str() |
| mysql.rs:47:71:47:92 | remote_string.as_str() |
| mysql.rs:48:46:48:67 | remote_string.as_str() |
| mysql.rs:49:33:49:54 | remote_string.as_str() |
| mysql.rs:50:46:50:67 | remote_string.as_str() |
| mysql.rs:52:37:52:58 | remote_string.as_str() |
| mysql.rs:56:14:56:35 | remote_string.as_str() |
| mysql.rs:62:14:62:35 | remote_string.as_str() |
| mysql.rs:66:40:66:61 | remote_string.as_str() |
| mysql.rs:67:39:67:60 | remote_string.as_str() |
| mysql.rs:70:14:70:35 | remote_string.as_str() |
| mysql.rs:100:24:100:39 | ...::from(...) |
| mysql.rs:101:26:101:85 | ...::from(...) |
| mysql.rs:103:13:103:66 | ...::from(...) |
| mysql.rs:104:30:104:83 | ...::from(...) |
| mysql.rs:126:45:126:66 | remote_string.as_str() |
| mysql.rs:128:38:128:59 | remote_string.as_str() |
| mysql.rs:130:33:130:54 | remote_string.as_str() |
| mysql.rs:131:54:131:75 | remote_string.as_str() |
| mysql.rs:135:18:135:39 | remote_string.as_str() |
| mysql.rs:140:40:140:61 | remote_string.as_str() |
| mysql.rs:142:62:142:83 | remote_string.as_str() |
| mysql.rs:145:31:145:52 | remote_string.as_str() |
| sqlx.rs:46:24:46:44 | ...::from(...) |
| sqlx.rs:47:56:47:76 | ...::from(...) |
| sqlx.rs:48:97:48:117 | ...::from(...) |
@@ -33,8 +14,6 @@ multipleCallTargets
| sqlx.rs:51:24:51:77 | ...::from(...) |
| sqlx.rs:55:26:55:79 | ...::from(...) |
| sqlx.rs:61:28:61:81 | ...::from(...) |
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() |
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() |
| sqlx.rs:99:24:99:44 | ...::from(...) |
| sqlx.rs:100:97:100:117 | ...::from(...) |
| sqlx.rs:101:24:101:77 | ...::from(...) |

View File

@@ -36,7 +36,7 @@ edges
| mysql.rs:12:13:12:29 | mut remote_string | mysql.rs:18:71:18:83 | remote_string | provenance | |
| mysql.rs:12:33:12:54 | ...::get | mysql.rs:12:33:12:77 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
| mysql.rs:12:33:12:77 | ...::get(...) [Ok] | mysql.rs:12:33:13:21 | ... .unwrap() | provenance | MaD:31 |
| mysql.rs:12:33:13:21 | ... .unwrap() | mysql.rs:12:33:14:19 | ... .text() [Ok] | provenance | MaD:34 |
| mysql.rs:12:33:13:21 | ... .unwrap() | mysql.rs:12:33:14:19 | ... .text() [Ok] | provenance | MaD:33 |
| mysql.rs:12:33:14:19 | ... .text() [Ok] | mysql.rs:12:33:15:40 | ... .unwrap_or(...) | provenance | MaD:32 |
| mysql.rs:12:33:15:40 | ... .unwrap_or(...) | mysql.rs:12:13:12:29 | mut remote_string | provenance | |
| mysql.rs:17:13:17:24 | unsafe_query | mysql.rs:25:38:25:49 | unsafe_query | provenance | |
@@ -113,7 +113,7 @@ edges
| mysql.rs:97:13:97:29 | mut remote_string | mysql.rs:103:71:103:83 | remote_string | provenance | |
| mysql.rs:97:33:97:54 | ...::get | mysql.rs:97:33:97:77 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
| mysql.rs:97:33:97:77 | ...::get(...) [Ok] | mysql.rs:97:33:98:21 | ... .unwrap() | provenance | MaD:31 |
| mysql.rs:97:33:98:21 | ... .unwrap() | mysql.rs:97:33:99:19 | ... .text() [Ok] | provenance | MaD:34 |
| mysql.rs:97:33:98:21 | ... .unwrap() | mysql.rs:97:33:99:19 | ... .text() [Ok] | provenance | MaD:33 |
| mysql.rs:97:33:99:19 | ... .text() [Ok] | mysql.rs:97:33:100:40 | ... .unwrap_or(...) | provenance | MaD:32 |
| mysql.rs:97:33:100:40 | ... .unwrap_or(...) | mysql.rs:97:13:97:29 | mut remote_string | provenance | |
| mysql.rs:102:13:102:24 | unsafe_query | mysql.rs:110:38:110:49 | unsafe_query | provenance | |
@@ -177,15 +177,13 @@ edges
| sqlx.rs:48:9:48:21 | remote_string | sqlx.rs:59:17:59:72 | MacroExpr | provenance | |
| sqlx.rs:48:25:48:46 | ...::get | sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
| sqlx.rs:48:25:48:69 | ...::get(...) [Ok] | sqlx.rs:48:25:48:78 | ... .unwrap() | provenance | MaD:31 |
| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:34 |
| sqlx.rs:48:25:48:78 | ... .unwrap() | sqlx.rs:48:25:48:85 | ... .text() [Ok] | provenance | MaD:33 |
| sqlx.rs:48:25:48:85 | ... .text() [Ok] | sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | provenance | MaD:32 |
| sqlx.rs:48:25:48:118 | ... .unwrap_or(...) | sqlx.rs:48:9:48:21 | remote_string | provenance | |
| sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | provenance | MaD:29 |
| sqlx.rs:53:26:53:36 | &arg_string [&ref] | sqlx.rs:53:9:53:22 | unsafe_query_1 [&ref] | provenance | |
| sqlx.rs:53:27:53:36 | arg_string | sqlx.rs:53:26:53:36 | &arg_string [&ref] | provenance | |
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:29 |
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:33 |
| sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | provenance | MaD:33 |
| sqlx.rs:54:26:54:39 | &remote_string [&ref] | sqlx.rs:54:9:54:22 | unsafe_query_2 [&ref] | provenance | |
| sqlx.rs:54:27:54:39 | remote_string | sqlx.rs:54:26:54:39 | &remote_string [&ref] | provenance | |
| sqlx.rs:55:9:55:22 | unsafe_query_3 | sqlx.rs:81:29:81:42 | unsafe_query_3 | provenance | |
@@ -200,8 +198,8 @@ edges
| sqlx.rs:56:9:56:22 | unsafe_query_4 | sqlx.rs:82:29:82:51 | unsafe_query_4.as_str() | provenance | MaD:29 |
| sqlx.rs:59:17:59:72 | ...::format(...) | sqlx.rs:59:17:59:72 | { ... } | provenance | |
| sqlx.rs:59:17:59:72 | ...::must_use(...) | sqlx.rs:56:9:56:22 | unsafe_query_4 | provenance | |
| sqlx.rs:59:17:59:72 | MacroExpr | sqlx.rs:59:17:59:72 | ...::format(...) | provenance | MaD:35 |
| sqlx.rs:59:17:59:72 | { ... } | sqlx.rs:59:17:59:72 | ...::must_use(...) | provenance | MaD:36 |
| sqlx.rs:59:17:59:72 | MacroExpr | sqlx.rs:59:17:59:72 | ...::format(...) | provenance | MaD:34 |
| sqlx.rs:59:17:59:72 | { ... } | sqlx.rs:59:17:59:72 | ...::must_use(...) | provenance | MaD:35 |
| sqlx.rs:78:25:78:47 | unsafe_query_1.as_str() [&ref] | sqlx.rs:78:13:78:23 | ...::query | provenance | MaD:20 Sink:MaD:20 |
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() [&ref] | sqlx.rs:80:17:80:27 | ...::query | provenance | MaD:20 Sink:MaD:20 |
| sqlx.rs:81:29:81:42 | unsafe_query_3 | sqlx.rs:81:29:81:51 | unsafe_query_3.as_str() [&ref] | provenance | MaD:29 |
@@ -213,7 +211,7 @@ edges
| sqlx.rs:100:9:100:21 | remote_string | sqlx.rs:102:84:102:96 | remote_string | provenance | |
| sqlx.rs:100:25:100:46 | ...::get | sqlx.rs:100:25:100:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
| sqlx.rs:100:25:100:69 | ...::get(...) [Ok] | sqlx.rs:100:25:100:78 | ... .unwrap() | provenance | MaD:31 |
| sqlx.rs:100:25:100:78 | ... .unwrap() | sqlx.rs:100:25:100:85 | ... .text() [Ok] | provenance | MaD:34 |
| sqlx.rs:100:25:100:78 | ... .unwrap() | sqlx.rs:100:25:100:85 | ... .text() [Ok] | provenance | MaD:33 |
| sqlx.rs:100:25:100:85 | ... .text() [Ok] | sqlx.rs:100:25:100:118 | ... .unwrap_or(...) | provenance | MaD:32 |
| sqlx.rs:100:25:100:118 | ... .unwrap_or(...) | sqlx.rs:100:9:100:21 | remote_string | provenance | |
| sqlx.rs:102:9:102:22 | unsafe_query_1 | sqlx.rs:113:31:113:44 | unsafe_query_1 | provenance | |
@@ -255,7 +253,7 @@ edges
| sqlx.rs:173:9:173:21 | remote_string | sqlx.rs:175:84:175:96 | remote_string | provenance | |
| sqlx.rs:173:25:173:46 | ...::get | sqlx.rs:173:25:173:69 | ...::get(...) [Ok] | provenance | Src:MaD:23 |
| sqlx.rs:173:25:173:69 | ...::get(...) [Ok] | sqlx.rs:173:25:173:78 | ... .unwrap() | provenance | MaD:31 |
| sqlx.rs:173:25:173:78 | ... .unwrap() | sqlx.rs:173:25:173:85 | ... .text() [Ok] | provenance | MaD:34 |
| sqlx.rs:173:25:173:78 | ... .unwrap() | sqlx.rs:173:25:173:85 | ... .text() [Ok] | provenance | MaD:33 |
| sqlx.rs:173:25:173:85 | ... .text() [Ok] | sqlx.rs:173:25:173:118 | ... .unwrap_or(...) | provenance | MaD:32 |
| sqlx.rs:173:25:173:118 | ... .unwrap_or(...) | sqlx.rs:173:9:173:21 | remote_string | provenance | |
| sqlx.rs:175:9:175:22 | unsafe_query_1 | sqlx.rs:188:29:188:42 | unsafe_query_1 | provenance | |
@@ -302,10 +300,9 @@ models
| 30 | Summary: <core::option::Option>::unwrap_or; Argument[self].Field[core::option::Option::Some(0)]; ReturnValue; value |
| 31 | Summary: <core::result::Result>::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
| 32 | Summary: <core::result::Result>::unwrap_or; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
| 33 | Summary: <core::str>::as_str; Argument[self]; ReturnValue; value |
| 34 | Summary: <reqwest::blocking::response::Response>::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
| 35 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
| 36 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value |
| 33 | Summary: <reqwest::blocking::response::Response>::text; Argument[self]; ReturnValue.Field[core::result::Result::Ok(0)]; taint |
| 34 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
| 35 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value |
nodes
| mysql.rs:12:13:12:29 | mut remote_string | semmle.label | mut remote_string |
| mysql.rs:12:33:12:54 | ...::get | semmle.label | ...::get |

View File

@@ -13,7 +13,6 @@ multipleCallTargets
| test_storage.rs:73:25:73:67 | ...::from(...) |
| test_storage.rs:75:25:75:65 | ...::from(...) |
| test_storage.rs:76:25:76:65 | ...::from(...) |
| test_storage.rs:77:14:77:24 | s1.as_str() |
| test_storage.rs:78:25:78:65 | ...::from(...) |
| test_storage.rs:79:25:79:65 | ...::from(...) |
| test_storage.rs:80:25:80:70 | ...::from(...) |

View File

@@ -1,2 +0,0 @@
multipleCallTargets
| request_forgery_tests.rs:30:36:30:52 | user_url.as_str() |