Skip to content

Commit

Permalink
wip2
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Jun 26, 2024
1 parent 187a1bb commit 09b4672
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 52 deletions.
9 changes: 1 addition & 8 deletions csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import csharp
private import internal.Location

/**
* INTERNAL: Do not use.
Expand Down Expand Up @@ -65,14 +66,6 @@ private ControlFlowElement getBody(Callable c) {
result = getStatementBody(c)
}

pragma[nomagic]
private SourceLocation getASourceLocation(Element e) {
result = e.getALocation().(SourceLocation) and
not exists(e.getALocation().(SourceLocation).getMappedLocation())
or
result = e.getALocation().(SourceLocation).getMappedLocation()
}

pragma[nomagic]
private predicate hasNoSourceLocation(Element e) { not exists(getASourceLocation(e)) }

Expand Down
24 changes: 24 additions & 0 deletions csharp/ql/lib/semmle/code/csharp/Location.qll
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ class Location extends @location {

/** Gets the 1-based column number (inclusive) where this location ends. */
final int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) }

/** Holds if this location starts strictly before the specified location. */
bindingset[this, other]
pragma[inline_late]
predicate strictlyBefore(Location other) {
this.getFile() = other.getFile() and
(
this.getStartLine() < other.getStartLine()
or
this.getStartLine() = other.getStartLine() and this.getStartColumn() < other.getStartColumn()
)
}

/** Holds if this location starts before the specified location. */
bindingset[this, other]
pragma[inline_late]
predicate before(Location other) {
this.getFile() = other.getFile() and
(
this.getStartLine() < other.getStartLine()
or
this.getStartLine() = other.getStartLine() and this.getStartColumn() <= other.getStartColumn()
)
}
}

