Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
private import rust
private import codeql.rust.Concepts
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSummary
private import codeql.rust.internal.PathResolution
private import codeql.rust.internal.typeinference.Type
private import codeql.rust.internal.typeinference.TypeMention

/**
* A call to the `starts_with` method on a `Path`.
Expand Down Expand Up @@ -297,3 +300,28 @@ class Vec extends Struct {
/** Gets the type parameter representing the element type. */
TypeParam getElementTypeParam() { result = this.getGenericParamList().getTypeParam(0) }
}

private class ReflexiveFrom extends SummarizedCallable::Range {
ReflexiveFrom() {
exists(ImplItemNode impl |
impl.resolveTraitTy().(Trait).getCanonicalPath() = "core::convert::From" and
this = impl.getAnAssocItem() and
impl.isBlanketImplementation() and
this.getParam(0)
.getTypeRepr()
.(TypeMention)
.resolveType()
.(TypeParamTypeParameter)
.getTypeParam() = impl.getTypeParam(0)
)
}

override predicate propagatesFlow(
string input, string output, boolean preservesValue, string model
) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = true and
model = "ReflexiveFrom"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ module SatisfiesBlanketConstraint<

/**
* Holds if the argument type `at` satisfies the first non-trivial blanket
* constraint of `impl`.
* constraint of `impl`, or if there are no non-trivial constraints of `impl`.
*/
pragma[nomagic]
predicate satisfiesBlanketConstraint(ArgumentType at, ImplItemNode impl) {
Expand All @@ -135,6 +135,11 @@ module SatisfiesBlanketConstraint<
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
)
or
exists(TypeParam blanketTypeParam |
hasBlanketCandidate(at, impl, _, blanketTypeParam) and
not hasFirstNonTrivialTraitBound(blanketTypeParam, _)
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,26 @@ private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
)
}

pragma[nomagic]
private predicate isBlanketImpl(ImplItemNode impl, Trait trait) {
impl.isBlanketImplementation() and
trait = impl.resolveTraitTy()
}

/**
* Holds if `impl` is an implementation of `trait` and if another implementation
* exists for the same type.
*/
pragma[nomagic]
private predicate implHasSibling(Impl impl, Trait trait) { implSiblings(trait, impl, _) }
private predicate implHasSibling(ImplItemNode impl, Trait trait) {
implSiblings(trait, impl, _)
or
exists(ImplItemNode other |
isBlanketImpl(impl, trait) and
isBlanketImpl(other, trait) and
impl != other
)
}

/**
* Holds if type parameter `tp` of `trait` occurs in the function `f` with the name
Expand Down
24 changes: 22 additions & 2 deletions rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
string toString() { result = call.toString() + " [arg " + pos + "]" }
}

private module ArgIsInstantiationOfInput implements
private module ArgIsInstantiationOfToIndexInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
Expand Down Expand Up @@ -388,7 +388,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
}

private module ArgIsInstantiationOfToIndex =
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfInput>;
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfToIndexInput>;

pragma[nomagic]
private predicate argsAreInstantiationsOfToIndex(
Expand All @@ -412,4 +412,24 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
rnk = max(int r | toCheckRanked(i, f, _, r))
)
}

pragma[nomagic]
private predicate argsAreNotInstantiationsOf0(
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i
) {
ArgIsInstantiationOfToIndex::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
}

/**
* Holds if _some_ argument of `call` has a type that is not an instantiation of the
* type of the corresponding parameter of `f` inside `i`.
*/
pragma[nomagic]
predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(FunctionPosition pos |
argsAreNotInstantiationsOf0(call, pos, i) and
call.hasTargetCand(i, f) and
Input::toCheck(i, f, pos, _)
)
}
}
114 changes: 81 additions & 33 deletions rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,11 @@ private class BorrowKind extends TBorrowKind {
}
}

private predicate isNotConstrainedTypeParameter(Type t) {
not t instanceof TypeParameter or
t.(TypeParamTypeParameter).getTypeParam() = any(TypeParam tp | not exists(tp.getATypeBound()))
}

