Merge branch 'master' of git.semmle.com:Semmle/ql into ASPNetPagesValidateRequest

# Conflicts:
#	change-notes/1.24/analysis-csharp.md
This commit is contained in:
Calum Grant
2019-12-05 15:58:47 +00:00
226 changed files with 12451 additions and 10055 deletions

View File

@@ -0,0 +1,52 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
The <code>requestValidationMode</code> attribute in ASP.NET is used to configure built-in validation to
protect applications against code injections. Downgrading or disabling
this configuration is not recommended. The default value of 4.5
is the only recommended value, as previous versions only test a subset of requests.
</p>
</overview>
<recommendation>
<p>
Always set <code>requestValidationMode</code> to 4.5, or leave it at its default value.
</p>
</recommendation>
<example>
<p>
The following example shows the <code>requestValidationMode</code>
attribute set to a value of 4.0, which disables some protections and
ignores individual <code>Page</code> directives:
</p>
<sample src="ASPNetRequestValidationModeBad.config" />
<p>
Setting the value to 4.5 enables request validation for all requests:
</p>
<sample src="ASPNetRequestValidationModeGood.config" />
</example>
<references>
<li>
Microsoft:
<a
href="https://docs.microsoft.com/en-us/dotnet/api/system.web.configuration.httpruntimesection.requestvalidationmode?view=netframework-4.8">HttpRuntimeSection.RequestValidationMode Property
</a>.
</li>
<li>
OWASP:
<a
href="https://www.owasp.org/index.php/ASP.NET_Request_Validation">ASP.NET Request Validation</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Insecure configuration for ASP.NET requestValidationMode
* @description Setting 'requestValidationMode' to less than 4.5 disables built-in validations
* included by default in ASP.NET. Disabling or downgrading this protection is not
* recommended.
* @kind problem
* @id cs/insecure-request-validation-mode
* @problem.severity warning
* @tags security
* external/cwe/cwe-016
*/
import csharp
from XMLAttribute reqValidationMode
where
reqValidationMode.getName().toLowerCase() = "requestvalidationmode" and
reqValidationMode.getValue().toFloat() < 4.5
select reqValidationMode,
"Insecure value for requestValidationMode (" + reqValidationMode.getValue() + ")."

View File

@@ -0,0 +1,5 @@
<configuration>
<system.web>
<httpRuntime requestValidationMode="4.0"/>
</system.web>
</configuration>

View File

@@ -0,0 +1,5 @@
<configuration>
<system.web>
<httpRuntime requestValidationMode="4.5"/>
</system.web>
</configuration>

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline]
private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) {
nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind() and
simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
viableParamArg(call, p, arg)
)
}
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind)
)
}

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline]
private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) {
nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind() and
simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
viableParamArg(call, p, arg)
)
}
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind)
)
}

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline]
private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) {
nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind() and
simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
viableParamArg(call, p, arg)
)
}
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind)
)
}

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline]
private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) {
nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind() and
simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
viableParamArg(call, p, arg)
)
}
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind)
)
}

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline]
private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) {
nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind() and
simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
viableParamArg(call, p, arg)
)
}
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind)
)
}

View File

@@ -360,7 +360,7 @@ private module ImplCommon {
*/
cached
predicate read(Node node1, Content f, Node node2) {
readStep(node1, f, node2) and storeStep(_, f, _)
readStep(node1, f, node2)
or
exists(DataFlowCall call, ReturnKind kind |
read0(call, kind, node1, f) and

View File

@@ -113,6 +113,12 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon
scope = e2 and
isSuccessor = true
)
or
e2 = any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument() and
isSuccessor = true
)
)
}

View File

