Merge branch 'master' into objectapi-to-valueapi-expectedmappingforformatstring

This commit is contained in:
Rebecca Valentine
2020-02-18 21:33:12 -08:00
committed by GitHub
353 changed files with 18730 additions and 14708 deletions

View File

@@ -2,5 +2,4 @@
"*/ql/test/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml",
"codeql/.codeqlmanifest.json" ] }
"misc/suite-helpers/qlpack.yml" ] }

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
# editor and OS artifacts
*~
.DS_STORE
*.swp
# query compilation caches
.cache

View File

@@ -27,10 +27,10 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
## Changes to libraries
* The data-flow library has been improved when flow through functions needs to be
combined with both taint tracking and flow through fields allowing more flow
to be tracked. This affects and improves some security queries, which may
report additional results.
* The data-flow library has been improved, which affects and improves some security queries. The improvements are:
- Track flow through functions that combine taint tracking with flow through fields.
- Track flow through clone-like functions, that is, functions that read contents of a field from a
parameter and stores the value in the field of a returned object.
* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
* The new class `StackVariable` should be used in place of `LocalScopeVariable`

View File

@@ -33,10 +33,10 @@ The following changes in version 1.24 affect C# analysis in all applications.
## Changes to libraries
* The data-flow library has been improved when flow through methods needs to be
combined with both taint tracking and flow through fields allowing more flow
to be tracked. This affects and improves most security queries, which may
report additional results.
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
- Track flow through methods that combine taint tracking with flow through fields.
- Track flow through clone-like methods, that is, methods that read contents of a field from a
parameter and stores the value in the field of a returned object.
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.

View File

@@ -26,10 +26,10 @@ The following changes in version 1.24 affect Java analysis in all applications.
## Changes to libraries
* The data-flow library has been improved when flow through methods needs to be
combined with both taint tracking and flow through fields allowing more flow
to be tracked. This affects and improves most security queries, which may
report additional results.
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
- Track flow through methods that combine taint tracking with flow through fields.
- Track flow through clone-like methods, that is, methods that read contents of a field from a
parameter and stores the value in the field of a returned object.
* Identification of test classes has been improved. Previously, one of the
match conditions would classify any class with a name containing the string
"Test" as a test class, but now this matching has been replaced with one that

View File