/** An empty location. */
Expand Down
32 changes: 22 additions & 10 deletions csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import csharp
*/
module Ssa {
private import internal.SsaImpl as SsaImpl
private import semmle.code.csharp.internal.Location

pragma[nomagic]
private predicate assignableDefinitionLocalVariable(
Expand Down Expand Up @@ -578,9 +579,22 @@ module Ssa {
override Location getLocation() { result = this.getCallable().getLocation() }
}

bindingset[e1, e2]
pragma[inline_late]
private predicate inSameFile(Location e1, Location e2) { e1.getFile() = e2.getFile() }
private predicate relevantLocation(Location l1, Location l2) {

Check warning

Code scanning / CodeQL

Dead code Warning

This code is never used, and it's not publicly exported.
exists(ImplicitParameterDefinition def |
l1 = def.getParameter().getALocation() and
l2 = def.getBasicBlock().getLocation()
)
}

private module NearestLocationInput implements NearestLocationInputSig {
class C = ImplicitParameterDefinition;

predicate relevantLocations(ImplicitParameterDefinition def, Location l1, Location l2) {
not def.getBasicBlock() instanceof ControlFlow::BasicBlocks::EntryBlock and
l1 = def.getParameter().getALocation() and
l2 = def.getBasicBlock().getLocation()
}
}

/**
* An SSA definition representing the implicit initialization of a parameter
Expand All @@ -604,13 +618,11 @@ module Ssa {
}

override Location getLocation() {
// Each parameter has an implicit entry definition. In case of multiple bodies,
// we need to match the location of the parameter with the location of the
// assigning basic block; requiring them to be in the same file should be
// sufficient (multiple method bodies in the same file would be a compilation
// error).
result = p.getALocation() and
inSameFile(result, this.getBasicBlock().getLocation())
this.getBasicBlock() instanceof ControlFlow::BasicBlocks::EntryBlock and
result = p.getLocation()
or
// multi-bodied method: use matching parameter location
NearestLocation<NearestLocationInput>::nearestLocation(this, result, _)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ module BaseSsa {
c = entry.getCallable() and
// In case `c` has multiple bodies, we want each body to gets its own implicit
// entry definition. In case `c` doesn't have multiple bodies, the line below
// is simply the same as `bb = entry`.
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
// will be in the entry block.
bb = entry.getFirstNode().getASuccessor().getBasicBlock() and
c = v.getCallable()
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dispatch.RuntimeCallable
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
private import semmle.code.csharp.internal.Location

/**
* Gets a source declaration of callable `c` that has a body and is
Expand Down Expand Up @@ -35,7 +36,7 @@ private module Cached {
newtype TDataFlowCallable =
TCallable(Callable c, Location l) {
c.isUnboundDeclaration() and
l = [c.getLocation(), c.getALocation().(SourceLocation)]
l = [c.getLocation(), getASourceLocation(c)]
} or
TSummarizedCallable(FlowSummary::SummarizedCallable sc) or
TFieldOrPropertyCallable(FieldOrProperty f) or
Expand Down Expand Up @@ -160,19 +161,29 @@ class RefReturnKind extends OutRefReturnKind, TRefReturnKind {
override string toString() { result = "ref parameter " + pos }
}

bindingset[e1, e2]
pragma[inline_late]
private predicate inSameFile(Location e1, Location e2) { e1.getFile() = e2.getFile() }
private module NearestLocationInput implements NearestLocationInputSig {
class C = ControlFlow::BasicBlock;

predicate relevantLocations(ControlFlow::BasicBlock bb, Location l1, Location l2) {
not bb instanceof ControlFlow::BasicBlocks::EntryBlock and
exists(DataFlowCallable c |
bb.getCallable() = c.asCallable(l1) and
l2 = bb.getLocation()
)
}
}

/** A callable used for data flow. */
class DataFlowCallable extends TDataFlowCallable {
/** Gets the underlying source code callable, if any. */
Callable asCallable(Location l) { this = TCallable(result, l) }

/** Gets a basic block belonging to this callable. */
ControlFlow::BasicBlock getABasicBlock() {
exists(Location l |
result.getCallable() = this.asCallable(l) and
inSameFile(result.getLocation(), l)
exists(Location l | result.getCallable() = this.asCallable(l) |
result instanceof ControlFlow::BasicBlocks::EntryBlock
or
NearestLocation<NearestLocationInput>::nearestLocation(result, l, _)
)
}

Expand Down Expand Up @@ -316,6 +327,8 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
or
folderDist(l.getFile().getParentContainer(),
this.getLocation().getFile().getParentContainer(), dist)
or
dist = 2147483647
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ private import semmle.code.csharp.frameworks.NHibernate
private import semmle.code.csharp.frameworks.Razor
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.threading.Tasks
private import semmle.code.csharp.internal.Location
private import codeql.util.Unit
private import codeql.util.Boolean

Expand Down Expand Up @@ -186,13 +187,14 @@ private module ThisFlow {
i = p.getPosition() + 1
)
or
exists(Location l, ControlFlow::BasicBlocks::EntryBlock entry |
n.(InstanceParameterNode).getCallable(l) = entry.getCallable() and
exists(DataFlowCallable c, ControlFlow::BasicBlocks::EntryBlock entry |
n.(InstanceParameterNode).isParameterOf(c, _) and
bb = c.getABasicBlock() and
// In case `c` has multiple bodies, we want each body to gets its own implicit
// entry definition. In case `c` doesn't have multiple bodies, the line below
// is simply the same as `bb = entry`.
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
// will be in the entry block.
bb = entry.getFirstNode().getASuccessor().getBasicBlock() and
inSameFile(l, bb.getLocation()) and
i = -1
)
}
Expand Down Expand Up @@ -1073,10 +1075,6 @@ private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantGvnType t) {
result = getANonTypeParameterSubType(t)
}

bindingset[e1, e2]
pragma[inline_late]
private predicate inSameFile(Location e1, Location e2) { e1.getFile() = e2.getFile() }

/** A callable with an implicit `this` parameter. */
private class InstanceCallable extends Callable {
private Location l;
Expand Down Expand Up @@ -1119,13 +1117,7 @@ private module Cached {
TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
cfn = def.getExpr().getAControlFlowNode()
} or
TExplicitParameterNode(Parameter p, Location l) {
exists(Location cloc |
p = any(DataFlowCallable dfc).asCallable(cloc).getAParameter() and
l = p.getALocation() and
inSameFile(l, cloc)
)
} or
TExplicitParameterNode(Parameter p, Location l) { l = [p.getLocation(), getASourceLocation(p)] } or
TInstanceParameterNode(InstanceCallable c, Location l) { l = c.getARelevantLocation() } or
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn, Boolean isPostUpdate) {
Expand Down Expand Up @@ -1173,7 +1165,7 @@ private module Cached {
} or
TPrimaryConstructorThisAccessNode(Parameter p, Boolean isPostUpdate, Location l) {
p.getCallable() instanceof PrimaryConstructor and
l = [p.getLocation(), p.getALocation().(SourceLocation)]
l = [p.getLocation(), getASourceLocation(p)]
} or
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)

Expand Down Expand Up @@ -1359,6 +1351,34 @@ abstract class ParameterNodeImpl extends NodeImpl {
}

private module ParameterNodes {
pragma[nomagic]
private predicate ssaParamDef(Ssa::ImplicitParameterDefinition ssaDef, Parameter p, Location l) {
p = ssaDef.getParameter() and
l = ssaDef.getLocation()
}

private module NearestLocationInputParamAfterCallable implements NearestLocationInputSig {
class C = ExplicitParameterNode;

predicate relevantLocations(ExplicitParameterNode p, Location l1, Location l2) {
exists(DataFlowCallable c |
c.asCallable(l1).getParameter(_) = p.getParameter() and
l2 = p.getLocation()
)
}
}

private module NearestLocationInputParamBeforeCallable implements NearestLocationInputSig {
class C = ExplicitParameterNode;

predicate relevantLocations(ExplicitParameterNode p, Location l1, Location l2) {
exists(DataFlowCallable c |
c.asCallable(l2).getParameter(_) = p.getParameter() and
l1 = p.getLocation()
)
}
}

/**
* The value of an explicit parameter at function entry, viewed as a node in a data
* flow graph.
Expand All @@ -1369,16 +1389,16 @@ private module ParameterNodes {

ExplicitParameterNode() { this = TExplicitParameterNode(parameter, location) }

Parameter getParameter() { result = parameter }

/** Gets the SSA definition corresponding to this parameter, if any. */
Ssa::ImplicitParameterDefinition getSsaDefinition() {
result.getSourceVariable().getAssignable() = parameter and
inSameFile(result.getLocation(), location)
}
Ssa::ImplicitParameterDefinition getSsaDefinition() { ssaParamDef(result, parameter, location) }

override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
exists(Location cloc |
c.asCallable(cloc).getParameter(pos.getPosition()) = parameter and
inSameFile(location, cloc)
exists(Location cloc | c.asCallable(cloc).getParameter(pos.getPosition()) = parameter |
NearestLocation<NearestLocationInputParamAfterCallable>::nearestLocation(this, cloc, _)
or
NearestLocation<NearestLocationInputParamBeforeCallable>::nearestLocation(this, _, cloc)
)
}

Expand Down Expand Up @@ -1920,6 +1940,17 @@ private class InstanceParameterAccessPreNode extends InstanceParameterAccessNode
override string toStringImpl() { result = "this" }
}

private module NearestLocationInputPrimaryConstructorParameter implements NearestLocationInputSig {
class C = PrimaryConstructorThisAccessNode;

predicate relevantLocations(PrimaryConstructorThisAccessNode p, Location l1, Location l2) {
exists(DataFlowCallable c |
c.asCallable(l1).getParameter(_) = p.getParameter() and
l2 = p.getLocation()
)
}
}

/**
* A data-flow node used to synthesize the body of a primary constructor.
*
Expand Down Expand Up @@ -1947,7 +1978,8 @@ abstract private class PrimaryConstructorThisAccessNode extends NodeImpl,
override DataFlowCallable getEnclosingCallableImpl() {
exists(Location cloc |
result.asCallable(cloc) = p.getCallable() and
inSameFile(cloc, location)
NearestLocation<NearestLocationInputPrimaryConstructorParameter>::nearestLocation(this, cloc,
_)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,8 @@ private module Cached {
c = entry.getCallable() and
// In case `c` has multiple bodies, we want each body to gets its own implicit
// entry definition. In case `c` doesn't have multiple bodies, the line below
// is simply the same as `bb = entry`.
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
// will be in the entry block.
bb = entry.getFirstNode().getASuccessor().getBasicBlock() and
c = v.getEnclosingCallable()
|
Expand Down
60 changes: 60 additions & 0 deletions csharp/ql/lib/semmle/code/csharp/internal/Location.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
private import csharp

private SourceLocation fromMappedLocation(SourceLocation loc) { result.getMappedLocation() = loc }

/** Holds if `l1` and `l2` are in the same file. */
bindingset[l1, l2]
pragma[inline_late]
predicate inSameFile(Location l1, Location l2) {
[l1, fromMappedLocation(l1)].getFile() = [l2, fromMappedLocation(l2)].getFile()
}

/** Gets a source location for `e`, if any. */
pragma[nomagic]
SourceLocation getASourceLocation(Element e) {
result = e.getALocation().(SourceLocation) and

Check warning

Code scanning / CodeQL

Redundant cast Warning

Redundant cast to
SourceLocation
.
not exists(e.getALocation().(SourceLocation).getMappedLocation())
or
result = e.getALocation().(SourceLocation).getMappedLocation()
}

/** Provides the input to `NearestLocation`. */
signature module NearestLocationInputSig {
class C {
string toString();

Location getLocation();
}

predicate relevantLocations(C c, Location l1, Location l2);
}

private Location unmap(Location l) {
result = fromMappedLocation(l)
or
not exists(fromMappedLocation(l)) and
result = l
}

module NearestLocation<NearestLocationInputSig Input> {
pragma[nomagic]
predicate nearestLocation(
Input::C c, Location l1, Location l2, Location l1unmapped, Location l2unmapped
) {
Input::relevantLocations(c, l1, l2) and
l1unmapped = unmap(l1) and
l2unmapped = unmap(l2) and
l1unmapped.before(l2unmapped)
}

pragma[nomagic]
predicate nearestLocation(Input::C c, Location l1, Location l2) {
l1 =
max(Location loc, Location l1unmapped, Location l2unmapped, int startline1, int startcolumn1 |
nearestLocation(c, loc, l2, l1unmapped, l2unmapped) and
l1unmapped.hasLocationInfo(_, startline1, startcolumn1, _, _)
|
loc order by startline1, startcolumn1
)
}
}
Loading

0 comments on commit 09b4672

Please sign in to comment.