Merge branch 'main' into criemen/js-bazel

This commit is contained in:
Cornelius Riemenschneider
2023-11-08 16:11:47 +01:00
committed by GitHub
128 changed files with 7800 additions and 702 deletions

View File

@@ -31,6 +31,11 @@ abstract class MustFlowConfiguration extends string {
*/
abstract predicate isSink(Operand sink);
/**
* Holds if data flow through `instr` is prohibited.
*/
predicate isBarrier(Instruction instr) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
@@ -48,18 +53,21 @@ abstract class MustFlowConfiguration extends string {
*/
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
this.isSource(source.getInstruction()) and
source.getASuccessor+() = sink
source.getASuccessor*() = sink
}
}
/** Holds if `node` flows from a source. */
pragma[nomagic]
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
config.isSource(node)
or
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
not config.isBarrier(node) and
(
config.isSource(node)
or
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
)
}

View File

@@ -17,9 +17,7 @@ private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
* `upper` is true, and can be traced back to a guard represented by `reason`.
*/
predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getUnconvertedResultExpression() = e
|
exists(SemanticExprConfig::Expr semExpr | semExpr.getUnconvertedResultExpression() = e |
semBounded(semExpr, b, delta, upper, reason)
)
}
@@ -30,9 +28,7 @@ predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
* The `Expr` may be a conversion.
*/
predicate convertedBounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
exists(SemanticExprConfig::Expr semExpr |
semExpr.getConverted().getConvertedResultExpression() = e
|
exists(SemanticExprConfig::Expr semExpr | semExpr.getConvertedResultExpression() = e |
semBounded(semExpr, b, delta, upper, reason)
)
}

View File

@@ -100,7 +100,7 @@ predicate exprMightOverflowNegatively(Expr expr) {
lowerBound(expr) < exprMinVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getAst() = expr and
semExpr.getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(false, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, false, _, _, _)
)
@@ -126,7 +126,7 @@ predicate exprMightOverflowPositively(Expr expr) {
upperBound(expr) > exprMaxVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getAst() = expr and
semExpr.getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(true, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, true, _, _, _)
)

View File