@@ -9,22 +9,26 @@
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
* 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)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [Handlebars](https://www.npmjs.com/package/handlebars)
- [Electron](https://electronjs.org/)
- [Handlebars](https://www.npmjs.com/package/handlebars)
- [Koa](https://www.npmjs.com/package/koa)
- [Node.js](https://nodejs.org/)
- [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)
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
- [for-in](https://www.npmjs.com/package/for-in)
- [for-own](https://www.npmjs.com/package/for-own)
- [http2](https://nodejs.org/api/http2.html)
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
- [react](https://www.npmjs.com/package/react)
- [send](https://www.npmjs.com/package/send)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [ws](https://github.com/websockets/ws)
## New queries
@@ -34,6 +38,7 @@
| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
| Missing await (`js/missing-await`) | correctness | Highlights expressions that operate directly on a promise object in a nonsensical way, instead of awaiting its result. Results are shown on LGTM by default. |
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive copying operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
## Changes to existing queries
@@ -47,6 +52,7 @@
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
## Changes to libraries

View File

@@ -0,0 +1,37 @@
# Improvements to Python analysis
The following changes in version 1.24 affect Python analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
### Web framework support
The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have
been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
- py/path-injection
- py/command-line-injection
- py/reflective-xss
- py/sql-injection
- py/code-injection
- py/unsafe-deserialization
- py/url-redirection
The QL-library support for the web framework Twisted have been fixed so they provide a proper
HttpResponseTaintSink, instead of a TaintSink. This will enable results for the following
queries:
- py/reflective-xss
- py/stack-trace-exposure
## Changes to libraries

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -301,3 +301,5 @@ class DataFlowCall extends Expr {
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }

View File

@@ -335,6 +335,12 @@ 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
// Taint postfix and prefix crement operations when their operand is tainted.
result.(CrementOperation).getAnOperand() = sink.asExpr()
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
}
predicate tainted(Expr source, Element tainted) {

View File

@@ -210,3 +210,5 @@ class DataFlowCall extends CallInstruction {
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }

View File

@@ -3,9 +3,12 @@
*/
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.models.interfaces.DataFlow
private newtype TIRDataFlowNode =
@@ -300,10 +303,12 @@ ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
VariableNode variableNode(Variable v) { result.getVariable() = v }
/**
* DEPRECATED: See UninitializedNode.
*
* Gets the `Node` corresponding to the value of an uninitialized local
* variable `v`.
*/
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
Node uninitializedNode(LocalVariable v) { none() }
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local

View File

@@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

View File

@@ -2,10 +2,10 @@ private import ValueNumberingImports
private import cpp
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
@@ -19,19 +19,18 @@ newtype TValueNumber =
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
TValueNumber rightOperand
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
@@ -99,28 +98,49 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
@@ -139,42 +159,40 @@ private predicate fieldAddressValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getUnary()) = operand
}
@@ -194,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
@@ -206,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
@@ -236,12 +258,12 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(IRVariable var |
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
@@ -249,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
@@ -264,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
@@ -281,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |

View File

@@ -96,7 +96,7 @@ private module Cached {
}
cached
Instruction getMemoryOperandDefinition(
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
@@ -142,6 +142,19 @@ private module Cached {
overlap instanceof MustExactlyOverlap
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
not (
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.

View File

@@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

View File

@@ -2,10 +2,10 @@ private import ValueNumberingImports
private import cpp
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
@@ -19,19 +19,18 @@ newtype TValueNumber =
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
TValueNumber rightOperand
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
@@ -99,28 +98,49 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
@@ -139,42 +159,40 @@ private predicate fieldAddressValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getUnary()) = operand
}
@@ -194,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
@@ -206,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
@@ -236,12 +258,12 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(IRVariable var |
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
@@ -249,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
@@ -264,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
@@ -281,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |

View File

@@ -102,6 +102,19 @@ private module Cached {
result = getMemoryOperandDefinition(instr, _, _)
}
/**
* Gets a non-phi instruction that defines an operand of `instr` but only if
* both `instr` and the result have neighbor on the other side of the edge
* between them. This is a necessary condition for being in a cycle, and it
* removes about two thirds of the tuples that would otherwise be in this
* predicate.
*/
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
result = getNonPhiOperandDef(instr) and
exists(getNonPhiOperandDef(result)) and
instr = getNonPhiOperandDef(_)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
@@ -115,7 +128,7 @@ private module Cached {
cached
predicate isInCycle(Instruction instr) {
instr instanceof Instruction and
getNonPhiOperandDef+(instr) = instr
getNonPhiOperandDefOfIntermediate+(instr) = instr
}
cached

View File

@@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

View File

@@ -2,10 +2,10 @@ private import ValueNumberingImports
private import cpp
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
@@ -19,19 +19,18 @@ newtype TValueNumber =
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
TValueNumber rightOperand
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
@@ -99,28 +98,49 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
@@ -139,42 +159,40 @@ private predicate fieldAddressValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
tvalueNumber(instr.getUnary()) = operand
}
@@ -194,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
@@ -206,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
@@ -236,12 +258,12 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(IRVariable var |
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
@@ -249,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
@@ -264,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
@@ -281,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |

View File

@@ -96,7 +96,7 @@ private module Cached {
}
cached
Instruction getMemoryOperandDefinition(
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
@@ -142,6 +142,19 @@ private module Cached {
overlap instanceof MustExactlyOverlap
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
not (
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.

View File

@@ -97,6 +97,8 @@ class GVN extends TValueNumber {
or
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
or
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
or
this instanceof TUniqueValueNumber and result = "Unique"
}

View File

@@ -2,4 +2,4 @@
* Support for tracking tainted data through the program.
*/
import TaintTrackingImpl
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking

View File

@@ -1 +1 @@
import GlobalValueNumberingImpl
import semmle.code.cpp.ir.internal.ASTValueNumbering

View File

@@ -15,39 +15,27 @@
| 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:14:40:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | envStrGlobal |
| 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:45:13:45:24 | envStrGlobal | 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:50:15:50:24 | envStr_ptr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | |
| 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 | ! ... | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | envStrGlobal |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | envStrGlobal |
| 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:25:64:32 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | |
| 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:67:7:67:13 | copying | |
| 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:69:10:69:13 | copy | |
| 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:12:70:15 | copy | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | |
| 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 | |

View File

@@ -1,4 +1,4 @@
import semmle.code.cpp.security.TaintTracking as AST
import semmle.code.cpp.security.TaintTrackingImpl as AST
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp

View File

@@ -1,40 +1,42 @@
| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 |
| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 |
| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 7:c7-c7 |
| test.cpp:5:12:5:13 | p1 | 5:c12-c13 6:c12-c13 |
| test.cpp:16:3:16:3 | x | 16:c3-c3 17:c3-c3 |
| test.cpp:16:7:16:8 | p0 | 16:c7-c8 17:c7-c8 |
| test.cpp:16:7:16:13 | ... + ... | 16:c7-c13 17:c7-c13 |
| test.cpp:16:7:16:24 | ... + ... | 16:c7-c24 17:c7-c24 18:c7-c7 |
| test.cpp:16:12:16:13 | p1 | 16:c12-c13 17:c12-c13 |
| test.cpp:16:17:16:24 | global01 | 16:c17-c24 17:c17-c24 |
| test.cpp:29:7:29:8 | p0 | 29:c7-c8 31:c7-c8 |
| test.cpp:29:7:29:13 | ... + ... | 29:c7-c13 31:c7-c13 |
| test.cpp:29:12:29:13 | p1 | 29:c12-c13 31:c12-c13 |
| test.cpp:31:7:31:24 | ... + ... | 31:c7-c24 32:c7-c7 |
| test.cpp:43:7:43:8 | p0 | 43:c7-c8 45:c7-c8 |
| test.cpp:43:7:43:13 | ... + ... | 43:c7-c13 45:c7-c13 |
| test.cpp:43:12:43:13 | p1 | 43:c12-c13 45:c12-c13 |
| test.cpp:44:9:44:9 | 0 | 44:c9-c9 51:c25-c25 53:c18-c21 56:c39-c42 59:c17-c20 88:c12-c12 |
| test.cpp:45:7:45:24 | ... + ... | 45:c7-c24 46:c7-c7 |
| test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 |
| test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 |
| test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 |
| test.cpp:53:18:53:21 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 |
| test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:56:14:56:16 | ptr | 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 |
| test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 |
| test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 79:c7-c7 |
| test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 |
| test.cpp:105:11:105:12 | (Base *)... | 105:c11-c12 106:c14-c35 107:c11-c12 |
| test.cpp:105:11:105:12 | pd | 105:c11-c12 106:c33-c34 |
| test.cpp:105:15:105:15 | b | 105:c15-c15 107:c15-c15 109:c10-c10 |
| test.cpp:125:11:125:12 | pa | 125:c11-c12 126:c11-c12 128:c3-c4 129:c11-c12 |
| test.cpp:125:15:125:15 | x | 125:c15-c15 126:c15-c15 128:c7-c7 |
| test.cpp:136:11:136:18 | global_a | 136:c11-c18 137:c11-c18 139:c3-c10 |
| test.cpp:136:21:136:21 | x | 136:c21-c21 137:c21-c21 139:c13-c13 |
| test.cpp:144:11:144:12 | pa | 144:c11-c12 145:c11-c12 147:c3-c4 149:c11-c12 |
| test.cpp:145:15:145:15 | y | 145:c15-c15 147:c7-c7 |
| test.cpp:153:11:153:18 | global_a | 153:c11-c18 154:c11-c18 156:c3-c10 |
| test.cpp:153:21:153:21 | x | 153:c21-c21 154:c21-c21 |
| test.cpp:5:3:5:3 | GVN | 5:c3-c3 6:c3-c3 |
| test.cpp:5:7:5:8 | GVN | 5:c7-c8 6:c7-c8 |
| test.cpp:5:7:5:13 | GVN | 5:c7-c13 6:c7-c13 7:c7-c7 |
| test.cpp:5:12:5:13 | GVN | 5:c12-c13 6:c12-c13 |
| test.cpp:16:3:16:3 | GVN | 16:c3-c3 17:c3-c3 |
| test.cpp:16:7:16:8 | GVN | 16:c7-c8 17:c7-c8 |
| test.cpp:16:7:16:13 | GVN | 16:c7-c13 17:c7-c13 |
| test.cpp:16:7:16:24 | GVN | 16:c7-c24 17:c7-c24 18:c7-c7 |
| test.cpp:16:12:16:13 | GVN | 16:c12-c13 17:c12-c13 |
| test.cpp:16:17:16:24 | GVN | 16:c17-c24 17:c17-c24 |
| test.cpp:29:3:29:3 | GVN | 29:c3-c3 31:c3-c3 |
| test.cpp:29:7:29:8 | GVN | 29:c7-c8 31:c7-c8 |
| test.cpp:29:7:29:13 | GVN | 29:c7-c13 31:c7-c13 |
| test.cpp:29:12:29:13 | GVN | 29:c12-c13 31:c12-c13 |
| test.cpp:31:7:31:24 | GVN | 31:c7-c24 32:c7-c7 |
| test.cpp:43:3:43:3 | GVN | 43:c3-c3 45:c3-c3 |
| test.cpp:43:7:43:8 | GVN | 43:c7-c8 45:c7-c8 |
| test.cpp:43:7:43:13 | GVN | 43:c7-c13 45:c7-c13 |
| test.cpp:43:7:43:24 | GVN | 43:c7-c24 45:c7-c24 46:c7-c7 |
| test.cpp:43:12:43:13 | GVN | 43:c12-c13 45:c12-c13 |
| test.cpp:43:17:43:24 | GVN | 43:c17-c24 45:c17-c24 |
| test.cpp:44:3:44:5 | GVN | 44:c3-c5 44:c4-c5 |
| test.cpp:53:10:53:13 | GVN | 53:c10-c13 56:c21-c24 |
| test.cpp:53:10:53:13 | GVN | 53:c10-c13 56:c21-c24 |
| test.cpp:53:11:53:13 | GVN | 53:c11-c13 56:c22-c24 |
| test.cpp:53:18:53:21 | GVN | 53:c18-c21 56:c39-c42 59:c17-c20 |
| test.cpp:56:14:56:16 | GVN | 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 |
| test.cpp:62:5:62:10 | GVN | 62:c5-c10 65:c10-c15 |
| test.cpp:77:20:77:28 | GVN | 77:c20-c28 79:c7-c7 |
| test.cpp:79:11:79:14 | GVN | 79:c11-c14 79:c24-c27 |
| test.cpp:92:11:92:16 | GVN | 92:c11-c16 92:c15-c16 93:c10-c10 |
| test.cpp:105:11:105:12 | GVN | 105:c11-c12 106:c33-c34 |
| test.cpp:105:11:105:12 | GVN | 105:c11-c12 106:c33-c34 107:c11-c12 |
| test.cpp:105:15:105:15 | GVN | 105:c15-c15 107:c15-c15 109:c10-c10 |
| test.cpp:113:3:113:5 | GVN | 113:c3-c5 115:c3-c5 |
| test.cpp:125:11:125:12 | GVN | 125:c11-c12 126:c11-c12 128:c3-c4 129:c11-c12 |
| test.cpp:125:15:125:15 | GVN | 125:c15-c15 126:c15-c15 |
| test.cpp:128:11:128:11 | GVN | 128:c11-c11 129:c15-c15 |
| test.cpp:136:11:136:18 | GVN | 136:c11-c18 137:c11-c18 139:c3-c10 |
| test.cpp:144:11:144:12 | GVN | 144:c11-c12 145:c11-c12 147:c3-c4 149:c11-c12 |
| test.cpp:144:15:144:15 | GVN | 144:c15-c15 149:c15-c15 |
| test.cpp:153:11:153:18 | GVN | 153:c11-c18 154:c11-c18 156:c3-c10 |

View File

@@ -0,0 +1,58 @@
| test.cpp:5:3:5:13 | ... = ... | |
| test.cpp:6:3:6:13 | ... = ... | |
| test.cpp:7:3:7:7 | ... = ... | |
| test.cpp:10:16:10:16 | 1 | |
| test.cpp:16:3:16:24 | ... = ... | |
| test.cpp:17:3:17:24 | ... = ... | |
| test.cpp:18:3:18:7 | ... = ... | |
| test.cpp:21:16:21:16 | 2 | |
| test.cpp:29:3:29:24 | ... = ... | |
| test.cpp:30:3:30:17 | call to change_global02 | |
| test.cpp:31:3:31:24 | ... = ... | |
| test.cpp:32:3:32:7 | ... = ... | |
| test.cpp:35:16:35:16 | 3 | |
| test.cpp:43:3:43:24 | ... = ... | |
| test.cpp:44:3:44:9 | ... = ... | |
| test.cpp:45:3:45:24 | ... = ... | |
| test.cpp:46:3:46:7 | ... = ... | |
| test.cpp:51:25:51:25 | (unsigned int)... | |
| test.cpp:53:10:53:13 | (int)... | |
| test.cpp:53:10:53:13 | * ... | LoadTotalOverlap, Unary |
| test.cpp:53:18:53:21 | (int)... | |
| test.cpp:55:5:55:15 | ... = ... | |
| test.cpp:56:12:56:25 | (...) | |
| test.cpp:56:12:56:43 | ... && ... | |
| test.cpp:56:13:56:16 | (int)... | |
| test.cpp:56:13:56:16 | * ... | Unary, Unique |
| test.cpp:56:21:56:24 | (int)... | |
| test.cpp:56:21:56:24 | * ... | LoadTotalOverlap, Unary |
| test.cpp:56:30:56:43 | (...) | |
| test.cpp:56:31:56:34 | (int)... | |
| test.cpp:56:31:56:34 | * ... | Unary, Unique |
| test.cpp:56:39:56:42 | (int)... | |
| test.cpp:56:47:56:51 | ... ++ | |
| test.cpp:59:9:59:12 | (int)... | |
| test.cpp:59:9:59:12 | * ... | Unary, Unique |
| test.cpp:59:17:59:20 | (int)... | |
| test.cpp:62:5:62:12 | ... ++ | |
| test.cpp:77:20:77:28 | call to getAValue | Unary, Unique |
| test.cpp:77:20:77:30 | (signed short)... | |
| test.cpp:79:7:79:7 | (int)... | |
| test.cpp:79:7:79:7 | v | Unary, Unary |
| test.cpp:79:11:79:20 | (int)... | |
| test.cpp:79:17:79:20 | val1 | LoadTotalOverlap, Unary |
| test.cpp:79:24:79:33 | (int)... | |
| test.cpp:79:30:79:33 | val2 | LoadTotalOverlap, Unary |
| test.cpp:80:5:80:19 | ... = ... | |
| test.cpp:80:9:80:17 | call to getAValue | Unary, Unique |
| test.cpp:80:9:80:19 | (signed short)... | |
| test.cpp:88:3:88:20 | ... = ... | |
| test.cpp:88:12:88:12 | (void *)... | |
| test.cpp:105:11:105:12 | (Base *)... | |
| test.cpp:105:11:105:12 | pd | InheritanceConversion, InitializeParameter |
| test.cpp:106:14:106:35 | static_cast<Base *>... | |
| test.cpp:106:33:106:34 | pd | InheritanceConversion, InitializeParameter |
| test.cpp:128:3:128:11 | ... = ... | |
| test.cpp:139:3:139:24 | ... = ... | |
| test.cpp:147:3:147:18 | ... = ... | |
| test.cpp:156:3:156:17 | ... = ... | |

View File

@@ -1,142 +0,0 @@
| test.cpp:5:3:5:13 | ... = ... | test.cpp:5:3:5:13 | ... = ... | AST only |
| test.cpp:6:3:6:13 | ... = ... | test.cpp:6:3:6:13 | ... = ... | AST only |
| test.cpp:7:3:7:7 | ... = ... | test.cpp:7:3:7:7 | ... = ... | AST only |
| test.cpp:10:16:10:16 | 1 | test.cpp:10:16:10:16 | 1 | AST only |
| test.cpp:16:3:16:24 | ... = ... | test.cpp:16:3:16:24 | ... = ... | AST only |
| test.cpp:17:3:17:24 | ... = ... | test.cpp:17:3:17:24 | ... = ... | AST only |
| test.cpp:18:3:18:7 | ... = ... | test.cpp:18:3:18:7 | ... = ... | AST only |
| test.cpp:21:16:21:16 | 2 | test.cpp:21:16:21:16 | 2 | AST only |
| test.cpp:29:3:29:3 | x | test.cpp:31:3:31:3 | x | IR only |
| test.cpp:29:3:29:24 | ... = ... | test.cpp:29:3:29:24 | ... = ... | AST only |
| test.cpp:30:3:30:17 | call to change_global02 | test.cpp:30:3:30:17 | call to change_global02 | AST only |
| test.cpp:31:3:31:3 | x | test.cpp:29:3:29:3 | x | IR only |
| test.cpp:31:3:31:24 | ... = ... | test.cpp:31:3:31:24 | ... = ... | AST only |
| test.cpp:32:3:32:7 | ... = ... | test.cpp:32:3:32:7 | ... = ... | AST only |
| test.cpp:35:16:35:16 | 3 | test.cpp:35:16:35:16 | 3 | AST only |
| test.cpp:43:3:43:3 | x | test.cpp:45:3:45:3 | x | IR only |
| test.cpp:43:3:43:24 | ... = ... | test.cpp:43:3:43:24 | ... = ... | AST only |
| test.cpp:43:7:43:24 | ... + ... | test.cpp:45:7:45:24 | ... + ... | IR only |
| test.cpp:43:7:43:24 | ... + ... | test.cpp:46:7:46:7 | x | IR only |
| test.cpp:43:17:43:24 | global03 | test.cpp:45:17:45:24 | global03 | IR only |
| test.cpp:44:3:44:5 | * ... | test.cpp:44:4:44:5 | p2 | IR only |
| test.cpp:44:3:44:9 | ... = ... | test.cpp:44:3:44:9 | ... = ... | AST only |
| test.cpp:44:4:44:5 | p2 | test.cpp:44:3:44:5 | * ... | IR only |
| test.cpp:44:9:44:9 | 0 | test.cpp:51:25:51:25 | 0 | AST only |
| test.cpp:44:9:44:9 | 0 | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:44:9:44:9 | 0 | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:44:9:44:9 | 0 | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:44:9:44:9 | 0 | test.cpp:88:12:88:12 | 0 | AST only |
| test.cpp:45:3:45:3 | x | test.cpp:43:3:43:3 | x | IR only |
| test.cpp:45:3:45:24 | ... = ... | test.cpp:45:3:45:24 | ... = ... | AST only |
| test.cpp:45:7:45:24 | ... + ... | test.cpp:43:7:43:24 | ... + ... | IR only |
| test.cpp:45:17:45:24 | global03 | test.cpp:43:17:43:24 | global03 | IR only |
| test.cpp:46:3:46:7 | ... = ... | test.cpp:46:3:46:7 | ... = ... | AST only |
| test.cpp:46:7:46:7 | x | test.cpp:43:7:43:24 | ... + ... | IR only |
| test.cpp:51:25:51:25 | 0 | test.cpp:44:9:44:9 | 0 | AST only |
| test.cpp:51:25:51:25 | 0 | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:51:25:51:25 | 0 | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:51:25:51:25 | 0 | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:51:25:51:25 | 0 | test.cpp:88:12:88:12 | 0 | AST only |
| test.cpp:51:25:51:25 | (unsigned int)... | test.cpp:51:25:51:25 | (unsigned int)... | AST only |
| test.cpp:53:10:53:13 | (int)... | test.cpp:53:10:53:13 | (int)... | AST only |
| test.cpp:53:10:53:13 | (int)... | test.cpp:56:21:56:24 | (int)... | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:44:9:44:9 | 0 | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:51:25:51:25 | 0 | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:53:18:53:21 | (int)... | test.cpp:88:12:88:12 | 0 | AST only |
| test.cpp:55:5:55:15 | ... = ... | test.cpp:55:5:55:15 | ... = ... | AST only |
| test.cpp:56:12:56:25 | (...) | test.cpp:56:12:56:25 | (...) | AST only |
| test.cpp:56:12:56:43 | ... && ... | test.cpp:56:12:56:43 | ... && ... | AST only |
| test.cpp:56:13:56:16 | (int)... | test.cpp:56:13:56:16 | (int)... | AST only |
| test.cpp:56:13:56:16 | (int)... | test.cpp:56:31:56:34 | (int)... | AST only |
| test.cpp:56:13:56:16 | (int)... | test.cpp:59:9:59:12 | (int)... | AST only |
| test.cpp:56:13:56:16 | * ... | test.cpp:56:31:56:34 | * ... | AST only |
| test.cpp:56:13:56:16 | * ... | test.cpp:59:9:59:12 | * ... | AST only |
| test.cpp:56:21:56:24 | (int)... | test.cpp:53:10:53:13 | (int)... | AST only |
| test.cpp:56:21:56:24 | (int)... | test.cpp:56:21:56:24 | (int)... | AST only |
| test.cpp:56:30:56:43 | (...) | test.cpp:56:30:56:43 | (...) | AST only |
| test.cpp:56:31:56:34 | (int)... | test.cpp:56:13:56:16 | (int)... | AST only |
| test.cpp:56:31:56:34 | (int)... | test.cpp:56:31:56:34 | (int)... | AST only |
| test.cpp:56:31:56:34 | (int)... | test.cpp:59:9:59:12 | (int)... | AST only |
| test.cpp:56:31:56:34 | * ... | test.cpp:56:13:56:16 | * ... | AST only |
| test.cpp:56:31:56:34 | * ... | test.cpp:59:9:59:12 | * ... | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:44:9:44:9 | 0 | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:51:25:51:25 | 0 | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:56:39:56:42 | (int)... | test.cpp:88:12:88:12 | 0 | AST only |
| test.cpp:56:47:56:51 | ... ++ | test.cpp:56:47:56:51 | ... ++ | AST only |
| test.cpp:59:9:59:12 | (int)... | test.cpp:56:13:56:16 | (int)... | AST only |
| test.cpp:59:9:59:12 | (int)... | test.cpp:56:31:56:34 | (int)... | AST only |
| test.cpp:59:9:59:12 | (int)... | test.cpp:59:9:59:12 | (int)... | AST only |
| test.cpp:59:9:59:12 | * ... | test.cpp:56:13:56:16 | * ... | AST only |
| test.cpp:59:9:59:12 | * ... | test.cpp:56:31:56:34 | * ... | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:44:9:44:9 | 0 | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:51:25:51:25 | 0 | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:59:17:59:20 | (int)... | test.cpp:88:12:88:12 | 0 | AST only |
| test.cpp:62:5:62:12 | ... ++ | test.cpp:62:5:62:12 | ... ++ | AST only |
| test.cpp:77:20:77:28 | call to getAValue | test.cpp:79:7:79:7 | v | IR only |
| test.cpp:77:20:77:30 | (signed short)... | test.cpp:77:20:77:30 | (signed short)... | AST only |
| test.cpp:77:20:77:30 | (signed short)... | test.cpp:79:7:79:7 | v | AST only |
| test.cpp:79:7:79:7 | (int)... | test.cpp:79:7:79:7 | (int)... | AST only |
| test.cpp:79:7:79:7 | v | test.cpp:77:20:77:28 | call to getAValue | IR only |
| test.cpp:79:7:79:7 | v | test.cpp:77:20:77:30 | (signed short)... | AST only |
| test.cpp:79:11:79:20 | (int)... | test.cpp:79:11:79:20 | (int)... | AST only |
| test.cpp:79:24:79:33 | (int)... | test.cpp:79:24:79:33 | (int)... | AST only |
| test.cpp:80:5:80:19 | ... = ... | test.cpp:80:5:80:19 | ... = ... | AST only |
| test.cpp:80:9:80:19 | (signed short)... | test.cpp:80:9:80:19 | (signed short)... | AST only |
| test.cpp:88:3:88:20 | ... = ... | test.cpp:88:3:88:20 | ... = ... | AST only |
| test.cpp:88:12:88:12 | 0 | test.cpp:44:9:44:9 | 0 | AST only |
| test.cpp:88:12:88:12 | 0 | test.cpp:51:25:51:25 | 0 | AST only |
| test.cpp:88:12:88:12 | 0 | test.cpp:53:18:53:21 | (int)... | AST only |
| test.cpp:88:12:88:12 | 0 | test.cpp:56:39:56:42 | (int)... | AST only |
| test.cpp:88:12:88:12 | 0 | test.cpp:59:17:59:20 | (int)... | AST only |
| test.cpp:88:12:88:12 | (void *)... | test.cpp:88:12:88:12 | (void *)... | AST only |
| test.cpp:92:11:92:16 | ... = ... | test.cpp:92:15:92:16 | 10 | IR only |
| test.cpp:92:11:92:16 | ... = ... | test.cpp:93:10:93:10 | x | IR only |
| test.cpp:92:15:92:16 | 10 | test.cpp:92:11:92:16 | ... = ... | IR only |
| test.cpp:92:15:92:16 | 10 | test.cpp:93:10:93:10 | x | IR only |
| test.cpp:93:10:93:10 | x | test.cpp:92:11:92:16 | ... = ... | IR only |
| test.cpp:93:10:93:10 | x | test.cpp:92:15:92:16 | 10 | IR only |
| test.cpp:105:11:105:12 | (Base *)... | test.cpp:105:11:105:12 | (Base *)... | AST only |
| test.cpp:105:11:105:12 | (Base *)... | test.cpp:106:14:106:35 | static_cast<Base *>... | AST only |
| test.cpp:105:11:105:12 | (Base *)... | test.cpp:107:11:107:12 | pb | AST only |
| test.cpp:105:11:105:12 | pd | test.cpp:107:11:107:12 | pb | IR only |
| test.cpp:106:14:106:35 | static_cast<Base *>... | test.cpp:105:11:105:12 | (Base *)... | AST only |
| test.cpp:106:14:106:35 | static_cast<Base *>... | test.cpp:106:14:106:35 | static_cast<Base *>... | AST only |
| test.cpp:106:14:106:35 | static_cast<Base *>... | test.cpp:107:11:107:12 | pb | AST only |
| test.cpp:106:33:106:34 | pd | test.cpp:107:11:107:12 | pb | IR only |
| test.cpp:107:11:107:12 | pb | test.cpp:105:11:105:12 | (Base *)... | AST only |
| test.cpp:107:11:107:12 | pb | test.cpp:105:11:105:12 | pd | IR only |
| test.cpp:107:11:107:12 | pb | test.cpp:106:14:106:35 | static_cast<Base *>... | AST only |
| test.cpp:107:11:107:12 | pb | test.cpp:106:33:106:34 | pd | IR only |
| test.cpp:113:3:113:5 | a | test.cpp:115:3:115:5 | a | IR only |
| test.cpp:115:3:115:5 | a | test.cpp:113:3:113:5 | a | IR only |
| test.cpp:125:15:125:15 | x | test.cpp:128:7:128:7 | x | AST only |
| test.cpp:126:15:126:15 | x | test.cpp:128:7:128:7 | x | AST only |
| test.cpp:128:3:128:11 | ... = ... | test.cpp:128:3:128:11 | ... = ... | AST only |
| test.cpp:128:7:128:7 | x | test.cpp:125:15:125:15 | x | AST only |
| test.cpp:128:7:128:7 | x | test.cpp:126:15:126:15 | x | AST only |
| test.cpp:128:11:128:11 | n | test.cpp:129:15:129:15 | x | IR only |
| test.cpp:129:15:129:15 | x | test.cpp:128:11:128:11 | n | IR only |
| test.cpp:136:21:136:21 | x | test.cpp:137:21:137:21 | x | AST only |
| test.cpp:136:21:136:21 | x | test.cpp:139:13:139:13 | x | AST only |
| test.cpp:137:21:137:21 | x | test.cpp:136:21:136:21 | x | AST only |
| test.cpp:137:21:137:21 | x | test.cpp:139:13:139:13 | x | AST only |
| test.cpp:139:3:139:24 | ... = ... | test.cpp:139:3:139:24 | ... = ... | AST only |
| test.cpp:139:13:139:13 | x | test.cpp:136:21:136:21 | x | AST only |
| test.cpp:139:13:139:13 | x | test.cpp:137:21:137:21 | x | AST only |
| test.cpp:144:15:144:15 | x | test.cpp:149:15:149:15 | x | IR only |
| test.cpp:145:15:145:15 | y | test.cpp:147:7:147:7 | y | AST only |
| test.cpp:147:3:147:18 | ... = ... | test.cpp:147:3:147:18 | ... = ... | AST only |
| test.cpp:147:7:147:7 | y | test.cpp:145:15:145:15 | y | AST only |
| test.cpp:149:15:149:15 | x | test.cpp:144:15:144:15 | x | IR only |
| test.cpp:153:21:153:21 | x | test.cpp:154:21:154:21 | x | AST only |
| test.cpp:154:21:154:21 | x | test.cpp:153:21:153:21 | x | AST only |
| test.cpp:156:3:156:17 | ... = ... | test.cpp:156:3:156:17 | ... = ... | AST only |

View File

@@ -12,6 +12,5 @@
| test.cpp:46:2:46:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
| test.cpp:47:2:47:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
| test.cpp:60:3:60:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:63:3:63:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:68:2:68:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:79:3:79:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:82:3:82:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |

View File

@@ -1,5 +1,10 @@
| tests.c:28:3:28:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
| tests.c:29:3:29:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
| tests.c:34:25:34:33 | buffer100 | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |

View File

@@ -1,4 +1,3 @@
| test1.c:18:16:18:16 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
| test1.c:33:11:33:11 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
| test1.c:37:11:37:11 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
| test1.c:53:15:53:15 | j | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |

View File

@@ -16,15 +16,7 @@
| argvLocal.c:132:15:132:20 | ... + ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:135:9:135:12 | ... ++ | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
| argvLocal.c:136:15:136:18 | -- ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:115:13:115:16 | argv | argv |
| argvLocal.c:139:9:139:26 | ... ? ... : ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:140:15:140:32 | ... ? ... : ... | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:126:10:126:13 | argv | argv |
| argvLocal.c:144:9:144:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:145:15:145:16 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:100:7:100:10 | argv | argv |
| argvLocal.c:150:9:150:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:149:15:149:18 | argv | argv |
| argvLocal.c:151:15:151:16 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:149:15:149:18 | argv | argv |
| argvLocal.c:156:9:156:10 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:155:23:155:26 | argv | argv |
| argvLocal.c:157:15:157:16 | i9 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:155:23:155:26 | argv | argv |
| argvLocal.c:162:9:162:11 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:161:42:161:45 | argv | argv |
| argvLocal.c:163:15:163:17 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:161:42:161:45 | argv | argv |
| argvLocal.c:167:18:167:20 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | argvLocal.c:166:18:166:21 | argv | argv |
| argvLocal.c:168:24:168:26 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format) | argvLocal.c:166:18:166:21 | argv | argv |

View File

@@ -3,6 +3,3 @@
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
| funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
| funcsLocal.c:47:9:47:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:46:7:46:9 | * ... | gets |
| funcsLocal.c:53:9:53:11 | * ... | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:52:8:52:11 | call to gets | gets |
| funcsLocal.c:58:9:58:10 | e1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |

View File

@@ -1,3 +1,4 @@
| ifs.c:62:9:62:10 | c7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:61:8:61:11 | argv | argv |
| ifs.c:69:9:69:10 | c8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:68:8:68:11 | argv | argv |
| ifs.c:75:9:75:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:74:8:74:11 | argv | argv |
| ifs.c:81:9:81:10 | i2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:80:8:80:11 | argv | argv |

View File

@@ -1,6 +1,8 @@
| test.cpp:42:31:42:36 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:43:31:43:36 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:45:31:45:36 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:48:25:48:30 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:49:17:49:30 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:21:52:27 | call to realloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:55:11:55:24 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |

View File

@@ -1,7 +1,6 @@
| test.c:17:10:17:12 | min | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:8:9:8:15 | 2147483647 | Extreme value |
| test.c:48:3:48:5 | sc2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:47:9:47:16 | - ... | Extreme value |
| test.c:50:3:50:5 | sc3 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:49:9:49:16 | 127 | Extreme value |
| test.c:56:3:56:5 | sc5 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:9:54:16 | 127 | Extreme value |
| test.c:59:3:59:5 | sc6 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:58:9:58:16 | 127 | Extreme value |
| test.c:63:3:63:5 | sc8 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:62:9:62:16 | - ... | Extreme value |
| test.c:75:3:75:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value |

View File

@@ -8,4 +8,3 @@
| test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value |
| test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
| test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
| test.cpp:37:7:37:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:18:9:18:12 | call to rand | Uncontrolled value |

View File

@@ -1 +1,2 @@
| test.cpp:24:10:24:35 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:25:9:25:27 | ... = ... | ... = ... |
| test.cpp:41:10:41:38 | ! ... | Reliance on untrusted input $@ to raise privilege at $@ | test.cpp:20:29:20:34 | call to getenv | call to getenv | test.cpp:42:8:42:26 | ... = ... | ... = ... |

View File

@@ -1,3 +1,4 @@
using System;
using System.IO;
using Microsoft.CodeAnalysis;
@@ -9,10 +10,12 @@ namespace Semmle.Extraction.CSharp.Entities
public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(Location);
trapFile.Write('_');
trapFile.Write(symbol.Name);
trapFile.Write(";localvar");
throw new InvalidOperationException();
}
public override void WriteQuotedId(TextWriter trapFile)
{
trapFile.Write('*');
}
public override void Populate(TextWriter trapFile) { }

View File

@@ -53,7 +53,7 @@ namespace Semmle.Extraction
public abstract void WriteId(System.IO.TextWriter trapFile);
public void WriteQuotedId(TextWriter trapFile)
public virtual void WriteQuotedId(TextWriter trapFile)
{
trapFile.Write("@\"");
WriteId(trapFile);

View File

@@ -11,6 +11,7 @@
import semmle.code.csharp.serialization.Serialization
import semmle.code.csharp.controlflow.Guards
import semmle.code.csharp.dataflow.DataFlow
/**
* The result is a write to the field `f`, assigning it the value
@@ -29,7 +30,11 @@ GuardedExpr checkedWrite(Field f, Variable v, IfStmt check) {
Expr uncheckedWrite(Callable callable, Field f) {
result = f.getAnAssignedValue() and
result.getEnclosingCallable() = callable and
not callable.calls*(checkedWrite(f, _, _).getEnclosingCallable())
not callable.calls*(checkedWrite(f, _, _).getEnclosingCallable()) and
// Exclude object creations because they were not deserialized
not exists(Expr src | DataFlow::localExprFlow(src, result) |
src instanceof ObjectCreation or src.hasValue()
)
}
from BinarySerializableType t, Field f, IfStmt check, Expr write, Expr unsafeWrite

View File

@@ -50,7 +50,7 @@ module Stages {
cached
module DataFlowStage {
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon::Public
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
cached

View File

@@ -3,7 +3,7 @@ private import cil
private import dotnet
private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon::Public
private import DataFlowImplCommon
private import ControlFlowReachability
private import DelegateDataFlow
private import semmle.code.csharp.Caching
@@ -380,7 +380,9 @@ private module Cached {
a = cfn.getElement() and
t = a.stripCasts().getType()
|
t instanceof RefType or
t instanceof RefType and
not t instanceof NullType
or
t = any(TypeParameter tp | not tp.isValueType())
)
or
@@ -1495,3 +1497,5 @@ private predicate viableConstantBooleanParamArg(
b = arg.getBooleanValue()
)
}
int accessPathLimit() { result = 3 }

View File

@@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

View File

@@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

View File

@@ -96,7 +96,7 @@ private module Cached {
}
cached
Instruction getMemoryOperandDefinition(
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
@@ -142,6 +142,19 @@ private module Cached {
overlap instanceof MustExactlyOverlap
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
not (
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.

View File

@@ -69,7 +69,6 @@ nodes
| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| CallSensitivityFlow.cs:189:40:189:40 | o : Object | semmle.label | o : Object |
| CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | semmle.label | access to parameter o |
| CallSensitivityFlow.cs:203:22:203:22 | access to parameter o | semmle.label | access to parameter o |
#select
| CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | $@ | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | access to parameter o |
| CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | $@ | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | access to parameter o |

View File

@@ -174,6 +174,37 @@ edges
| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem |
| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] |
| G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | G.cs:52:14:52:31 | access to field Elem |
| H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [FieldA] : Object |
| H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object |
| H.cs:24:21:24:28 | call to method Clone [FieldA] : Object | H.cs:25:14:25:18 | access to local variable clone [FieldA] : Object |
| H.cs:24:27:24:27 | access to local variable a [FieldA] : Object | H.cs:24:21:24:28 | call to method Clone [FieldA] : Object |
| H.cs:25:14:25:18 | access to local variable clone [FieldA] : Object | H.cs:25:14:25:25 | access to field FieldA |
| H.cs:43:9:43:9 | [post] access to local variable a [FieldA] : Object | H.cs:44:27:44:27 | access to local variable a [FieldA] : Object |
| H.cs:43:20:43:31 | object creation of type Object : Object | H.cs:43:9:43:9 | [post] access to local variable a [FieldA] : Object |
| H.cs:44:17:44:28 | call to method Transform [FieldB] : Object | H.cs:45:14:45:14 | access to local variable b [FieldB] : Object |
| H.cs:44:27:44:27 | access to local variable a [FieldA] : Object | H.cs:44:17:44:28 | call to method Transform [FieldB] : Object |
| H.cs:45:14:45:14 | access to local variable b [FieldB] : Object | H.cs:45:14:45:21 | access to field FieldB |
| H.cs:63:9:63:9 | [post] access to local variable a [FieldA] : Object | H.cs:64:22:64:22 | access to local variable a [FieldA] : Object |
| H.cs:63:20:63:31 | object creation of type Object : Object | H.cs:63:9:63:9 | [post] access to local variable a [FieldA] : Object |
| H.cs:64:22:64:22 | access to local variable a [FieldA] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 [FieldB] : Object |
| H.cs:64:25:64:26 | [post] access to local variable b1 [FieldB] : Object | H.cs:65:14:65:15 | access to local variable b1 [FieldB] : Object |
| H.cs:65:14:65:15 | access to local variable b1 [FieldB] : Object | H.cs:65:14:65:22 | access to field FieldB |
| H.cs:88:17:88:17 | [post] access to local variable a [FieldA] : Object | H.cs:89:14:89:14 | access to local variable a [FieldA] : Object |
| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:88:17:88:17 | [post] access to local variable a [FieldA] : Object |
| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:88:34:88:35 | [post] access to local variable b1 [FieldB] : Object |
| H.cs:88:34:88:35 | [post] access to local variable b1 [FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 [FieldB] : Object |
| H.cs:89:14:89:14 | access to local variable a [FieldA] : Object | H.cs:89:14:89:21 | access to field FieldA |
| H.cs:90:14:90:15 | access to local variable b1 [FieldB] : Object | H.cs:90:14:90:22 | access to field FieldB |
| H.cs:112:9:112:9 | [post] access to local variable a [FieldA] : Object | H.cs:113:31:113:31 | access to local variable a [FieldA] : Object |
| H.cs:112:20:112:31 | object creation of type Object : Object | H.cs:112:9:112:9 | [post] access to local variable a [FieldA] : Object |
| H.cs:113:17:113:32 | call to method TransformWrap [FieldB] : Object | H.cs:114:14:114:14 | access to local variable b [FieldB] : Object |
| H.cs:113:31:113:31 | access to local variable a [FieldA] : Object | H.cs:113:17:113:32 | call to method TransformWrap [FieldB] : Object |
| H.cs:114:14:114:14 | access to local variable b [FieldB] : Object | H.cs:114:14:114:21 | access to field FieldB |
| H.cs:130:9:130:9 | [post] access to local variable a [FieldA] : Object | H.cs:131:18:131:18 | access to local variable a [FieldA] : Object |
| H.cs:130:20:130:31 | object creation of type Object : Object | H.cs:130:9:130:9 | [post] access to local variable a [FieldA] : Object |
| H.cs:131:18:131:18 | access to local variable a [FieldA] : Object | H.cs:131:14:131:19 | call to method Get |
| H.cs:147:17:147:32 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a |
| H.cs:147:25:147:31 | object creation of type A : A | H.cs:147:17:147:32 | call to method Through : A |
nodes
| A.cs:5:17:5:23 | object creation of type C : C | semmle.label | object creation of type C : C |
| A.cs:6:17:6:25 | call to method Make [c] : C | semmle.label | call to method Make [c] : C |
@@ -371,6 +402,44 @@ nodes
| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] |
| G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | semmle.label | access to field Box1 [Elem] : Elem |
| G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem |
| H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:23:20:23:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:24:21:24:28 | call to method Clone [FieldA] : Object | semmle.label | call to method Clone [FieldA] : Object |
| H.cs:24:27:24:27 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:25:14:25:18 | access to local variable clone [FieldA] : Object | semmle.label | access to local variable clone [FieldA] : Object |
| H.cs:25:14:25:25 | access to field FieldA | semmle.label | access to field FieldA |
| H.cs:43:9:43:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:43:20:43:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:44:17:44:28 | call to method Transform [FieldB] : Object | semmle.label | call to method Transform [FieldB] : Object |
| H.cs:44:27:44:27 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:45:14:45:14 | access to local variable b [FieldB] : Object | semmle.label | access to local variable b [FieldB] : Object |
| H.cs:45:14:45:21 | access to field FieldB | semmle.label | access to field FieldB |
| H.cs:63:9:63:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:63:20:63:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:64:22:64:22 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:64:25:64:26 | [post] access to local variable b1 [FieldB] : Object | semmle.label | [post] access to local variable b1 [FieldB] : Object |
| H.cs:65:14:65:15 | access to local variable b1 [FieldB] : Object | semmle.label | access to local variable b1 [FieldB] : Object |
| H.cs:65:14:65:22 | access to field FieldB | semmle.label | access to field FieldB |
| H.cs:88:17:88:17 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:88:20:88:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:88:34:88:35 | [post] access to local variable b1 [FieldB] : Object | semmle.label | [post] access to local variable b1 [FieldB] : Object |
| H.cs:89:14:89:14 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:89:14:89:21 | access to field FieldA | semmle.label | access to field FieldA |
| H.cs:90:14:90:15 | access to local variable b1 [FieldB] : Object | semmle.label | access to local variable b1 [FieldB] : Object |
| H.cs:90:14:90:22 | access to field FieldB | semmle.label | access to field FieldB |
| H.cs:112:9:112:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:112:20:112:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:113:17:113:32 | call to method TransformWrap [FieldB] : Object | semmle.label | call to method TransformWrap [FieldB] : Object |
| H.cs:113:31:113:31 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:114:14:114:14 | access to local variable b [FieldB] : Object | semmle.label | access to local variable b [FieldB] : Object |
| H.cs:114:14:114:21 | access to field FieldB | semmle.label | access to field FieldB |
| H.cs:130:9:130:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object |
| H.cs:130:20:130:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| H.cs:131:14:131:19 | call to method Get | semmle.label | call to method Get |
| H.cs:131:18:131:18 | access to local variable a [FieldA] : Object | semmle.label | access to local variable a [FieldA] : Object |
| H.cs:147:17:147:32 | call to method Through : A | semmle.label | call to method Through : A |
| H.cs:147:25:147:31 | object creation of type A : A | semmle.label | object creation of type A : A |
| H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a |
#select
| A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:23 | object creation of type C : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:23 | object creation of type C : C | object creation of type C : C |
| A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:22 | object creation of type C1 : C1 | object creation of type C1 : C1 |
@@ -410,3 +479,11 @@ nodes
| G.cs:39:14:39:35 | call to method GetElem | G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:23:18:23:27 | object creation of type Elem : Elem | object creation of type Elem : Elem |
| G.cs:39:14:39:35 | call to method GetElem | G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:31:18:31:27 | object creation of type Elem : Elem | object creation of type Elem : Elem |
| G.cs:52:14:52:31 | access to field Elem | G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:52:14:52:31 | access to field Elem | $@ | G.cs:44:18:44:27 | object creation of type Elem : Elem | object creation of type Elem : Elem |
| H.cs:25:14:25:25 | access to field FieldA | H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:25:14:25:25 | access to field FieldA | $@ | H.cs:23:20:23:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:45:14:45:21 | access to field FieldB | H.cs:43:20:43:31 | object creation of type Object : Object | H.cs:45:14:45:21 | access to field FieldB | $@ | H.cs:43:20:43:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:65:14:65:22 | access to field FieldB | H.cs:63:20:63:31 | object creation of type Object : Object | H.cs:65:14:65:22 | access to field FieldB | $@ | H.cs:63:20:63:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:89:14:89:21 | access to field FieldA | H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:89:14:89:21 | access to field FieldA | $@ | H.cs:88:20:88:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:90:14:90:22 | access to field FieldB | H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:90:14:90:22 | access to field FieldB | $@ | H.cs:88:20:88:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:114:14:114:21 | access to field FieldB | H.cs:112:20:112:31 | object creation of type Object : Object | H.cs:114:14:114:21 | access to field FieldB | $@ | H.cs:112:20:112:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:131:14:131:19 | call to method Get | H.cs:130:20:130:31 | object creation of type Object : Object | H.cs:131:14:131:19 | call to method Get | $@ | H.cs:130:20:130:31 | object creation of type Object : Object | object creation of type Object : Object |
| H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:31 | object creation of type A : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:31 | object creation of type A : A | object creation of type A : A |

View File

@@ -0,0 +1,154 @@
public class H
{
class A
{
public object FieldA;
}
class B
{
public object FieldB;
}
A Clone(A a)
{
var ret = new A();
ret.FieldA = a.FieldA;
return ret;
}
void M1(object o)
{
var a = new A();
a.FieldA = new object();
var clone = Clone(a);
Sink(clone.FieldA); // flow
a = new A();
a.FieldA = o;
clone = Clone(a);
Sink(clone.FieldA); // no flow
}
B Transform(A a)
{
var b = new B();
b.FieldB = a.FieldA;
return b;
}
void M2(object o)
{
var a = new A();
a.FieldA = new object();
var b = Transform(a);
Sink(b.FieldB); // flow
a = new A();
a.FieldA = o;
b = Transform(a);
Sink(b.FieldB); // no flow
}
void TransformArg(A a, B b1, B b2)
{
b1.FieldB = a.FieldA;
}
void M3(object o)
{
var a = new A();
var b1 = new B();
var b2 = new B();
a.FieldA = new object();
TransformArg(a, b1, b2);
Sink(b1.FieldB); // flow
Sink(b2.FieldB); // no flow
a = new A();
b1 = new B();
b2 = new B();
a.FieldA = o;
TransformArg(a, b1, b2);
Sink(b1.FieldB); // no flow
Sink(b2.FieldB); // no flow
}
void SetArgs(A a, object o, B b1, B b2)
{
a.FieldA = o;
TransformArg(a, b1, b2);
}
void M4(object o)
{
var a = new A();
var b1 = new B();
var b2 = new B();
SetArgs(a, new object(), b1, b2);
Sink(a.FieldA); // flow
Sink(b1.FieldB); // flow
Sink(b2.FieldB); // no flow
a = new A();
b1 = new B();
b2 = new B();
SetArgs(a, o, b1, b2);
Sink(a.FieldA); // no flow
Sink(b1.FieldB); // no flow
Sink(b2.FieldB); // no flow
}
B TransformWrap(A a)
{
var temp = new B();
temp.FieldB = a;
return Transform((A)temp.FieldB);
}
void M5(object o)
{
var a = new A();
a.FieldA = new object();
var b = TransformWrap(a);
Sink(b.FieldB); // flow
a = new A();
a.FieldA = o;
b = TransformWrap(a);
Sink(b.FieldB); // no flow
}
object Get(A a)
{
return Transform(a).FieldB;
}
void M6(object o)
{
var a = new A();
a.FieldA = new object();
Sink(Get(a)); // flow
a = new A();
a.FieldA = o;
Sink(Get(a)); // no flow
}
object Through(object o)
{
var a = new A();
a.FieldA = o as A;
return Transform(a).FieldB;
}
void M7()
{
var a = Through(new A());
Sink(a); // flow
var b = Through(new B());
Sink(b); // no flow
}
public static void Sink(object o) { }
}

View File

@@ -269,8 +269,6 @@ nodes
| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String |
| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String |
| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |
| GlobalDataFlow.cs:60:38:60:50 | access to parameter nonSinkParam0 | semmle.label | access to parameter nonSinkParam0 |
| GlobalDataFlow.cs:61:61:61:73 | access to parameter nonSinkParam0 | semmle.label | access to parameter nonSinkParam0 |
| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |
| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String |
| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |

View File

@@ -1824,6 +1824,8 @@
| GlobalDataFlow.cs:84:126:84:126 | x : Object | GlobalDataFlow.cs:84:135:84:135 | access to parameter x : String |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x : String |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x : String |
| GlobalDataFlow.cs:84:126:84:126 | x : String | GlobalDataFlow.cs:84:135:84:135 | access to parameter x : String |
| GlobalDataFlow.cs:84:126:84:126 | x : String[] | GlobalDataFlow.cs:84:126:84:126 | x |
@@ -1904,6 +1906,8 @@
| GlobalDataFlow.cs:86:129:86:129 | y : Object | GlobalDataFlow.cs:86:135:86:135 | access to parameter y : String |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y : String |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y : String |
| GlobalDataFlow.cs:86:129:86:129 | y : String | GlobalDataFlow.cs:86:135:86:135 | access to parameter y : String |
| GlobalDataFlow.cs:86:129:86:129 | y : String[] | GlobalDataFlow.cs:86:129:86:129 | y |
@@ -2124,10 +2128,14 @@
| GlobalDataFlow.cs:90:97:90:97 | s : Object | GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String | GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String |
| GlobalDataFlow.cs:90:97:90:97 | s : String[] | GlobalDataFlow.cs:90:97:90:97 | s |
@@ -2144,6 +2152,8 @@
| GlobalDataFlow.cs:90:103:90:109 | ... + ... : String | GlobalDataFlow.cs:90:91:90:109 | [output] (...) => ... : String |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:109:90:109 | access to parameter s : String | GlobalDataFlow.cs:90:103:90:109 | ... + ... : String |
| GlobalDataFlow.cs:90:112:90:112 | x : Object | GlobalDataFlow.cs:90:112:90:112 | x |
@@ -2592,6 +2602,8 @@
| GlobalDataFlow.cs:114:76:114:76 | x : Object | GlobalDataFlow.cs:114:81:114:81 | access to parameter x : String |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x : String |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x : String |
| GlobalDataFlow.cs:114:76:114:76 | x : String | GlobalDataFlow.cs:114:81:114:81 | access to parameter x : String |
| GlobalDataFlow.cs:114:76:114:76 | x : String[] | GlobalDataFlow.cs:114:76:114:76 | x |
@@ -2658,6 +2670,8 @@
| GlobalDataFlow.cs:116:127:116:127 | y : Object | GlobalDataFlow.cs:116:133:116:133 | access to parameter y : String |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y : String |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y : String |
| GlobalDataFlow.cs:116:127:116:127 | y : String | GlobalDataFlow.cs:116:133:116:133 | access to parameter y : String |
| GlobalDataFlow.cs:116:127:116:127 | y : String[] | GlobalDataFlow.cs:116:127:116:127 | y |
@@ -2720,6 +2734,8 @@
| GlobalDataFlow.cs:118:124:118:124 | x : Object | GlobalDataFlow.cs:118:133:118:133 | access to parameter x : String |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x : String |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x : String |
| GlobalDataFlow.cs:118:124:118:124 | x : String | GlobalDataFlow.cs:118:133:118:133 | access to parameter x : String |
| GlobalDataFlow.cs:118:124:118:124 | x : String[] | GlobalDataFlow.cs:118:124:118:124 | x |
@@ -4252,14 +4268,20 @@
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : String[] | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:292:31:292:40 | sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:294:15:294:24 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T |
| GlobalDataFlow.cs:295:16:295:25 | access to parameter sinkParam8 : T | GlobalDataFlow.cs:82:84:82:94 | [output] delegate creation of type Func<String,String> |
@@ -4827,6 +4849,10 @@
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T |
| GlobalDataFlow.cs:429:22:429:22 | SSA def(x) : T | GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T |
@@ -4871,20 +4897,32 @@
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : IEnumerable<T> |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : IEnumerable<T> |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : IEnumerable<T> |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : IEnumerable<T> |
| GlobalDataFlow.cs:431:44:431:47 | delegate call : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : IEnumerable<T> |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:80:79:80:79 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:112:84:112:84 | x : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : T |
| GlobalDataFlow.cs:431:46:431:46 | access to local variable x : T | GlobalDataFlow.cs:431:44:431:47 | delegate call : T |

View File

@@ -316,8 +316,6 @@ nodes
| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String |
| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String |
| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |
| GlobalDataFlow.cs:60:38:60:50 | access to parameter nonSinkParam0 | semmle.label | access to parameter nonSinkParam0 |
| GlobalDataFlow.cs:61:61:61:73 | access to parameter nonSinkParam0 | semmle.label | access to parameter nonSinkParam0 |
| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |
| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String |
| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String |

View File

@@ -10,14 +10,14 @@ public class Test1
{
if (v == "valid")
{
f = v /* safe write */;
f = v; // GOOD
}
}
[OnDeserializing]
public void Deserialize()
{
f = "invalid" /* unsafe write */;
f = $"invalid"; // BAD
}
}
@@ -30,19 +30,19 @@ public class Test2
{
if (v == "valid")
{
f = v /* safe write */;
f = v; // GOOD
}
}
[OnDeserializing]
public void Deserialize()
{
var v = "invalid";
f = v /* unsafe write -- false negative */;
var v = $"invalid";
f = v; // BAD: False negative
if (v == "valid")
{
f = v; /* safe write */
f = v; // GOOD
}
}
}
@@ -56,25 +56,25 @@ public class Test3
{
if (v == "valid")
{
f = v /* safe write */;
f = v; // GOOD
}
}
[OnDeserializing]
public void Deserialize()
{
var v = "invalid";
f = v /* unsafe write -- false negative */;
var v = $"invalid";
f = v; // GOOD: False negative
Assign(v);
}
private void Assign(string v)
{
f = v /* unsafe write -- false negative */;
f = v; // GOOD: False negative
if (v == "valid")
{
f = v /* safe write */;
f = v; // GOOD
}
}
}
@@ -88,21 +88,21 @@ public class Test4
{
if (v == "valid")
{
f = v /* safe write */;
f = v; // GOOD
}
}
[OnDeserializing]
public void Deserialize()
{
var v = "invalid";
var v = $"invalid";
if (v == "valid")
Assign(v);
}
private void Assign(string v)
{
f = v /* safe write */;
f = v; // GOOD
}
}
@@ -115,13 +115,13 @@ public class Test5 : ISerializable
{
if (age < 0)
throw new ArgumentException(nameof(age));
Age = age /* safe write */;
Age = age; // GOOD
}
[OnDeserializing]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
Age = info.GetInt32("age"); /* unsafe write */;
Age = info.GetInt32("age"); // BAD
}
}
@@ -134,7 +134,7 @@ public class Test6 : ISerializable
{
if (age < 0)
throw new ArgumentException(nameof(age));
Age = age /* safe write */;
Age = age; // GOOD
}
[OnDeserializing]
@@ -143,7 +143,7 @@ public class Test6 : ISerializable
int age = info.GetInt32("age");
if (age < 0)
throw new SerializationException("age");
Age = age; /* safe write */;
Age = age; // GOOD
}
}
@@ -156,7 +156,7 @@ public class Test7 : ISerializable
{
if (age < 0)
throw new ArgumentException(nameof(age));
Age = age /* safe write */;
Age = age; // GOOD
}
[OnDeserializing]
@@ -165,6 +165,27 @@ public class Test7 : ISerializable
int age = info.GetInt32("age");
if (false)
throw new SerializationException("age");
Age = age; /* unsafe write */;
Age = age; // BAD
}
}
[Serializable]
public class Test8 : ISerializable
{
string Options;
public int Age;
public Test8(string options)
{
if (options == null)
throw new ArgumentNullException(nameof(options));
Options = options; // GOOD
}
[OnDeserializing]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
Options = new string(""); // GOOD: A created object
}
}

View File

@@ -1,4 +1,4 @@
| RuntimeChecksBypass.cs:20:13:20:21 | "invalid" | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:7:19:7:19 | f | f | RuntimeChecksBypass.cs:11:9:14:9 | if (...) ... | check |
| RuntimeChecksBypass.cs:20:13:20:22 | $"..." | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:7:19:7:19 | f | f | RuntimeChecksBypass.cs:11:9:14:9 | if (...) ... | check |
| RuntimeChecksBypass.cs:124:15:124:34 | call to method GetInt32 | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:112:16:112:18 | Age | Age | RuntimeChecksBypass.cs:116:9:117:53 | if (...) ... | check |
| RuntimeChecksBypass.cs:168:15:168:17 | access to local variable age | This write to $@ may be circumventing a $@. | RuntimeChecksBypass.cs:153:16:153:18 | Age | Age | RuntimeChecksBypass.cs:157:9:158:53 | if (...) ... | check |
| RuntimeChecksBypassBad.cs:19:15:19:34 | call to method GetInt32 | This write to $@ may be circumventing a $@. | RuntimeChecksBypassBad.cs:7:16:7:18 | Age | Age | RuntimeChecksBypassBad.cs:11:9:12:53 | if (...) ... | check |

View File

@@ -113,7 +113,7 @@ Then we can make the source more specific, for example an access to a public par
where
fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and
call.getCallee() = fileReader and
DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(fc.getArgument(0)))
DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0)))
select p
The following example finds calls to formatting functions where the format string is not hard-coded.

