Merge remote-tracking branch 'upstream/master' into mergeback-20181130

This commit is contained in:
Jonas Jensen
2018-11-30 10:13:33 +01:00
233 changed files with 7209 additions and 1359 deletions

View File

@@ -27,7 +27,8 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> TypeParameters => gc.TypeParameters.Concat(declaringType.TypeParameters);
public override IEnumerable<Type> MethodParameters => genericParams == null ? Enumerable.Empty<Type>() : genericParams;
public override IEnumerable<Type> MethodParameters =>
genericParams == null ? gc.MethodParameters : gc.MethodParameters.Concat(genericParams);
public int GenericParameterCount => signature.GenericParameterCount;
@@ -47,14 +48,14 @@ namespace Semmle.Extraction.CIL.Entities
internal protected Id MakeMethodId(Type parent, Id methodName)
{
var id = signature.ReturnType.MakeId(gc) + space + parent.ShortId + dot + methodName;
var id = signature.ReturnType.MakeId(this) + space + parent.ShortId + dot + methodName;
if (signature.GenericParameterCount > 0)
{
id += tick + signature.GenericParameterCount;
}
id += open + CIL.Id.CommaSeparatedList(signature.ParameterTypes.Select(p => p.MakeId(gc))) + close;
id += open + CIL.Id.CommaSeparatedList(signature.ParameterTypes.Select(p => p.MakeId(this))) + close;
return id;
}

View File

@@ -779,8 +779,8 @@ namespace Semmle.Extraction.CIL.Entities
public override Id MakeId(bool inContext) => elementType.GetId(inContext) + openBracket + rank + closeBracket;
static readonly StringId openBracket = new StringId("[]");
static readonly StringId closeBracket = new StringId("[]");
static readonly StringId openBracket = new StringId("[");
static readonly StringId closeBracket = new StringId("]");
public override Id Name => elementType.Name + openBracket + closeBracket;
@@ -1107,11 +1107,20 @@ namespace Semmle.Extraction.CIL.Entities
ITypeSignature IConstructedTypeProvider<ITypeSignature>.GetGenericInstantiation(ITypeSignature genericType, ImmutableArray<ITypeSignature> typeArguments) =>
new Instantiation { genericType = genericType, typeArguments = typeArguments };
static readonly Id open = Id.Create("{");
static readonly Id close = Id.Create("}");
class GenericMethodParameter : ITypeSignature
{
public object innerGc;
public int index;
static readonly Id excl = Id.Create("M!");
public Id MakeId(GenericContext gc) => excl + index;
public Id MakeId(GenericContext outerGc)
{
if (innerGc != outerGc && innerGc is Method method)
return open + method.Label.Value + close + excl + index;
return excl + index;
}
}
class GenericTypeParameter : ITypeSignature
@@ -1122,7 +1131,7 @@ namespace Semmle.Extraction.CIL.Entities
}
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetGenericMethodParameter(object genericContext, int index) =>
new GenericMethodParameter { index = index };
new GenericMethodParameter { innerGc = genericContext, index = index };
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetGenericTypeParameter(object genericContext, int index) =>
new GenericTypeParameter { index = index };

View File

@@ -38,14 +38,12 @@ namespace Semmle.Extraction.CSharp.Entities
Context.Compilation.GetTypeByMetadataName(valueTypeName) :
Context.Compilation.ObjectType;
var constraintTypes = new List<Type>();
foreach (var abase in symbol.ConstraintTypes)
{
if (abase.TypeKind != TypeKind.Interface)
baseType = abase;
var t = Create(Context, abase);
Context.Emit(Tuples.specific_type_parameter_constraints(constraints, t.TypeRef));
constraintTypes.Add(t);
}
Context.Emit(Tuples.types(this, Semmle.Extraction.Kinds.TypeKind.TYPE_PARAMETER, symbol.Name));
@@ -67,12 +65,15 @@ namespace Semmle.Extraction.CSharp.Entities
clauses = clauses.Concat(declSyntaxReferences.OfType<ClassDeclarationSyntax>().SelectMany(c => c.ConstraintClauses));
clauses = clauses.Concat(declSyntaxReferences.OfType<InterfaceDeclarationSyntax>().SelectMany(c => c.ConstraintClauses));
clauses = clauses.Concat(declSyntaxReferences.OfType<StructDeclarationSyntax>().SelectMany(c => c.ConstraintClauses));
int i = 0;
foreach (var clause in clauses.Where(c => c.Name.ToString() == symbol.Name))
foreach (var clause in clauses.Where(c => c.Name.Identifier.Text == symbol.Name))
{
TypeMention.Create(Context, clause.Name, this, this);
foreach (var constraint in clause.Constraints.OfType<TypeConstraintSyntax>())
TypeMention.Create(Context, constraint.Type, this, constraintTypes[i++]);
{
var ti = Context.Model(constraint).GetTypeInfo(constraint.Type);
var target = Type.Create(Context, ti.Type);
TypeMention.Create(Context, constraint.Type, this, target);
}
}
}
}

View File