@@ -12,9 +12,6 @@ class SemBasicBlock extends Specific::BasicBlock {
/** Holds if this block (transitively) dominates `otherblock`. */
final predicate bbDominates(SemBasicBlock otherBlock) { Specific::bbDominates(this, otherBlock) }
/** Holds if this block has dominance information. */
final predicate hasDominanceInformation() { Specific::hasDominanceInformation(this) }
/** Gets an expression that is evaluated in this basic block. */
final SemExpr getAnExpr() { result.getBasicBlock() = this }

View File

@@ -4,6 +4,7 @@
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
private import SemanticType
/**
* An language-neutral expression.
@@ -241,8 +242,21 @@ class SemConvertExpr extends SemUnaryExpr {
SemConvertExpr() { opcode instanceof Opcode::Convert }
}
private import semmle.code.cpp.ir.IR as IR
/** A conversion instruction which is guaranteed to not overflow. */
private class SafeConversion extends IR::ConvertInstruction {
SafeConversion() {
exists(SemType tFrom, SemType tTo |
tFrom = getSemanticType(super.getUnary().getResultIRType()) and
tTo = getSemanticType(super.getResultIRType()) and
conversionCannotOverflow(tFrom, tTo)
)
}
}
class SemCopyValueExpr extends SemUnaryExpr {
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue }
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue or this instanceof SafeConversion }
}
class SemNegateExpr extends SemUnaryExpr {

View File

@@ -12,87 +12,10 @@ private import semmle.code.cpp.ir.ValueNumbering
module SemanticExprConfig {
class Location = Cpp::Location;
/** A `ConvertInstruction` or a `CopyValueInstruction`. */
private class Conversion extends IR::UnaryInstruction {
Conversion() {
this instanceof IR::CopyValueInstruction
or
this instanceof IR::ConvertInstruction
}
/** Holds if this instruction converts a value of type `tFrom` to a value of type `tTo`. */
predicate converts(SemType tFrom, SemType tTo) {
tFrom = getSemanticType(this.getUnary().getResultIRType()) and
tTo = getSemanticType(this.getResultIRType())
}
}
/**
* Gets a conversion-like instruction that consumes `op`, and
* which is guaranteed to not overflow.
*/
private IR::Instruction safeConversion(IR::Operand op) {
exists(Conversion conv, SemType tFrom, SemType tTo |
conv.converts(tFrom, tTo) and
conversionCannotOverflow(tFrom, tTo) and
conv.getUnaryOperand() = op and
result = conv
)
}
/** Holds if `i1 = i2` or if `i2` is a safe conversion that consumes `i1`. */
private predicate idOrSafeConversion(IR::Instruction i1, IR::Instruction i2) {
not i1.getResultIRType() instanceof IR::IRVoidType and
(
i1 = i2
or
i2 = safeConversion(i1.getAUse()) and
i1.getBlock() = i2.getBlock()
)
}
module Equiv = QlBuiltins::EquivalenceRelation<IR::Instruction, idOrSafeConversion/2>;
/**
* The expressions on which we perform range analysis.
*/
class Expr extends Equiv::EquivalenceClass {
/** Gets the n'th instruction in this equivalence class. */
private IR::Instruction getInstruction(int n) {
result =
rank[n + 1](IR::Instruction instr, int i, IR::IRBlock block |
this = Equiv::getEquivalenceClass(instr) and block.getInstruction(i) = instr
|
instr order by i
)
}
/** Gets a textual representation of this element. */
string toString() { result = this.getUnconverted().toString() }
/** Gets the basic block of this expression. */
IR::IRBlock getBlock() { result = this.getUnconverted().getBlock() }
/** Gets the unconverted instruction associated with this expression. */
IR::Instruction getUnconverted() { result = this.getInstruction(0) }
/**
* Gets the final instruction associated with this expression. This
* represents the result after applying all the safe conversions.
*/
IR::Instruction getConverted() {
exists(int n |
result = this.getInstruction(n) and
not exists(this.getInstruction(n + 1))
)
}
/** Gets the type of the result produced by this instruction. */
IR::IRType getResultIRType() { result = this.getConverted().getResultIRType() }
/** Gets the location of the source code for this expression. */
Location getLocation() { result = this.getUnconverted().getLocation() }
}
class Expr = IR::Instruction;
SemBasicBlock getExprBasicBlock(Expr e) { result = getSemanticBasicBlock(e.getBlock()) }
@@ -139,12 +62,12 @@ module SemanticExprConfig {
predicate stringLiteral(Expr expr, SemType type, string value) {
anyConstantExpr(expr, type, value) and
expr.getUnconverted() instanceof IR::StringConstantInstruction
expr instanceof IR::StringConstantInstruction
}
predicate binaryExpr(Expr expr, Opcode opcode, SemType type, Expr leftOperand, Expr rightOperand) {
exists(IR::BinaryInstruction instr |
instr = expr.getUnconverted() and
instr = expr and
type = getSemanticType(instr.getResultIRType()) and
leftOperand = getSemanticExpr(instr.getLeft()) and
rightOperand = getSemanticExpr(instr.getRight()) and
@@ -154,14 +77,14 @@ module SemanticExprConfig {
}
predicate unaryExpr(Expr expr, Opcode opcode, SemType type, Expr operand) {
exists(IR::UnaryInstruction instr | instr = expr.getUnconverted() |
exists(IR::UnaryInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getUnary()) and
// REVIEW: Merge the two operand types.
opcode.toString() = instr.getOpcode().toString()
)
or
exists(IR::StoreInstruction instr | instr = expr.getUnconverted() |
exists(IR::StoreInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getSourceValue()) and
opcode instanceof Opcode::Store
@@ -170,13 +93,13 @@ module SemanticExprConfig {
predicate nullaryExpr(Expr expr, Opcode opcode, SemType type) {
exists(IR::LoadInstruction load |
load = expr.getUnconverted() and
load = expr and
type = getSemanticType(load.getResultIRType()) and
opcode instanceof Opcode::Load
)
or
exists(IR::InitializeParameterInstruction init |
init = expr.getUnconverted() and
init = expr and
type = getSemanticType(init.getResultIRType()) and
opcode instanceof Opcode::InitializeParameter
)
@@ -199,8 +122,6 @@ module SemanticExprConfig {
dominator.dominates(dominated)
}
predicate hasDominanceInformation(BasicBlock block) { any() }
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
@@ -209,17 +130,7 @@ module SemanticExprConfig {
newtype TSsaVariable =
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
TSsaPointerArithmeticGuard(ValueNumber instr) {
exists(Guard g, IR::Operand use |
use = instr.getAUse() and use.getIRType() instanceof IR::IRAddressType
|
g.comparesLt(use, _, _, _, _) or
g.comparesLt(_, use, _, _, _) or
g.comparesEq(use, _, _, _, _) or
g.comparesEq(_, use, _, _, _)
)
}
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() }
class SsaVariable extends TSsaVariable {
string toString() { none() }
@@ -228,8 +139,6 @@ module SemanticExprConfig {
IR::Instruction asInstruction() { none() }
ValueNumber asPointerArithGuard() { none() }
IR::Operand asOperand() { none() }
}
@@ -245,18 +154,6 @@ module SemanticExprConfig {
final override IR::Instruction asInstruction() { result = instr }
}
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
ValueNumber vn;
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(vn) }
final override string toString() { result = vn.toString() }
final override Location getLocation() { result = vn.getLocation() }
final override ValueNumber asPointerArithGuard() { result = vn }
}
class SsaOperand extends SsaVariable, TSsaOperand {
IR::Operand op;
@@ -289,11 +186,7 @@ module SemanticExprConfig {
)
}
Expr getAUse(SsaVariable v) {
result.getUnconverted().(IR::LoadInstruction).getSourceValue() = v.asInstruction()
or
result.getUnconverted() = v.asPointerArithGuard().getAnInstruction()
}
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
SemType getSsaVariableType(SsaVariable v) {
result = getSemanticType(v.asInstruction().getResultIRType())
@@ -332,10 +225,7 @@ module SemanticExprConfig {
final override Location getLocation() { result = block.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::Operand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
exists(IR::Operand operand | operand.getDef() = v.asInstruction() |
not operand instanceof IR::PhiInputOperand and
operand.getUse().getBlock() = block
)
@@ -353,10 +243,7 @@ module SemanticExprConfig {
final override Location getLocation() { result = succ.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::PhiInputOperand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
exists(IR::PhiInputOperand operand | operand.getDef() = v.asInstruction() |
operand.getPredecessorBlock() = pred and
operand.getUse().getBlock() = succ
)
@@ -433,7 +320,7 @@ module SemanticExprConfig {
}
/** Gets the expression associated with `instr`. */
SemExpr getSemanticExpr(IR::Instruction instr) { result = Equiv::getEquivalenceClass(instr) }
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
}
predicate getSemanticExpr = SemanticExprConfig::getSemanticExpr/1;

View File

@@ -35,32 +35,4 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
Specific::implies_v2(g1, b1, g2, b2)
}
/**
* Holds if `guard` directly controls the position `controlled` with the
* value `testIsTrue`.
*/
pragma[nomagic]
predicate semGuardDirectlyControlsSsaRead(
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
) {
guard.directlyControls(controlled.(SemSsaReadPositionBlock).getBlock(), testIsTrue)
or
exists(SemSsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
)
}
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate semGuardControlsSsaRead(SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue) {
semGuardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
or
exists(SemGuard guard0, boolean testIsTrue0 |
semImplies_v2(guard0, testIsTrue0, guard, testIsTrue) and
semGuardControlsSsaRead(guard0, controlled, testIsTrue0)
)
}
SemGuard semGetComparisonGuard(SemRelationalExpr e) { result = Specific::comparisonGuard(e) }

View File

@@ -63,36 +63,3 @@ class SemSsaReadPositionBlock extends SemSsaReadPosition {
SemExpr getAnExpr() { result = this.getBlock().getAnExpr() }
}
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
edge.phiInput(phi, inp) and
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
not edge.getOrigBlock().hasDominanceInformation()
)
}
/**
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
* graph.
*
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
* not work, because there is a cycle with multiple entry points, meaning there are
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
* remove all detectable back-edges using the normal condition that the predecessor block is
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
* edges.
*/
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
}
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
pred.getASuccessor() = succ and
not succ.bbDominates(pred)
}

View File

@@ -72,14 +72,12 @@ module Sem implements Semantic {
class BasicBlock = SemBasicBlock;
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class Guard = SemGuard;
predicate implies_v2 = semImplies_v2/4;
predicate guardDirectlyControlsSsaRead = semGuardDirectlyControlsSsaRead/3;
predicate guardControlsSsaRead = semGuardControlsSsaRead/3;
class Type = SemType;
class IntegerType = SemIntegerType;
@@ -100,8 +98,6 @@ module Sem implements Semantic {
class SsaReadPositionBlock = SemSsaReadPositionBlock;
predicate backEdge = semBackEdge/3;
predicate conversionCannotOverflow(Type fromType, Type toType) {
SemanticType::conversionCannotOverflow(fromType, toType)
}

View File

@@ -294,7 +294,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(lowerbound)
|
testIsTrue = true and
@@ -318,7 +318,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(upperbound)
|
testIsTrue = true and
@@ -343,7 +343,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
pos.hasReadOfVar(pragma[only_bind_into](v)) and
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
guardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
e = ssaRead(pragma[only_bind_into](v), D::fromInt(0)) and
guard.isEquality(eqbound, e, polarity) and
isEq = polarity.booleanXor(testIsTrue).booleanNot() and

View File

@@ -13,7 +13,8 @@
*/
import cpp
import semmle.code.cpp.controlflow.StackVariableReachability
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
/**
* Auxiliary predicate: Types that don't require initialization
@@ -33,31 +34,6 @@ predicate allocatedType(Type t) {
allocatedType(t.getUnspecifiedType())
}
/**
* A declaration of a local variable that leaves the
* variable uninitialized.
*/
DeclStmt declWithNoInit(LocalVariable v) {
result.getADeclaration() = v and
not exists(v.getInitializer()) and
/* The type of the variable is not stack-allocated. */
exists(Type t | t = v.getType() | not allocatedType(t))
}
class UninitialisedLocalReachability extends StackVariableReachability {
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
// only report the _first_ possibly uninitialized use
useOfVarActual(v, node) or
definitionBarrier(v, node)
}
}
pragma[noinline]
predicate containsInlineAssembly(Function f) { exists(AsmStmt s | s.getEnclosingFunction() = f) }
@@ -82,8 +58,33 @@ VariableAccess commonException() {
containsInlineAssembly(result.getEnclosingFunction())
}
from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va
predicate isSinkImpl(Instruction sink, VariableAccess va) {
exists(LoadInstruction load |
va = load.getUnconvertedResultExpression() and
not va = commonException() and
sink = load.getSourceValue()
)
}
class MustFlow extends MustFlowConfiguration {
MustFlow() { this = "MustFlow" }
override predicate isSource(Instruction source) {
source instanceof UninitializedInstruction and
exists(Type t | t = source.getResultType() | not allocatedType(t))
}
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
override predicate allowInterproceduralFlow() { none() }
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
}
from
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
where
r.reaches(_, v, va) and
not va = commonException()
conf.hasFlowPath(source, sink) and
isSinkImpl(sink.getInstruction(), va) and
v = va.getTarget()
select va, "The variable $@ may not be initialized at this access.", v, v.getName()

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/uninitialized-local` query has been improved to produce fewer false positives.

View File

@@ -18,7 +18,7 @@ int test2(struct List* p) {
int count = 0;
for (; p; p = p->next) {
count = (count+1) % 10;
range(count); // $ range=<=9 range=>=-9 range="<=Phi: p | Store: count+1"
range(count); // $ range=<=9 range=>=-9
}
range(count); // $ range=>=-9 range=<=9
return count;
@@ -29,7 +29,7 @@ int test3(struct List* p) {
for (; p; p = p->next) {
range(count++); // $ range=>=-9 range=<=9
count = count % 10;
range(count); // $ range=<=9 range=>=-9 range="<=Store: ... +++0" range="<=Phi: p | Store: count+1"
range(count); // $ range=<=9 range=>=-9
}
range(count); // $ range=>=-9 range=<=9
return count;
@@ -317,7 +317,7 @@ int test_mult01(int a, int b) {
range(b); // $ range=<=23 range=>=-13
int r = a*b; // $ overflow=+- -143 .. 253
range(r);
total += r; // $ overflow=+
total += r; // $ overflow=+-
range(total); // $ MISSING: range=">=... * ...+0"
}
if (3 <= a && a <= 11 && -13 <= b && b <= 0) {
@@ -365,7 +365,7 @@ int test_mult02(int a, int b) {
range(b); // $ range=<=23 range=>=-13
int r = a*b; // $ overflow=+- -143 .. 253
range(r);
total += r; // $ overflow=+
total += r; // $ overflow=+-
range(total); // $ MISSING: range=">=... * ...+0"
}
if (0 <= a && a <= 11 && -13 <= b && b <= 0) {
@@ -460,7 +460,7 @@ int test_mult04(int a, int b) {
range(b); // $ range=<=23 range=>=-13
int r = a*b; // $ overflow=+- -391 .. 221
range(r);
total += r; // $ overflow=-
total += r; // $ overflow=+-
range(total); // $ MISSING: range="<=... * ...+0"
}
if (-17 <= a && a <= 0 && -13 <= b && b <= 0) {
@@ -508,7 +508,7 @@ int test_mult05(int a, int b) {
range(b); // $ range=<=23 range=>=-13
int r = a*b; // $ overflow=+- -391 .. 221
range(r);
total += r; // $ overflow=-
total += r; // $ overflow=+-
range(total); // $ MISSING: range="<=... * ...+0"
}
if (-17 <= a && a <= -2 && -13 <= b && b <= 0) {
@@ -974,7 +974,7 @@ void test_mod_neg(int s) {
void test_mod_ternary(int s, bool b) {
int s2 = s % (b ? 5 : 500);
range(s2); // $ range=>=-499 range=<=499 range="<=Phi: ... ? ... : ...-1"
range(s2); // $ range=>=-499 range=<=499
}
void test_mod_ternary2(int s, bool b1, bool b2) {

View File

@@ -130,3 +130,19 @@ void test_div(int x) {
range(x >> 2); // $ range=>=0 range=<=2
}
}
struct X { int n; };
void read_argument(const X *);
// This test exists purely to ensure that modulus analysis terminates in the
// presence of inexact phi operands. The LoadInstruction on `while(x->n) { ... }`
// reads from a PhiInstruction with two input operands: an exact operand defined
// by the StoreInstruction generated by `x->n--` and an inexact operand coming
// from the WriteSideEffect generated by `read_argument(x)`. If we don't consider
// the inexact operand modulus analysis fails to terminate.
void nonterminating_without_operands_as_ssa(X *x) {
read_argument(x);
while (x->n) {
x->n--;
}
}

View File

@@ -22,16 +22,10 @@ edges
| test.cpp:52:19:52:37 | call to malloc | test.cpp:53:12:53:23 | ... + ... |
| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | end |
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:67:9:67:14 | ... = ... |
| test.cpp:194:15:194:33 | call to malloc | test.cpp:195:17:195:23 | ... + ... |
| test.cpp:195:17:195:23 | ... + ... | test.cpp:195:17:195:23 | ... + ... |
| test.cpp:195:17:195:23 | ... + ... | test.cpp:201:5:201:19 | ... = ... |
| test.cpp:195:17:195:23 | ... + ... | test.cpp:201:5:201:19 | ... = ... |
| test.cpp:205:15:205:33 | call to malloc | test.cpp:206:17:206:23 | ... + ... |
| test.cpp:206:17:206:23 | ... + ... | test.cpp:206:17:206:23 | ... + ... |
| test.cpp:206:17:206:23 | ... + ... | test.cpp:213:5:213:13 | ... = ... |
| test.cpp:206:17:206:23 | ... + ... | test.cpp:213:5:213:13 | ... = ... |
| test.cpp:231:18:231:30 | new[] | test.cpp:232:3:232:20 | ... = ... |
| test.cpp:238:20:238:32 | new[] | test.cpp:239:5:239:22 | ... = ... |
| test.cpp:260:13:260:24 | new[] | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | * ... |
@@ -127,18 +121,10 @@ nodes
| test.cpp:53:12:53:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:60:34:60:37 | mk_array output argument | semmle.label | mk_array output argument |
| test.cpp:67:9:67:14 | ... = ... | semmle.label | ... = ... |
| test.cpp:194:15:194:33 | call to malloc | semmle.label | call to malloc |
| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:201:5:201:19 | ... = ... | semmle.label | ... = ... |
| test.cpp:205:15:205:33 | call to malloc | semmle.label | call to malloc |
| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:213:5:213:13 | ... = ... | semmle.label | ... = ... |
| test.cpp:231:18:231:30 | new[] | semmle.label | new[] |
| test.cpp:232:3:232:20 | ... = ... | semmle.label | ... = ... |
| test.cpp:238:20:238:32 | new[] | semmle.label | new[] |
| test.cpp:239:5:239:22 | ... = ... | semmle.label | ... = ... |
| test.cpp:260:13:260:24 | new[] | semmle.label | new[] |
| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
@@ -220,10 +206,7 @@ subpaths
| test.cpp:30:14:30:15 | * ... | test.cpp:28:15:28:37 | call to malloc | test.cpp:30:14:30:15 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:28:15:28:37 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
| test.cpp:32:14:32:21 | * ... | test.cpp:28:15:28:37 | call to malloc | test.cpp:32:14:32:21 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:28:15:28:37 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
| test.cpp:67:9:67:14 | ... = ... | test.cpp:52:19:52:37 | call to malloc | test.cpp:67:9:67:14 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:52:19:52:37 | call to malloc | call to malloc | test.cpp:53:20:53:23 | size | size |
| test.cpp:201:5:201:19 | ... = ... | test.cpp:194:15:194:33 | call to malloc | test.cpp:201:5:201:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:194:15:194:33 | call to malloc | call to malloc | test.cpp:195:21:195:23 | len | len |
| test.cpp:213:5:213:13 | ... = ... | test.cpp:205:15:205:33 | call to malloc | test.cpp:213:5:213:13 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:205:15:205:33 | call to malloc | call to malloc | test.cpp:206:21:206:23 | len | len |
| test.cpp:232:3:232:20 | ... = ... | test.cpp:231:18:231:30 | new[] | test.cpp:232:3:232:20 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:231:18:231:30 | new[] | new[] | test.cpp:232:11:232:15 | index | index |
| test.cpp:239:5:239:22 | ... = ... | test.cpp:238:20:238:32 | new[] | test.cpp:239:5:239:22 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:238:20:238:32 | new[] | new[] | test.cpp:239:13:239:17 | index | index |
| test.cpp:264:13:264:14 | * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
| test.cpp:274:5:274:10 | ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
| test.cpp:358:14:358:26 | end_plus_one indirection | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | end_plus_one indirection | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |

View File

@@ -198,7 +198,7 @@ void test12(unsigned len, unsigned index) {
return;
}
p[index] = '\0'; // $ deref=L195->L201 // BAD
p[index] = '\0'; // $ MISSING: deref=L195->L201 // BAD [NOT DETECTED]
}
void test13(unsigned len, unsigned index) {
@@ -229,14 +229,14 @@ void test15(unsigned index) {
return;
}
int* newname = new int[size];
newname[index] = 0; // $ alloc=L231 deref=L232 // GOOD [FALSE POSITIVE]
newname[index] = 0; // GOOD
}
void test16(unsigned index) {
unsigned size = index + 13;
if(size >= index) {
int* newname = new int[size];
newname[index] = 0; // $ alloc=L238 deref=L239 // GOOD [FALSE POSITIVE]
newname[index] = 0; // GOOD
}
}

View File

@@ -1,14 +1,7 @@
| test.cpp:12:6:12:8 | foo | The variable $@ may not be initialized at this access. | test.cpp:11:6:11:8 | foo | foo |
| test.cpp:30:6:30:8 | foo | The variable $@ may not be initialized at this access. | test.cpp:26:6:26:8 | foo | foo |
| test.cpp:46:6:46:8 | foo | The variable $@ may not be initialized at this access. | test.cpp:42:6:42:8 | foo | foo |
| test.cpp:55:7:55:9 | foo | The variable $@ may not be initialized at this access. | test.cpp:50:6:50:8 | foo | foo |
| test.cpp:67:7:67:9 | foo | The variable $@ may not be initialized at this access. | test.cpp:61:6:61:8 | foo | foo |
| test.cpp:92:6:92:8 | foo | The variable $@ may not be initialized at this access. | test.cpp:82:6:82:8 | foo | foo |
| test.cpp:113:6:113:8 | foo | The variable $@ may not be initialized at this access. | test.cpp:111:6:111:8 | foo | foo |
| test.cpp:132:9:132:9 | j | The variable $@ may not be initialized at this access. | test.cpp:126:6:126:6 | j | j |
| test.cpp:219:3:219:3 | x | The variable $@ may not be initialized at this access. | test.cpp:218:7:218:7 | x | x |
| test.cpp:243:13:243:13 | i | The variable $@ may not be initialized at this access. | test.cpp:241:6:241:6 | i | i |
| test.cpp:329:9:329:11 | val | The variable $@ may not be initialized at this access. | test.cpp:321:6:321:8 | val | val |
| test.cpp:336:10:336:10 | a | The variable $@ may not be initialized at this access. | test.cpp:333:7:333:7 | a | a |
| test.cpp:369:10:369:10 | a | The variable $@ may not be initialized at this access. | test.cpp:358:7:358:7 | a | a |
| test.cpp:378:9:378:11 | val | The variable $@ may not be initialized at this access. | test.cpp:359:6:359:8 | val | val |

View File

@@ -27,7 +27,7 @@ void test4(bool b) {
if (b) {
foo = 1;
}
use(foo); // BAD
use(foo); // BAD [NOT DETECTED]
}
void test5() {
@@ -43,7 +43,7 @@ void test5(int count) {
for (int i = 0; i < count; i++) {
foo = i;
}
use(foo); // BAD
use(foo); // BAD [NOT DETECTED]
}
void test6(bool b) {
@@ -52,7 +52,7 @@ void test6(bool b) {
foo = 42;
}
if (b) {
use(foo); // GOOD (REPORTED, FP)
use(foo); // GOOD
}
}
@@ -64,7 +64,7 @@ void test7(bool b) {
set = true;
}
if (set) {
use(foo); // GOOD (REPORTED, FP)
use(foo); // GOOD
}
}
@@ -89,7 +89,7 @@ void test9(int count) {
if (!set) {
foo = 42;
}
use(foo); // GOOD (REPORTED, FP)
use(foo); // GOOD
}
void test10() {
@@ -129,7 +129,7 @@ int absWrong(int i) {
} else if (i < 0) {
j = -i;
}
return j; // wrong: j may not be initialized before use
return j; // wrong: j may not be initialized before use [NOT DETECTED]
}
// Example from qhelp
@@ -326,7 +326,7 @@ int test28() {
a = false;
c = false;
}
return val; // GOOD [FALSE POSITIVE]
return val; // GOOD
}
int test29() {
@@ -472,4 +472,64 @@ void test44() {
int y = 1;
void(x + y); // BAD
}
enum class State { StateA, StateB, StateC };
int exhaustive_switch(State s) {
int y;
switch(s) {
case State::StateA:
y = 1;
break;
case State::StateB:
y = 2;
break;
case State::StateC:
y = 3;
break;
}
return y; // GOOD (y is always initialized)
}
int exhaustive_switch_2(State s) {
int y;
switch(s) {
case State::StateA:
y = 1;
break;
default:
y = 2;
break;
}
return y; // GOOD (y is always initialized)
}
int non_exhaustive_switch(State s) {
int y;
switch(s) {
case State::StateA:
y = 1;
break;
case State::StateB:
y = 2;
break;
}
return y; // BAD [NOT DETECTED] (y is not initialized when s = StateC)
}
int non_exhaustive_switch_2(State s) {
int y;
switch(s) {
case State::StateA:
y = 1;
break;
case State::StateB:
y = 2;
break;
}
if(s != State::StateC) {
return y; // GOOD (y is not initialized when s = StateC, but if s = StateC we won't reach this point)
}
return 0;
}

View File

@@ -82,8 +82,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
? new[] { options.SolutionFile }
: allNonBinaryFiles.SelectFileNamesByExtension(".sln");
var dllPaths = options.DllDirs.Count == 0
? allFiles.SelectFileNamesByExtension(".dll").ToList()
: options.DllDirs.Select(Path.GetFullPath).ToList();
? allFiles.SelectFileNamesByExtension(".dll").ToHashSet()
: options.DllDirs.Select(Path.GetFullPath).ToHashSet();
if (options.UseNuGet)
{
@@ -107,7 +107,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
.RequiredPaths
.Select(d => Path.Combine(packageDirectory.DirInfo.FullName, d))
.ToList();
dllPaths.AddRange(paths);
dllPaths.UnionWith(paths);
LogAllUnusedPackages(dependencies);
DownloadMissingPackages(allNonBinaryFiles, dllPaths);
@@ -205,7 +205,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private void AddNetFrameworkDlls(List<string> dllPaths)
private void AddNetFrameworkDlls(ISet<string> dllPaths)
{
// Multiple dotnet framework packages could be present.
// The order of the packages is important, we're adding the first one that is present in the nuget cache.
@@ -218,13 +218,19 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
};
var frameworkPath = packagesInPrioOrder
.Select(GetPackageDirectory)
.FirstOrDefault(dir => dir is not null);
.Select((s, index) => (Index: index, Path: GetPackageDirectory(s)))
.FirstOrDefault(pair => pair.Path is not null);
if (frameworkPath is not null)
if (frameworkPath.Path is not null)
{
dllPaths.Add(frameworkPath);
progressMonitor.LogInfo("Found .NET Core/Framework DLLs in NuGet packages. Not adding installation directory.");
dllPaths.Add(frameworkPath.Path);
progressMonitor.LogInfo($"Found .NET Core/Framework DLLs in NuGet packages at {frameworkPath.Path}. Not adding installation directory.");
for (var i = frameworkPath.Index + 1; i < packagesInPrioOrder.Length; i++)
{
RemoveNugetPackageReference(packagesInPrioOrder[i], dllPaths);
}
return;
}
@@ -249,7 +255,29 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
dllPaths.Add(runtimeLocation);
}
private void AddAspNetCoreFrameworkDlls(List<string> dllPaths)
private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllPaths)
{
if (!options.UseNuGet)
{
return;
}
var packageFolder = packageDirectory.DirInfo.FullName.ToLowerInvariant();
if (packageFolder == null)
{
return;
}
var packagePathPrefix = Path.Combine(packageFolder, packagePrefix.ToLowerInvariant());
var toRemove = dllPaths.Where(s => s.ToLowerInvariant().StartsWith(packagePathPrefix));
foreach (var path in toRemove)
{
dllPaths.Remove(path);
progressMonitor.RemovedReference(path);
}
}
private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
{
if (!fileContent.IsNewProjectStructureUsed || !fileContent.UseAspNetCoreDlls)
{
@@ -269,7 +297,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private void AddMicrosoftWindowsDesktopDlls(List<string> dllPaths)
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
{
if (GetPackageDirectory("microsoft.windowsdesktop.app.ref") is string windowsDesktopApp)
{
@@ -628,7 +656,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
assets = assetFiles;
}
private void DownloadMissingPackages(List<FileInfo> allFiles, List<string> dllPaths)
private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<string> dllPaths)
{
var nugetConfigs = allFiles.SelectFileNamesByName("nuget.config").ToArray();
string? nugetConfig = null;

View File

@@ -91,7 +91,7 @@ namespace Semmle.Extraction.CSharp.Entities
// The current argument is not named
// so the previous ones were also not named
// so the child index matches the parameter index.
isParamsParameter = Symbol?.AttributeConstructor?.Parameters[childIndex].IsParams == true;
isParamsParameter = Symbol.AttributeConstructor?.Parameters[childIndex].IsParams == true;
argSyntax = ctorArguments[childIndex];
}

View File

@@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities
Position.Span.Start.Line + 1, Position.Span.Start.Character + 1,
Position.Span.End.Line + 1, Position.Span.End.Character);
var mapped = Symbol!.GetMappedLineSpan();
var mapped = Symbol.GetMappedLineSpan();
if (mapped.HasMappedPath && mapped.IsValid)
{
var mappedLoc = Create(Context, Location.Create(mapped.Path, default, mapped.Span));

View File

@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.CodeAnalysis;
@@ -30,6 +31,7 @@ namespace Semmle.Extraction
/// <typeparam name="TSymbol">The type of the symbol.</typeparam>
public abstract class CachedEntity<TSymbol> : CachedEntity where TSymbol : notnull
{
[NotNull]
public TSymbol Symbol { get; }
protected CachedEntity(Context context, TSymbol symbol) : base(context)

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -14,4 +13,9 @@
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
<RemoveDir Directories=".\bin" />
<RemoveDir Directories=".\obj" />
<RemoveDir Directories=".\myout" />
</Target>
</Project>

View File

@@ -8,3 +8,8 @@ check_diagnostics()
# Explicitly build and then run tests.
run_codeql_database_create(['dotnet clean', 'rm -rf test-db', 'dotnet build -o myout', 'dotnet test myout/dotnet_test.dll'], test_db="test2-db", lang="csharp")
check_diagnostics(test_db="test2-db")
thisDir = os.path.abspath(os.getcwd())
# Explicit build and then run tests using the absolute path.
run_codeql_database_create(['dotnet clean', 'rm -rf test2-db', 'dotnet build -o myout', f'dotnet test {thisDir}/myout/dotnet_test.dll'], test_db="test3-db", lang="csharp")
check_diagnostics(test_db="test3-db")

View File

@@ -0,0 +1,163 @@
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/Microsoft.CSharp.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/Microsoft.VisualBasic.Core.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/Microsoft.VisualBasic.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/Microsoft.Win32.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/Microsoft.Win32.Registry.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.AppContext.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Buffers.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Collections.Concurrent.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Collections.Immutable.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Collections.NonGeneric.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Collections.Specialized.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Collections.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.Annotations.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.DataAnnotations.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.EventBasedAsync.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.TypeConverter.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ComponentModel.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Configuration.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Console.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Core.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Data.Common.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Data.DataSetExtensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Data.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.Contracts.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.Debug.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.DiagnosticSource.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.FileVersionInfo.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.Process.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.StackTrace.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.TextWriterTraceListener.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.Tools.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.TraceSource.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Diagnostics.Tracing.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Drawing.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Drawing.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Dynamic.Runtime.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Formats.Asn1.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Formats.Tar.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Globalization.Calendars.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Globalization.Extensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Globalization.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Compression.Brotli.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Compression.FileSystem.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Compression.ZipFile.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Compression.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.FileSystem.AccessControl.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.FileSystem.DriveInfo.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.FileSystem.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.FileSystem.Watcher.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.FileSystem.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.IsolatedStorage.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.MemoryMappedFiles.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Pipes.AccessControl.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.Pipes.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.UnmanagedMemoryStream.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.IO.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Linq.Expressions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Linq.Parallel.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Linq.Queryable.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Linq.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Memory.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Http.Json.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Http.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.HttpListener.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Mail.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.NameResolution.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.NetworkInformation.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Ping.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Quic.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Requests.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Security.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.ServicePoint.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.Sockets.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.WebClient.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.WebHeaderCollection.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.WebProxy.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.WebSockets.Client.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.WebSockets.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Net.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Numerics.Vectors.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Numerics.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ObjectModel.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.DispatchProxy.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Emit.ILGeneration.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Emit.Lightweight.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Emit.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Extensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Metadata.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.TypeExtensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Reflection.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Resources.Reader.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Resources.ResourceManager.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Resources.Writer.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.CompilerServices.Unsafe.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.CompilerServices.VisualC.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Extensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Handles.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.InteropServices.JavaScript.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.InteropServices.RuntimeInformation.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.InteropServices.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Intrinsics.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Loader.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Numerics.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Serialization.Formatters.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Serialization.Json.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Serialization.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Serialization.Xml.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.Serialization.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Runtime.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.AccessControl.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Claims.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.Algorithms.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.Cng.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.Csp.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.Encoding.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.OpenSsl.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.Primitives.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.X509Certificates.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Cryptography.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Principal.Windows.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.Principal.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.SecureString.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Security.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ServiceModel.Web.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ServiceProcess.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.Encoding.CodePages.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.Encoding.Extensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.Encoding.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.Encodings.Web.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.Json.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Text.RegularExpressions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Channels.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Overlapped.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Tasks.Dataflow.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Tasks.Extensions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Tasks.Parallel.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Tasks.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Thread.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.ThreadPool.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.Timer.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Threading.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Transactions.Local.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Transactions.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.ValueTuple.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Web.HttpUtility.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Web.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Windows.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.Linq.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.ReaderWriter.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.Serialization.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XDocument.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XPath.XDocument.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XPath.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XmlDocument.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.XmlSerializer.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.Xml.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/System.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/WindowsBase.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/mscorlib.dll |
| /microsoft.netcore.app.ref/7.0.2/ref/net7.0/netstandard.dll |

View File

@@ -0,0 +1,15 @@
import csharp
private string getPath(Assembly a) {
not a.getCompilation().getOutputAssembly() = a and
exists(string s | s = a.getFile().getAbsolutePath() |
result =
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
or
result = s and
not exists(s.indexOf("GitHub/packages/"))
)
}
from Assembly a
select getPath(a)

View File

@@ -0,0 +1 @@
var dummy = "dummy";

View File

@@ -0,0 +1,5 @@
{
"sdk": {
"version": "7.0.102"
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,3 @@
from create_database_utils import *
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])

View File

@@ -87,28 +87,16 @@
*/
import csharp
private import ExternalFlowExtensions as Extensions
private import internal.AccessPathSyntax
private import internal.DataFlowDispatch
private import internal.DataFlowPrivate
private import internal.DataFlowPublic
private import internal.FlowSummaryImpl::Public
private import internal.FlowSummaryImpl::Private::External
private import internal.FlowSummaryImplSpecific
import ExternalFlowExtensions
private import AccessPathSyntax
private import DataFlowDispatch
private import DataFlowPrivate
private import DataFlowPublic
private import FlowSummaryImpl::Public
private import FlowSummaryImpl::Private::External
private import FlowSummaryImplSpecific
private import codeql.mad.ModelValidation as SharedModelVal
/** Holds if a source model exists for the given parameters. */
predicate sourceModel = Extensions::sourceModel/9;
/** Holds if a sink model exists for the given parameters. */
predicate sinkModel = Extensions::sinkModel/9;
/** Holds if a summary model exists for the given parameters. */
predicate summaryModel = Extensions::summaryModel/10;
/** Holds if a neutral model exists for the given parameters. */
predicate neutralModel = Extensions::neutralModel/6;
private predicate relevantNamespace(string namespace) {
sourceModel(namespace, _, _, _, _, _, _, _, _) or
sinkModel(namespace, _, _, _, _, _, _, _, _) or
@@ -310,10 +298,17 @@ class UnboundCallable extends Callable {
}
}
private predicate hasName(Declaration d, string name) {
d.(Operator).getFunctionName() = name
or
not d instanceof Operator and
d.hasName(name)
}
pragma[nomagic]
private predicate callableSpecInfo(Callable c, string namespace, string type, string name) {
c.getDeclaringType().hasQualifiedName(namespace, type) and
c.getName() = name
hasName(c, name)
}
pragma[nomagic]
@@ -326,7 +321,7 @@ private predicate subtypeSpecCandidate(string name, UnboundValueOrRefType t) {
pragma[nomagic]
private predicate callableInfo(Callable c, string name, UnboundValueOrRefType decl) {
name = c.getName() and
hasName(c, name) and
decl = c.getDeclaringType()
}
@@ -387,7 +382,7 @@ private Element interpretElement0(
subtypes = true and result.(UnboundCallable).overridesOrImplementsUnbound(m)
) and
m.getDeclaringType() = t and
m.hasName(name)
hasName(m, name)
|
signature = ""
or

View File

@@ -12,7 +12,7 @@ private import DataFlowImplCommon
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import semmle.code.csharp.Unification
private import semmle.code.csharp.dataflow.ExternalFlow
private import ExternalFlow
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
/**

View File

@@ -4,7 +4,7 @@
*/
import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
module HardcodedSymmetricEncryptionKey {
private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm

View File

@@ -7,7 +7,7 @@ private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.security.dataflow.flowsources.Local
private import semmle.code.csharp.frameworks.system.codedom.Compiler
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A data flow source for user input treated as code vulnerabilities.

View File

@@ -6,7 +6,7 @@ import csharp
private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.frameworks.system.Diagnostics
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A source specific to command injection vulnerabilities.

View File

@@ -8,7 +8,7 @@ private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.frameworks.system.DirectoryServices
private import semmle.code.csharp.frameworks.system.directoryservices.Protocols
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A data flow source for unvalidated user input that is used to construct LDAP queries.

View File

@@ -8,7 +8,7 @@ private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A data flow source for untrusted user input used in log entries.

View File

@@ -7,7 +7,7 @@ private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.security.dataflow.flowsources.Local
private import semmle.code.csharp.frameworks.Sql
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A source specific to SQL injection vulnerabilities.

View File

@@ -9,7 +9,7 @@ private import semmle.code.csharp.frameworks.system.Web
private import semmle.code.csharp.frameworks.system.web.Mvc
private import semmle.code.csharp.security.Sanitizers
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* A data flow source for unvalidated URL redirect vulnerabilities.

View File

@@ -9,7 +9,7 @@ private import semmle.code.csharp.frameworks.system.Web
private import semmle.code.csharp.frameworks.system.web.UI
private import semmle.code.csharp.security.dataflow.flowsinks.Html
private import semmle.code.csharp.security.dataflow.flowsinks.Remote
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.frameworks.ServiceStack::XSS
/**

View File

@@ -7,7 +7,7 @@ private import Remote
private import semmle.code.csharp.commons.Loggers
private import semmle.code.csharp.frameworks.system.Web
private import semmle.code.csharp.frameworks.system.IO
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/**
* An external location sink.

View File

@@ -13,7 +13,7 @@ private import semmle.code.csharp.frameworks.system.web.UI
private import semmle.code.csharp.frameworks.system.web.ui.WebControls
private import semmle.code.csharp.frameworks.system.windows.Forms
private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.asp.AspNet
/**

View File

@@ -4,7 +4,7 @@
import csharp
private import semmle.code.csharp.frameworks.system.windows.Forms
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/** A data flow source of local data. */
abstract class LocalFlowSource extends DataFlow::Node {

View File

@@ -12,7 +12,7 @@ private import semmle.code.csharp.frameworks.system.web.ui.WebControls
private import semmle.code.csharp.frameworks.WCF
private import semmle.code.csharp.frameworks.microsoft.Owin
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {

View File

@@ -3,7 +3,7 @@
*/
import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.frameworks.system.data.Common
private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.EntityFramework

View File

@@ -7,7 +7,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from string namespaceAndType, int rows
where

View File

@@ -2,10 +2,10 @@
private import csharp
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
private import semmle.code.csharp.security.dataflow.flowsources.Remote

View File

@@ -7,7 +7,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from string namespace, int pkgs, string kind, string part, int n
where modelCoverage(namespace, pkgs, kind, part, n)

View File

@@ -5,7 +5,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from string package, string type, string name, string signature, string kind, string provenance
where

View File

@@ -5,7 +5,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from
string namespace, string type, boolean subtypes, string name, string signature, string ext,

View File

@@ -5,7 +5,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from
string namespace, string type, boolean subtypes, string name, string signature, string ext,

View File

@@ -5,7 +5,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
from
string namespace, string type, boolean subtypes, string name, string signature, string ext,

View File

@@ -1,5 +1,5 @@
private import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate

View File

@@ -1,5 +1,5 @@
private import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.frameworks.Test
private import ModelEditor

View File

@@ -8,7 +8,7 @@ private import semmle.code.csharp.commons.Util as Util
private import semmle.code.csharp.commons.Collections as Collections
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.frameworks.system.linq.Expressions
import semmle.code.csharp.dataflow.ExternalFlow as ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate

View File

@@ -3,7 +3,7 @@
*/
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import Taint::PathGraph
import ModelValidation

View File

@@ -1,6 +1,6 @@
import csharp
import DataFlow
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import ModelValidation
from DataFlow::Node node, string kind

View File

@@ -1,6 +1,6 @@
import csharp
import DataFlow
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import ModelValidation
from DataFlow::Node node, string kind

View File

@@ -1,6 +1,6 @@
import csharp
import DataFlow
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import ModelValidation
import semmle.code.csharp.dataflow.FlowSummary
import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch

View File

@@ -1,5 +1,5 @@
import csharp
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import semmle.code.csharp.dataflow.internal.AccessPathSyntax
import ModelValidation

View File

@@ -1,6 +1,6 @@
import shared.FlowSummaries
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate::Csv
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.ExternalFlow
class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable instanceof SummarizedCallable
{

View File

@@ -1,5 +1,8 @@
semmle-extractor-options: /nostdlib /noconfig
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/Newtonsoft.Json/13.0.3/Newtonsoft.Json.csproj
semmle-extractor-options: --load-sources-from-project:../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/Dapper/2.0.90/Dapper.csproj
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/ServiceStack/6.2.0/ServiceStack.csproj
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/ServiceStack.OrmLite.SqlServer/6.2.0/ServiceStack.OrmLite.SqlServer.csproj
semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs
semmle-extractor-options: ${testdir}/../../../resources/stubs/EntityFramework.cs

View File

@@ -1,6 +1,6 @@
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework
import shared.FlowSummaries
import semmle.code.csharp.dataflow.ExternalFlow as ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow
private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable instanceof EFSummarizedCallable
{ }

View File

@@ -1,5 +1,5 @@
import semmle.code.csharp.frameworks.Sql
import semmle.code.csharp.dataflow.ExternalFlow
import semmle.code.csharp.dataflow.internal.ExternalFlow
import semmle.code.csharp.dataflow.internal.DataFlowPublic
query predicate sqlExpressions(SqlExpr se, Expr e) { se.getSql() = e }

View File

@@ -80,16 +80,15 @@ function RegisterExtractorPack(id)
end
end
-- for `dotnet test`, we should not append `-p:UseSharedCompilation=false` to the command line
-- if an `exe` or `dll` is passed as an argument as the call is forwarded to vstest.
if testMatch and (arg:match('%.exe$') or arg:match('%.dll')) then
match = false
break
end
-- we have found a sub-command, ignore all strings that look like sub-command names from now on
inSubCommandPosition = false
end
-- for `dotnet test`, we should not append `-p:UseSharedCompilation=false` to the command line
-- if an `exe` or `dll` is passed as an argument as the call is forwarded to vstest.
if testMatch and (arg:match('%.exe$') or arg:match('%.dll')) then
match = false
break
end
-- if we see a separator to `dotnet run`, inject just prior to the existing separator
if arg == '--' then
dotnetRunNeedsSeparator = false

View File

@@ -39,7 +39,7 @@ Changing the labels of query history items
The query history **Format** setting controls how the extension lists queries in the query history. By default, each item has a label with the following format::
%q on %d - %s, %r result count [%t]
%q on %d - %s %r [%t]
- ``%q`` is the query name
- ``%d`` is the database name
@@ -107,6 +107,19 @@ You can also edit the items shown in the Variant Analysis Repositories panel by
You can change the items shown in the panel or add new items by directly editing this file.
Configuring settings for adding databases
------------------------------------------------
To automatically add database source folders to your workspace, you can enable the **Adding Databases > Add Database Source to Workspace** setting.
This setting is disabled by default. You may want to enable the setting if you regularly browse the source code of databases, for example to view the abstract syntax tree of the code. For more information, see ":ref:`Exploring the structure of your source code <exploring-the-structure-of-your-source-code>`."
.. pull-quote:: Note
If you are in a single-folder workspace, adding database source folders will cause the workspace to reload as a multi-root workspace. This may cause query history and database lists to be reset.
Before enabling this setting, we recommend that you save your workspace as a multi-root workspace. For more information, see "`Multi-root Workspaces <https://code.visualstudio.com/docs/editor/multi-root-workspaces>`__" in the Visual Studio Code help.
Configuring settings for testing queries locally
------------------------------------------------

View File

@@ -18,20 +18,26 @@ CodeQL for Visual Studio Code contains an AST viewer. The viewer consists of a g
Viewing the abstract syntax tree of a source file
--------------------------------------------------
1. Open a source file from a CodeQL database. For example, you can navigate to a source file in the File Explorer.
1. Open the CodeQL Databases view and right-click the database that you want to explore. Click **Add Database Source to Workspace**.
.. image:: ../images/codeql-for-visual-studio-code/add-database-source-to-workspace.png
:width: 350
:alt: Add database source to workspace
2. Navigate to a CodeQL database's source file in the File Explorer.
.. image:: ../images/codeql-for-visual-studio-code/open-source-file.png
:width: 350
:alt: Open a source file
2. Run **CodeQL: View AST** from the Command Palette. This runs a CodeQL query (usually called ``printAST.ql``) over the active file, which may take a few seconds.
3. Run **CodeQL: View AST** from the Command Palette. This runs a CodeQL query (usually called ``printAST.ql``) over the active file, which may take a few seconds.
.. pull-quote:: Note
If you don't have an appropriate ``printAST.ql`` query in your workspace, the **CodeQL: View AST** command won't work. To fix this, you can update your copy of the `CodeQL <https://github.com/github/codeql>`__ repository from ``main``. If you do this, you may need to upgrade your databases. Also, query caches may be discarded and your next query runs could be slower.
3. Once the query has run, the AST viewer displays the structure of the source file.
4. To see the nested structure, click the arrows and expand the nodes.
4. Once the query has run, the AST viewer displays the structure of the source file.
5. To see the nested structure, click the arrows and expand the nodes.
.. image:: ../images/codeql-for-visual-studio-code/explore-ast.png
:alt: Explore the AST

View File

@@ -22,7 +22,7 @@ About extensible predicates
At a high level, there are two main components to using data extensions. The query writer defines one or more extensible predicates in their query libraries. CLI and code scanning users who want to augment these predicates supply one or more extension files whose data gets injected into the extensible predicate during evaluation. The extension files are either stored directly in the repository where the codebase to be analyzed is hosted, or downloaded as CodeQL model packs.
This example of an extensible predicate for a source is taken from the core Java libraries https://github.com/github/codeql/blob/main/java/ql/lib/semmle/code/java/dataflow/ExternalFlowExtensions.qll#L8-L11
This example of an extensible predicate for a source is taken from the core Java libraries https://github.com/github/codeql/blob/main/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll#L8-L11
.. code-block:: ql

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -47,7 +47,7 @@ endif
qhelp-to-markdown:
scripts/qhelp-to-markdown.sh ql/src "$(QHELP_OUT_DIR)"
tools: $(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES))) tools/tokenizer.jar
tools: tools-codeql tools/tokenizer.jar
.PHONY: $(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES)))
$(addsuffix $(EXE),$(addprefix tools/bin/,$(BINARIES))):
@@ -67,7 +67,10 @@ tools-osx64: $(addprefix tools/osx64/,$(BINARIES))
.PHONY: $(addprefix tools/osx64/,$(BINARIES))
$(addprefix tools/osx64/,$(BINARIES)):
GOOS=darwin GOARCH=amd64 go build -C extractor -mod=vendor -o ../$@ ./cli/$(@F)
GOOS=darwin GOARCH=amd64 go build -C extractor -mod=vendor -o ../$@.amd64 ./cli/$(@F)
GOOS=darwin GOARCH=arm64 go build -C extractor -mod=vendor -o ../$@.arm64 ./cli/$(@F)
lipo -create $@.amd64 $@.arm64 -output $@
rm $@.amd64 $@.arm64
tools-win64: $(addsuffix .exe,$(addprefix tools/win64/,$(BINARIES)))

View File

@@ -74,7 +74,7 @@
*/
private import go
private import ExternalFlowExtensions as Extensions
import internal.ExternalFlowExtensions
private import internal.DataFlowPrivate
private import internal.FlowSummaryImpl::Private::External
private import internal.FlowSummaryImplSpecific
@@ -82,15 +82,6 @@ private import internal.AccessPathSyntax
private import FlowSummary
private import codeql.mad.ModelValidation as SharedModelVal
/** Holds if a source model exists for the given parameters. */
predicate sourceModel = Extensions::sourceModel/9;
/** Holds if a sink model exists for the given parameters. */
predicate sinkModel = Extensions::sinkModel/9;
/** Holds if a summary model exists for the given parameters. */
predicate summaryModel = Extensions::summaryModel/10;
/** Holds if `package` have MaD framework coverage. */
private predicate packageHasMaDCoverage(string package) {
sourceModel(package, _, _, _, _, _, _, _, _) or

View File

@@ -93,7 +93,7 @@ private import internal.DataFlowPrivate
private import internal.FlowSummaryImpl::Private::External
private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import internal.AccessPathSyntax
private import ExternalFlowExtensions as Extensions
private import internal.ExternalFlowExtensions as Extensions
private import FlowSummary
private import codeql.mad.ModelValidation as SharedModelVal

View File

@@ -211,7 +211,11 @@ module Sem implements Semantic {
class BasicBlock = J::BasicBlock;
class Guard extends GL::Guard {
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getABBSuccessor() }
final private class FinalGuard = GL::Guard;
class Guard extends FinalGuard {
Expr asExpr() { result = this }
}
@@ -219,14 +223,6 @@ module Sem implements Semantic {
GL::implies_v2(g1, b1, g2, b2)
}
predicate guardDirectlyControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
RU::guardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
}
predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
RU::guardControlsSsaRead(guard, controlled, testIsTrue)
}
class Type = J::Type;
class IntegerType extends J::IntegralType {
@@ -261,6 +257,10 @@ module Sem implements Semantic {
class SsaReadPositionPhiInputEdge extends SsaReadPosition instanceof SsaReadPos::SsaReadPositionPhiInputEdge
{
BasicBlock getOrigBlock() { result = super.getOrigBlock() }
BasicBlock getPhiBlock() { result = super.getPhiBlock() }
predicate phiInput(SsaPhiNode phi, SsaVariable inp) { super.phiInput(phi, inp) }
}
@@ -268,10 +268,6 @@ module Sem implements Semantic {
BasicBlock getBlock() { result = super.getBlock() }
}
predicate backEdge(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge) {
RU::backEdge(phi, inp, edge)
}
predicate conversionCannotOverflow = safeCast/2;
}

View File

@@ -7,6 +7,18 @@ private import SSA
private import semmle.code.java.controlflow.internal.GuardsLogic
private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon
private import semmle.code.java.Constants
private import semmle.code.java.dataflow.RangeAnalysis
private import codeql.rangeanalysis.internal.RangeUtils
private module U = MakeUtils<Sem, IntDelta>;
private predicate backEdge = U::backEdge/3;
predicate ssaRead = U::ssaRead/2;
predicate guardDirectlyControlsSsaRead = U::guardDirectlyControlsSsaRead/3;
predicate guardControlsSsaRead = U::guardControlsSsaRead/3;
/**
* Holds if `v` is an input to `phi` that is not along a back edge, and the
@@ -145,79 +157,6 @@ class ConstantStringExpr extends Expr {
string getStringValue() { constantStringExpr(this, result) }
}
bindingset[f]
private predicate okInt(float f) { -2.pow(31) <= f and f <= 2.pow(31) - 1 }
/**
* Gets an expression that equals `v - d`.
*/
Expr ssaRead(SsaVariable v, int delta) {
result = v.getAUse() and delta = 0
or
exists(int d1, ConstantIntegerExpr c |
result.(AddExpr).hasOperands(ssaRead(v, d1), c) and
delta = d1 - c.getIntValue() and
okInt(d1.(float) - c.getIntValue().(float))
)
or
exists(SubExpr sub, int d1, ConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = ssaRead(v, d1) and
sub.getRightOperand() = c and
delta = d1 + c.getIntValue() and
okInt(d1.(float) + c.getIntValue().(float))
)
or
v.(SsaExplicitUpdate).getDefiningExpr().(PreIncExpr) = result and delta = 0
or
v.(SsaExplicitUpdate).getDefiningExpr().(PreDecExpr) = result and delta = 0
or
v.(SsaExplicitUpdate).getDefiningExpr().(PostIncExpr) = result and delta = 1 // x++ === ++x - 1
or
v.(SsaExplicitUpdate).getDefiningExpr().(PostDecExpr) = result and delta = -1 // x-- === --x + 1
or
v.(SsaExplicitUpdate).getDefiningExpr().(Assignment) = result and delta = 0
or
result.(AssignExpr).getSource() = ssaRead(v, delta)
}
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
predicate backEdge(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge) {
edge.phiInput(phi, inp) and
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
not hasDominanceInformation(edge.getOrigBlock())
)
}
/**
* Holds if `guard` directly controls the position `controlled` with the
* value `testIsTrue`.
*/
predicate guardDirectlyControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
guard.directlyControls(controlled.(SsaReadPositionBlock).getBlock(), testIsTrue)
or
exists(SsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
)
}
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
guardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
or
exists(Guard guard0, boolean testIsTrue0 |
implies_v2(guard0, testIsTrue0, guard, testIsTrue) and
guardControlsSsaRead(guard0, controlled, testIsTrue0)
)
}
/**
* Gets a condition that tests whether `v` equals `e + delta`.
*

View File

@@ -0,0 +1,10 @@
/** Provides definitions for working with the JMS library. */
import java
/** The method `ObjectMessage.getObject`. */
class ObjectMessageGetObjectMethod extends Method {
ObjectMessageGetObjectMethod() {
this.hasQualifiedName(["javax", "jakarta"] + ".jms", "ObjectMessage", "getObject")
}
}

View File

@@ -4,6 +4,7 @@
import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.TaintTracking2
private import semmle.code.java.dispatch.VirtualDispatch
private import semmle.code.java.frameworks.Kryo
private import semmle.code.java.frameworks.XStream
private import semmle.code.java.frameworks.SnakeYaml
@@ -15,6 +16,7 @@ private import semmle.code.java.frameworks.HessianBurlap
private import semmle.code.java.frameworks.Castor
private import semmle.code.java.frameworks.Jackson
private import semmle.code.java.frameworks.Jabsorb
private import semmle.code.java.frameworks.Jms
private import semmle.code.java.frameworks.JoddJson
private import semmle.code.java.frameworks.Flexjson
private import semmle.code.java.frameworks.google.Gson
@@ -224,6 +226,11 @@ predicate unsafeDeserialization(MethodCall ma, Expr sink) {
m instanceof GsonDeserializeMethod and
sink = ma.getArgument(0) and
UnsafeTypeFlow::flowToExpr(ma.getArgument(1))
or
m.getASourceOverriddenMethod*() instanceof ObjectMessageGetObjectMethod and
sink = ma.getQualifier().getUnderlyingExpr() and
// If we can see an implementation, we trust dataflow to find a path to the other sinks instead
not exists(viableCallable(ma))
)
}

View File

@@ -3,7 +3,7 @@
*
* James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for
* Regular Expression Denial-of-Service Attacks. NSS 2013.
* (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf)
* (https://arxiv.org/abs/1301.0849)
* Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression
* Exponential Runtime via Substructural Logics. 2014.
* (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf)

View File

@@ -15,7 +15,7 @@ may have unforeseen effects, such as the execution of arbitrary code.
<p>
There are many different serialization frameworks. This query currently
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap,
Jackson, Jabsorb, Jodd JSON, Flexjson, Gson and Java IO serialization through
Jackson, Jabsorb, Jodd JSON, Flexjson, Gson, JMS, and Java IO serialization through
<code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
</p>
</overview>
@@ -74,6 +74,12 @@ Recommendations specific to particular frameworks supported by this query:
<li><b>Recommendation</b>: Do not use with untrusted user input.</li>
</ul>
<p></p>
<p><b>ObjectMesssage</b> - <code>Java EE/Jakarta EE</code></p>
<ul>
<li><b>Secure by Default</b>: Depends on the JMS implementation.</li>
<li><b>Recommendation</b>: Do not use with untrusted user input.</li>
</ul>
<p></p>
</recommendation>
<example>
@@ -158,6 +164,10 @@ RCE in Flexjson:
Android Intent deserialization vulnerabilities with GSON parser:
<a href="https://blog.oversecured.com/Exploiting-memory-corruption-vulnerabilities-on-Android/#insecure-use-of-json-parsers">Insecure use of JSON parsers</a>.
</li>
<li>
Research by Matthias Kaiser:
<a href="https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf">Pwning Your Java Messaging With Deserialization Vulnerabilities</a>.
</li>
</references>
</qhelp>

View File

@@ -10,7 +10,7 @@
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li>
<li>James Kirrage, Asiri Rathnayake, Hayo Thielecke:
<a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
<a href="https://arxiv.org/abs/1301.0849">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The query `java/unsafe-deserialization` has been improved to detect insecure calls to `ObjectMessage.getObject` in JMS.

View File

@@ -0,0 +1,9 @@
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
public class ObjectMessageTest implements MessageListener {
public void onMessage(Message message) {
((ObjectMessage) message).getObject(); // $ unsafeDeserialization
}
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0:${testdir}/../../../stubs/serialkiller-4.0.0
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0:${testdir}/../../../stubs/serialkiller-4.0.0:${testdir}/../../../stubs/jms-api-1

View File

@@ -892,10 +892,15 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
// For named packages, find the main file.
String name = packageJson.getName();
if (name != null) {
Path entryPoint = guessPackageMainFile(path, packageJson, FileType.TYPESCRIPT.getExtensions());
if (entryPoint == null) {
// Try a TypeScript-recognized JS extension instead
entryPoint = guessPackageMainFile(path, packageJson, Arrays.asList(".js", ".jsx"));
Path entryPoint = null;
try {
entryPoint = guessPackageMainFile(path, packageJson, FileType.TYPESCRIPT.getExtensions());
if (entryPoint == null) {
// Try a TypeScript-recognized JS extension instead
entryPoint = guessPackageMainFile(path, packageJson, Arrays.asList(".js", ".jsx"));
}
} catch (InvalidPathException ignore) {
// can happen if the `main:` field is invalid. E.g. on Windows a path like `dist/*.js` will crash.
}
if (entryPoint != null) {
System.out.println(relativePath + ": Main file set to " + sourceRoot.relativize(entryPoint));

View File

@@ -51,6 +51,7 @@ private module AsmCrypto {
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
DataFlow::Node input;
CryptographicAlgorithm algorithm; // non-functional
DataFlow::PropRead algorithmSelection;
private string algorithmName;
private string methodName;
@@ -68,11 +69,14 @@ private module AsmCrypto {
exists(DataFlow::SourceNode asmCrypto |
asmCrypto = DataFlow::globalVarRef("asmCrypto") and
algorithm.matchesName(algorithmName) and
this = asmCrypto.getAPropertyRead(algorithmName).getAMemberCall(methodName) and
algorithmSelection = asmCrypto.getAPropertyRead(algorithmName) and
this = algorithmSelection.getAMemberCall(methodName) and
input = this.getArgument(0)
)
}
override DataFlow::Node getInitialization() { result = algorithmSelection }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -103,6 +107,7 @@ private module BrowserIdCrypto {
private class Apply extends CryptographicOperation::Range instanceof DataFlow::MethodCallNode {
CryptographicAlgorithm algorithm; // non-functional
DataFlow::CallNode keygen;
Apply() {
/*
@@ -122,8 +127,7 @@ private module BrowserIdCrypto {
*/
exists(
DataFlow::SourceNode mod, DataFlow::Node algorithmNameNode, DataFlow::CallNode keygen,
DataFlow::FunctionNode callback
DataFlow::SourceNode mod, DataFlow::Node algorithmNameNode, DataFlow::FunctionNode callback
|
mod = DataFlow::moduleImport("browserid-crypto") and
keygen = mod.getAMemberCall("generateKeypair") and
@@ -134,6 +138,8 @@ private module BrowserIdCrypto {
)
}
override DataFlow::Node getInitialization() { result = keygen }
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -239,6 +245,8 @@ private module NodeJSCrypto {
Apply() { this = instantiation.getAMethodCall(any(string m | m = "update" or m = "write")) }
override DataFlow::Node getInitialization() { result = instantiation }
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
override CryptographicAlgorithm getAlgorithm() { result = instantiation.getAlgorithm() }
@@ -324,7 +332,9 @@ private module CryptoJS {
)
}
private API::CallNode getEncryptionApplication(API::Node input, CryptographicAlgorithm algorithm) {
private API::CallNode getEncryptionApplication(
API::Node input, API::Node algorithmNode, CryptographicAlgorithm algorithm
) {
/*
* ```
* var CryptoJS = require("crypto-js");
@@ -338,11 +348,14 @@ private module CryptoJS {
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
*/
result = getAlgorithmNode(algorithm).getMember("encrypt").getACall() and
algorithmNode = getAlgorithmNode(algorithm) and
result = algorithmNode.getMember("encrypt").getACall() and
input = result.getParameter(0)
}
private API::CallNode getDirectApplication(API::Node input, CryptographicAlgorithm algorithm) {
private API::CallNode getDirectApplication(
API::Node input, API::Node algorithmNode, CryptographicAlgorithm algorithm
) {
/*
* ```
* var CryptoJS = require("crypto-js");
@@ -357,7 +370,8 @@ private module CryptoJS {
* Also matches where `CryptoJS.<algorithmName>` has been replaced by `require("crypto-js/<algorithmName>")`
*/
result = getAlgorithmNode(algorithm).getACall() and
algorithmNode = getAlgorithmNode(algorithm) and
result = algorithmNode.getACall() and
input = result.getParameter(0)
}
@@ -389,18 +403,23 @@ private module CryptoJS {
private class Apply extends CryptographicOperation::Range instanceof API::CallNode {
API::Node input;
CryptographicAlgorithm algorithm; // non-functional
DataFlow::Node instantiation;
Apply() {
this = getEncryptionApplication(input, algorithm)
or
this = getDirectApplication(input, algorithm)
or
exists(InstantiatedAlgorithm instantiation |
this = getUpdatedApplication(input, instantiation) and
algorithm = instantiation.getAlgorithm()
exists(API::Node algorithmNode |
this = getEncryptionApplication(input, algorithmNode, algorithm)
or
this = getDirectApplication(input, algorithmNode, algorithm)
|
instantiation = algorithmNode.asSource()
)
or
this = getUpdatedApplication(input, instantiation) and
algorithm = instantiation.(InstantiatedAlgorithm).getAlgorithm()
}
override DataFlow::Node getInitialization() { result = instantiation }
override DataFlow::Node getAnInput() { result = input.asSink() }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -504,6 +523,8 @@ private module TweetNaCl {
)
}
override DataFlow::Node getInitialization() { result = this }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -539,6 +560,7 @@ private module HashJs {
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
DataFlow::Node input;
CryptographicAlgorithm algorithm; // non-functional
DataFlow::CallNode init;
Apply() {
/*
@@ -554,10 +576,13 @@ private module HashJs {
* Also matches where `hash.<algorithmName>()` has been replaced by a more specific require a la `require("hash.js/lib/hash/sha/512")`
*/
this = getAlgorithmNode(algorithm).getAMemberCall("update") and
init = getAlgorithmNode(algorithm) and
this = init.getAMemberCall("update") and
input = super.getArgument(0)
}
override DataFlow::Node getInitialization() { result = init }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -653,6 +678,8 @@ private module Forge {
algorithm = cipher.getAlgorithm()
}
override DataFlow::Node getInitialization() { result = cipher }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -715,6 +742,8 @@ private module Md5 {
super.getArgument(0) = input
}
override DataFlow::Node getInitialization() { result = this }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -731,17 +760,18 @@ private module Bcrypt {
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
DataFlow::Node input;
CryptographicAlgorithm algorithm;
API::Node init;
Apply() {
// `require("bcrypt").hash(password);` with minor naming variations
algorithm.matchesName("BCRYPT") and
this =
API::moduleImport(["bcrypt", "bcryptjs", "bcrypt-nodejs"])
.getMember(["hash", "hashSync"])
.getACall() and
init = API::moduleImport(["bcrypt", "bcryptjs", "bcrypt-nodejs"]) and
this = init.getMember(["hash", "hashSync"]).getACall() and
super.getArgument(0) = input
}
override DataFlow::Node getInitialization() { result = init.asSource() }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
@@ -769,6 +799,8 @@ private module Hasha {
)
}
override DataFlow::Node getInitialization() { result = this }
override DataFlow::Node getAnInput() { result = input }
override CryptographicAlgorithm getAlgorithm() { result = algorithm }

View File

@@ -40,6 +40,9 @@ module Cryptography {
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
DataFlow::Node getInitialization() { result = super.getInitialization() }
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
DataFlow::Node getAnInput() { result = super.getAnInput() }
@@ -65,6 +68,9 @@ module Cryptography {
* extend `CryptographicOperation` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
abstract DataFlow::Node getInitialization();
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
abstract CryptographicAlgorithm getAlgorithm();

View File

@@ -19,7 +19,10 @@ module BrokenCryptoAlgorithm {
/**
* A data flow sink for sensitive information in broken or weak cryptographic algorithms.
*/
abstract class Sink extends DataFlow::Node { }
abstract class Sink extends DataFlow::Node {
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
abstract DataFlow::Node getInitialization();
}
/**
* A sanitizer for sensitive information in broken or weak cryptographic algorithms.
@@ -38,15 +41,17 @@ module BrokenCryptoAlgorithm {
* An expression used by a broken or weak cryptographic algorithm.
*/
class WeakCryptographicOperationSink extends Sink {
CryptographicOperation application;
WeakCryptographicOperationSink() {
exists(CryptographicOperation application |
(
application.getAlgorithm().isWeak()
or
application.getBlockMode().isWeak()
) and
this = application.getAnInput()
)
(
application.getAlgorithm().isWeak()
or
application.getBlockMode().isWeak()
) and
this = application.getAnInput()
}
override DataFlow::Node getInitialization() { result = application.getInitialization() }
}
}

View File

@@ -3,7 +3,7 @@
*
* James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for
* Regular Expression Denial-of-Service Attacks. NSS 2013.
* (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf)
* (https://arxiv.org/abs/1301.0849)
* Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression
* Exponential Runtime via Substructural Logics. 2014.
* (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf)

View File

@@ -10,7 +10,7 @@
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li>
<li>James Kirrage, Asiri Rathnayake, Hayo Thielecke:
<a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
<a href="https://arxiv.org/abs/1301.0849">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
</li>
</references>
</qhelp>

View File

@@ -16,9 +16,14 @@ import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmQuery
import semmle.javascript.security.SensitiveActions
import DataFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Source sourceNode,
Sink sinkNode
where
cfg.hasFlowPath(source, sink) and
not source.getNode() instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
select sink.getNode(), source, sink, "A broken or weak cryptographic algorithm depends on $@.",
source.getNode(), "sensitive data from " + source.getNode().(Source).describe()
sourceNode = source.getNode() and
sinkNode = sink.getNode() and
not sourceNode instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash
select sinkNode, source, sink, "$@ depends on $@.", sinkNode.getInitialization(),
"A broken or weak cryptographic algorithm", sourceNode,
"sensitive data from " + sourceNode.describe()

View File

@@ -26,8 +26,8 @@ edges
| tst.js:19:17:19:24 | password | tst.js:19:17:19:24 | password |
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText |
#select
| tst.js:11:17:11:26 | secretText | tst.js:3:18:3:24 | trusted | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
| tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:11:17:11:26 | secretText | sensitive data from an access to secretText |
| tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | A broken or weak cryptographic algorithm depends on $@. | tst.js:17:17:17:25 | o.trusted | sensitive data from an access to trusted |
| tst.js:22:21:22:30 | secretText | tst.js:3:18:3:24 | trusted | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | A broken or weak cryptographic algorithm depends on $@. | tst.js:22:21:22:30 | secretText | sensitive data from an access to secretText |
| tst.js:11:17:11:26 | secretText | tst.js:3:18:3:24 | trusted | tst.js:11:17:11:26 | secretText | $@ depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | A broken or weak cryptographic algorithm | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
| tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | tst.js:11:17:11:26 | secretText | $@ depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | A broken or weak cryptographic algorithm | tst.js:11:17:11:26 | secretText | sensitive data from an access to secretText |
| tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | tst.js:17:17:17:25 | o.trusted | $@ depends on $@. | tst.js:5:19:5:49 | crypto. ... ', key) | A broken or weak cryptographic algorithm | tst.js:17:17:17:25 | o.trusted | sensitive data from an access to trusted |
| tst.js:22:21:22:30 | secretText | tst.js:3:18:3:24 | trusted | tst.js:22:21:22:30 | secretText | $@ depends on $@. | tst.js:21:22:21:60 | crypto. ... ', key) | A broken or weak cryptographic algorithm | tst.js:3:18:3:24 | trusted | sensitive data from an access to trusted |
| tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | tst.js:22:21:22:30 | secretText | $@ depends on $@. | tst.js:21:22:21:60 | crypto. ... ', key) | A broken or weak cryptographic algorithm | tst.js:22:21:22:30 | secretText | sensitive data from an access to secretText |

View File

@@ -1352,7 +1352,10 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract ControlFlowNode getNode();
/** Gets the enclosing callable of this call. */
abstract DataFlowCallable getEnclosingCallable();
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
/** Gets the scope of this node, if any. */
abstract Scope getScope();
/** Gets the location of this dataflow call. */
abstract Location getLocation();
@@ -1400,7 +1403,7 @@ class NormalCall extends ExtractedDataFlowCall, TNormalCall {
override ControlFlowNode getNode() { result = call }
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
override Scope getScope() { result = call.getScope() }
override DataFlowCallable getCallable() { result.(DataFlowFunction).getScope() = target }
@@ -1450,7 +1453,7 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
override ControlFlowNode getNode() { result = call }
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
override Scope getScope() { result = call.getScope() }
}
/**
@@ -1474,6 +1477,8 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
override Scope getScope() { none() }
override DataFlowCallable getCallable() { none() }
override ArgumentNode getArgument(ArgumentPosition apos) { none() }

View File

@@ -1044,3 +1044,11 @@ class ContentApprox = Unit;
/** Gets an approximated value for content `c`. */
pragma[inline]
ContentApprox getContentApprox(Content c) { any() }
/** Helper for `.getEnclosingCallable`. */
DataFlowCallable getCallableScope(Scope s) {
result.getScope() = s
or
not exists(DataFlowCallable c | c.getScope() = s) and
result = getCallableScope(s.getEnclosingScope())
}

View File

@@ -117,14 +117,6 @@ newtype TNode =
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
}
/** Helper for `Node::getEnclosingCallable`. */
private DataFlowCallable getCallableScope(Scope s) {
result.getScope() = s
or
not exists(DataFlowCallable c | c.getScope() = s) and
result = getCallableScope(s.getEnclosingScope())
}
private import semmle.python.internal.CachedStages
/**

Some files were not shown because too many files have changed in this diff Show More