View File

@@ -393,16 +393,73 @@ aggregation in a simpler form:
Monotonic aggregates
====================
In addition to the standard aggregates, QL also supports monotonic aggregates.
These are a slightly different way of computing aggregates which have some advantages.
For example, you can use monotonic aggregates :ref:`recursively <recursion>`.
You can't do this with normal aggregates.
In addition to standard aggregates, QL also supports monotonic aggregates.
Monotonic aggregates differ from standard aggregates in the way that they deal with the
values generated by the ``<expression>`` part of the formula:
For more information and examples, see `Monotonic aggregates in QL
<https://help.semmle.com/QL/learn-ql/advanced/monotonic-aggregates.html>`_.
- Standard aggregates take the ``<expression>`` values for each ``<formula>`` value and
flatten them into a list. A single aggregation function is applied to all the values.
- Monotonic aggregates take an ``<expression>`` for each value given by the ``<formula>``,
and create combinations of all the possible values. The aggregation
function is applied to each of the resulting combinations.
.. TODO: Eventually replace this link with just the relevant examples.
(Some of the content is a duplicate of the above discussion on aggregates.)
In general, if the ``<expression>`` is total and functional, then monotonic aggregates are
equivalent to standard aggregates. Results differ when there is not precisely one ``<expression>``
value for each value generated by the ``<formula>``:
- If there are missing ``<expression>`` values (that is, there is no
``<expression>`` value for a value generated by the ``<formula>``), monotonic aggregates
won't compute a result, as you cannot create combinations of values
including exactly one ``<expression>`` value for each value generated by the ``<formula>``.
- If there is more than one ``<expression>`` per ``<formula>`` result, you can create multiple
combinations of values including exactly one ``<expression>`` value for each
value generated by the ``<formula>``. Here, the aggregation function is applied to each of the
resulting combinations.
Recursive monotonic aggregates
------------------------------
Monotonic aggregates may be used :ref:`recursively <recursion>`, but the recursive call may only appear in the
expression, and not in the range. The recursive semantics for aggregates are the same as the
recursive semantics for the rest of QL. For example, we might define a predicate to calculate
the distance of a node in a graph from the leaves as follows:
.. code-block:: ql
int depth(Node n) {
if not exists(n.getAChild())
then result = 0
else result = 1 + max(Node child | child = n.getAChild() | depth(child))
}
Here the recursive call is in the expression, which is legal. The recursive semantics for aggregates
are the same as the recursive semantics for the rest of QL. If you understand how aggregates work in
the non-recursive case then you should not find it difficult to use them recursively. However, it is
worth seeing how the evaluation of a recursive aggregation proceeds.
Consider the depth example we just saw with the following graph as input (arrows point from children to parents):
.. |image0| image:: ../images/monotonic-aggregates-graph.png
|image0|
Then the evaluation of the ``depth`` predicate proceeds as follows:
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **Stage** | **depth** | **Comments** |
+===========+============================================+==========================================================================================================================================================================+
| 0 |   | We always begin with the empty set. |
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | ``(0, b), (0, d), (0, e)`` | The nodes with no children have depth 0. The recursive step for **a** and **c** fails to produce a value, since some of their children do not have values for ``depth``. |
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 2 | ``(0, b), (0, d), (0, e), (1, c)`` | The recursive step for **c** succeeds, since ``depth`` now has a value for all its children (**d** and **e**). The recursive step for **a** still fails. |
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 3 | ``(0, b), (0, d), (0, e), (1, c), (2, a)`` | The recursive step for **a** succeeds, since ``depth`` now has a value for all its children (**b** and **c**). |
+-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Here, we can see that at the intermediate stages it is very important for the aggregate to
fail if some of the children lack a value - this prevents erroneous values being added.
.. index:: any

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
private import java
private import DataFlowUtil
private import DataFlowImplCommon::Public
private import DataFlowImplCommon
private import DataFlowDispatch
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.SSA
@@ -324,3 +324,5 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
guard.controls(n.asExpr().getBasicBlock(), arg.getBooleanValue().booleanNot())
)
}
int accessPathLimit() { result = 5 }