@@ -14,16 +14,53 @@
import csharp
import semmle.code.csharp.controlflow.Guards
import semmle.code.csharp.commons.ComparisonTest
from RelationalOperation cmp, Variable array, Variable index, ElementAccess ea, VariableAccess indexAccess
// Look for `index <= array.Length` or `array.Length >= index`
where (cmp instanceof GEExpr or cmp instanceof LEExpr)
and cmp.getGreaterOperand() = any(PropertyAccess pa | pa.getQualifier() = array.getAnAccess() and pa.getTarget().hasName("Length"))
and cmp.getLesserOperand() = index.getAnAccess()
/** A comparison of an index variable with the length of an array. */
class IndexGuard extends ComparisonTest {
VariableAccess indexAccess;
Variable array;
IndexGuard() {
this.getFirstArgument() = indexAccess and
this.getSecondArgument() = any(PropertyAccess lengthAccess |
lengthAccess.getQualifier() = array.getAnAccess() and
lengthAccess.getTarget().hasName("Length")
)
}
/** Holds if this comparison applies to array `arr` and index `ind`. */
predicate controls(Variable arr, Variable ind) {
arr = array and
ind.getAnAccess() = indexAccess
}
/** Holds if this comparison guards `expr`. */
predicate guards(GuardedExpr expr, boolean condition) {
expr.isGuardedBy(this.getExpr(), indexAccess, condition)
}
/** Holds if this comparison is an incorrect `<=` or equivalent. */
predicate isIncorrect() {
this.getComparisonKind().isLessThanEquals()
}
}
from IndexGuard incorrectGuard, Variable array, Variable index, ElementAccess ea, GuardedExpr indexAccess
where
// Look for `index <= array.Length` or `array.Length >= index`
incorrectGuard.controls(array, index) and
incorrectGuard.isIncorrect() and
// Look for `array[index]`
and ea.getQualifier() = array.getAnAccess()
and ea.getIndex(0) = indexAccess
and indexAccess = index.getAnAccess()
ea.getQualifier() = array.getAnAccess() and
ea.getIndex(0) = indexAccess and
indexAccess = index.getAnAccess() and
// Where the index access is guarded by the comparison
and indexAccess.(GuardedExpr).isGuardedBy(cmp, index.getAnAccess(), true)
select cmp, "Off-by-one index comparison against length leads to possible out of bounds $@.", ea, ea.toString()
incorrectGuard.guards(indexAccess, true) and
// And there are no other guards
not exists(IndexGuard validGuard |
not validGuard.isIncorrect() and
validGuard.controls(array, index) and
validGuard.guards(indexAccess, _)
)
select incorrectGuard, "Off-by-one index comparison against length leads to possible out of bounds $@.", ea, ea.toString()

View File

@@ -45,4 +45,10 @@ class UnboundGenericMethod extends UnboundGeneric, Method { }
class ConstructedType extends ConstructedGeneric, Type { }
/** A constructed generic method. */
class ConstructedMethod extends ConstructedGeneric, Method { }
class ConstructedMethod extends ConstructedGeneric, Method {
final override UnboundGenericMethod getUnboundGeneric() { result = getUnboundMethod() }
final override Location getLocation() {
result = getUnboundGeneric().getLocation()
}
}

View File

