Merge from master

This commit is contained in:
Dave Bartolomeo
2020-02-04 18:33:10 -07:00
66 changed files with 3896 additions and 849 deletions

View File

@@ -18,6 +18,7 @@ The following changes in version 1.24 affect C# analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the variable is named `_` in a `foreach` statement. |
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
## Removal of old queries

View File

@@ -7,7 +7,9 @@
* Imports with the `.js` extension can now be resolved to a TypeScript file,
when the import refers to a file generated by TypeScript.
- The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
* Support for the following frameworks and libraries has been improved:
- [react](https://www.npmjs.com/package/react)
@@ -18,6 +20,7 @@
- [Socket.IO](https://socket.io/)
- [ws](https://github.com/websockets/ws)
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
- [Koa](https://www.npmjs.com/package/koa)
## New queries

View File

@@ -6,7 +6,6 @@ private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
cached
private newtype TNode =
@@ -689,9 +688,9 @@ class BarrierGuard extends GuardCondition {
/** Gets a node guarded by this guard. */
final ExprNode getAGuardedNode() {
exists(GVN value, boolean branch |
result.getExpr() = value.getAnExpr() and
this.checks(value.getAnExpr(), branch) and
exists(SsaDefinition def, Variable v, boolean branch |
result.getExpr() = def.getAUse(v) and
this.checks(def.getAUse(v), branch) and
this.controls(result.getExpr().getBasicBlock(), branch)
)
}

View File

@@ -151,6 +151,22 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// from `a`.
i2.(PointerAddInstruction).getLeft() = i1
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
exists(IRFunction f, Parameter parameter |
i1 = getInitializeParameter(f, parameter) and
i2 = getInitializeIndirection(f, parameter)
)
or
// Until we have flow through indirections across calls, we'll take flow out
// of the indirection and into the argument.
// When we get proper flow through indirections across calls, this code can be
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
exists(ReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
)
or
// Flow from argument to return value
i2 =
any(CallInstruction call |
@@ -176,6 +192,18 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
)
}
pragma[noinline]
private InitializeIndirectionInstruction getInitializeIndirection(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
pragma[noinline]
private InitializeParameterInstruction getInitializeParameter(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
/**
* Get an instruction that goes into argument `argumentIndex` of `call`. This
* can be either directly or through one pointer indirection.
@@ -273,23 +301,6 @@ private Element adjustedSink(DataFlow::Node sink) {
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// For compatibility, send flow from argument read side effects to their
// corresponding argument expression
exists(IndirectReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
or
exists(BufferReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
or
exists(SizedBufferReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
}
predicate tainted(Expr source, Element tainted) {

View File

@@ -26,7 +26,7 @@ private predicate hasResultMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)
@@ -43,7 +43,7 @@ private predicate hasOperandMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)

View File

@@ -1,608 +1 @@
/**
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
* The predicate `globalValueNumber` converts an expression into a `GVN`,
* which is an abstract type representing the value of the expression. If
* two expressions have the same `GVN` then they compute the same value.
* For example:
*
* ```
* void f(int x, int y) {
* g(x+y, x+y);
* }
* ```
*
* In this example, both arguments in the call to `g` compute the same value,
* so both arguments have the same `GVN`. In other words, we can find
* this call with the following query:
*
* ```
* from FunctionCall call, GVN v
* where v = globalValueNumber(call.getArgument(0))
* and v = globalValueNumber(call.getArgument(1))
* select call
* ```
*
* The analysis is conservative, so two expressions might have different
* `GVN`s even though the actually always compute the same value. The most
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
/*
* Note to developers: the correctness of this module depends on the
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
* sync with each other. If you change this module then make sure that the
* change is symmetric across all three.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
/**
* Holds if the result is a control flow node that might change the
* value of any global variable. This is used in the implementation
* of `GVN_OtherVariable`, because we need to be quite conservative when
* we assign a value number to a global variable. For example:
*
* ```
* x = g+1;
* dosomething();
* y = g+1;
* ```
*
* It is not safe to assign the same value number to both instances
* of `g+1` in this example, because the call to `dosomething` might
* change the value of `g`.
*/
private ControlFlowNode nodeWithPossibleSideEffect() {
result instanceof Call
or
// If the lhs of an assignment is not analyzable by SSA, then
// we need to treat the assignment as having a possible side-effect.
result instanceof Assignment and not result instanceof SsaDefinition
or
result instanceof CrementOperation and not result instanceof SsaDefinition
or
exists(LocalVariable v |
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
)
or
result instanceof AsmStmt
}
/**
* Gets the entry node of the control flow graph of which `node` is a
* member.
*/
cached
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
result = node.getControlFlowScope().getEntryPoint() and
result.getASuccessor*() = node
}
/**
* Holds if there is a control flow edge from `src` to `dst` or
* if `dst` is an expression with a possible side-effect. The idea
* is to treat side effects as entry points in the control flow
* graph so that we can use the dominator tree to find the most recent
* side-effect.
*/
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
src.getASuccessor() = dst
or
// Add an edge from the entry point to any node that might have a side
// effect.
dst = nodeWithPossibleSideEffect() and
src = getControlFlowEntry(dst)
}
/**
* Holds if `dominator` is the immediate dominator of `node` in
* the side-effect CFG.
*/
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
/**
* Gets the most recent side effect. To be more precise, `result` is a
* dominator of `node` and no side-effects can occur between `result` and
* `node`.
*
* `sideEffectCFG` has an edge from the function entry to every node with a
* side-effect. This means that every node with a side-effect has the
* function entry as its immediate dominator. So if node `x` dominates node
* `y` then there can be no side effects between `x` and `y` unless `x` is
* the function entry. So the optimal choice for `result` has the function
* entry as its immediate dominator.
*
* Example:
*
* ```
* 000: int f(int a, int b, int *p) {
* 001: int r = 0;
* 002: if (a) {
* 003: if (b) {
* 004: sideEffect1();
* 005: }
* 006: } else {
* 007: sideEffect2();
* 008: }
* 009: if (a) {
* 010: r++; // Not a side-effect, because r is an SSA variable.
* 011: }
* 012: if (b) {
* 013: r++; // Not a side-effect, because r is an SSA variable.
* 014: }
* 015: return *p;
* 016: }
* ```
*
* Suppose we want to find the most recent side-effect for the dereference
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
* the immediate dominator tree looks like this:
*
* 000 - 001 - 002 - 003
* - 004
* - 007
* - 009 - 010
* - 012 - 013
* - 015
*
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
* Therefore, the most recent side effect for line 015 is line 009.
*/
cached
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
exists(ControlFlowNode entry |
functionEntry(entry) and
iDomEffect(entry, result) and
iDomEffect*(result, node)
)
}
/** Used to represent the "global value number" of an expression. */
cached
private newtype GVNBase =
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
// If the local variable does not have a defining value, then
// we use the SsaDefinition as its global value number.
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
mk_UndefinedStackVariable(x, def, _)
} or
// Variables with no SSA information. As a crude (but safe)
// approximation, we use `mostRecentSideEffect` to compute a definition
// location for the variable. This ensures that two instances of the same
// global variable will only get the same value number if they are
// guaranteed to have the same value.
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
GVN_FieldAccess(GVN s, Field f) {
mk_DotFieldAccess(s, f, _) or
mk_PointerFieldAccess_with_deref(s, f, _) or
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
} or
// Dereference a pointer. The value might have changed since the last
// time the pointer was dereferenced, so we need to include a definition
// location. As a crude (but safe) approximation, we use
// `mostRecentSideEffect` to compute a definition location.
GVN_Deref(GVN p, ControlFlowNode dominator) {
mk_Deref(p, dominator, _) or
mk_PointerFieldAccess(p, _, dominator, _) or
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
} or
GVN_ThisExpr(Function fcn) {
mk_ThisExpr(fcn, _) or
mk_ImplicitThisFieldAccess(fcn, _, _, _)
} or
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
/**
* A Global Value Number. A GVN is an abstract representation of the value
* computed by an expression. The relationship between `Expr` and `GVN` is
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
* expressions can have the same `GVN`. If two expressions have the same
* `GVN`, it means that they compute the same value at run time. The `GVN`
* is an opaque value, so you cannot deduce what the run-time value of an
* expression will be from its `GVN`. The only use for the `GVN` of an
* expression is to find other expressions that compute the same value.
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
*
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
class GVN extends GVNBase {
GVN() { this instanceof GVNBase }
/** Gets an expression that has this GVN. */
Expr getAnExpr() { this = globalValueNumber(result) }
/** Gets the kind of the GVN. This can be useful for debugging. */
string getKind() {
if this instanceof GVN_IntConst
then result = "IntConst"
else
if this instanceof GVN_FloatConst
then result = "FloatConst"
else
if this instanceof GVN_UndefinedStackVariable
then result = "UndefinedStackVariable"
else
if this instanceof GVN_OtherVariable
then result = "OtherVariable"
else
if this instanceof GVN_FieldAccess
then result = "FieldAccess"
else
if this instanceof GVN_Deref
then result = "Deref"
else
if this instanceof GVN_ThisExpr
then result = "ThisExpr"
else
if this instanceof GVN_Conversion
then result = "Conversion"
else
if this instanceof GVN_BinaryOp
then result = "BinaryOp"
else
if this instanceof GVN_UnaryOp
then result = "UnaryOp"
else
if this instanceof GVN_ArrayAccess
then result = "ArrayAccess"
else
if this instanceof GVN_Unanalyzable
then result = "Unanalyzable"
else result = "error"
}
/**
* Gets an example of an expression with this GVN.
* This is useful for things like implementing toString().
*/
private Expr exampleExpr() {
// Pick the expression with the minimum source location string. This is
// just an arbitrary way to pick an expression with this `GVN`.
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
}
/** Gets a textual representation of this element. */
string toString() { result = exampleExpr().toString() }
/** Gets the primary location of this element. */
Location getLocation() { result = exampleExpr().getLocation() }
}
private predicate analyzableIntConst(Expr e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1
}
private predicate mk_IntConst(int val, Type t, Expr e) {
analyzableIntConst(e) and
val = e.getValue().toInt() and
t = e.getUnspecifiedType()
}
private predicate analyzableFloatConst(Expr e) {
strictcount(e.getValue().toFloat()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
not analyzableIntConst(e)
}
private predicate mk_FloatConst(float val, Type t, Expr e) {
analyzableFloatConst(e) and
val = e.getValue().toFloat() and
t = e.getUnspecifiedType()
}
private predicate analyzableStackVariable(VariableAccess access) {
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count(SsaDefinition def, Variable v |
def.getAUse(v) = access
|
def.getDefiningValue(v).getFullyConverted()
) <= 1 and
not analyzableConst(access)
}
// Note: this predicate only has a result if the access has no
// defining value. If there is a defining value, then there is no
// need to generate a fresh `GVN` for the access because `globalValueNumber`
// will follow the chain and use the GVN of the defining value.
private predicate mk_UndefinedStackVariable(
StackVariable x, SsaDefinition def, VariableAccess access
) {
analyzableStackVariable(access) and
access = def.getAUse(x) and
not exists(def.getDefiningValue(x))
}
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
analyzableDotFieldAccess(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_PointerFieldAccess(
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
) {
analyzablePointerFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
/**
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
* extra `GVN_Deref` around the qualifier.
*/
private predicate mk_PointerFieldAccess_with_deref(
GVN new_qualifier, Field target, PointerFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_PointerFieldAccess(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getEnclosingFunction()) = 1 and
not analyzableConst(access)
}
private predicate mk_ImplicitThisFieldAccess(
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
analyzableImplicitThisFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
fcn = access.getEnclosingFunction()
}
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
exists(Function fcn |
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
qualifier = GVN_ThisExpr(fcn)
)
}
private predicate mk_ImplicitThisFieldAccess_with_deref(
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
/**
* Holds if `access` is an access of a variable that does
* not have SSA information. (For example, because the variable
* is global.)
*/
private predicate analyzableOtherVariable(VariableAccess access) {
not access instanceof FieldAccess and
not exists(SsaDefinition def | access = def.getAUse(_)) and
strictcount(access.getTarget()) = 1 and
strictcount(mostRecentSideEffect(access)) = 1 and
not analyzableConst(access)
}
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
analyzableOtherVariable(access) and
x = access.getTarget() and
dominator = mostRecentSideEffect(access)
}
private predicate analyzableConversion(Conversion conv) {
strictcount(conv.getUnspecifiedType()) = 1 and
strictcount(conv.getExpr()) = 1 and
not analyzableConst(conv)
}
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
analyzableConversion(conv) and
t = conv.getUnspecifiedType() and
child = globalValueNumber(conv.getExpr())
}
private predicate analyzableBinaryOp(BinaryOperation op) {
op.isPure() and
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
analyzableBinaryOp(op) and
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableUnaryOp(UnaryOperation op) {
not op instanceof PointerDereferenceExpr and
op.isPure() and
strictcount(op.getOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
analyzableUnaryOp(op) and
child = globalValueNumber(op.getOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableThisExpr(ThisExpr thisExpr) {
strictcount(thisExpr.getEnclosingFunction()) = 1 and
not analyzableConst(thisExpr)
}
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
analyzableThisExpr(thisExpr) and
fcn = thisExpr.getEnclosingFunction()
}
private predicate analyzableArrayAccess(ArrayExpr ae) {
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(ae)) = 1 and
not analyzableConst(ae)
}
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
analyzableArrayAccess(ae) and
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
dominator = mostRecentSideEffect(ae)
}
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
strictcount(deref.getOperand().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(deref)) = 1 and
not analyzableConst(deref)
}
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
analyzablePointerDereferenceExpr(deref) and
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
dominator = mostRecentSideEffect(deref)
}
/** Gets the global value number of expression `e`. */
cached
GVN globalValueNumber(Expr e) {
exists(int val, Type t |
mk_IntConst(val, t, e) and
result = GVN_IntConst(val, t)
)
or
exists(float val, Type t |
mk_FloatConst(val, t, e) and
result = GVN_FloatConst(val, t)
)
or
// Local variable with a defining value.
exists(StackVariable x, SsaDefinition def |
analyzableStackVariable(e) and
e = def.getAUse(x) and
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
)
or
// Local variable without a defining value.
exists(StackVariable x, SsaDefinition def |
mk_UndefinedStackVariable(x, def, e) and
result = GVN_UndefinedStackVariable(x, def)
)
or
// Variable with no SSA information.
exists(Variable x, ControlFlowNode dominator |
mk_OtherVariable(x, dominator, e) and
result = GVN_OtherVariable(x, dominator)
)
or
exists(GVN qualifier, Field target |
mk_DotFieldAccess(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(Function fcn |
mk_ThisExpr(fcn, e) and
result = GVN_ThisExpr(fcn)
)
or
exists(Type t, GVN child |
mk_Conversion(t, child, e) and
result = GVN_Conversion(t, child)
)
or
exists(GVN lhs, GVN rhs, string opname |
mk_BinaryOp(lhs, rhs, opname, e) and
result = GVN_BinaryOp(lhs, rhs, opname)
)
or
exists(GVN child, string opname |
mk_UnaryOp(child, opname, e) and
result = GVN_UnaryOp(child, opname)
)
or
exists(GVN x, GVN i, ControlFlowNode dominator |
mk_ArrayAccess(x, i, dominator, e) and
result = GVN_ArrayAccess(x, i, dominator)
)
or
exists(GVN p, ControlFlowNode dominator |
mk_Deref(p, dominator, e) and
result = GVN_Deref(p, dominator)
)
or
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
}
private predicate analyzableConst(Expr e) {
analyzableIntConst(e) or
analyzableFloatConst(e)
}
/**
* Holds if the expression is explicitly handled by `globalValueNumber`.
* Unanalyzable expressions still need to be given a global value number,
* but it will be a unique number that is not shared with any other
* expression.
*/
private predicate analyzableExpr(Expr e) {
analyzableConst(e) or
analyzableStackVariable(e) or
analyzableDotFieldAccess(e) or
analyzablePointerFieldAccess(e) or
analyzableImplicitThisFieldAccess(e) or
analyzableOtherVariable(e) or
analyzableConversion(e) or
analyzableBinaryOp(e) or
analyzableUnaryOp(e) or
analyzableThisExpr(e) or
analyzableArrayAccess(e) or
analyzablePointerDereferenceExpr(e)
}
import GlobalValueNumberingImpl

View File

@@ -0,0 +1,608 @@
/**
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
* The predicate `globalValueNumber` converts an expression into a `GVN`,
* which is an abstract type representing the value of the expression. If
* two expressions have the same `GVN` then they compute the same value.
* For example:
*
* ```
* void f(int x, int y) {
* g(x+y, x+y);
* }
* ```
*
* In this example, both arguments in the call to `g` compute the same value,
* so both arguments have the same `GVN`. In other words, we can find
* this call with the following query:
*
* ```
* from FunctionCall call, GVN v
* where v = globalValueNumber(call.getArgument(0))
* and v = globalValueNumber(call.getArgument(1))
* select call
* ```
*
* The analysis is conservative, so two expressions might have different
* `GVN`s even though the actually always compute the same value. The most
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
/*
* Note to developers: the correctness of this module depends on the
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
* sync with each other. If you change this module then make sure that the
* change is symmetric across all three.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
/**
* Holds if the result is a control flow node that might change the
* value of any global variable. This is used in the implementation
* of `GVN_OtherVariable`, because we need to be quite conservative when
* we assign a value number to a global variable. For example:
*
* ```
* x = g+1;
* dosomething();
* y = g+1;
* ```
*
* It is not safe to assign the same value number to both instances
* of `g+1` in this example, because the call to `dosomething` might
* change the value of `g`.
*/
private ControlFlowNode nodeWithPossibleSideEffect() {
result instanceof Call
or
// If the lhs of an assignment is not analyzable by SSA, then
// we need to treat the assignment as having a possible side-effect.
result instanceof Assignment and not result instanceof SsaDefinition
or
result instanceof CrementOperation and not result instanceof SsaDefinition
or
exists(LocalVariable v |
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
)
or
result instanceof AsmStmt
}
/**
* Gets the entry node of the control flow graph of which `node` is a
* member.
*/
cached
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
result = node.getControlFlowScope().getEntryPoint() and
result.getASuccessor*() = node
}
/**
* Holds if there is a control flow edge from `src` to `dst` or
* if `dst` is an expression with a possible side-effect. The idea
* is to treat side effects as entry points in the control flow
* graph so that we can use the dominator tree to find the most recent
* side-effect.
*/
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
src.getASuccessor() = dst
or
// Add an edge from the entry point to any node that might have a side
// effect.
dst = nodeWithPossibleSideEffect() and
src = getControlFlowEntry(dst)
}
/**
* Holds if `dominator` is the immediate dominator of `node` in
* the side-effect CFG.
*/
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
/**
* Gets the most recent side effect. To be more precise, `result` is a
* dominator of `node` and no side-effects can occur between `result` and
* `node`.
*
* `sideEffectCFG` has an edge from the function entry to every node with a
* side-effect. This means that every node with a side-effect has the
* function entry as its immediate dominator. So if node `x` dominates node
* `y` then there can be no side effects between `x` and `y` unless `x` is
* the function entry. So the optimal choice for `result` has the function
* entry as its immediate dominator.
*
* Example:
*
* ```
* 000: int f(int a, int b, int *p) {
* 001: int r = 0;
* 002: if (a) {
* 003: if (b) {
* 004: sideEffect1();
* 005: }
* 006: } else {
* 007: sideEffect2();
* 008: }
* 009: if (a) {
* 010: r++; // Not a side-effect, because r is an SSA variable.
* 011: }
* 012: if (b) {
* 013: r++; // Not a side-effect, because r is an SSA variable.
* 014: }
* 015: return *p;
* 016: }
* ```
*
* Suppose we want to find the most recent side-effect for the dereference
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
* the immediate dominator tree looks like this:
*
* 000 - 001 - 002 - 003
* - 004
* - 007
* - 009 - 010
* - 012 - 013
* - 015
*
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
* Therefore, the most recent side effect for line 015 is line 009.
*/
cached
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
exists(ControlFlowNode entry |
functionEntry(entry) and
iDomEffect(entry, result) and
iDomEffect*(result, node)
)
}
/** Used to represent the "global value number" of an expression. */
cached
private newtype GVNBase =
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
// If the local variable does not have a defining value, then
// we use the SsaDefinition as its global value number.
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
mk_UndefinedStackVariable(x, def, _)
} or
// Variables with no SSA information. As a crude (but safe)
// approximation, we use `mostRecentSideEffect` to compute a definition
// location for the variable. This ensures that two instances of the same
// global variable will only get the same value number if they are
// guaranteed to have the same value.
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
GVN_FieldAccess(GVN s, Field f) {
mk_DotFieldAccess(s, f, _) or
mk_PointerFieldAccess_with_deref(s, f, _) or
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
} or
// Dereference a pointer. The value might have changed since the last
// time the pointer was dereferenced, so we need to include a definition
// location. As a crude (but safe) approximation, we use
// `mostRecentSideEffect` to compute a definition location.
GVN_Deref(GVN p, ControlFlowNode dominator) {
mk_Deref(p, dominator, _) or
mk_PointerFieldAccess(p, _, dominator, _) or
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
} or
GVN_ThisExpr(Function fcn) {
mk_ThisExpr(fcn, _) or
mk_ImplicitThisFieldAccess(fcn, _, _, _)
} or
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
/**
* A Global Value Number. A GVN is an abstract representation of the value
* computed by an expression. The relationship between `Expr` and `GVN` is
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
* expressions can have the same `GVN`. If two expressions have the same
* `GVN`, it means that they compute the same value at run time. The `GVN`
* is an opaque value, so you cannot deduce what the run-time value of an
* expression will be from its `GVN`. The only use for the `GVN` of an
* expression is to find other expressions that compute the same value.
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
*
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
class GVN extends GVNBase {
GVN() { this instanceof GVNBase }
/** Gets an expression that has this GVN. */
Expr getAnExpr() { this = globalValueNumber(result) }
/** Gets the kind of the GVN. This can be useful for debugging. */
string getKind() {
if this instanceof GVN_IntConst
then result = "IntConst"
else
if this instanceof GVN_FloatConst
then result = "FloatConst"
else
if this instanceof GVN_UndefinedStackVariable
then result = "UndefinedStackVariable"
else
if this instanceof GVN_OtherVariable
then result = "OtherVariable"
else
if this instanceof GVN_FieldAccess
then result = "FieldAccess"
else
if this instanceof GVN_Deref
then result = "Deref"
else
if this instanceof GVN_ThisExpr
then result = "ThisExpr"
else
if this instanceof GVN_Conversion
then result = "Conversion"
else
if this instanceof GVN_BinaryOp
then result = "BinaryOp"
else
if this instanceof GVN_UnaryOp
then result = "UnaryOp"
else
if this instanceof GVN_ArrayAccess
then result = "ArrayAccess"
else
if this instanceof GVN_Unanalyzable
then result = "Unanalyzable"
else result = "error"
}
/**
* Gets an example of an expression with this GVN.
* This is useful for things like implementing toString().
*/
private Expr exampleExpr() {
// Pick the expression with the minimum source location string. This is
// just an arbitrary way to pick an expression with this `GVN`.
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
}
/** Gets a textual representation of this element. */
string toString() { result = exampleExpr().toString() }
/** Gets the primary location of this element. */
Location getLocation() { result = exampleExpr().getLocation() }
}
private predicate analyzableIntConst(Expr e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1
}
private predicate mk_IntConst(int val, Type t, Expr e) {
analyzableIntConst(e) and
val = e.getValue().toInt() and
t = e.getUnspecifiedType()
}
private predicate analyzableFloatConst(Expr e) {
strictcount(e.getValue().toFloat()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
not analyzableIntConst(e)
}
private predicate mk_FloatConst(float val, Type t, Expr e) {
analyzableFloatConst(e) and
val = e.getValue().toFloat() and
t = e.getUnspecifiedType()
}
private predicate analyzableStackVariable(VariableAccess access) {
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count(SsaDefinition def, Variable v |
def.getAUse(v) = access
|
def.getDefiningValue(v).getFullyConverted()
) <= 1 and
not analyzableConst(access)
}
// Note: this predicate only has a result if the access has no
// defining value. If there is a defining value, then there is no
// need to generate a fresh `GVN` for the access because `globalValueNumber`
// will follow the chain and use the GVN of the defining value.
private predicate mk_UndefinedStackVariable(
StackVariable x, SsaDefinition def, VariableAccess access
) {
analyzableStackVariable(access) and
access = def.getAUse(x) and
not exists(def.getDefiningValue(x))
}
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
analyzableDotFieldAccess(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_PointerFieldAccess(
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
) {
analyzablePointerFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
/**
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
* extra `GVN_Deref` around the qualifier.
*/
private predicate mk_PointerFieldAccess_with_deref(
GVN new_qualifier, Field target, PointerFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_PointerFieldAccess(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getEnclosingFunction()) = 1 and
not analyzableConst(access)
}
private predicate mk_ImplicitThisFieldAccess(
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
analyzableImplicitThisFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
fcn = access.getEnclosingFunction()
}
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
exists(Function fcn |
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
qualifier = GVN_ThisExpr(fcn)
)
}
private predicate mk_ImplicitThisFieldAccess_with_deref(
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
/**
* Holds if `access` is an access of a variable that does
* not have SSA information. (For example, because the variable
* is global.)
*/
private predicate analyzableOtherVariable(VariableAccess access) {
not access instanceof FieldAccess and
not exists(SsaDefinition def | access = def.getAUse(_)) and
strictcount(access.getTarget()) = 1 and
strictcount(mostRecentSideEffect(access)) = 1 and
not analyzableConst(access)
}
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
analyzableOtherVariable(access) and
x = access.getTarget() and
dominator = mostRecentSideEffect(access)
}
private predicate analyzableConversion(Conversion conv) {
strictcount(conv.getUnspecifiedType()) = 1 and
strictcount(conv.getExpr()) = 1 and
not analyzableConst(conv)
}
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
analyzableConversion(conv) and
t = conv.getUnspecifiedType() and
child = globalValueNumber(conv.getExpr())
}
private predicate analyzableBinaryOp(BinaryOperation op) {
op.isPure() and
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
analyzableBinaryOp(op) and
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableUnaryOp(UnaryOperation op) {
not op instanceof PointerDereferenceExpr and
op.isPure() and
strictcount(op.getOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
analyzableUnaryOp(op) and
child = globalValueNumber(op.getOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableThisExpr(ThisExpr thisExpr) {
strictcount(thisExpr.getEnclosingFunction()) = 1 and
not analyzableConst(thisExpr)
}
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
analyzableThisExpr(thisExpr) and
fcn = thisExpr.getEnclosingFunction()
}
private predicate analyzableArrayAccess(ArrayExpr ae) {
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(ae)) = 1 and
not analyzableConst(ae)
}
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
analyzableArrayAccess(ae) and
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
dominator = mostRecentSideEffect(ae)
}
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
strictcount(deref.getOperand().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(deref)) = 1 and
not analyzableConst(deref)
}
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
analyzablePointerDereferenceExpr(deref) and
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
dominator = mostRecentSideEffect(deref)
}
/** Gets the global value number of expression `e`. */
cached
GVN globalValueNumber(Expr e) {
exists(int val, Type t |
mk_IntConst(val, t, e) and
result = GVN_IntConst(val, t)
)
or
exists(float val, Type t |
mk_FloatConst(val, t, e) and
result = GVN_FloatConst(val, t)
)
or
// Local variable with a defining value.
exists(StackVariable x, SsaDefinition def |
analyzableStackVariable(e) and
e = def.getAUse(x) and
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
)
or
// Local variable without a defining value.
exists(StackVariable x, SsaDefinition def |
mk_UndefinedStackVariable(x, def, e) and
result = GVN_UndefinedStackVariable(x, def)
)
or
// Variable with no SSA information.
exists(Variable x, ControlFlowNode dominator |
mk_OtherVariable(x, dominator, e) and
result = GVN_OtherVariable(x, dominator)
)
or
exists(GVN qualifier, Field target |
mk_DotFieldAccess(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(Function fcn |
mk_ThisExpr(fcn, e) and
result = GVN_ThisExpr(fcn)
)
or
exists(Type t, GVN child |
mk_Conversion(t, child, e) and
result = GVN_Conversion(t, child)
)
or
exists(GVN lhs, GVN rhs, string opname |
mk_BinaryOp(lhs, rhs, opname, e) and
result = GVN_BinaryOp(lhs, rhs, opname)
)
or
exists(GVN child, string opname |
mk_UnaryOp(child, opname, e) and
result = GVN_UnaryOp(child, opname)
)
or
exists(GVN x, GVN i, ControlFlowNode dominator |
mk_ArrayAccess(x, i, dominator, e) and
result = GVN_ArrayAccess(x, i, dominator)
)
or
exists(GVN p, ControlFlowNode dominator |
mk_Deref(p, dominator, e) and
result = GVN_Deref(p, dominator)
)
or
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
}
private predicate analyzableConst(Expr e) {
analyzableIntConst(e) or
analyzableFloatConst(e)
}
/**
* Holds if the expression is explicitly handled by `globalValueNumber`.
* Unanalyzable expressions still need to be given a global value number,
* but it will be a unique number that is not shared with any other
* expression.
*/
private predicate analyzableExpr(Expr e) {
analyzableConst(e) or
analyzableStackVariable(e) or
analyzableDotFieldAccess(e) or
analyzablePointerFieldAccess(e) or
analyzableImplicitThisFieldAccess(e) or
analyzableOtherVariable(e) or
analyzableConversion(e) or
analyzableBinaryOp(e) or
analyzableUnaryOp(e) or
analyzableThisExpr(e) or
analyzableArrayAccess(e) or
analyzablePointerDereferenceExpr(e)
}

View File

@@ -21,14 +21,18 @@
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:25 | call to getenv |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:32 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | buf |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:32:11:32:26 | p#0 |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:30 | call to getenv |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:37 | (void *)... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:26:39:34 | call to inet_addr |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:50:39:61 | & ... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:40:10:40:10 | a |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |

View File

@@ -5,8 +5,8 @@
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:3:21:3:22 | s1 | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:21:8:21:10 | buf | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:15:22:17 | buf | AST only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address | AST only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... | IR only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion | IR only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p | IR only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |

View File

@@ -48,7 +48,7 @@ struct XY {
void bg_stackstruct(XY s1, XY s2) {
s1.x = source();
if (guarded(s1.x)) {
sink(s1.x); // no flow
sink(s1.x); // no flow [FALSE POSITIVE in AST]
} else if (guarded(s1.y)) {
sink(s1.x); // flow
} else if (guarded(s2.y)) {

View File

@@ -3,6 +3,7 @@
| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:51:13:51:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |

View File

@@ -1,3 +1,4 @@
| BarrierGuard.cpp:49:10:49:15 | BarrierGuard.cpp:51:13:51:13 | AST only |
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
| clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only |

View File

@@ -0,0 +1,17 @@
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | IR only |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | IR only |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | IR only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | IR only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |

View File

@@ -0,0 +1,16 @@
import semmle.code.cpp.security.TaintTracking as AST
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp
from Expr source, Element tainted, string side
where
AST::taintedIncludingGlobalVars(source, tainted, _) and
not IR::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "AST only"
or
IR::taintedIncludingGlobalVars(source, tainted, _) and
not AST::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "IR only"
select source, tainted, side

View File

@@ -0,0 +1,49 @@
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |

View File

@@ -0,0 +1,7 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
from Expr source, Element tainted, string globalVar
where
taintedIncludingGlobalVars(source, tainted, globalVar) and
not tainted.getLocation().getFile().getExtension() = "h"
select source, tainted, globalVar

View File

@@ -1136,50 +1136,105 @@ ssa.cpp:
# 239| v239_8(void) = AliasedUse : ~m244_5
# 239| v239_9(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<bool>) = VariableAddress[b] :
# 247| m247_7(bool) = InitializeParameter[b] : &:r247_6
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_7
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<char *>) = VariableAddress[src] :
# 247| m247_7(char *) = InitializeParameter[src] : &:r247_6
# 247| r247_8(char *) = Load : &:r247_6, m247_7
# 247| m247_9(unknown) = InitializeIndirection[src] : &:r247_8
# 247| r247_10(glval<int>) = VariableAddress[size] :
# 247| m247_11(int) = InitializeParameter[size] : &:r247_10
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_11
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_9
# 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9
# 248| r248_11(char *) = Convert : r248_8
# 248| m248_12(char *) = Store : &:r248_1, r248_11
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_7
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| m249_5(char) = Store : &:r249_4, r249_1
# 249| m249_6(unknown) = Chi : total:m248_10, partial:m249_5
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_12
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_7
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_11
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_12
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_12(void) = ReturnIndirection : &:r247_8, ~m250_13
# 247| r247_13(glval<char *>) = VariableAddress[#return] :
# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4
# 247| v247_15(void) = UnmodeledUse : mu*
# 247| v247_16(void) = AliasedUse : ~m250_13
# 247| v247_17(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| m254_2(unknown) = AliasedDefinition :
# 254| m254_3(unknown) = InitializeNonLocal :
# 254| m254_4(unknown) = Chi : total:m254_2, partial:m254_3
# 254| mu254_5(unknown) = UnmodeledDefinition :
# 254| r254_6(glval<bool>) = VariableAddress[b] :
# 254| m254_7(bool) = InitializeParameter[b] : &:r254_6
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_7
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| m249_3(unknown) = ^CallSideEffect : ~m247_4
# 249| m249_4(unknown) = Chi : total:m247_4, partial:m249_3
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| m256_3(unknown) = ^CallSideEffect : ~m254_4
# 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| m252_3(unknown) = ^CallSideEffect : ~m247_4
# 252| m252_4(unknown) = Chi : total:m247_4, partial:m252_3
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| m259_3(unknown) = ^CallSideEffect : ~m254_4
# 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3
#-----| Goto -> Block 3
# 255| Block 3
# 255| m255_1(unknown) = Phi : from 1:~m249_4, from 2:~m252_4
# 255| r255_2(glval<char *>) = VariableAddress[s] :
# 255| r255_3(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_4(char *) = Convert : r255_3
# 255| m255_5(char *) = Store : &:r255_2, r255_4
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_5
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~m247_3
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_8(glval<char>) = VariableAddress[#return] :
# 247| v247_9(void) = ReturnValue : &:r247_8, m256_7
# 247| v247_10(void) = UnmodeledUse : mu*
# 247| v247_11(void) = AliasedUse : ~m255_1
# 247| v247_12(void) = ExitFunction :
# 262| Block 3
# 262| m262_1(unknown) = Phi : from 1:~m256_4, from 2:~m259_4
# 262| r262_2(glval<char *>) = VariableAddress[s] :
# 262| r262_3(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_4(char *) = Convert : r262_3
# 262| m262_5(char *) = Store : &:r262_2, r262_4
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_5
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~m254_3
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_8(glval<char>) = VariableAddress[#return] :
# 254| v254_9(void) = ReturnValue : &:r254_8, m263_7
# 254| v254_10(void) = UnmodeledUse : mu*
# 254| v254_11(void) = AliasedUse : ~m262_1
# 254| v254_12(void) = ExitFunction :

View File

@@ -1131,50 +1131,105 @@ ssa.cpp:
# 239| v239_8(void) = AliasedUse : ~m244_5
# 239| v239_9(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<bool>) = VariableAddress[b] :
# 247| m247_7(bool) = InitializeParameter[b] : &:r247_6
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_7
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<char *>) = VariableAddress[src] :
# 247| m247_7(char *) = InitializeParameter[src] : &:r247_6
# 247| r247_8(char *) = Load : &:r247_6, m247_7
# 247| m247_9(unknown) = InitializeIndirection[src] : &:r247_8
# 247| r247_10(glval<int>) = VariableAddress[size] :
# 247| m247_11(int) = InitializeParameter[size] : &:r247_10
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_11
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_4
# 248| m248_10(unknown) = Chi : total:m247_4, partial:m248_9
# 248| r248_11(char *) = Convert : r248_8
# 248| m248_12(char *) = Store : &:r248_1, r248_11
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_7
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| m249_5(char) = Store : &:r249_4, r249_1
# 249| m249_6(unknown) = Chi : total:m247_9, partial:m249_5
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_12
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_7
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_11
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 250| m250_13(unknown) = Chi : total:m248_10, partial:m250_12
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_12
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_12(void) = ReturnIndirection : &:r247_8, ~m249_6
# 247| r247_13(glval<char *>) = VariableAddress[#return] :
# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4
# 247| v247_15(void) = UnmodeledUse : mu*
# 247| v247_16(void) = AliasedUse : ~m250_13
# 247| v247_17(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| m254_2(unknown) = AliasedDefinition :
# 254| m254_3(unknown) = InitializeNonLocal :
# 254| m254_4(unknown) = Chi : total:m254_2, partial:m254_3
# 254| mu254_5(unknown) = UnmodeledDefinition :
# 254| r254_6(glval<bool>) = VariableAddress[b] :
# 254| m254_7(bool) = InitializeParameter[b] : &:r254_6
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_7
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| m249_3(unknown) = ^CallSideEffect : ~m247_4
# 249| m249_4(unknown) = Chi : total:m247_4, partial:m249_3
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| m256_3(unknown) = ^CallSideEffect : ~m254_4
# 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| m252_3(unknown) = ^CallSideEffect : ~m247_4
# 252| m252_4(unknown) = Chi : total:m247_4, partial:m252_3
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| m259_3(unknown) = ^CallSideEffect : ~m254_4
# 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3
#-----| Goto -> Block 3
# 255| Block 3
# 255| m255_1(unknown) = Phi : from 1:~m249_4, from 2:~m252_4
# 255| r255_2(glval<char *>) = VariableAddress[s] :
# 255| r255_3(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_4(char *) = Convert : r255_3
# 255| m255_5(char *) = Store : &:r255_2, r255_4
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_5
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~m247_3
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_8(glval<char>) = VariableAddress[#return] :
# 247| v247_9(void) = ReturnValue : &:r247_8, m256_7
# 247| v247_10(void) = UnmodeledUse : mu*
# 247| v247_11(void) = AliasedUse : ~m255_1
# 247| v247_12(void) = ExitFunction :
# 262| Block 3
# 262| m262_1(unknown) = Phi : from 1:~m256_4, from 2:~m259_4
# 262| r262_2(glval<char *>) = VariableAddress[s] :
# 262| r262_3(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_4(char *) = Convert : r262_3
# 262| m262_5(char *) = Store : &:r262_2, r262_4
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_5
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~m254_3
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_8(glval<char>) = VariableAddress[#return] :
# 254| v254_9(void) = ReturnValue : &:r254_8, m263_7
# 254| v254_10(void) = UnmodeledUse : mu*
# 254| v254_11(void) = AliasedUse : ~m262_1
# 254| v254_12(void) = ExitFunction :

View File

@@ -244,6 +244,13 @@ void ExplicitConstructorCalls() {
c2.g();
}
char *VoidStarIndirectParameters(char *src, int size) {
char *dst = new char[size];
*src = 'a';
memcpy(dst, src, size);
return dst;
}
char StringLiteralAliasing2(bool b) {
if (b) {
ExternalFunc();

View File

@@ -1053,46 +1053,97 @@ ssa.cpp:
# 239| v239_7(void) = AliasedUse : ~mu239_4
# 239| v239_8(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<bool>) = VariableAddress[b] :
# 247| m247_6(bool) = InitializeParameter[b] : &:r247_5
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_6
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<char *>) = VariableAddress[src] :
# 247| m247_6(char *) = InitializeParameter[src] : &:r247_5
# 247| r247_7(char *) = Load : &:r247_5, m247_6
# 247| mu247_8(unknown) = InitializeIndirection[src] : &:r247_7
# 247| r247_9(glval<int>) = VariableAddress[size] :
# 247| m247_10(int) = InitializeParameter[size] : &:r247_9
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_10
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_4
# 248| r248_10(char *) = Convert : r248_8
# 248| m248_11(char *) = Store : &:r248_1, r248_10
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_6
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| mu249_5(char) = Store : &:r249_4, r249_1
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_11
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_6
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_10
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_4
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_11
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_11(void) = ReturnIndirection : &:r247_7, ~mu247_4
# 247| r247_12(glval<char *>) = VariableAddress[#return] :
# 247| v247_13(void) = ReturnValue : &:r247_12, m251_4
# 247| v247_14(void) = UnmodeledUse : mu*
# 247| v247_15(void) = AliasedUse : ~mu247_4
# 247| v247_16(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| mu254_2(unknown) = AliasedDefinition :
# 254| mu254_3(unknown) = InitializeNonLocal :
# 254| mu254_4(unknown) = UnmodeledDefinition :
# 254| r254_5(glval<bool>) = VariableAddress[b] :
# 254| m254_6(bool) = InitializeParameter[b] : &:r254_5
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_6
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| mu249_3(unknown) = ^CallSideEffect : ~mu247_4
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| mu256_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| mu252_3(unknown) = ^CallSideEffect : ~mu247_4
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| mu259_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 255| Block 3
# 255| r255_1(glval<char *>) = VariableAddress[s] :
# 255| r255_2(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_3(char *) = Convert : r255_2
# 255| m255_4(char *) = Store : &:r255_1, r255_3
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_4
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~mu247_4
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_7(glval<char>) = VariableAddress[#return] :
# 247| v247_8(void) = ReturnValue : &:r247_7, m256_7
# 247| v247_9(void) = UnmodeledUse : mu*
# 247| v247_10(void) = AliasedUse : ~mu247_4
# 247| v247_11(void) = ExitFunction :
# 262| Block 3
# 262| r262_1(glval<char *>) = VariableAddress[s] :
# 262| r262_2(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_3(char *) = Convert : r262_2
# 262| m262_4(char *) = Store : &:r262_1, r262_3
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_4
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~mu254_4
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_7(glval<char>) = VariableAddress[#return] :
# 254| v254_8(void) = ReturnValue : &:r254_7, m263_7
# 254| v254_9(void) = UnmodeledUse : mu*
# 254| v254_10(void) = AliasedUse : ~mu254_4
# 254| v254_11(void) = ExitFunction :

View File

@@ -1053,46 +1053,97 @@ ssa.cpp:
# 239| v239_7(void) = AliasedUse : ~mu239_4
# 239| v239_8(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<bool>) = VariableAddress[b] :
# 247| m247_6(bool) = InitializeParameter[b] : &:r247_5
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_6
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<char *>) = VariableAddress[src] :
# 247| m247_6(char *) = InitializeParameter[src] : &:r247_5
# 247| r247_7(char *) = Load : &:r247_5, m247_6
# 247| mu247_8(unknown) = InitializeIndirection[src] : &:r247_7
# 247| r247_9(glval<int>) = VariableAddress[size] :
# 247| m247_10(int) = InitializeParameter[size] : &:r247_9
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_10
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_4
# 248| r248_10(char *) = Convert : r248_8
# 248| m248_11(char *) = Store : &:r248_1, r248_10
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_6
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| mu249_5(char) = Store : &:r249_4, r249_1
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_11
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_6
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_10
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_4
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_11
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_11(void) = ReturnIndirection : &:r247_7, ~mu247_4
# 247| r247_12(glval<char *>) = VariableAddress[#return] :
# 247| v247_13(void) = ReturnValue : &:r247_12, m251_4
# 247| v247_14(void) = UnmodeledUse : mu*
# 247| v247_15(void) = AliasedUse : ~mu247_4
# 247| v247_16(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| mu254_2(unknown) = AliasedDefinition :
# 254| mu254_3(unknown) = InitializeNonLocal :
# 254| mu254_4(unknown) = UnmodeledDefinition :
# 254| r254_5(glval<bool>) = VariableAddress[b] :
# 254| m254_6(bool) = InitializeParameter[b] : &:r254_5
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_6
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| mu249_3(unknown) = ^CallSideEffect : ~mu247_4
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| mu256_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| mu252_3(unknown) = ^CallSideEffect : ~mu247_4
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| mu259_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 255| Block 3
# 255| r255_1(glval<char *>) = VariableAddress[s] :
# 255| r255_2(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_3(char *) = Convert : r255_2
# 255| m255_4(char *) = Store : &:r255_1, r255_3
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_4
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~mu247_4
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_7(glval<char>) = VariableAddress[#return] :
# 247| v247_8(void) = ReturnValue : &:r247_7, m256_7
# 247| v247_9(void) = UnmodeledUse : mu*
# 247| v247_10(void) = AliasedUse : ~mu247_4
# 247| v247_11(void) = ExitFunction :
# 262| Block 3
# 262| r262_1(glval<char *>) = VariableAddress[s] :
# 262| r262_2(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_3(char *) = Convert : r262_2
# 262| m262_4(char *) = Store : &:r262_1, r262_3
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_4
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~mu254_4
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_7(glval<char>) = VariableAddress[#return] :
# 254| v254_8(void) = ReturnValue : &:r254_7, m263_7
# 254| v254_9(void) = UnmodeledUse : mu*
# 254| v254_10(void) = AliasedUse : ~mu254_4
# 254| v254_11(void) = ExitFunction :

View File

@@ -27,7 +27,8 @@ class DangerousExpression extends Expr {
e instanceof MethodCall
or
e instanceof ArrayAccess
)
) and
not exists(Expr e | this = e.getParent*() | e.(Call).getTarget().getAParameter().isOutOrRef())
}
}

View File

@@ -20,6 +20,9 @@ class Test
var b = true;
b &= c.Method(); // GOOD
b |= c[0]; // GOOD
if (c == null | c.Method(out _)) ; // GOOD
if (c == null | (c.Method() | c.Method(out _))) ; // GOOD
}
class C
@@ -28,6 +31,7 @@ class Test
public string Property { get; set; }
public bool this[int i] { get { return false; } set { } }
public bool Method() { return false; }
public bool Method(out int x) { x = 0; return false; }
}
}

View File

@@ -251,8 +251,13 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
}
}
}
if (isNamedNodeWithSymbol(node)) {
let symbol = typeChecker.getSymbolAtLocation(node.name);
let symbolNode =
isNamedNodeWithSymbol(node) ? node.name :
ts.isImportDeclaration(node) ? node.moduleSpecifier :
ts.isExternalModuleReference(node) ? node.expression :
null;
if (symbolNode != null) {
let symbol = typeChecker.getSymbolAtLocation(symbolNode);
if (symbol != null) {
node.$symbol = typeTable.getSymbolId(symbol);
}

View File

@@ -1,5 +1,6 @@
package com.semmle.js.ast;
import com.semmle.ts.ast.INodeWithSymbol;
import java.util.List;
/**
@@ -14,13 +15,15 @@ import java.util.List;
* import "m";
* </pre>
*/
public class ImportDeclaration extends Statement {
public class ImportDeclaration extends Statement implements INodeWithSymbol {
/** List of import specifiers detailing how declarations are imported; may be empty. */
private final List<ImportSpecifier> specifiers;
/** The module from which declarations are imported. */
private final Literal source;
private int symbol = -1;
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source) {
super("ImportDeclaration", loc);
this.specifiers = specifiers;
@@ -39,4 +42,14 @@ public class ImportDeclaration extends Statement {
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);
}
@Override
public int getSymbol() {
return this.symbol;
}
@Override
public void setSymbol(int symbol) {
this.symbol = symbol;
}
}

View File

@@ -1555,6 +1555,7 @@ public class ASTExtractor {
Label lbl = super.visit(nd, c);
visit(nd.getSource(), lbl, -1);
visitAll(nd.getSpecifiers(), lbl);
emitNodeSymbol(nd, lbl);
return lbl;
}
@@ -1705,6 +1706,7 @@ public class ASTExtractor {
public Label visit(ExternalModuleReference nd, Context c) {
Label key = super.visit(nd, c);
visit(nd.getExpression(), key, 0);
emitNodeSymbol(nd, key);
return key;
}
@@ -2061,12 +2063,14 @@ public class ASTExtractor {
@Override
public Label visit(AssignmentPattern nd, Context c) {
additionalErrors.add(new ParseError("Unexpected assignment pattern.", nd.getLoc().getStart()));
additionalErrors.add(
new ParseError("Unexpected assignment pattern.", nd.getLoc().getStart()));
return super.visit(nd, c);
}
}
public List<ParseError> extract(Node root, Platform platform, SourceType sourceType, int toplevelKind) {
public List<ParseError> extract(
Node root, Platform platform, SourceType sourceType, int toplevelKind) {
lexicalExtractor.getMetrics().startPhase(ExtractionPhase.ASTExtractor_extract);
trapwriter.addTuple("toplevels", toplevelLabel, toplevelKind);
locationManager.emitNodeLocation(root, toplevelLabel);

View File

@@ -1202,7 +1202,9 @@ public class TypeScriptASTConverter {
private Node convertExternalModuleReference(JsonObject node, SourceLocation loc)
throws ParseError {
return new ExternalModuleReference(loc, convertChild(node, "expression"));
ExternalModuleReference moduleRef = new ExternalModuleReference(loc, convertChild(node, "expression"));
attachSymbolInformation(moduleRef, node);
return moduleRef;
}
private Node convertFalseKeyword(SourceLocation loc) {
@@ -1366,7 +1368,9 @@ public class TypeScriptASTConverter {
}
}
}
return new ImportDeclaration(loc, specifiers, src);
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src);
attachSymbolInformation(importDecl, node);
return importDecl;
}
private Node convertImportEqualsDeclaration(JsonObject node, SourceLocation loc)

View File

@@ -4,8 +4,9 @@ import com.semmle.js.ast.Expression;
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.Visitor;
public class ExternalModuleReference extends Expression {
public class ExternalModuleReference extends Expression implements INodeWithSymbol {
private final Expression expression;
private int symbol = -1;
public ExternalModuleReference(SourceLocation loc, Expression expression) {
super("ExternalModuleReference", loc);
@@ -20,4 +21,14 @@ public class ExternalModuleReference extends Expression {
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);
}
@Override
public int getSymbol() {
return this.symbol;
}
@Override
public void setSymbol(int symbol) {
this.symbol = symbol;
}
}

View File

@@ -47,25 +47,13 @@ abstract class EnumeratedPropName extends DataFlow::Node {
*/
abstract DataFlow::Node getSourceObject();
/**
* Gets a local reference of the source object.
*/
SourceNode getASourceObjectRef() {
exists(SourceNode root, string path |
getSourceObject() = AccessPath::getAReferenceTo(root, path) and
result = AccessPath::getAReferenceTo(root, path)
)
or
result = getSourceObject().getALocalSource()
}
/**
* Gets a property read that accesses the corresponding property value in the source object.
*
* For example, gets `src[key]` in `for (var key in src) { src[key]; }`.
*/
PropRead getASourceProp() {
result = getASourceObjectRef().getAPropertyRead() and
result = AccessPath::getAnAliasedSourceNode(getSourceObject()).getAPropertyRead() and
result.getPropertyNameExpr().flow().getImmediatePredecessor*() = this
}
}
@@ -125,7 +113,7 @@ class EntriesEnumeratedPropName extends EnumeratedPropName {
* Holds if the properties of `node` are enumerated locally.
*/
predicate arePropertiesEnumerated(DataFlow::SourceNode node) {
node = any(EnumeratedPropName name).getASourceObjectRef()
node = AccessPath::getAnAliasedSourceNode(any(EnumeratedPropName name).getSourceObject())
}
/**

View File

@@ -0,0 +1,18 @@
/**
* @name Unresolvable imports
* @description The number of imports that could not be resolved to a module.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/unresolvable-imports
*/
import javascript
import CallGraphQuality
Import unresolvableImport() {
not exists(result.getImportedModule())
}
select projectRoot(), count(unresolvableImport())

View File

@@ -412,4 +412,17 @@ module AccessPath {
isAssignedInUniqueFile(name)
)
}
/**
* Gets a `SourceNode` that refers to the same value or access path as the given node.
*/
pragma[inline]
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
exists(DataFlow::SourceNode root, string accessPath |
node = AccessPath::getAReferenceTo(root, accessPath) and
result = AccessPath::getAReferenceTo(root, accessPath)
)
or
result = node.getALocalSource()
}
}

View File

@@ -148,6 +148,16 @@ abstract class Import extends ASTNode {
)
}
/**
* Gets the imported module, as determined by the TypeScript compiler, if any.
*/
private Module resolveFromTypeScriptSymbol() {
exists(CanonicalName symbol |
ast_node_symbol(this, symbol) and
ast_node_symbol(result, symbol)
)
}
/**
* Gets the module this import refers to.
*
@@ -162,7 +172,8 @@ abstract class Import extends ASTNode {
else (
result = resolveAsProvidedModule() or
result = resolveImportedPath() or
result = resolveFromTypeRoot()
result = resolveFromTypeRoot() or
result = resolveFromTypeScriptSymbol()
)
}

View File

@@ -84,10 +84,16 @@ File tryExtensions(Folder dir, string basename, int priority) {
File resolveMainModule(PackageJSON pkg, int priority) {
if exists(MainModulePath::of(pkg))
then
exists(Container c | c = MainModulePath::of(pkg).resolve() |
result = c and priority = 0
exists(PathExpr main | main = MainModulePath::of(pkg) |
result = main.resolve() and priority = 0
or
result = tryExtensions(c, "index", priority)
result = tryExtensions(main.resolve(), "index", priority)
or
not exists(main.resolve()) and
not exists(main.getExtension()) and
exists(int n | n = main.getNumComponent() |
result = tryExtensions(main.resolveUpTo(n-1), main.getComponent(n-1), priority)
)
)
else result = tryExtensions(pkg.getFile().getParentContainer(), "index", priority)
}

View File

@@ -165,10 +165,10 @@ module StringOps {
StartsWith_Substring() {
astNode.hasOperands(call.asExpr(), substring.asExpr()) and
(call.getMethodName() = "substring" or call.getMethodName() = "substr") and
(call.getMethodName() = "substring" or call.getMethodName() = "substr" or call.getMethodName() = "slice") and
call.getNumArgument() = 2 and
(
substring.getALocalSource().getAPropertyRead("length").flowsTo(call.getArgument(1))
AccessPath::getAnAliasedSourceNode(substring).getAPropertyRead("length").flowsTo(call.getArgument(1))
or
substring.getStringValue().length() = call.getArgument(1).asExpr().getIntValue()
)

View File

@@ -9,9 +9,9 @@ module Koa {
/**
* An expression that creates a new Koa application.
*/
class AppDefinition extends HTTP::Servers::StandardServerDefinition, NewExpr {
class AppDefinition extends HTTP::Servers::StandardServerDefinition, InvokeExpr {
AppDefinition() {
// `app = new Koa()`
// `app = new Koa()` / `app = Koa()`
this = DataFlow::moduleImport("koa").getAnInvocation().asExpr()
}
}
@@ -115,6 +115,26 @@ module Koa {
override RouteHandler getRouteHandler() { result = ctx.getRouteHandler() }
}
/**
* A Koa request source, accessed through the a request property of a
* generator route handler (deprecated in Koa 3).
*/
private class GeneratorRequestSource extends HTTP::Servers::RequestSource {
RouteHandler rh;
GeneratorRequestSource() {
exists(DataFlow::FunctionNode fun | fun = rh |
fun.getFunction().isGenerator() and
fun.getReceiver().getAPropertyRead("request") = this
)
}
/**
* Gets the route handler that provides this response.
*/
override RouteHandler getRouteHandler() { result = rh }
}
/**
* A Koa response source, that is, an access to the `response` property
* of a context object.

View File

@@ -17,7 +17,7 @@ abstract class LoggerCall extends DataFlow::CallNode {
/**
* Gets a log level name that is used in RFC5424, `npm`, `console`.
*/
private string getAStandardLoggerMethodName() {
string getAStandardLoggerMethodName() {
result = "crit" or
result = "debug" or
result = "error" or

View File

@@ -55,7 +55,7 @@ module ClientSideUrlRedirect {
// exclude `location.href.split('?')[0]`, which can never refer to the query string
not exists(PropAccess pacc | mce = pacc.getBase() | pacc.getPropertyName() = "0")
or
(methodName = "substring" or methodName = "substr") and
(methodName = "substring" or methodName = "substr" or methodName = "slice") and
// exclude `location.href.substring(0, ...)` and similar, which can
// never refer to the query string
not mce.getArgument(0).(NumberLiteral).getIntValue() = 0

View File

@@ -21,15 +21,6 @@ module DomBasedXss {
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or
exists(PropAccess pacc | pacc = node.asExpr() |
isSafeLocationProperty(pacc)
or
// `$(location.hash)` is a fairly common and safe idiom
// (because `location.hash` always starts with `#`),
// so we mark `hash` as safe for the purposes of this query
pacc.getPropertyName() = "hash"
)
or
node instanceof Sanitizer
}
}

View File

@@ -12,15 +12,33 @@ module ExceptionXss {
import Xss as Xss
private import semmle.javascript.dataflow.InferredTypes
/**
* Gets the name of a method that does not leak taint from its arguments if an exception is thrown by the method.
*/
private string getAnUnlikelyToThrowMethodName() {
result = "getElementById" or // document.getElementById
result = "indexOf" or // String.prototype.indexOf
result = "assign" or // Object.assign
result = "pick" or // _.pick
result = getAStandardLoggerMethodName() or // log.info etc.
result = "val" or // $.val
result = "parse" or // JSON.parse
result = "stringify" or // JSON.stringify
result = "test" or // RegExp.prototype.test
result = "setItem" or // localStorage.setItem
result = "existsSync" or
// the "fs" methods are a mix of "this is safe" and "you have bigger problems".
exists(ExternalMemberDecl decl | decl.hasQualifiedName("fs", result)) or
// Array methods are generally exception safe.
exists(ExternalMemberDecl decl | decl.hasQualifiedName("Array", result))
}
/**
* Holds if `node` is unlikely to cause an exception containing sensitive information to be thrown.
*/
private predicate isUnlikelyToThrowSensitiveInformation(DataFlow::Node node) {
node = any(DataFlow::CallNode call | call.getCalleeName() = "getElementById").getAnArgument()
or
node = any(DataFlow::CallNode call | call.getCalleeName() = "indexOf").getAnArgument()
or
node = any(DataFlow::CallNode call | call.getCalleeName() = "stringify").getAnArgument()
node = any(DataFlow::CallNode call | call.getCalleeName() = getAnUnlikelyToThrowMethodName())
.getAnArgument()
or
node = DataFlow::globalVarRef("console").getAMemberCall(_).getAnArgument()
}
@@ -38,6 +56,7 @@ module ExceptionXss {
*/
predicate canThrowSensitiveInformation(DataFlow::Node node) {
not isUnlikelyToThrowSensitiveInformation(node) and
not node instanceof Xss::Shared::Sink and // removes duplicates from js/xss.
(
// in the case of reflective calls the below ensures that both InvokeNodes have no known callee.
forex(DataFlow::InvokeNode call | call.getAnArgument() = node | not exists(call.getACallee()))
@@ -79,15 +98,15 @@ module ExceptionXss {
}
/**
* Get the parameter in the callback that contains an error.
* Gets the parameter in the callback that contains an error.
* In the current implementation this is always the first parameter.
*/
DataFlow::Node getErrorParam() { result = errorParameter }
}
/**
* Gets the error parameter for a callback that is supplied to the same call as `pred` is an argument to.
* For example: `outerCall(foo, <pred>, bar, (<result>, val) => { ... })`.
* Gets the error parameter for a callback that is supplied to the same call as `pred` is an argument to.
* For example: `outerCall(foo, <pred>, bar, (<result>, val) => { ... })`.
*/
DataFlow::Node getCallbackErrorParam(DataFlow::Node pred) {
exists(DataFlow::CallNode call, Callback callback |
@@ -101,8 +120,8 @@ module ExceptionXss {
/**
* Gets the data-flow node to which any exceptions thrown by
* this expression will propagate.
* This predicate adds, on top of `Expr::getExceptionTarget`, exceptions
* propagated by callbacks.
* This predicate adds, on top of `Expr::getExceptionTarget`, exceptions
* propagated by callbacks.
*/
private DataFlow::Node getExceptionTarget(DataFlow::Node pred) {
result = pred.asExpr().getExceptionTarget()

View File

@@ -259,6 +259,24 @@ module DomBasedXss {
}
}
/**
* A property read from a safe property is considered a sanitizer.
*/
class SafePropertyReadSanitizer extends Sanitizer, DataFlow::Node {
SafePropertyReadSanitizer() {
exists(PropAccess pacc | pacc = this.asExpr() |
isSafeLocationProperty(pacc)
or
// `$(location.hash)` is a fairly common and safe idiom
// (because `location.hash` always starts with `#`),
// so we mark `hash` as safe for the purposes of this query
pacc.getPropertyName() = "hash"
or
pacc.getPropertyName() = "length"
)
}
}
/**
* A regexp replacement involving an HTML meta-character, viewed as a sanitizer for
* XSS vulnerabilities.

View File

@@ -688,7 +688,7 @@ case @symbol.kind of
;
@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype;
@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr;
@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr | @importdeclaration | @externalmodulereference;
ast_node_symbol(
unique int node: @ast_node_with_symbol ref,

View File

@@ -1,6 +1,7 @@
| b | src/node_modules/b/lib/index.js:1:1:2:0 | <toplevel> |
| b | src/node_modules/b/lib/index.ts:1:1:2:0 | <toplevel> |
| c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| test-package | src/index.js:1:1:4:0 | <toplevel> |
| test-package | src/lib/tst2.js:1:1:1:14 | <toplevel> |
| test-package | src/lib/tst.js:1:1:4:0 | <toplevel> |

View File

@@ -1,4 +1,5 @@
| b | src/node_modules/b/lib/index.ts:1:1:2:0 | <toplevel> |
| c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| test-package | src/index.js:1:1:4:0 | <toplevel> |
| third-party-module | src/node_modules/third-party-module/fancy.js:1:1:4:0 | <toplevel> |

View File

@@ -1,4 +1,5 @@
| src/node_modules/b/package.json:1:1:4:1 | {\\n "na ... "lib"\\n} |
| src/node_modules/c/package.json:1:1:4:1 | {\\n "na ... src/"\\n} |
| src/node_modules/d/package.json:1:1:4:1 | {\\n "na ... main"\\n} |
| src/node_modules/third-party-module/package.json:1:1:5:1 | {\\n "na ... y.js"\\n} |
| src/package.json:1:1:18:1 | {\\n "na ... "\\n }\\n} |

View File

@@ -0,0 +1 @@
export default "d";

View File

@@ -0,0 +1,4 @@
{
"name": "d",
"main": "main"
}

View File

@@ -14,3 +14,5 @@
| tst.js:19:9:19:36 | A.subst ... "web/" | tst.js:19:9:19:9 | A | tst.js:19:31:19:36 | "web/" | true |
| tst.js:32:9:32:32 | strings ... h(A, B) | tst.js:32:28:32:28 | A | tst.js:32:31:32:31 | B | true |
| tst.js:33:9:33:47 | strings ... h(A, B) | tst.js:33:43:33:43 | A | tst.js:33:46:33:46 | B | true |
| tst.js:34:9:34:34 | A.slice ... ) !== B | tst.js:34:9:34:9 | A | tst.js:34:34:34:34 | B | false |
| tst.js:35:9:35:42 | A.slice ... = B.foo | tst.js:35:9:35:9 | A | tst.js:35:38:35:42 | B.foo | false |

View File

@@ -31,4 +31,6 @@ function f(A, B) {
if (strings.startsWith(A, B)) {}
if (strings.caseInsensitiveStartsWith(A, B)) {}
if (A.slice(0, B.length) !== B) {}
if (A.slice(0, B.foo.length) !== B.foo) {}
}

View File

@@ -0,0 +1,11 @@
symbols
| src/lib/foo.ts:1:1:4:0 | <toplevel> | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
| src/lib/foo.ts:1:8:3:1 | functio ... 123;\\n} | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
| test/test_foo.ts:1:1:1:28 | import ... @/foo"; | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
| test/test_foo.ts:1:1:7:0 | <toplevel> | library-tests/TypeScript/PathMapping/test/test_foo.ts |
| test/test_foo.ts:2:17:2:32 | require("@/foo") | library-tests/TypeScript/PathMapping/src/lib/foo.ts |
| test/test_foo.ts:4:1:4:5 | foo() | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
| test/test_foo.ts:6:1:6:12 | foolib.foo() | foo in library-tests/TypeScript/PathMapping/src/lib/foo.ts |
#select
| test/test_foo.ts:1:1:1:28 | import ... @/foo"; | src/lib/foo.ts:1:1:4:0 | <toplevel> |
| test/test_foo.ts:2:17:2:32 | require("@/foo") | src/lib/foo.ts:1:1:4:0 | <toplevel> |

View File

@@ -0,0 +1,8 @@
import javascript
query predicate symbols(ASTNode astNode, CanonicalName symbol) {
ast_node_symbol(astNode, symbol)
}
from Import imprt
select imprt, imprt.getImportedModule()

View File

@@ -0,0 +1,3 @@
export function foo() {
return 123;
}

View File

@@ -0,0 +1,6 @@
import { foo } from "@/foo";
import foolib = require("@/foo");
foo();
foolib.foo();

View File

@@ -0,0 +1,9 @@
{
"include": ["."],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/lib/*"]
}
}
}

View File

@@ -54,3 +54,8 @@ app2.use(async ctx => {
var headers = ctx.headers;
headers.foo;
});
var app3 = Koa();
app3.use(function*(){
this.request.url;
});

View File

@@ -3,6 +3,7 @@ test_RouteSetup
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) |
| src/koa.js:59:1:61:2 | app3.us ... url;\\n}) |
test_RequestInputAccess
| src/koa.js:19:3:19:18 | ctx.request.body | body | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:20:3:20:23 | ctx.req ... ery.foo | parameter | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -24,6 +25,7 @@ test_RequestInputAccess
| src/koa.js:49:2:49:14 | cookies.get() | cookie | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:52:2:52:10 | query.foo | parameter | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:55:2:55:12 | headers.foo | header | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:60:2:60:17 | this.request.url | url | src/koa.js:59:10:61:1 | functio ... .url;\\n} |
test_RouteHandler_getAResponseHeader
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header1 | src/koa.js:11:3:11:25 | this.se ... 1', '') |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header2 | src/koa.js:12:3:12:37 | this.re ... 2', '') |
@@ -75,6 +77,7 @@ test_RouteHandler_getAContextExpr
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:48:16:48:18 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:51:14:51:16 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:54:16:54:18 | ctx |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:5 | this |
test_HeaderDefinition
| src/koa.js:11:3:11:25 | this.se ... 1', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:12:3:12:37 | this.re ... 2', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -87,6 +90,7 @@ test_RouteSetup_getServer
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:59:1:61:2 | app3.us ... url;\\n}) | src/koa.js:58:12:58:16 | Koa() |
test_HeaderDefinition_getAHeaderName
| src/koa.js:11:3:11:25 | this.se ... 1', '') | header1 |
| src/koa.js:12:3:12:37 | this.re ... 2', '') | header2 |
@@ -116,14 +120,17 @@ test_RouteSetup_getARouteHandler
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:59:1:61:2 | app3.us ... url;\\n}) | src/koa.js:59:10:61:1 | functio ... .url;\\n} |
test_AppDefinition
| src/koa.js:2:12:2:33 | new (re ... oa'))() |
| src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:58:12:58:16 | Koa() |
test_RouteHandler
| src/koa.js:7:1:7:22 | functio ... r1() {} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:58:12:58:16 | Koa() |
test_RequestExpr
| src/koa.js:19:3:19:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:20:3:20:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -133,6 +140,7 @@ test_RequestExpr
| src/koa.js:24:3:24:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:25:3:25:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:26:3:26:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:60:2:60:13 | this.request | src/koa.js:59:10:61:1 | functio ... .url;\\n} |
test_RouteHandler_getARequestExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:19:3:19:13 | ctx.request |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:20:3:20:13 | ctx.request |
@@ -142,6 +150,7 @@ test_RouteHandler_getARequestExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:24:3:24:13 | ctx.request |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:25:3:25:13 | ctx.request |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:13 | ctx.request |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:13 | this.request |
test_ContextExpr
| src/koa.js:11:3:11:6 | this | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:12:3:12:6 | this | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -174,6 +183,7 @@ test_ContextExpr
| src/koa.js:48:16:48:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:51:14:51:16 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:54:16:54:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:60:2:60:5 | this | src/koa.js:59:10:61:1 | functio ... .url;\\n} |
test_RedirectInvocation
| src/koa.js:43:2:43:18 | ctx.redirect(url) | src/koa.js:43:15:43:17 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:44:2:44:27 | ctx.res ... ct(url) | src/koa.js:44:24:44:26 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |

View File

@@ -1145,6 +1145,34 @@ nodes
| normalizedPaths.js:228:21:228:24 | path |
| normalizedPaths.js:228:21:228:24 | path |
| normalizedPaths.js:228:21:228:24 | path |
| normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path |
| normalizedPaths.js:236:33:236:46 | req.query.path |
| normalizedPaths.js:236:33:236:46 | req.query.path |
| normalizedPaths.js:236:33:236:46 | req.query.path |
| normalizedPaths.js:236:33:236:46 | req.query.path |
| normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:250:21:250:24 | path |
| tainted-require.js:7:19:7:37 | req.param("module") |
| tainted-require.js:7:19:7:37 | req.param("module") |
| tainted-require.js:7:19:7:37 | req.param("module") |
@@ -2903,6 +2931,42 @@ edges
| normalizedPaths.js:226:35:226:48 | req.query.path | normalizedPaths.js:226:14:226:49 | pathMod ... y.path) |
| normalizedPaths.js:226:35:226:48 | req.query.path | normalizedPaths.js:226:14:226:49 | pathMod ... y.path) |
| normalizedPaths.js:226:35:226:48 | req.query.path | normalizedPaths.js:226:14:226:49 | pathMod ... y.path) |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:238:19:238:22 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:245:21:245:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:7:236:47 | path | normalizedPaths.js:250:21:250:24 | path |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) | normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) | normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) | normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:14:236:47 | pathMod ... y.path) | normalizedPaths.js:236:7:236:47 | path |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
| tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") |
| tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") |
| tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") |
@@ -3016,6 +3080,9 @@ edges
| normalizedPaths.js:210:21:210:34 | normalizedPath | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:210:21:210:34 | normalizedPath | This path depends on $@. | normalizedPaths.js:174:14:174:27 | req.query.path | a user-provided value |
| normalizedPaths.js:222:21:222:24 | path | normalizedPaths.js:214:35:214:48 | req.query.path | normalizedPaths.js:222:21:222:24 | path | This path depends on $@. | normalizedPaths.js:214:35:214:48 | req.query.path | a user-provided value |
| normalizedPaths.js:228:21:228:24 | path | normalizedPaths.js:226:35:226:48 | req.query.path | normalizedPaths.js:228:21:228:24 | path | This path depends on $@. | normalizedPaths.js:226:35:226:48 | req.query.path | a user-provided value |
| normalizedPaths.js:238:19:238:22 | path | normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:238:19:238:22 | path | This path depends on $@. | normalizedPaths.js:236:33:236:46 | req.query.path | a user-provided value |
| normalizedPaths.js:245:21:245:24 | path | normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:245:21:245:24 | path | This path depends on $@. | normalizedPaths.js:236:33:236:46 | req.query.path | a user-provided value |
| normalizedPaths.js:250:21:250:24 | path | normalizedPaths.js:236:33:236:46 | req.query.path | normalizedPaths.js:250:21:250:24 | path | This path depends on $@. | normalizedPaths.js:236:33:236:46 | req.query.path | a user-provided value |
| tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | This path depends on $@. | tainted-require.js:7:19:7:37 | req.param("module") | a user-provided value |
| tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | a user-provided value |
| tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | a user-provided value |

View File

@@ -231,3 +231,21 @@ app.get('/replace', (req, res) => {
fs.readFileSync(path); // OK
}
});
app.get('/resolve-path', (req, res) => {
let path = pathModule.resolve(req.query.path);
fs.readFileSync(path); // NOT OK
var self = something();
if (path.substring(0, self.dir.length) === self.dir)
fs.readFileSync(path); // OK
else
fs.readFileSync(path); // NOT OK - wrong polarity
if (path.slice(0, self.dir.length) === self.dir)
fs.readFileSync(path); // OK
else
fs.readFileSync(path); // NOT OK - wrong polarity
});

View File

@@ -9,6 +9,9 @@ nodes
| etherpad.js:9:16:9:53 | req.que ... e + ")" |
| etherpad.js:11:12:11:19 | response |
| etherpad.js:11:12:11:19 | response |
| exception-xss.js:190:12:190:24 | req.params.id |
| exception-xss.js:190:12:190:24 | req.params.id |
| exception-xss.js:190:12:190:24 | req.params.id |
| formatting.js:4:9:4:29 | evil |
| formatting.js:4:16:4:29 | req.query.evil |
| formatting.js:4:16:4:29 | req.query.evil |
@@ -77,6 +80,7 @@ edges
| etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:9:16:9:53 | req.que ... e + ")" |
| etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:9:16:9:53 | req.que ... e + ")" |
| etherpad.js:9:16:9:53 | req.que ... e + ")" | etherpad.js:9:5:9:53 | response |
| exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id |
| formatting.js:4:9:4:29 | evil | formatting.js:6:43:6:46 | evil |
| formatting.js:4:9:4:29 | evil | formatting.js:7:49:7:52 | evil |
| formatting.js:4:16:4:29 | req.query.evil | formatting.js:4:9:4:29 | evil |
@@ -131,6 +135,7 @@ edges
#select
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
| etherpad.js:11:12:11:19 | response | etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:11:12:11:19 | response | Cross-site scripting vulnerability due to $@. | etherpad.js:9:16:9:30 | req.query.jsonp | user-provided value |
| exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id | Cross-site scripting vulnerability due to $@. | exception-xss.js:190:12:190:24 | req.params.id | user-provided value |
| formatting.js:6:14:6:47 | util.fo ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| formatting.js:7:14:7:53 | require ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| partial.js:10:14:10:18 | x + y | partial.js:13:42:13:48 | req.url | partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value |

View File

@@ -1,4 +1,5 @@
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
| exception-xss.js:190:12:190:24 | req.params.id | Cross-site scripting vulnerability due to $@. | exception-xss.js:190:12:190:24 | req.params.id | user-provided value |
| formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value |

View File

@@ -183,4 +183,34 @@ app.get('/user/:id', function (req, res) {
}
$('myId').html(res); // NOT OK!
});
});
});
app.get('/user/:id', function (req, res) {
try {
res.send(req.params.id);
} catch(err) {
res.send(err); // OK (the above `res.send()` is already reported by js/xss)
}
});
var fs = require("fs");
(function () {
var foo = document.location.search;
try {
// A series of functions does not throw tainted exceptions.
Object.assign(foo, foo)
_.pick(foo, foo);
[foo, foo].join(join);
$.val(foo);
JSON.parse(foo);
/bla/.test(foo);
console.log(foo);
log.info(foo);
localStorage.setItem(foo);
} catch (e) {
$('myId').html(e); // OK
}
})();

View File

@@ -318,3 +318,10 @@ function basicExceptions() {
function handlebarsSafeString() {
return new Handlebars.SafeString(location); // NOT OK!
}
function test2() {
var target = document.location.search
// OK
$('myId').html(target.length)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: add TypeScript symbols to import declarations
compatibility: backwards

View File

@@ -50,8 +50,16 @@ class EssaVariable extends TEssaDefinition {
* Note that this differs from `EssaVariable.getAUse()`.
*/
ControlFlowNode getASourceUse() {
exists(SsaSourceVariable var |
result = use_for_var(var) and
result = var.getASourceUse()
)
}
pragma[nomagic]
private ControlFlowNode use_for_var(SsaSourceVariable var) {
result = this.getAUse() and
result = this.getSourceVariable().getASourceUse()
var = this.getSourceVariable()
}
/** Gets the scope of this variable. */
@@ -268,11 +276,16 @@ class PhiFunction extends EssaDefinition, TPhiFunction {
not exists(this.inputEdgeRefinement(result))
}
pragma[noinline]
private SsaSourceVariable pred_var(BasicBlock pred) {
result = this.getSourceVariable() and
pred = this.nonPiInput()
}
/** Gets another definition of the same source variable that reaches this definition. */
private EssaDefinition reachingDefinition(BasicBlock pred) {
result.getScope() = this.getScope() and
result.getSourceVariable() = this.getSourceVariable() and
pred = this.nonPiInput() and
result.getSourceVariable() = pred_var(pred) and
result.reachesEndOfBlock(pred)
}

View File

@@ -284,7 +284,7 @@ cached module PointsToInternal {
ssa_definition_points_to(var.getDefinition(), context, value, origin)
or
exists(EssaVariable prev |
ssaShortCut(prev, var) and
ssaShortCut+(prev, var) and
variablePointsTo(prev, context, value, origin)
)
}
@@ -305,10 +305,6 @@ cached module PointsToInternal {
start = def.getInput() and
end.getDefinition() = def
)
or
exists(EssaVariable mid |
ssaShortCut(start, mid) and ssaShortCut(mid, end)
)
}
pragma [noinline]
@@ -578,6 +574,13 @@ cached module PointsToInternal {
)
or
/* Undefined variable */
undefined_variable(def, context, value, origin)
or
/* Builtin not defined in outer scope */
builtin_not_in_outer_scope(def, context, value, origin)
}
private predicate undefined_variable(ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(Scope scope |
not def.getVariable().getName() = "__name__" and
not def.getVariable().isMetaVariable() and
@@ -587,8 +590,9 @@ cached module PointsToInternal {
def.getSourceVariable() instanceof LocalVariable and (context.isImport() or context.isRuntime() or context.isMain())
) and
value = ObjectInternal::undefined() and origin = def.getDefiningNode()
or
/* Builtin not defined in outer scope */
}
private predicate builtin_not_in_outer_scope(ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(Module mod, GlobalVariable var |
var = def.getSourceVariable() and
mod = def.getScope().getEnclosingModule() and
@@ -1113,8 +1117,17 @@ module InterProceduralPointsTo {
* Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. */
pragma [noinline]
private predicate callsite_entry_value_transfer(EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, PointsToContext callee) {
entry_def.getSourceVariable() = caller_var.getSourceVariable() and
callsite_calls_function(caller_var.getAUse(), caller, entry_def.getScope(), callee, _)
exists(ControlFlowNode use, SsaSourceVariable var |
var_and_use(caller_var, use, var) and
entry_def.getSourceVariable() = var and
callsite_calls_function(use, caller, entry_def.getScope(), callee, _)
)
}
pragma[nomagic]
private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) {
use = caller_var.getAUse() and
var = caller_var.getSourceVariable()
}
/** Helper for `scope_entry_value_transfer`. */