View File

@@ -380,10 +380,25 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
taintPreservingArgumentToMethod(m, i) and
tracked = sink.(MethodAccess).getArgument(i)
)
or
exists(MethodAccess ma |
taintPreservingArgumentToMethod(ma.getMethod()) and
tracked = ma.getAnArgument() and
sink = ma
)
}
/**
* Holds if `method` is a library method that return tainted data if its
* Holds if `method` is a library method that returns tainted data if any
* of its arguments are tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method) {
method.getDeclaringType() instanceof TypeString and
(method.hasName("format") or method.hasName("join"))
}
/**
* Holds if `method` is a library method that returns tainted data if its
* `arg`th argument is tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method, int arg) {

View File

@@ -32,7 +32,6 @@ nodes
| A2.java:15:15:15:28 | new Integer(...) : Number | semmle.label | new Integer(...) : Number |
| A2.java:27:27:27:34 | o : Number | semmle.label | o : Number |
| A2.java:29:9:29:9 | o | semmle.label | o |
| A2.java:37:10:37:10 | o | semmle.label | o |
| A.java:14:29:14:36 | o : Number | semmle.label | o : Number |
| A.java:16:9:16:9 | o | semmle.label | o |
| A.java:20:30:20:37 | o : Number | semmle.label | o : Number |

View File

@@ -1,6 +1,7 @@
| Read | A.java:5:12:5:15 | this | A.java:5:12:5:19 | this.foo | A.java:2:7:2:9 | foo |
| Read | A.java:21:13:21:13 | a | A.java:21:13:21:22 | getFoo(...) | A.java:2:7:2:9 | foo |
| Read | A.java:23:9:23:9 | a | A.java:23:9:23:19 | aGetter(...) | A.java:2:7:2:9 | foo |
| Read | A.java:24:9:24:10 | a2 | A.java:24:9:24:23 | notAGetter(...) | A.java:2:7:2:9 | foo |
| Read | A.java:45:12:45:38 | maybeIdWrap(...) | A.java:45:12:45:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
| Read | A.java:49:12:49:38 | maybeIdWrap(...) | A.java:49:12:49:42 | maybeIdWrap(...).foo | A.java:2:7:2:9 | foo |
| Store | A.java:9:16:9:16 | x | A.java:9:5:9:8 | this [post update] | A.java:2:7:2:9 | foo |

View File

@@ -1,8 +1,18 @@
import java
import semmle.code.java.dataflow.internal.DataFlowImplCommon::Public
import semmle.code.java.dataflow.internal.DataFlowImplCommon
import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Public
import semmle.code.java.dataflow.internal.DataFlowImplSpecific::Private
private predicate read(Node n1, Content f, Node n2) {
readDirect(n1, f, n2) or
argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
}
private predicate store(Node n1, Content f, Node n2) {
storeDirect(n1, f, n2) or
argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
}
from Node n1, Content f, Node n2, string k
where
read(n1, f, n2) and k = "Read"

View File

@@ -14,6 +14,7 @@
+ semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-079/UnsafeJQueryPlugin.ql: /Security/CWE/CWE-079
+ semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089
+ semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094
+ semmlecode-javascript-queries/Security/CWE-094/UnsafeDynamicMethodAccess.ql: /Security/CWE/CWE-094

View File

@@ -0,0 +1,101 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Library plugins, such as those for the jQuery library, are often
configurable through options provided by the clients of the
plugin.
Clients, however, do not know the implementation details
of the plugin, so it is important to document the capabilities of each
option. The documentation for the plugin options that the client is
responsible for sanitizing is of particular importance.
Otherwise, the plugin may write user input (for example, a URL query
parameter) to a web page without properly sanitizing it first,
which allows for a cross-site scripting vulnerability in the client
application through dynamic HTML construction.
</p>
</overview>
<recommendation>
<p>
Document all options that can lead to cross-site scripting
attacks, and guard against unsafe inputs where dynamic HTML
construction is not intended.
</p>
</recommendation>
<example>
<p>
The following example shows a jQuery plugin that selects a
DOM element, and copies its text content to another DOM element. The
selection is performed by using the plugin option
<code>sourceSelector</code> as a CSS selector.
</p>
<sample src="examples/UnsafeJQueryPlugin.js" />
<p>
This is, however, not a safe plugin, since the call to
<code>jQuery</code> interprets <code>sourceSelector</code> as HTML if
it is a string that starts with <code>&lt;</code>.
</p>
<p>
Instead of documenting that the client is responsible for
sanitizing <code>sourceSelector</code>, the plugin can use
<code>jQuery.find</code> to always interpret
<code>sourceSelector</code> as a CSS selector:
</p>
<sample src="examples/UnsafeJQueryPlugin_safe.js" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet">DOM based
XSS Prevention Cheat Sheet</a>.
</li>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
(Cross Site Scripting) Prevention Cheat Sheet</a>.
</li>
<li>
OWASP
<a href="https://www.owasp.org/index.php/DOM_Based_XSS">DOM Based XSS</a>.
</li>
<li>
OWASP
<a href="https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting">Types of Cross-Site
Scripting</a>.
</li>
<li>
Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
</li>
<li>
jQuery: <a href="https://learn.jquery.com/plugins/basic-plugin-creation/">Plugin creation</a>.
</li>
<li>
Bootstrap: <a href="https://github.com/twbs/bootstrap/pull/27047">XSS vulnerable bootstrap plugins</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Unsafe jQuery plugin
* @description A jQuery plugin that unintentionally constructs HTML from some of its options may be unsafe to use for clients.
* @kind path-problem
* @problem.severity warning
* @precision high
* @id js/unsafe-jquery-plugin
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116
* frameworks/jquery
*/
import javascript
import semmle.javascript.security.dataflow.UnsafeJQueryPlugin::UnsafeJQueryPlugin
import DataFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, JQueryPluginMethod plugin
where
cfg.hasFlowPath(source, sink) and
source.getNode().(Source).getPlugin() = plugin and
not isLikelyIntentionalHtmlSink(plugin, sink.getNode())
select sink.getNode(), source, sink, "Potential XSS vulnerability in the $@.", plugin,
"'$.fn." + plugin.getPluginName() + "' plugin"

