Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ private import AstImport
class Ast extends TAst {
string toString() { none() }

pragma[nomagic]
final Ast getParent() { result.getChild(_) = this }

Location getLocation() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ class CallExpr extends Expr, TCallExpr {
*/
Expr getCallee() { none() }

/** Holds if an argument with name `name` is provided to this call. */
/**
* Holds if an argument with name `name` is provided to this call.
* Note: `name` is normalized to lower case.
*/
final predicate hasNamedArgument(string name) { exists(this.getNamedArgument(name)) }

/** Gets the argument to this call with the name `name`. */
/**
* Gets the named argument with the given name.
* Note: `name` is normalized to lower case.
*/
Expr getNamedArgument(string name) { none() }

/** Gets any argument to this call. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
private import AstImport

class CmdCall extends CallExpr, TCmd {
final override string getLowerCaseName() { result = getRawAst(this).(Raw::Cmd).getLowerCaseName() }
final override string getLowerCaseName() {
result = getRawAst(this).(Raw::Cmd).getLowerCaseName()
}

final override Expr getArgument(int i) { synthChild(getRawAst(this), cmdArgument(i), result) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class PipelineByPropertyNameParameter extends Parameter instanceof PipelineByPro
* Gets the iterator variable that is used to iterate over the elements in the pipeline.
*/
PipelineByPropertyNameIteratorVariable getIteratorVariable() { result.getParameter() = this }

ProcessBlock getProcessBlock() { result = this.getIteratorVariable().getProcessBlock() }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ private import Scope
class Ast extends @ast {
final string toString() { none() }

pragma[nomagic]
final Ast getParent() { result.getAChild() = this }

Ast getChild(ChildIndex i) { none() }

pragma[nomagic]
final Ast getAChild() { result = this.getChild(_) }

Location getLocation() { none() }
Expand Down
45 changes: 27 additions & 18 deletions powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ newtype VarKind =
PipelineIteratorKind() or
PipelineByPropertyNameIteratorKind(string name) {
exists(Raw::ProcessBlock pb |
name = pb.getScriptBlock().getParamBlock().getAPipelineByPropertyNameParameter().getLowerCaseName()
name =
pb.getScriptBlock().getParamBlock().getAPipelineByPropertyNameParameter().getLowerCaseName()
)
}

Expand Down Expand Up @@ -771,28 +772,35 @@ private module IteratorAccessSynth {
// TODO: We could join on something other than the string if we wanted (i.e., the raw parameter).
v.getPropertyName().toLowerCase() = result and
result =
pb.getScriptBlock()
.getParamBlock()
.getAPipelineByPropertyNameParameter()
.getLowerCaseName()
pb.getScriptBlock().getParamBlock().getAPipelineByPropertyNameParameter().getLowerCaseName()
}

private Raw::Ast getParent(Raw::Ast a) { a.getParent() = result }

private predicate isVarAccess(Raw::VarAccess va) { any() }

private predicate isProcessBlock(Raw::ProcessBlock pb) { any() }

private Raw::ProcessBlock getProcessBlock(Raw::VarAccess va) =
doublyBoundedFastTC(getParent/1, isVarAccess/1, isProcessBlock/1)(va, result)

private class IteratorAccessSynth extends Synthesis {
final override predicate isRelevant(Raw::Ast a) {
exists(Raw::ProcessBlock pb, Raw::VarAccess va |
va = a and
pb = va.getParent+()
|
exists(Raw::VarAccess va | va = a |
va.getUserPath() = "_"
or
va.getUserPath().toLowerCase() =
pb.getScriptBlock().getParamBlock().getPipelineParameter().getLowerCaseName()
or
va.getUserPath().toLowerCase() =
pb.getScriptBlock()
.getParamBlock()
.getAPipelineByPropertyNameParameter()
.getLowerCaseName()
exists(Raw::ProcessBlock pb |
pragma[only_bind_into](pb) = getProcessBlock(pragma[only_bind_into](va))
|
va.getUserPath().toLowerCase() =
pb.getScriptBlock().getParamBlock().getPipelineParameter().getLowerCaseName()
or
va.getUserPath().toLowerCase() =
pb.getScriptBlock()
.getParamBlock()
.getAPipelineByPropertyNameParameter()
.getLowerCaseName()
)
)
}

Expand All @@ -810,7 +818,7 @@ private module IteratorAccessSynth {

private PipelineOrPipelineByPropertyNameIteratorVariable varAccess(Raw::VarAccess va) {
exists(Raw::ProcessBlock pb |
pb = va.getParent+() and
pb = getProcessBlock(va) and
result = TVariableSynth(pb, _) and
va.getUserPath().toLowerCase() = getAPipelineIteratorName(pb, result)
)
Expand Down Expand Up @@ -896,6 +904,7 @@ private module PipelineAccess {
exists(PipelineByPropertyNameParameter pipelineVar, Raw::PipelineByPropertyNameParameter p |
i = processBlockPipelineByPropertyNameVarReadAccess(p.getLowerCaseName()) and
getResultAst(p) = pipelineVar and
pipelineVar = TVariableSynth(pb.getScriptBlock(), _) and
child = SynthChild(VarAccessSynthKind(pipelineVar))
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ module Private {

PipelineParameterImpl() {
exists(int index |
i = FunParam(index) and
any(Synthesis s).pipelineParameterHasIndex(super.getDeclaringScopeImpl(), index)
i = FunParam(pragma[only_bind_into](index)) and
any(Synthesis s)
.pipelineParameterHasIndex(super.getDeclaringScopeImpl(), pragma[only_bind_into](index))
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,17 @@ module Trees {
override predicate last(AstNode last, Completion c) {
// Exit the loop body when the condition is false
last(this.getCondition(), last, c) and
this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot())
this.entersLoopWhenConditionIs(pragma[only_bind_into](c.(BooleanCompletion)
.getValue()
.booleanNot()))
or
super.last(last, c)
}

override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Condition -> body
last(this.getCondition(), pred, c) and
this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and
this.entersLoopWhenConditionIs(pragma[only_bind_into](c.(BooleanCompletion).getValue())) and
first(this.getBody(), succ)
or
// Body -> condition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,27 +465,47 @@ class NamedSet extends NamedSet0 {
/** Gets the non-empty set of names, if any. */
NamedSetModule::Set asNonEmpty() { this = TNonEmptyNamedSet(result) }

/** Gets the `i`'th name in this set according to some ordering. */
private string getRankedName(int i) {
result = rank[i + 1](string s | s = this.getALowerCaseName() | s)
}

/** Holds if this is the empty set. */
predicate isEmpty() { this = TEmptyNamedSet() }

/** Gets a name in this set. */
string getAName() { this.asNonEmpty().contains(result) }
int getSize() {
result = strictcount(this.getALowerCaseName())
or
this.isEmpty() and
result = 0
}

/** Gets a lower-case name in this set. */
string getALowerCaseName() { this.asNonEmpty().contains(result) }

/** Gets the textual representation of this set. */
string toString() {
result = "{" + strictconcat(this.getAName(), ", ") + "}"
result = "{" + strictconcat(this.getALowerCaseName(), ", ") + "}"
or
this.isEmpty() and
result = "{}"
}

private CfgNodes::ExprNodes::CallExprCfgNode getABindingCallRec(int i) {
exists(string name | name = this.getRankedName(i) and exists(result.getNamedArgument(name)) |
i = 0
or
result = this.getABindingCallRec(i - 1)
)
}

/**
* Gets a `CfgNodes::CallCfgNode` that provides a named parameter for every name in `this`.
*
* NOTE: The `CfgNodes::CallCfgNode` may also provide more names.
*/
CfgNodes::ExprNodes::CallExprCfgNode getABindingCall() {
forex(string name | name = this.getAName() | exists(result.getNamedArgument(name)))
result = this.getABindingCallRec(this.getSize() - 1)
or
this.isEmpty() and
exists(result)
Expand All @@ -496,16 +516,28 @@ class NamedSet extends NamedSet0 {
* this set.
*/
CfgNodes::ExprNodes::CallExprCfgNode getAnExactBindingCall() {
forex(string name | name = this.getAName() | exists(result.getNamedArgument(name))) and
forex(string name | exists(result.getNamedArgument(name)) | name = this.getAName())
result = this.getABindingCallRec(this.getSize() - 1) and
strictcount(string name | result.hasNamedArgument(name)) = this.getSize()
or
this.isEmpty() and
not exists(result.getNamedArgument(_))
}

pragma[nomagic]
private Function getAFunctionRec(int i) {
i = 0 and
result.getAParameter().getLowerCaseName() = this.getRankedName(0)
or
exists(string name |
pragma[only_bind_into](name) = this.getRankedName(i) and
result.getAParameter().getLowerCaseName() = pragma[only_bind_into](name) and
result = this.getAFunctionRec(i - 1)
)
}

/** Gets a function that has a parameter for each name in this set. */
Function getAFunction() {
forex(string name | name = this.getAName() | result.getAParameter().matchesName(name))
result = this.getAFunctionRec(this.getSize() - 1)
or
this.isEmpty() and
exists(result)
Expand Down Expand Up @@ -533,6 +565,12 @@ private module ParameterNodes {
}
}

bindingset[p]
pragma[inline_late]
private predicate namedSetHasParameter(NamedSet ns, Parameter p) {
ns.getALowerCaseName() = p.getLowerCaseName()
}

/**
* The value of a normal parameter at function entry, viewed as a node in a data
* flow graph.
Expand Down Expand Up @@ -566,13 +604,13 @@ private module ParameterNodes {
f = parameter.getFunction() and
f = ns.getAFunction() and
name = parameter.getLowerCaseName() and
not name = ns.getAName() and
not name = ns.getALowerCaseName() and
j =
i -
count(int k, Parameter p |
k < i and
p = getNormalParameter(f, k) and
p.getLowerCaseName() = ns.getAName()
namedSetHasParameter(ns, p)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
* guard to `branch`.
*/
predicate controlsBranchEdge(SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, boolean branch) {
hasBranchEdge(bb1, bb2, branch)
this.hasBranchEdge(bb1, bb2, branch)
}
/**
* Holds if the evaluation of this guard to `branch` corresponds to the edge
Expand Down