@@ -2,6 +2,8 @@
private import semmle.code.csharp.frameworks.system.Diagnostics
private import semmle.code.csharp.frameworks.test.VisualStudio
private import semmle.code.csharp.frameworks.System
private import ControlFlow
private import ControlFlow::BasicBlocks
/** An assertion method. */
abstract class AssertMethod extends Method {
@@ -46,6 +48,49 @@ class Assertion extends MethodCall {
Expr getExpr() {
result = this.getArgumentForParameter(target.getAssertedParameter())
}
pragma[nomagic]
private JoinBlockPredecessor getAPossiblyDominatedPredecessor(JoinBlock jb) {
exists(BasicBlock bb |
bb = this.getAControlFlowNode().getBasicBlock() |
result = bb.getASuccessor*()
) and
result.getASuccessor() = jb
}
pragma[nomagic]
private predicate isPossiblyDominatedJoinBlock(JoinBlock jb) {
exists(this.getAPossiblyDominatedPredecessor(jb)) and
forall(BasicBlock pred |
pred = jb.getAPredecessor() |
pred = this.getAPossiblyDominatedPredecessor(jb)
)
}
/**
* Holds if this assertion strictly dominates basic block `bb`. That is, `bb`
* can only be reached from the callable entry point by going via *some* basic
* block containing this element.
*
* This predicate is different from
* `this.getAControlFlowNode().getBasicBlock().strictlyDominates(bb)`
* in that it takes control flow splitting into account.
*/
pragma[nomagic]
predicate strictlyDominates(BasicBlock bb) {
this.getAControlFlowNode().getBasicBlock().immediatelyDominates(bb)
or
if bb instanceof JoinBlock then
this.isPossiblyDominatedJoinBlock(bb) and
forall(BasicBlock pred |
pred = this.getAPossiblyDominatedPredecessor(bb) |
this.strictlyDominates(pred)
or
this.getAControlFlowNode().getBasicBlock() = pred
)
else
this.strictlyDominates(bb.getAPredecessor())
}
}
/** A trivially failing assertion, for example `Debug.Assert(false)`. */

View File

@@ -15,7 +15,7 @@ class BasicBlock extends TBasicBlockStart {
result.getFirstNode() = getLastNode().getASuccessor()
}
/** Gets an immediate successor of this basic block of a given flow type, if any. */
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) {
result.getFirstNode() = this.getLastNode().getASuccessorByType(t)
}
@@ -25,6 +25,11 @@ class BasicBlock extends TBasicBlockStart {
result.getASuccessor() = this
}
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) {
result.getASuccessorByType(t) = this
}
/**
* Gets an immediate `true` successor, if any.
*
@@ -134,6 +139,31 @@ class BasicBlock extends TBasicBlockStart {
result = strictcount(getANode())
}
/**
* Holds if this basic block immediately dominates basic block `bb`.
*
* That is, all paths reaching basic block `bb` from some entry point
* basic block must go through this basic block (which is an immediate
* predecessor of `bb`).
*
* Example:
*
* ```
* int M(string s) {
* if (s == null)
* throw new ArgumentNullException(nameof(s));
* return s.Length;
* }
* ```
*
* The basic block starting on line 2 strictly dominates the
* basic block on line 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check).
*/
predicate immediatelyDominates(BasicBlock bb) {
bbIDominates(this, bb)
}
/**
* Holds if this basic block strictly dominates basic block `bb`.
*
@@ -419,24 +449,42 @@ private predicate exitBB(BasicBlock bb) {
bb.getLastNode() instanceof ControlFlow::Nodes::ExitNode
}
/**
* A basic block with more than one predecessor.
*/
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlock {
JoinBlock() { getFirstNode().isJoin() }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlock {
JoinBlockPredecessor() {
this.getASuccessor() instanceof JoinBlock
}
}
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlock {
ConditionBlock() {
this.getLastNode().isCondition()
}
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
succ = this.getASuccessorByType(s) and
forall(BasicBlock pred |
pred = succ.getAPredecessor() and pred != this |
succ.dominates(pred)
)
}
/**
* Holds if basic block `controlled` is controlled by this basic block with
* Boolean value `testIsTrue`. That is, `controlled` can only be reached from
* the callable entry point by going via the true edge (`testIsTrue = true`)
* or false edge (`testIsTrue = false`) out of this basic block.
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
/*
@@ -473,7 +521,7 @@ class ConditionBlock extends BasicBlock {
* directly.
*/
exists(BasicBlock succ |
isCandidateSuccessor(succ, s) |
this.immediatelyControls(succ, s) |
succ.dominates(controlled)
)
}
@@ -529,14 +577,6 @@ class ConditionBlock extends BasicBlock {
impliesSub(getLastNode().getElement(), cond, testIsTrue, condIsTrue) and
controls(controlled, any(BooleanSuccessor s | s.getValue() = testIsTrue))
}
private predicate isCandidateSuccessor(BasicBlock succ, ConditionalSuccessor s) {
succ = this.getASuccessorByType(s) and
forall(BasicBlock pred |
pred = succ.getAPredecessor() and pred != this |
succ.dominates(pred)
)
}
}
/**

View File

@@ -1,7 +1,10 @@
/** Provides the class `ControlFlowElement`. */
import csharp
import csharp
private import semmle.code.csharp.ExprOrStmtParent
private import ControlFlow
private import ControlFlow::BasicBlocks
private import SuccessorTypes
/**
* A program element that can possess control flow. That is, either a statement or
@@ -25,21 +28,21 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
* several `ControlFlow::Node`s, for example to represent the continuation
* flow in a `try/catch/finally` construction.
*/
ControlFlow::Node getAControlFlowNode() {
Node getAControlFlowNode() {
result.getElement() = this
}
/**
* Gets a first control flow node executed within this element.
*/
ControlFlow::Node getAControlFlowEntryNode() {
Node getAControlFlowEntryNode() {
result = ControlFlowGraph::Internal::getAControlFlowEntryNode(this).getAControlFlowNode()
}
/**
* Gets a potential last control flow node executed within this element.
*/
ControlFlow::Node getAControlFlowExitNode() {
Node getAControlFlowExitNode() {
result = ControlFlowGraph::Internal::getAControlFlowExitNode(this).getAControlFlowNode()
}
@@ -59,7 +62,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
/** Gets an element that is reachable from this element. */
ControlFlowElement getAReachableElement() {
// Reachable in same basic block
exists(ControlFlow::BasicBlock bb, int i, int j |
exists(BasicBlock bb, int i, int j |
bb.getNode(i) = getAControlFlowNode() and
bb.getNode(j) = result.getAControlFlowNode() and
i < j
@@ -68,4 +71,91 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
// Reachable in different basic blocks
getAControlFlowNode().getBasicBlock().getASuccessor+().getANode() = result.getAControlFlowNode()
}
/**
* Holds if basic block `succ` is immediately controlled by this control flow
* element with conditional value `s`. That is, `succ` can only be reached from
* the callable entry point by going via the `s` edge out of *some* basic block
* `pred` ending with this element, and `pred` is an immediate predecessor
* of `succ`.
*
* This predicate is different from
* `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).immediatelyControls(succ, s)`
* in that it takes control flow splitting into account.
*/
pragma[nomagic]
private predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
exists(ConditionBlock cb |
cb.getLastNode() = this.getAControlFlowNode() |
succ = cb.getASuccessorByType(s) and
forall(BasicBlock pred, SuccessorType t |
pred = succ.getAPredecessorByType(t) and pred != cb |
succ.dominates(pred)
or
// `pred` might be another split of `cfe`
pred.getLastNode().getElement() = this and
pred.getASuccessorByType(t) = succ and
t = s
)
)
}
pragma[nomagic]
private JoinBlockPredecessor getAPossiblyControlledPredecessor(JoinBlock controlled, ConditionalSuccessor s) {
exists(BasicBlock mid |
this.immediatelyControls(mid, s) |
result = mid.getASuccessor*()
) and
result.getASuccessor() = controlled
}
pragma[nomagic]
private predicate isPossiblyControlledJoinBlock(JoinBlock controlled, ConditionalSuccessor s) {
exists(this.getAPossiblyControlledPredecessor(controlled, s)) and
forall(BasicBlock pred |
pred = controlled.getAPredecessor() |
pred = this.getAPossiblyControlledPredecessor(controlled, s)
)
}
/**
* Holds if basic block `controlled` is controlled by this control flow element
* with conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of *some* basic block
* ending with this element.
*
* This predicate is different from
* `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).controls(controlled, s)`
* in that it takes control flow splitting into account.
*/
cached
predicate controlsBlock(BasicBlock controlled, ConditionalSuccessor s) {
this.immediatelyControls(controlled, s)
or
if controlled instanceof JoinBlock then
this.isPossiblyControlledJoinBlock(controlled, s) and
forall(BasicBlock pred |
pred = this.getAPossiblyControlledPredecessor(controlled, s) |
this.controlsBlock(pred, s)
)
else
this.controlsBlock(controlled.getAPredecessor(), s)
}
/**
* Holds if control flow element `controlled` is controlled by this control flow
* element with conditional value `s`. That is, `controlled` can only be reached
* from the callable entry point by going via the `s` edge out of this element.
*
* This predicate is different from
* `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).controls(controlled.getAControlFlowNode().getBasicBlock(), s)`
* in that it takes control flow splitting into account.
*/
pragma[inline] // potentially very large predicate, so must be inlined
predicate controlsElement(ControlFlowElement controlled, ConditionalSuccessor s) {
forex(BasicBlock bb |
bb = controlled.getAControlFlowNode().getBasicBlock() |
this.controlsBlock(bb, s)
)
}
}

View File

@@ -183,7 +183,7 @@ module ControlFlow {
)
}
/** Gets a successor node of a given flow type, if any. */
/** Gets a successor node of a given type, if any. */
Node getASuccessorByType(SuccessorType t) {
result = getASuccessorByType(this, t)
}
@@ -372,6 +372,7 @@ module ControlFlow {
class EntryBlock = BBs::EntryBasicBlock;
class ExitBlock = BBs::ExitBasicBlock;
class JoinBlock = BBs::JoinBlock;
class JoinBlockPredecessor = BBs::JoinBlockPredecessor;
class ConditionBlock = BBs::ConditionBlock;
}
@@ -2549,7 +2550,7 @@ module ControlFlow {
) > 1
}
private predicate isCandidateSuccessor(PreBasicBlock succ, ConditionalCompletion c) {
private predicate immediatelyControls(PreBasicBlock succ, ConditionalCompletion c) {
succ = succ(this.getLastElement(), c) and
forall(PreBasicBlock pred |
pred = succ.getAPredecessor() and pred != this |
@@ -2559,7 +2560,7 @@ module ControlFlow {
predicate controls(PreBasicBlock controlled, ConditionalSuccessor s) {
exists(PreBasicBlock succ, ConditionalCompletion c |
isCandidateSuccessor(succ, c) |
immediatelyControls(succ, c) |
succ.dominates(controlled) and
s.matchesCompletion(c)
)

View File

@@ -473,19 +473,40 @@ module Internal {
/** Holds if basic block `bb` only is reached when this guard has abstract value `v`. */
predicate controls(BasicBlock bb, AbstractValue v) {
exists(ConditionBlock cb, ConditionalSuccessor s, AbstractValue v0, Guard g |
cb.controls(bb, s) |
v0.branchImplies(cb.getLastNode().getElement(), s, g) and
exists(ControlFlowElement cfe, ConditionalSuccessor s, AbstractValue v0, Guard g |
cfe.controlsBlock(bb, s) |
v0.branchImplies(cfe, s, g) and
impliesSteps(g, v0, this, v)
)
}
/** Holds if control flow node `cfn` only is reached when this guard evaluates to `v`. */
predicate controlsNode(ControlFlow::Node cfn, AbstractValue v) {
/**
* Holds if control flow node `cfn` only is reached when this guard evaluates to `v`,
* because of an assertion.
*/
private predicate assertionControlsNode(ControlFlow::Node cfn, AbstractValue v) {
exists(Assertion a, Guard g, AbstractValue v0 |
a.getAControlFlowNode().dominates(cfn) |
asserts(a, g, v0) and
impliesSteps(g, v0, this, v)
|
a.strictlyDominates(cfn.getBasicBlock())
or
exists(BasicBlock bb, int i, int j |
bb.getNode(i) = a.getAControlFlowNode() |
bb.getNode(j) = cfn and
j > i
)
)
}
/**
* Holds if control flow element `cfe` only is reached when this guard evaluates to `v`,
* because of an assertion.
*/
predicate assertionControlsElement(ControlFlowElement cfe, AbstractValue v) {
forex(ControlFlow::Node cfn |
cfn = cfe.getAControlFlowNode() |
this.assertionControlsNode(cfn, v)
)
}
@@ -639,24 +660,33 @@ module Internal {
e = g.getAChildExpr*() |
g.controls(bb, _)
or
g.controlsNode(bb.getANode(), _)
g.assertionControlsElement(bb.getANode().getElement(), _)
)
}
}
private cached module Cached {
pragma[noinline]
private predicate isGuardedBy0(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
exists(ControlFlow::Node cfn |
(g.controls(cfn.getBasicBlock(), v) or g.controlsNode(cfn, v)) and
cfn = guarded.getAControlFlowNode() and
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
private predicate isGuardedBy0(ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
cfn = guarded.getAControlFlowNode() and
g.controls(cfn.getBasicBlock(), v) and
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
}
pragma[noinline]
private predicate isGuardedBy1(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
forex(ControlFlow::Node cfn |
cfn = guarded.getAControlFlowNode() |
isGuardedBy0(cfn, guarded, g, sub, v)
)
or
g.assertionControlsElement(guarded, v) and
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
}
cached
predicate isGuardedBy(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v) {
isGuardedBy0(guarded, g, sub, v) and
isGuardedBy1(guarded, g, sub, v) and
sub = g.getAChildExpr*() and
(
not guarded.hasSsaQualifier() and not sub.hasSsaQualifier()

View File

@@ -190,3 +190,10 @@ class SendingMethod extends SensitiveExecutionMethod {
)
}
}
/** A call to a method that sends data, and so should not be run conditionally on user input. */
class SensitiveExecutionMethodCall extends MethodCall {
SensitiveExecutionMethodCall() {
this.getTarget() instanceof SensitiveExecutionMethod
}
}

View File

@@ -68,26 +68,26 @@ module UserControlledBypassOfSensitiveMethod {
}
}
private predicate conditionControlsCall(SensitiveExecutionMethodCall call, SensitiveExecutionMethod def, Expr e, boolean cond) {
exists(ControlFlow::SuccessorTypes::BooleanSuccessor s |
cond = s.getValue() |
e.controlsElement(call, s)
) and
def = call.getTarget()
}
/**
* Calls to a sensitive method that are controlled by a condition
* on the given expression.
*/
predicate conditionControlsMethod(MethodCall call, Expr e) {
exists(ConditionBlock cb, SensitiveExecutionMethod def, boolean cond |
exists(ControlFlow::SuccessorTypes::BooleanSuccessor s |
cond = s.getValue() |
cb.controls(call.getAControlFlowNode().getBasicBlock(), s)
) and
def = call.getTarget() and
predicate conditionControlsMethod(SensitiveExecutionMethodCall call, Expr e) {
exists(SensitiveExecutionMethod def, boolean cond |
conditionControlsCall(call, def, e, cond) and
/*
* Exclude this condition if the other branch also contains a call to the same security
* sensitive method.
*/
not exists(ControlFlow::SuccessorTypes::BooleanSuccessor s |
cond = s.getValue().booleanNot() |
cb.controls(def.getACall().getAControlFlowNode().getBasicBlock(), s)
) and
e = cb.getLastNode().getElement()
not conditionControlsCall(_, def, e, cond.booleanNot())
)
}

View File

@@ -0,0 +1,2 @@
| Methods.dll:0:0:0:0 | Methods.Class1.F | Methods.dll:0:0:0:0 | Methods.Class1.F | Methods.dll:0:0:0:0 | Methods.Class1.G.!0 |
| Methods.dll:0:0:0:0 | Methods.Class1.F | Methods.dll:0:0:0:0 | Methods.Class1.F | Methods.dll:0:0:0:0 | Methods.Class1.H.!0 |

View File

@@ -0,0 +1,7 @@
import cil::CIL
from UnboundGenericMethod f, ConstructedMethod fc
where
fc.getUnboundMethod() = f and
f.getQualifiedName() = "Methods.Class1.F"
select f, fc, fc.getTypeArgument(0)

View File

@@ -0,0 +1,19 @@
/*
* A regression test for the CIL extractor - compiled into Methods.dll
* This tests the correct extraction of F<T>, and we should end up with
* 2 constructed methods of F<T>.
*/
// semmle-extractor-options: --cil
namespace Methods
{
public class Class1
{
public T F<T>(T t) { return new T[] { t }[0]; }
public T G<T>(T t) { return F(t); }
public T H<T>(T t) { return F(t); }
}
}

View File

@@ -62,3 +62,21 @@
| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false |
| Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true |
| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false |
| Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true |
| Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | true |
| Splitting.cs:25:13:25:13 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | false |
| Splitting.cs:35:13:35:13 | access to parameter o | Splitting.cs:32:17:32:25 | ... == ... | Splitting.cs:32:17:32:17 | access to parameter o | false |
| Splitting.cs:44:17:44:17 | access to parameter o | Splitting.cs:41:13:41:21 | ... != ... | Splitting.cs:41:13:41:13 | access to parameter o | true |
| Splitting.cs:46:17:46:17 | access to parameter o | Splitting.cs:41:13:41:21 | ... != ... | Splitting.cs:41:13:41:13 | access to parameter o | true |
| Splitting.cs:55:13:55:13 | access to parameter o | Splitting.cs:54:13:54:21 | ... != ... | Splitting.cs:54:13:54:13 | access to parameter o | true |
| Splitting.cs:66:20:66:20 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | true |
| Splitting.cs:68:13:68:13 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | false |
| Splitting.cs:69:16:69:16 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | false |
| Splitting.cs:78:24:78:24 | access to parameter o | Splitting.cs:76:13:76:21 | ... != ... | Splitting.cs:76:13:76:13 | access to parameter o | true |
| Splitting.cs:90:13:90:13 | access to parameter o | Splitting.cs:87:26:87:34 | ... != ... | Splitting.cs:87:26:87:26 | access to parameter o | true |
| Splitting.cs:99:13:99:13 | access to parameter o | Splitting.cs:97:26:97:34 | ... == ... | Splitting.cs:97:26:97:26 | access to parameter o | true |
| Splitting.cs:107:13:107:13 | access to parameter o | Splitting.cs:105:22:105:30 | ... != ... | Splitting.cs:105:22:105:22 | access to parameter o | true |
| Splitting.cs:109:13:109:13 | access to parameter o | Splitting.cs:105:22:105:30 | ... != ... | Splitting.cs:105:22:105:22 | access to parameter o | true |
| Splitting.cs:117:9:117:9 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |
| Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |
| Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |

View File

@@ -155,3 +155,39 @@
| Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true |
| Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | non-null |
| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false |
| Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null |
| Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true |
| Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | non-null |
| Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | true |
| Splitting.cs:25:13:25:13 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | null |
| Splitting.cs:25:13:25:13 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | false |
| Splitting.cs:35:13:35:13 | access to parameter o | Splitting.cs:32:17:32:17 | access to parameter o | Splitting.cs:32:17:32:17 | access to parameter o | non-null |
| Splitting.cs:35:13:35:13 | access to parameter o | Splitting.cs:32:17:32:25 | ... == ... | Splitting.cs:32:17:32:17 | access to parameter o | false |
| Splitting.cs:44:17:44:17 | access to parameter o | Splitting.cs:41:13:41:13 | access to parameter o | Splitting.cs:41:13:41:13 | access to parameter o | non-null |
| Splitting.cs:44:17:44:17 | access to parameter o | Splitting.cs:41:13:41:21 | ... != ... | Splitting.cs:41:13:41:13 | access to parameter o | true |
| Splitting.cs:46:17:46:17 | access to parameter o | Splitting.cs:41:13:41:13 | access to parameter o | Splitting.cs:41:13:41:13 | access to parameter o | non-null |
| Splitting.cs:46:17:46:17 | access to parameter o | Splitting.cs:41:13:41:21 | ... != ... | Splitting.cs:41:13:41:13 | access to parameter o | true |
| Splitting.cs:55:13:55:13 | access to parameter o | Splitting.cs:54:13:54:13 | access to parameter o | Splitting.cs:54:13:54:13 | access to parameter o | non-null |
| Splitting.cs:55:13:55:13 | access to parameter o | Splitting.cs:54:13:54:21 | ... != ... | Splitting.cs:54:13:54:13 | access to parameter o | true |
| Splitting.cs:66:20:66:20 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | non-null |
| Splitting.cs:66:20:66:20 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | true |
| Splitting.cs:68:13:68:13 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | null |
| Splitting.cs:68:13:68:13 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | false |
| Splitting.cs:69:16:69:16 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | Splitting.cs:65:13:65:13 | access to parameter o | null |
| Splitting.cs:69:16:69:16 | access to parameter o | Splitting.cs:65:13:65:21 | ... != ... | Splitting.cs:65:13:65:13 | access to parameter o | false |
| Splitting.cs:78:24:78:24 | access to parameter o | Splitting.cs:76:13:76:13 | access to parameter o | Splitting.cs:76:13:76:13 | access to parameter o | non-null |
| Splitting.cs:78:24:78:24 | access to parameter o | Splitting.cs:76:13:76:21 | ... != ... | Splitting.cs:76:13:76:13 | access to parameter o | true |
| Splitting.cs:90:13:90:13 | access to parameter o | Splitting.cs:87:26:87:26 | access to parameter o | Splitting.cs:87:26:87:26 | access to parameter o | non-null |
| Splitting.cs:90:13:90:13 | access to parameter o | Splitting.cs:87:26:87:34 | ... != ... | Splitting.cs:87:26:87:26 | access to parameter o | true |
| Splitting.cs:99:13:99:13 | access to parameter o | Splitting.cs:97:26:97:26 | access to parameter o | Splitting.cs:97:26:97:26 | access to parameter o | null |
| Splitting.cs:99:13:99:13 | access to parameter o | Splitting.cs:97:26:97:34 | ... == ... | Splitting.cs:97:26:97:26 | access to parameter o | true |
| Splitting.cs:107:13:107:13 | access to parameter o | Splitting.cs:105:22:105:22 | access to parameter o | Splitting.cs:105:22:105:22 | access to parameter o | non-null |
| Splitting.cs:107:13:107:13 | access to parameter o | Splitting.cs:105:22:105:30 | ... != ... | Splitting.cs:105:22:105:22 | access to parameter o | true |
| Splitting.cs:109:13:109:13 | access to parameter o | Splitting.cs:105:22:105:22 | access to parameter o | Splitting.cs:105:22:105:22 | access to parameter o | non-null |
| Splitting.cs:109:13:109:13 | access to parameter o | Splitting.cs:105:22:105:30 | ... != ... | Splitting.cs:105:22:105:22 | access to parameter o | true |
| Splitting.cs:117:9:117:9 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | non-null |
| Splitting.cs:117:9:117:9 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |
| Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | non-null |
| Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |
| Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | non-null |
| Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true |

View File

@@ -200,3 +200,25 @@
| Guards.cs:195:13:195:27 | call to method NotNullTest4 | true | Guards.cs:195:26:195:26 | access to parameter s | non-null |
| Guards.cs:197:13:197:29 | !... | false | Guards.cs:197:14:197:29 | call to method NullTestWrong | true |
| Guards.cs:197:13:197:29 | !... | true | Guards.cs:197:14:197:29 | call to method NullTestWrong | false |
| Splitting.cs:12:17:12:25 | ... != ... | false | Splitting.cs:12:17:12:17 | access to parameter o | null |
| Splitting.cs:12:17:12:25 | ... != ... | true | Splitting.cs:12:17:12:17 | access to parameter o | non-null |
| Splitting.cs:22:17:22:25 | ... != ... | false | Splitting.cs:22:17:22:17 | access to parameter o | null |
| Splitting.cs:22:17:22:25 | ... != ... | true | Splitting.cs:22:17:22:17 | access to parameter o | non-null |
| Splitting.cs:32:17:32:25 | ... == ... | false | Splitting.cs:32:17:32:17 | access to parameter o | non-null |
| Splitting.cs:32:17:32:25 | ... == ... | true | Splitting.cs:32:17:32:17 | access to parameter o | null |
| Splitting.cs:41:13:41:21 | ... != ... | false | Splitting.cs:41:13:41:13 | access to parameter o | null |
| Splitting.cs:41:13:41:21 | ... != ... | true | Splitting.cs:41:13:41:13 | access to parameter o | non-null |
| Splitting.cs:54:13:54:21 | ... != ... | false | Splitting.cs:54:13:54:13 | access to parameter o | null |
| Splitting.cs:54:13:54:21 | ... != ... | true | Splitting.cs:54:13:54:13 | access to parameter o | non-null |
| Splitting.cs:65:13:65:21 | ... != ... | false | Splitting.cs:65:13:65:13 | access to parameter o | null |
| Splitting.cs:65:13:65:21 | ... != ... | true | Splitting.cs:65:13:65:13 | access to parameter o | non-null |
| Splitting.cs:76:13:76:21 | ... != ... | false | Splitting.cs:76:13:76:13 | access to parameter o | null |
| Splitting.cs:76:13:76:21 | ... != ... | true | Splitting.cs:76:13:76:13 | access to parameter o | non-null |
| Splitting.cs:87:26:87:34 | ... != ... | false | Splitting.cs:87:26:87:26 | access to parameter o | null |
| Splitting.cs:87:26:87:34 | ... != ... | true | Splitting.cs:87:26:87:26 | access to parameter o | non-null |
| Splitting.cs:97:26:97:34 | ... == ... | false | Splitting.cs:97:26:97:26 | access to parameter o | non-null |
| Splitting.cs:97:26:97:34 | ... == ... | true | Splitting.cs:97:26:97:26 | access to parameter o | null |
| Splitting.cs:105:22:105:30 | ... != ... | false | Splitting.cs:105:22:105:22 | access to parameter o | null |
| Splitting.cs:105:22:105:30 | ... != ... | true | Splitting.cs:105:22:105:22 | access to parameter o | non-null |
| Splitting.cs:116:22:116:30 | ... != ... | false | Splitting.cs:116:22:116:22 | access to parameter o | null |
| Splitting.cs:116:22:116:30 | ... != ... | true | Splitting.cs:116:22:116:22 | access to parameter o | non-null |

View File

@@ -39,3 +39,17 @@
| Guards.cs:192:31:192:31 | access to parameter s |
| Guards.cs:194:31:194:31 | access to parameter s |
| Guards.cs:196:31:196:31 | access to parameter s |
| Splitting.cs:13:17:13:17 | access to parameter o |
| Splitting.cs:23:24:23:24 | access to parameter o |
| Splitting.cs:35:13:35:13 | access to parameter o |
| Splitting.cs:44:17:44:17 | access to parameter o |
| Splitting.cs:46:17:46:17 | access to parameter o |
| Splitting.cs:55:13:55:13 | access to parameter o |
| Splitting.cs:66:20:66:20 | access to parameter o |
| Splitting.cs:78:24:78:24 | access to parameter o |
| Splitting.cs:90:13:90:13 | access to parameter o |
| Splitting.cs:107:13:107:13 | access to parameter o |
| Splitting.cs:109:13:109:13 | access to parameter o |
| Splitting.cs:117:9:117:9 | access to parameter o |
| Splitting.cs:119:13:119:13 | access to parameter o |
| Splitting.cs:120:16:120:16 | access to parameter o |

View File

@@ -0,0 +1,122 @@
using System;
using System.Diagnostics;
/// <summary>
/// Tests related to CFG splitting.
/// </summary>
public class Splitting
{
void M1(bool b, object o)
{
if (b)
if (o != null)
o.ToString(); // null guarded
if (b)
o.ToString(); // not null guarded
o.ToString(); // not null guarded
}
string M2(bool b, object o)
{
if (b)
if (o != null)
return o.ToString(); // null guarded
if (b)
o.ToString(); // anti-null guarded
return o.ToString(); // not null guarded
}
string M3(bool b, object o)
{
if (b)
if (o == null)
return "";
if (b)
o.ToString(); // null guarded
return o.ToString(); // not null guarded
}
void M4(bool b, object o)
{
if (o != null)
{
if (b)
o.ToString(); // null guarded
if (b)
o.ToString(); // null guarded
}
}
string M5(bool b, object o)
{
if (b)
o.ToString(); // not null guarded
if (o != null)
o.ToString(); // null guarded
if (b)
o.ToString(); // not null guarded
return o.ToString(); // not null guarded
}
string M6(bool b, object o)
{
if (b)
o.ToString(); // not null guarded
if (o != null)
return o.ToString(); // null guarded
if (b)
o.ToString(); // anti-null guarded
return o.ToString(); // anti-null guarded
}
string M7(bool b, object o, bool b2)
{
if (b)
o.ToString(); // not null guarded
if (o != null)
if (b2)
return o.ToString(); // null guarded
if (b)
o.ToString(); // not null guarded
return o.ToString(); // not null guarded
}
void M8(bool b, object o)
{
if (b)
Debug.Assert(o != null);
o.ToString(); // not null guarded
if (b)
o.ToString(); // null guarded
o.ToString(); // not null guarded
}
string M9(bool b, object o)
{
if (b)
Debug.Assert(o == null);
if (b)
o.ToString(); // anti-null guarded
return o.ToString(); // not null guarded
}
void M10(bool b, object o)
{
Debug.Assert(o != null);
if (b)
o.ToString(); // null guarded
if (b)
o.ToString(); // null guarded
}
string M11(bool b, object o)
{
if (b)
o.ToString(); // not null guarded
Debug.Assert(o != null);
o.ToString(); // null guarded
if (b)
o.ToString(); // null guarded
return o.ToString(); // null guarded
}
}

View File

@@ -139,4 +139,12 @@ class LocalVariableTags
};
}
partial class C1<T> where T: DynamicType
{
}
partial class C1<T> where T: DynamicType
{
}
// semmle-extractor-options: /r:System.Dynamic.Runtime.dll

View File

@@ -64,3 +64,7 @@
| Program.cs:135:33:135:38 | String |
| Program.cs:135:41:135:46 | Object |
| Program.cs:137:10:137:15 | Object |
| Program.cs:142:27:142:27 | T |
| Program.cs:142:30:142:40 | DynamicType |
| Program.cs:146:27:146:27 | T |
| Program.cs:146:30:146:40 | DynamicType |

View File

@@ -2,7 +2,7 @@ using System;
class Test
{
static void Main(string[] args)
void Test1(string[] args)
{
// BAD: Loop upper bound is off-by-one
for (int i = 0; i <= args.Length; i++)
@@ -34,6 +34,11 @@ class Test
{
Console.WriteLine(args[j]);
}
}
void Test2(string[] args)
{
int j = 0;
// GOOD: Correct terminating value
if (args.Length > j)
@@ -41,4 +46,54 @@ class Test
Console.WriteLine(args[j]);
}
}
void Test3(string[] args)
{
// GOOD: Guarded by ternary operator.
for (int i = 0; i <= args.Length; i++)
{
string s = i < args.Length ? args[i] : "";
}
}
void Test4(string[] args)
{
int j = 0;
// GOOD: Guarded by ternary operator.
if( j <= args.Length )
{
string s = j < args.Length ? args[j] : "";
}
}
void Test5(string[] args)
{
// GOOD: A valid test of Length.
for (int i = 0; i != args.Length; i++)
{
Console.WriteLine(args[i]);
}
}
void Test6(string[] args)
{
int j = 0;
// GOOD: There is another correct test.
if( j <= args.Length )
{
if (j == args.Length) return;
Console.WriteLine(args[j]);
}
}
void Test7(string[] args)
{
// GOOD: Guarded by ||.
for (int i = 0; i <= args.Length; i++)
{
bool b = i == args.Length || args[i] == "x";
}
}
}