View File

@@ -0,0 +1,6 @@
jQuery.fn.copyText = function(options) {
// BAD may evaluate `options.sourceSelector` as HTML
var source = jQuery(options.sourceSelector),
text = source.text();
jQuery(this).text(text);
}

View File

@@ -0,0 +1,6 @@
jQuery.fn.copyText = function(options) {
// GOOD may not evaluate `options.sourceSelector` as HTML
var source = jQuery.find(options.sourceSelector),
text = source.text();
jQuery(this).text(text);
}

View File

@@ -78,7 +78,7 @@ predicate importLookup(ASTNode path, Module target, string kind) {
or
exists(ReExportDeclaration red |
path = red.getImportedPath() and
target = red.getImportedModule()
target = red.getReExportedModule()
)
)
}

View File

@@ -273,12 +273,12 @@ class BulkReExportDeclaration extends ReExportDeclaration, @exportalldeclaration
override ConstantString getImportedPath() { result = getChildExpr(0) }
override predicate exportsAs(LexicalName v, string name) {
getImportedModule().exportsAs(v, name) and
getReExportedES2015Module().exportsAs(v, name) and
not isShadowedFromBulkExport(this, name)
}
override DataFlow::Node getSourceNode(string name) {
result = getImportedModule().getAnExport().getSourceNode(name)
result = getReExportedES2015Module().getAnExport().getSourceNode(name)
}
}
@@ -379,7 +379,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
v = spec.getLocal().(LexicalAccess).getALexicalName()
or
this.(ReExportDeclaration).getImportedModule().exportsAs(v, spec.getLocalName())
this.(ReExportDeclaration).getReExportedES2015Module().exportsAs(v, spec.getLocalName())
)
}
@@ -393,7 +393,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
not exists(getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
or
exists(ReExportDeclaration red | red = this |
result = red.getImportedModule().getAnExport().getSourceNode(spec.getLocalName())
result = red.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
)
)
}
@@ -545,14 +545,18 @@ class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
}
/**
* A namespace export specifier.
* A namespace export specifier, that is `*` or `* as x` occuring in an export declaration.
*
* Example:
* Examples:
*
* ```
* export
* * // namespace export specifier
* from 'a';
*
* export
* * as x // namespace export specifier
* from 'a';
* ```
*/
class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifier { }
@@ -564,6 +568,7 @@ class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifie
*
* ```
* export * from 'a'; // bulk re-export declaration
* export * as x from 'a'; // namespace re-export declaration
* export { x } from 'a'; // named re-export declaration
* export x from 'a'; // default re-export declaration
* ```
@@ -572,8 +577,23 @@ abstract class ReExportDeclaration extends ExportDeclaration {
/** Gets the path of the module from which this declaration re-exports. */
abstract ConstantString getImportedPath();
/** Gets the module from which this declaration re-exports. */
/**
* DEPRECATED. Use `getReExportedES2015Module()` instead.
*
* Gets the module from which this declaration re-exports.
*/
deprecated
ES2015Module getImportedModule() {
result = getReExportedModule()
}
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
ES2015Module getReExportedES2015Module() {
result = getReExportedModule()
}
/** Gets the module from which this declaration re-exports. */
Module getReExportedModule() {
result.getFile() = getEnclosingModule().resolve(getImportedPath().(PathExpr))
or
result = resolveFromTypeRoot()
@@ -641,4 +661,4 @@ class OriginalExportDeclaration extends ExportDeclaration {
result = this.(ExportDefaultDeclaration).getSourceNode(name) or
result = this.(ExportNamedDeclaration).getSourceNode(name)
}
}
}