@@ -190,10 +190,16 @@ private module Internal {
abstract RuntimeCallable getADynamicTarget();
}
pragma[noinline]
private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) {
exists(oc.getAnOverrider(t))
}
pragma[noinline]
private predicate hasCallable(OverridableCallable source, ValueOrRefType t, OverridableCallable c) {
c.getSourceDeclaration() = source and
t.hasCallable(c) and
hasOverrider(c, t) and
hasQualifierTypeOverridden0(t, _) and
hasQualifierTypeOverridden1(source, _)
}
@@ -215,15 +221,19 @@ private module Internal {
pragma[noinline]
private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) {
exists(Type t0 | t0 = getAPossibleType(call.getQualifier(), false) |
t = t0
hasOverrider(_, t) and
(
exists(Type t0 | t0 = getAPossibleType(call.getQualifier(), false) |
t = t0
or
Unification::subsumes(t0, t)
or
t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()
)
or
Unification::subsumes(t0, t)
or
t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()
constrainedTypeParameterQualifierTypeSubsumes(t,
getAConstrainedTypeParameterQualifierType(call))
)
or
constrainedTypeParameterQualifierTypeSubsumes(t, getAConstrainedTypeParameterQualifierType(call))
}
pragma[noinline]

View File

@@ -1,11 +1,8 @@
private newtype TMemoryAccessKind =
TIndirectMemoryAccess() or
TIndirectMayMemoryAccess() or
TBufferMemoryAccess() or
TBufferMayMemoryAccess() or
TEscapedMemoryAccess() or
TEscapedMayMemoryAccess() or
TNonLocalMayMemoryAccess() or
TNonLocalMemoryAccess() or
TPhiMemoryAccess() or
TUnmodeledMemoryAccess() or
TChiTotalMemoryAccess() or
@@ -35,16 +32,6 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
final override predicate usesAddressOperand() { any() }
}
/**
* The operand or result may access some, all, or none of the memory at the address specified by the
* `AddressOperand` on the same instruction.
*/
class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess {
override string toString() { result = "indirect(may)" }
final override predicate usesAddressOperand() { any() }
}
/**
* The operand or result accesses memory starting at the address specified by the `AddressOperand`
* on the same instruction, accessing a number of consecutive elements given by the
@@ -56,17 +43,6 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
final override predicate usesAddressOperand() { any() }
}
/**
* The operand or result may access some, all, or none of the memory starting at the address
* specified by the `AddressOperand` on the same instruction, accessing a number of consecutive
* elements given by the `BufferSizeOperand`.
*/
class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess {
override string toString() { result = "buffer(may)" }
final override predicate usesAddressOperand() { any() }
}
/**
* The operand or result accesses all memory whose address has escaped.
*/
@@ -75,18 +51,11 @@ class EscapedMemoryAccess extends MemoryAccessKind, TEscapedMemoryAccess {
}
/**
* The operand or result may access all memory whose address has escaped.
* The operand or result access all memory whose address has escaped, other than data on the stack
* frame of the current function.
*/
class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
override string toString() { result = "escaped(may)" }
}
/**
* The operand or result may access all memory whose address has escaped, other than data on the
* stack frame of the current function.
*/
class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess {
override string toString() { result = "nonlocal(may)" }
class NonLocalMemoryAccess extends MemoryAccessKind, TNonLocalMemoryAccess {
override string toString() { result = "nonlocal" }
}
/**

View File

@@ -359,24 +359,25 @@ class Instruction extends Construction::TInstruction {
*/
int getDisplayIndexInBlock() {
exists(IRBlock block |
block = getBlock() and
(
exists(int index, int phiCount |
phiCount = count(block.getAPhiInstruction()) and
this = block.getInstruction(index) and
result = index + phiCount
this = block.getInstruction(result)
or
this = rank[-result - 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
or
this instanceof PhiInstruction and
this = rank[result + 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
)
)
}
private int getLineRank() {
this = rank[result](Instruction instr |
instr.getAST().getFile() = getAST().getFile() and
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
)
}
/**
* Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when
@@ -385,8 +386,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
result = getResultPrefix() + getBlock().getDisplayIndex().toString() + "_" +
getDisplayIndexInBlock().toString()
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
/**
@@ -550,6 +550,16 @@ class Instruction extends Construction::TInstruction {
*/
MemoryAccessKind getResultMemoryAccess() { none() }
/**
* Holds if the memory access performed by this instruction's result will not always write to
* every bit in the memory location. This is most commonly used for memory accesses that may or
* may not actually occur depending on runtime state (for example, the write side effect of an
* output parameter that is not written to on all paths), or for accesses where the memory
* location is a conservative estimate of the memory that might actually be accessed at runtime
* (for example, the global side effects of a function call).
*/
predicate hasResultMayMemoryAccess() { none() }
/**
* Gets the operand that holds the memory address to which this instruction stores its
* result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()`
@@ -1206,9 +1216,9 @@ class SideEffectInstruction extends Instruction {
class CallSideEffectInstruction extends SideEffectInstruction {
CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1306,9 +1316,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
}
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1318,9 +1328,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1332,9 +1342,9 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
}
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
}
@@ -1345,9 +1355,9 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
class InlineAsmInstruction extends Instruction {
InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**

View File

@@ -194,6 +194,16 @@ class MemoryOperand extends Operand {
*/
MemoryAccessKind getMemoryAccess() { none() }
/**
* Holds if the memory access performed by this operand will not always read from every bit in the
* memory location. This is most commonly used for memory accesses that may or may not actually
* occur depending on runtime state (for example, the write side effect of an output parameter
* that is not written to on all paths), or for accesses where the memory location is a
* conservative estimate of the memory that might actually be accessed at runtime (for example,
* the global side effects of a function call).
*/
predicate hasMayMemoryAccess() { none() }
/**
* Returns the operand that holds the memory address from which the current operand loads its
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
@@ -397,13 +407,13 @@ class SideEffectOperand extends TypedOperand {
override MemoryAccessKind getMemoryAccess() {
useInstr instanceof AliasedUseInstruction and
result instanceof NonLocalMayMemoryAccess
result instanceof NonLocalMemoryAccess
or
useInstr instanceof CallSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
result instanceof EscapedMemoryAccess
or
useInstr instanceof CallReadSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
result instanceof EscapedMemoryAccess
or
useInstr instanceof IndirectReadSideEffectInstruction and
result instanceof IndirectMemoryAccess
@@ -418,10 +428,22 @@ class SideEffectOperand extends TypedOperand {
result instanceof BufferMemoryAccess
or
useInstr instanceof IndirectMayWriteSideEffectInstruction and
result instanceof IndirectMayMemoryAccess
result instanceof IndirectMemoryAccess
or
useInstr instanceof BufferMayWriteSideEffectInstruction and
result instanceof BufferMayMemoryAccess
result instanceof BufferMemoryAccess
}
final override predicate hasMayMemoryAccess() {
useInstr instanceof AliasedUseInstruction
or
useInstr instanceof CallSideEffectInstruction
or
useInstr instanceof CallReadSideEffectInstruction
or
useInstr instanceof IndirectMayWriteSideEffectInstruction
or
useInstr instanceof BufferMayWriteSideEffectInstruction
}
}

View File

@@ -359,24 +359,25 @@ class Instruction extends Construction::TInstruction {
*/
int getDisplayIndexInBlock() {
exists(IRBlock block |
block = getBlock() and
(
exists(int index, int phiCount |
phiCount = count(block.getAPhiInstruction()) and
this = block.getInstruction(index) and
result = index + phiCount
this = block.getInstruction(result)
or
this = rank[-result - 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
or
this instanceof PhiInstruction and
this = rank[result + 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
)
)
}
private int getLineRank() {
this = rank[result](Instruction instr |
instr.getAST().getFile() = getAST().getFile() and
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
)
}
/**
* Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when
@@ -385,8 +386,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
result = getResultPrefix() + getBlock().getDisplayIndex().toString() + "_" +
getDisplayIndexInBlock().toString()
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
/**
@@ -550,6 +550,16 @@ class Instruction extends Construction::TInstruction {
*/
MemoryAccessKind getResultMemoryAccess() { none() }
/**
* Holds if the memory access performed by this instruction's result will not always write to
* every bit in the memory location. This is most commonly used for memory accesses that may or
* may not actually occur depending on runtime state (for example, the write side effect of an
* output parameter that is not written to on all paths), or for accesses where the memory
* location is a conservative estimate of the memory that might actually be accessed at runtime
* (for example, the global side effects of a function call).
*/
predicate hasResultMayMemoryAccess() { none() }
/**
* Gets the operand that holds the memory address to which this instruction stores its
* result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()`
@@ -1206,9 +1216,9 @@ class SideEffectInstruction extends Instruction {
class CallSideEffectInstruction extends SideEffectInstruction {
CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1306,9 +1316,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
}
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1318,9 +1328,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**
@@ -1332,9 +1342,9 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
}
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
}
@@ -1345,9 +1355,9 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
class InlineAsmInstruction extends Instruction {
InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm }
final override MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMayMemoryAccess
}
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
final override predicate hasResultMayMemoryAccess() { any() }
}
/**

View File

@@ -194,6 +194,16 @@ class MemoryOperand extends Operand {
*/
MemoryAccessKind getMemoryAccess() { none() }
/**
* Holds if the memory access performed by this operand will not always read from every bit in the
* memory location. This is most commonly used for memory accesses that may or may not actually
* occur depending on runtime state (for example, the write side effect of an output parameter
* that is not written to on all paths), or for accesses where the memory location is a
* conservative estimate of the memory that might actually be accessed at runtime (for example,
* the global side effects of a function call).
*/
predicate hasMayMemoryAccess() { none() }
/**
* Returns the operand that holds the memory address from which the current operand loads its
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
@@ -397,13 +407,13 @@ class SideEffectOperand extends TypedOperand {
override MemoryAccessKind getMemoryAccess() {
useInstr instanceof AliasedUseInstruction and
result instanceof NonLocalMayMemoryAccess
result instanceof NonLocalMemoryAccess
or
useInstr instanceof CallSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
result instanceof EscapedMemoryAccess
or
useInstr instanceof CallReadSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
result instanceof EscapedMemoryAccess
or
useInstr instanceof IndirectReadSideEffectInstruction and
result instanceof IndirectMemoryAccess
@@ -418,10 +428,22 @@ class SideEffectOperand extends TypedOperand {
result instanceof BufferMemoryAccess
or
useInstr instanceof IndirectMayWriteSideEffectInstruction and
result instanceof IndirectMayMemoryAccess
result instanceof IndirectMemoryAccess
or
useInstr instanceof BufferMayWriteSideEffectInstruction and
result instanceof BufferMayMemoryAccess
result instanceof BufferMemoryAccess
}
final override predicate hasMayMemoryAccess() {
useInstr instanceof AliasedUseInstruction
or
useInstr instanceof CallSideEffectInstruction
or
useInstr instanceof CallReadSideEffectInstruction
or
useInstr instanceof IndirectMayWriteSideEffectInstruction
or
useInstr instanceof BufferMayWriteSideEffectInstruction
}
}

View File

@@ -96,7 +96,7 @@ private module Cached {
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
overlap = Alias::getOverlap(defLocation, useLocation) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _)
)
else (
result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction() and
@@ -149,13 +149,13 @@ private module Cached {
) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset,
overlap) and
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
instr = Phi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
}
@@ -170,7 +170,7 @@ private module Cached {
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar)
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
)
}
@@ -396,11 +396,7 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
defLocation.getVirtualVariable() = vvar and
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
// instruction.
(
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap or
def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
)
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
)
}
@@ -560,22 +556,27 @@ module DefUse {
bindingset[defOffset, defLocation]
pragma[inline]
Instruction getDefinitionOrChiInstruction(
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation
OldBlock defBlock, int defOffset, Alias::MemoryLocation defLocation,
Alias::MemoryLocation actualDefLocation
) {
defOffset >= 0 and
exists(OldInstruction oldInstr |
oldInstr = defBlock.getInstruction(defOffset / 2) and
if (defOffset % 2) > 0
then
then (
// An odd offset corresponds to the `Chi` instruction.
result = Chi(oldInstr)
else
result = Chi(oldInstr) and
actualDefLocation = defLocation.getVirtualVariable()
) else (
// An even offset corresponds to the original instruction.
result = getNewInstruction(oldInstr)
result = getNewInstruction(oldInstr) and
actualDefLocation = defLocation
)
)
or
defOffset < 0 and
result = Phi(defBlock, defLocation)
result = Phi(defBlock, defLocation) and
actualDefLocation = defLocation
}
/**
@@ -713,10 +714,7 @@ module DefUse {
defLocation = Alias::getResultMemoryLocation(def) and
block.getInstruction(index) = def and
overlap = Alias::getOverlap(defLocation, useLocation) and
if
overlap instanceof MayPartiallyOverlap or
def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
if overlap instanceof MayPartiallyOverlap
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
else offset = index * 2 // The use will be connected to the definition on the original instruction.
)
@@ -801,20 +799,19 @@ module DefUse {
/**
* Holds if the `Phi` instruction for location `useLocation` at the beginning of block `phiBlock` has an operand along
* the incoming edge from `predBlock`, where that operand's definition is at offset `defOffset` in block `defBlock`,
* and overlaps the use operand with overlap relationship `overlap`.
* the incoming edge from `predBlock`, where that operand's definition is at offset `defOffset` in block `defBlock`.
*/
pragma[inline]
predicate hasPhiOperandDefinition(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset, Overlap overlap
OldBlock predBlock, OldBlock defBlock, int defOffset
) {
exists(int defRank |
definitionHasPhiNode(useLocation, phiBlock) and
predBlock = phiBlock.getAFeasiblePredecessor() and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
definitionReachesEndOfBlock(useLocation, defBlock, defRank, predBlock) and
overlap = Alias::getOverlap(defLocation, useLocation)
exists(Alias::getOverlap(defLocation, useLocation))
)
}
}

View File

@@ -1,5 +1,6 @@
import AliasAnalysis
private import SimpleSSAImports
import SimpleSSAPublicImports
private class IntValue = Ints::IntValue;
@@ -27,20 +28,19 @@ private predicate isVariableModeled(IRVariable var) {
// There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for
// `type = var.getType()` is sufficient.
forall(Instruction instr, Language::LanguageType type, IntValue bitOffset |
hasResultMemoryAccess(instr, var, type, bitOffset)
hasResultMemoryAccess(instr, var, type, bitOffset) and
not instr.hasResultMayMemoryAccess()
|
bitOffset = 0 and
type.getIRType() = var.getIRType() and
not (
instr.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
instr.getResultMemoryAccess() instanceof BufferMayMemoryAccess
)
not instr.hasResultMayMemoryAccess()
) and
forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset |
hasOperandMemoryAccess(operand, var, type, bitOffset)
|
bitOffset = 0 and
type.getIRType() = var.getIRType()
type.getIRType() = var.getIRType() and
not operand.hasMayMemoryAccess()
)
}

View File

@@ -2,4 +2,3 @@ import semmle.code.csharp.ir.implementation.raw.IR
import semmle.code.csharp.ir.internal.IntegerConstant as Ints
import semmle.code.csharp.ir.implementation.internal.OperandTag
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
import semmle.code.csharp.ir.internal.Overlap

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.internal.Overlap

View File

@@ -586,6 +586,9 @@
| LocalDataFlow.cs:480:67:480:68 | os | LocalDataFlow.cs:486:32:486:33 | access to parameter os |
| LocalDataFlow.cs:483:21:483:21 | access to parameter x | LocalDataFlow.cs:483:16:483:21 | ... = ... |
| LocalDataFlow.cs:486:32:486:33 | access to parameter os | LocalDataFlow.cs:486:26:486:33 | ... = ... |
| LocalDataFlow.cs:491:41:491:44 | args | LocalDataFlow.cs:493:29:493:32 | access to parameter args |
| LocalDataFlow.cs:493:29:493:32 | [post] access to parameter args | LocalDataFlow.cs:494:27:494:30 | access to parameter args |
| LocalDataFlow.cs:493:29:493:32 | access to parameter args | LocalDataFlow.cs:494:27:494:30 | access to parameter args |
| SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S |
| SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access |
| SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted |

View File

@@ -485,4 +485,12 @@ public class LocalDataFlow
IEnumerable<object> os2;
foreach(var o in os2 = os) { }
}
public static implicit operator LocalDataFlow(string[] args) => null;
public void ConversionFlow(string[] args)
{
Span<object> span = args; // flow (library operator)
LocalDataFlow x = args; // no flow (source code operator)
}
}

View File

@@ -736,6 +736,11 @@
| LocalDataFlow.cs:480:67:480:68 | os | LocalDataFlow.cs:486:32:486:33 | access to parameter os |
| LocalDataFlow.cs:483:21:483:21 | access to parameter x | LocalDataFlow.cs:483:16:483:21 | ... = ... |
| LocalDataFlow.cs:486:32:486:33 | access to parameter os | LocalDataFlow.cs:486:26:486:33 | ... = ... |
| LocalDataFlow.cs:491:41:491:44 | args | LocalDataFlow.cs:491:41:491:44 | args |
| LocalDataFlow.cs:491:41:491:44 | args | LocalDataFlow.cs:493:29:493:32 | access to parameter args |
| LocalDataFlow.cs:493:29:493:32 | [post] access to parameter args | LocalDataFlow.cs:494:27:494:30 | access to parameter args |
| LocalDataFlow.cs:493:29:493:32 | access to parameter args | LocalDataFlow.cs:493:29:493:32 | call to operator implicit conversion |
| LocalDataFlow.cs:493:29:493:32 | access to parameter args | LocalDataFlow.cs:494:27:494:30 | access to parameter args |
| SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S |
| SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access |
| SSA.cs:5:26:5:32 | tainted | SSA.cs:5:26:5:32 | tainted |

View File

@@ -50,7 +50,7 @@ namespace EFCoreTests
Sink(taintSource); // Tainted
Sink(new RawSqlString(taintSource)); // Tainted
Sink((RawSqlString)taintSource); // Tainted
Sink((RawSqlString)(FormattableString)$"{taintSource}"); // Not tainted
Sink((RawSqlString)(FormattableString)$"{taintSource}"); // Tainted, but not reported because conversion operator is in a stub .cs file
// Tainted via database, even though technically there were no reads or writes to the database in this particular case.
var p1 = new Person { Name = taintSource };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
| ASPNetRequestValidationModeBad.config:3:5:3:47 | requestValidationMode=4.0 | Insecure value for requestValidationMode (4.0). |

View File

@@ -0,0 +1 @@
Security Features/CWE-016/ASPNetRequestValidationMode.ql

View File

@@ -0,0 +1,5 @@
<configuration>
<system.web>
<httpRuntime requestValidationMode="4.0"/>
</system.web>
</configuration>

View File

@@ -0,0 +1,5 @@
<configuration>
<system.web>
<httpRuntime requestValidationMode="4.5"/>
</system.web>
</configuration>