/**
* Provides logic for resolving calls to methods.
*
Expand Down Expand Up @@ -2383,9 +2388,8 @@ private module MethodResolution {
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, path, t0) and
t.appliesTo(f, i, pos) and
// for now, we do not handle ambiguous targets when one of the types it iself
// a type parameter; we should be checking the constraints on that type parameter
// in this case
not t0 instanceof TypeParameter
// a constrained type parameter; we should be checking the constraints in this case
isNotConstrainedTypeParameter(t0)
)
}

Expand Down Expand Up @@ -2744,7 +2748,7 @@ private module NonMethodResolution {
* Gets the blanket function that this call may resolve to, if any.
*/
pragma[nomagic]
private NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
exists(string name |
this.hasNameAndArity(pragma[only_bind_into](name), _) and
ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and
Expand All @@ -2759,12 +2763,11 @@ private module NonMethodResolution {
predicate hasTrait() { exists(this.getTrait()) }

pragma[nomagic]
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
not this.hasTrait() and
result = this.getPathResolutionResolved() and
result = i.getASuccessor(_)
or
result = this.resolveCallTargetBlanketCand(i)
result = i.getASuccessor(_) and
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
}

AstNode getNodeAt(FunctionPosition pos) {
Expand Down Expand Up @@ -2796,6 +2799,20 @@ private module NonMethodResolution {
trait = this.getTrait()
}

pragma[nomagic]
predicate hasNoCompatibleNonBlanketTargetViaTypeInference() {
forall(ImplOrTraitItemNode i, Function f |
this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call).hasTargetCand(i, f)
|
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f)
)
}

predicate hasNoCompatibleNonBlanketTarget() {
not exists(this.resolveCallTargetViaPathResolution()) and
this.hasNoCompatibleNonBlanketTargetViaTypeInference()
}

/**
* Gets the target of this call, which can be resolved using only path resolution.
*/
Expand All @@ -2814,7 +2831,9 @@ private module NonMethodResolution {
result = this.resolveCallTargetBlanketCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
or
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result)
or
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result)
}

pragma[nomagic]
Expand Down Expand Up @@ -2853,7 +2872,11 @@ private module NonMethodResolution {
) {
exists(NonMethodCall fc, FunctionPosition pos |
fcp = MkCallAndBlanketPos(fc, pos) and
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, 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)
(fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
)
}
}
Expand Down Expand Up @@ -2888,37 +2911,28 @@ private module NonMethodResolution {
private module ArgIsInstantiationOfBlanketParam =
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;

private module NonMethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
t.appliesTo(f, i, pos) and
(
exists(Type t0 |
// for now, we do not handle ambiguous targets when one of the types it iself
// a type parameter; we should be checking the constraints on that type parameter
// in this case
not t0 instanceof TypeParameter
|
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
or
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
)
exists(Type t0 |
// for now, we do not handle ambiguous targets when one of the types it iself
// a constrained type parameter; we should be checking the constraints in this case
isNotConstrainedTypeParameter(t0)
|
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
or
// match against the trait function itself
exists(Trait trait |
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
TSelfTypeParameter(trait))
)
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
)
}

class Call extends NonMethodCall {
final class Call extends NonMethodCall {
Type getArgType(FunctionPosition pos, TypePath path) {
result = inferType(this.getNodeAt(pos), path)
}

predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveAssocCallTargetCand(i)
or
predicate hasTraitResolvedCand(ImplOrTraitItemNode i, Function f) {
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
this.hasTraitResolved(trait, resolved) and
traitFunctionDependsOnPos(trait, resolved, _, _, i1, f1)
Expand All @@ -2930,11 +2944,45 @@ private module NonMethodResolution {
i = trait
)
}

predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
BlanketImplementation::isBlanketLike(i, _, _)
}
}
}

private module NonMethodArgsAreInstantiationsOfBlanket =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfBlanketInput>;

private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
NonMethodArgsAreInstantiationsOfBlanketInput::toCheck(i, f, pos, t)
or
// match against the trait function itself
t.appliesTo(f, i, pos) and
exists(Trait trait |
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
TSelfTypeParameter(trait))
)
}

class Call extends NonMethodArgsAreInstantiationsOfBlanketInput::Call {
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetNonBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
not BlanketImplementation::isBlanketLike(i, _, _)
}
}
}

private module NonMethodArgsAreInstantiationsOf =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfInput>;
private module NonMethodArgsAreInstantiationsOfNonBlanket =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfNonBlanketInput>;
}

abstract private class TupleLikeConstructor extends Addressable {
Expand Down
Loading