View File

@@ -271,69 +271,84 @@ module TaintTracking {
ArrayFunctionTaintStep() { this = call }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
// `elt` and `ary`; similar for `forEach`
exists(string name, Function f, int i |
(name = "map" or name = "forEach") and
(i = 0 or i = 2) and
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
call.(DataFlow::MethodCallNode).getMethodName() = name and
pred = call.getReceiver() and
succ = DataFlow::parameterNode(f.getParameter(i))
)
or
// `array.map` with tainted return value in callback
exists(DataFlow::FunctionNode f |
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
pred = f.getAReturn() and
succ = call
)
or
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
exists(string name |
name = "push" or
name = "unshift"
|
pred = call.getAnArgument() and
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
)
or
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
exists(string name |
name = "push" or
name = "unshift"
|
pred = call.getASpreadArgument() and
// Make sure we handle reflective calls
succ = call.getReceiver().getALocalSource() and
call.getCalleeName() = name
)
or
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
exists(string name | name = "splice" |
pred = call.getArgument(2) and
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
)
or
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
exists(string name |
name = "pop" or
name = "shift" or
name = "slice" or
name = "splice"
|
call.(DataFlow::MethodCallNode).calls(pred, name) and
succ = call
)
or
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
pred = call.getAnArgument() and
succ = call
arrayFunctionTaintStep(pred, succ, call)
}
}
/**
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
*/
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
// `elt` and `ary`; similar for `forEach`
exists(string name, Function f, int i |
(name = "map" or name = "forEach") and
(i = 0 or i = 2) and
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
call.(DataFlow::MethodCallNode).getMethodName() = name and
pred = call.getReceiver() and
succ = DataFlow::parameterNode(f.getParameter(i))
)
or
// `array.map` with tainted return value in callback
exists(DataFlow::FunctionNode f |
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
pred = f.getAReturn() and
succ = call
)
or
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
exists(string name |
name = "push" or
name = "unshift"
|
pred = call.getAnArgument() and
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
)
or
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
exists(string name |
name = "push" or
name = "unshift"
|
pred = call.getASpreadArgument() and
// Make sure we handle reflective calls
succ = call.getReceiver().getALocalSource() and
call.getCalleeName() = name
)
or
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
exists(string name | name = "splice" |
pred = call.getArgument(2) and
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
)
or
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
exists(string name |
name = "pop" or
name = "shift" or
name = "slice" or
name = "splice"
|
call.(DataFlow::MethodCallNode).calls(pred, name) and
succ = call
)
or
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
pred = call.getAnArgument() and
succ = call
or
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
succ = call
or
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
succ = call and
pred = call.getAnArgument()
}
/**
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
* `k` is not a constant and `o` refers to some object literal; in this case, we consider

View File

@@ -65,7 +65,7 @@ private predicate mayDynamicallyComputeExports(Module m) {
or
// `m` re-exports all exports of some other module that dynamically computes its exports
exists(BulkReExportDeclaration rexp | rexp = m.(ES2015Module).getAnExport() |
mayDynamicallyComputeExports(rexp.getImportedModule())
mayDynamicallyComputeExports(rexp.getReExportedModule())
)
}
@@ -79,7 +79,7 @@ private predicate relevantExport(ES2015Module m, string x) {
)
or
exists(ReExportDeclaration rexp, string y |
rexp.getImportedModule() = m and
rexp.getReExportedModule() = m and
reExportsAs(rexp, x, y)
)
}
@@ -110,9 +110,9 @@ private predicate incompleteExport(ES2015Module m, string y) {
mayDependOnLookupPath(rexp.getImportedPath().getStringValue())
or
// unresolvable path
not exists(rexp.getImportedModule())
not exists(rexp.getReExportedModule())
or
exists(Module n | n = rexp.getImportedModule() |
exists(Module n | n = rexp.getReExportedModule() |
// re-export from CommonJS/AMD
mayDynamicallyComputeExports(n)
or
@@ -399,3 +399,16 @@ private class AnalyzedClosureGlobalAccessPath extends AnalyzedNode, AnalyzedProp
)
}
}
/**
* A namespace export declaration analyzed as a property write.
*/
private class AnalyzedExportNamespaceSpecifier extends AnalyzedPropertyWrite, DataFlow::ValueNode {
override ExportNamespaceSpecifier astNode;
override predicate writesValue(AbstractValue baseVal, string propName, AbstractValue value) {
baseVal = TAbstractExportsObject(getTopLevel()) and
propName = astNode.getExportedName() and
value = TAbstractExportsObject(astNode.getExportDeclaration().(ReExportDeclaration).getReExportedModule())
}
}

View File

@@ -549,4 +549,72 @@ module ClientRequest {
)
}
}
/**
* Gets a reference to an instance of `chrome-remote-interface`.
*
* An instantiation of `chrome-remote-interface` either accepts a callback or returns a promise.
*
* The `isPromise` parameter reflects whether the reference is a promise containing
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
*/
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
t.start() and
exists(DataFlow::CallNode call |
call = DataFlow::moduleImport("chrome-remote-interface").getAnInvocation()
|
result = call and isPromise = true
or
result = call.getCallback([0 .. 1]).getParameter(0) and isPromise = false
)
or
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2, isPromise).track(t2, t))
or
// Simple promise tracking.
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
pred = chromeRemoteInterface(t2, true) and
isPromise = false and
(
t2 = t and
exists(AwaitExpr await | DataFlow::valueNode(await.getOperand()).getALocalSource() = pred |
result.getEnclosingExpr() = await
)
or
t2 = t and
exists(DataFlow::MethodCallNode thenCall |
thenCall.getMethodName() = "then" and pred = thenCall.getReceiver().getALocalSource()
|
result = thenCall.getCallback(0).getParameter(0)
)
)
)
}
/**
* A call to navigate a browser controlled by `chrome-remote-interface` to a specific URL.
*/
class ChromeRemoteInterfaceRequest extends ClientRequest::Range, DataFlow::CallNode {
int optionsArg;
ChromeRemoteInterfaceRequest() {
exists(DataFlow::SourceNode instance |
instance = chromeRemoteInterface(DataFlow::TypeTracker::end(), false)
|
optionsArg = 0 and
this = instance.getAPropertyRead("Page").getAMemberCall("navigate")
or
optionsArg = 1 and
this = instance.getAMemberCall("send") and
this.getArgument(0).mayHaveStringValue("Page.navigate")
)
}
override DataFlow::Node getUrl() {
result = getArgument(optionsArg).getALocalSource().getAPropertyWrite("url").getRhs()
}
override DataFlow::Node getHost() { none() }
override DataFlow::Node getADataNode() { none() }
}
}

View File

@@ -119,8 +119,11 @@ module HTTP {
}
/**
* DEPRECATED: Use `http` or `https` directly as appropriate.
*
* Gets the string `http` or `https`.
*/
deprecated
string httpOrHttps() { result = "http" or result = "https" }
/**

View File

@@ -42,7 +42,18 @@ module NodeJSLib {
* Holds if `call` is an invocation of `http.createServer` or `https.createServer`.
*/
predicate isCreateServer(CallExpr call) {
call = DataFlow::moduleMember(HTTP::httpOrHttps(), "createServer").getAnInvocation().asExpr()
exists(string pkg, string fn |
pkg = "http" and fn = "createServer"
or
pkg = "https" and fn = "createServer"
or
// http2 compatibility API
pkg = "http2" and fn = "createServer"
or
pkg = "http2" and fn = "createSecureServer"
|
call = DataFlow::moduleMember(pkg, fn).getAnInvocation().asExpr()
)
}
/**
@@ -356,10 +367,12 @@ module NodeJSLib {
/** An expression that is passed as `http.request({ auth: <expr> }, ...)`. */
class Credentials extends CredentialsExpr {
Credentials() {
this = DataFlow::moduleMember(HTTP::httpOrHttps(), "request")
.getACall()
.getOptionArgument(0, "auth")
.asExpr()
exists(string http | http = "http" or http = "https" |
this = DataFlow::moduleMember(http, "request")
.getACall()
.getOptionArgument(0, "auth")
.asExpr()
)
}
override string getCredentialsKind() { result = "credentials" }
@@ -881,7 +894,6 @@ module NodeJSLib {
override string getSourceType() { result = "NodeJSClientRequest error event" }
}
/**
* An NodeJS EventEmitter instance.
* Events dispatched on this EventEmitter will be handled by event handlers registered on this EventEmitter.

View File

@@ -30,9 +30,14 @@ private DataFlow::Node commandArgument(SystemCommandExecution sys, DataFlow::Typ
t.start() and
result = sys.getACommandArgument()
or
exists(DataFlow::TypeBackTracker t2 |
t = t2.smallstep(result, commandArgument(sys, t2))
)
exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, commandArgument(sys, t2)))
}
/**
* Gets a data-flow node whose value ends up being interpreted as the command argument in `sys`.
*/
private DataFlow::Node commandArgument(SystemCommandExecution sys) {
result = commandArgument(sys, DataFlow::TypeBackTracker::end())
}
/**
@@ -43,11 +48,23 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys, DataFlow::
t.start() and
result = sys.getArgumentList().getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 |
result = argumentList(sys, t2).backtrack(t2, t)
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode pred |
pred = argumentList(sys, t2)
|
result = pred.backtrack(t2, t)
or
t = t2.continue() and
TaintTracking::arrayFunctionTaintStep(result, pred, _)
)
}
/**
* Gets a data-flow node whose value ends up being interpreted as the argument list in `sys`.
*/
private DataFlow::SourceNode argumentList(SystemCommandExecution sys) {
result = argumentList(sys, DataFlow::TypeBackTracker::end())
}
/**
* Holds if `source` contributes to the arguments of an indirect command execution `sys`.
*
@@ -61,15 +78,22 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys, DataFlow::
* let args = ["-c", cmd];
* childProcess.spawn(sh, args, cb);
* ```
* or
* ```
* let cmd = getCommand();
* childProcess.spawn("cmd.exe", ["/c"].concat(cmd), cb);
* ```
*/
predicate isIndirectCommandArgument(DataFlow::Node source, SystemCommandExecution sys) {
exists(
DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC
|
exists(DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC |
shellCmd(shell.asExpr(), dashC) and
shell = commandArgument(sys, DataFlow::TypeBackTracker::end()) and
args = argumentList(sys, DataFlow::TypeBackTracker::end()) and
shell = commandArgument(sys) and
args.getAPropertyWrite().getRhs().mayHaveStringValue(dashC) and
source = args.getAPropertyWrite().getRhs()
args = argumentList(sys) and
(
source = argumentList(sys)
or
source = argumentList(sys).getAPropertyWrite().getRhs()
)
)
}

View File

@@ -0,0 +1,55 @@
/**
* Provides a taint-tracking configuration for reasoning about DOM-based
* cross-site scripting vulnerabilities in unsafe jQuery plugins.
*/
import javascript
import semmle.javascript.security.dataflow.Xss
module UnsafeJQueryPlugin {
import UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin
/**
* A taint-tracking configuration for reasoning about XSS in unsafe jQuery plugins.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "UnsafeJQueryPlugin" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or
node instanceof DomBasedXss::Sanitizer
or
node instanceof Sanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
// jQuery plugins tend to be implemented as classes that store data in fields initialized by the constructor.
DataFlow::localFieldStep(src, sink)
}
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
// prefixing prevents forced html/css confusion:
// prefixing through concatenation:
StringConcatenation::taintStep(pred, succ, _, any(int i | i >= 1))
or
// prefixing through a poor-mans templating system:
exists(DataFlow::MethodCallNode replace |
replace = succ and
pred = replace.getArgument(1) and
replace.getMethodName() = "replace"
)
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
super.isSanitizerGuard(node) or
node instanceof IsElementSanitizer or
node instanceof PropertyPresenceSanitizer
}
}
}

View File

@@ -0,0 +1,258 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* unsafe jQuery plugins, as well as extension points for adding your
* own.
*/
import javascript
private import semmle.javascript.dataflow.InferredTypes
import semmle.javascript.security.dataflow.Xss
module UnsafeJQueryPlugin {
private import DataFlow::FlowLabel
/**
* A data flow source for unsafe jQuery plugins.
*/
abstract class Source extends DataFlow::Node {
/**
* Gets the plugin that this source is used in.
*/
abstract JQueryPluginMethod getPlugin();
}
/**
* A data flow sink for unsafe jQuery plugins.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for unsafe jQuery plugins.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* An argument that may act as a HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
*/
class AmbiguousHtmlOrSelectorArgument extends DataFlow::Node {
AmbiguousHtmlOrSelectorArgument() {
exists(JQuery::MethodCall call |
call.interpretsArgumentAsSelector(this) and call.interpretsArgumentAsHtml(this)
) and
// the $-function in particular will not construct HTML for non-string values
analyze().getAType() = TTString() and
// any fixed prefix makes the call unambiguous
not exists(DataFlow::Node prefix |
DomBasedXss::isPrefixOfJQueryHtmlString(this, prefix) and
prefix.mayHaveStringValue(_)
)
}
}
/**
* Holds for jQuery plugin definitions of the form `$.fn.<pluginName> = <plugin>`.
*/
private predicate jQueryPluginDefinition(string pluginName, DataFlow::Node plugin) {
exists(DataFlow::PropRead fn, DataFlow::PropWrite write |
fn = jquery().getAPropertyRead("fn") and
(
write = fn.getAPropertyWrite()
or
exists(ExtendCall extend, DataFlow::SourceNode source |
fn.flowsTo(extend.getDestinationOperand()) and
source = extend.getASourceOperand() and
write = source.getAPropertyWrite()
)
) and
plugin = write.getRhs() and
(
pluginName = write.getPropertyName() or
write.getPropertyNameExpr().flow().mayHaveStringValue(pluginName)
)
)
}
/**
* Gets a node that is registered as a jQuery plugin method at `def`.
*/
private DataFlow::SourceNode getAJQueryPluginMethod(
DataFlow::TypeBackTracker t, DataFlow::Node def
) {
t.start() and
jQueryPluginDefinition(_, def) and
result.flowsTo(def)
or
exists(DataFlow::TypeBackTracker t2 | result = getAJQueryPluginMethod(t2, def).backtrack(t2, t))
}
/**
* Gets a function that is registered as a jQuery plugin method at `def`.
*/
private DataFlow::FunctionNode getAJQueryPluginMethod(DataFlow::Node def) {
result = getAJQueryPluginMethod(DataFlow::TypeBackTracker::end(), def)
}
/**
* Gets an operand to `extend`.
*/
private DataFlow::SourceNode getAnExtendOperand(DataFlow::TypeBackTracker t, ExtendCall extend) {
t.start() and
result.flowsTo(extend.getAnOperand())
or
exists(DataFlow::TypeBackTracker t2 | result = getAnExtendOperand(t2, extend).backtrack(t2, t))
}
/**
* Gets an operand to `extend`.
*/
private DataFlow::SourceNode getAnExtendOperand(ExtendCall extend) {
result = getAnExtendOperand(DataFlow::TypeBackTracker::end(), extend)
}
/**
* A function that is registered as a jQuery plugin method.
*/
class JQueryPluginMethod extends DataFlow::FunctionNode {
string pluginName;
JQueryPluginMethod() {
exists(DataFlow::Node def |
jQueryPluginDefinition(pluginName, def) and
this = getAJQueryPluginMethod(def)
)
}
/**
* Gets the name of this plugin.
*/
string getPluginName() { result = pluginName }
}
/**
* Holds if `plugin` has a default option defined at `def`.
*/
private predicate hasDefaultOption(JQueryPluginMethod plugin, DataFlow::PropWrite def) {
exists(ExtendCall extend, JQueryPluginOptions options, DataFlow::SourceNode default |
options.getPlugin() = plugin and
options = getAnExtendOperand(extend) and
default = getAnExtendOperand(extend) and
default.getAPropertyWrite() = def
)
}
/**
* The client-provided options object for a jQuery plugin.
*/
class JQueryPluginOptions extends DataFlow::ParameterNode {
JQueryPluginMethod method;
JQueryPluginOptions() {
exists(string optionsPattern |
optionsPattern = "(?i)(opt(ion)?s?)" and
if method.getAParameter().getName().regexpMatch(optionsPattern)
then (
// use the last parameter named something like "options" if it exists ...
getName().regexpMatch(optionsPattern) and
this = method.getAParameter()
) else (
// ... otherwise, use the last parameter, unless it looks like a DOM node
this = method.getLastParameter() and
not getName().regexpMatch("(?i)(e(l(em(ent(s)?)?)?)?)")
)
)
}
/**
* Gets the plugin method that these options are used in.
*/
JQueryPluginMethod getPlugin() { result = method }
}
/**
* Expression of form `isElement(x)`, which sanitizes `x`.
*/
class IsElementSanitizer extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
IsElementSanitizer() {
// common ad hoc sanitizing calls
exists(string name | getCalleeName() = name |
name = "isElement" or name = "isDocument" or name = "isWindow"
)
}
override predicate sanitizes(boolean outcome, Expr e) {
outcome = true and e = getArgument(0).asExpr()
}
}
/**
* Expression like `typeof x.<?> !== "undefined"` or `x.<?>`, which sanitizes `x`, as it is unlikely to be a string afterwards.
*/
class PropertyPresenceSanitizer extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
DataFlow::Node input;
boolean polarity;
PropertyPresenceSanitizer() {
exists(DataFlow::PropRead read, string name |
not name = "length" and read.accesses(input, name)
|
exists(EqualityTest test |
polarity = test.getPolarity().booleanNot() and
this = test.flow()
|
exists(Expr undef | test.hasOperands(read.asExpr(), undef) |
SyntacticConstants::isUndefined(undef)
)
or
exists(Expr op1, Expr op2 | test.hasOperands(op1, op2) |
read.asExpr() = op1.(TypeofExpr).getOperand() and
op2.mayHaveStringValue(any(InferredType t | t = TTUndefined()).getTypeofTag())
)
)
or
polarity = true and
this = read
)
}
override predicate sanitizes(boolean outcome, Expr e) {
outcome = polarity and
e = input.asExpr()
}
}
/**
* The client-provided options object for a jQuery plugin, considered as a source for unsafe jQuery plugins.
*/
class JQueryPluginOptionsAsSource extends Source, JQueryPluginOptions {
override JQueryPluginMethod getPlugin() { result = JQueryPluginOptions.super.getPlugin() }
}
/**
* An argument that may act as a HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
*/
class AmbiguousHtmlOrSelectorArgumentAsSink extends Sink {
AmbiguousHtmlOrSelectorArgumentAsSink() { this instanceof AmbiguousHtmlOrSelectorArgument }
}
/**
* A hint that a value is expected to be treated as a HTML fragment later.
*/
class IntentionalHtmlFragmentHint extends Sanitizer {
IntentionalHtmlFragmentHint() {
this.(DataFlow::PropRead).getPropertyName().regexpMatch("(?i).*(html|template).*")
}
}
/**
* Holds if `plugin` likely expects `sink` to be treated as a HTML fragment.
*/
predicate isLikelyIntentionalHtmlSink(JQueryPluginMethod plugin, Sink sink) {
exists(DataFlow::PropWrite defaultDef, string default, DataFlow::PropRead finalRead |
hasDefaultOption(plugin, defaultDef) and
defaultDef.getPropertyName() = finalRead.getPropertyName() and
defaultDef.getRhs().mayHaveStringValue(default) and
default.regexpMatch("\\s*<.*") and
finalRead.flowsTo(sink)
)
}
}

View File

@@ -106,7 +106,7 @@ module DomBasedXss {
* Holds if `prefix` is a prefix of `htmlString`, which may be intepreted as
* HTML by a jQuery method.
*/
private predicate isPrefixOfJQueryHtmlString(DataFlow::Node htmlString, DataFlow::Node prefix) {
predicate isPrefixOfJQueryHtmlString(DataFlow::Node htmlString, DataFlow::Node prefix) {
any(JQuery::MethodCall call).interpretsArgumentAsHtml(htmlString) and
prefix = htmlString
or

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