Merge branch 'main' into py/add-ssrf-sinks

This commit is contained in:
Rasmus Wriedt Larsen
2022-03-04 11:50:12 +01:00
414 changed files with 32260 additions and 31653 deletions

View File

@@ -63,6 +63,7 @@ jobs:
qltest:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
slice: ["1/2", "2/2"]
steps:

29
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,29 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: local
hooks:
- id: codeql-format
name: Fix QL file formatting
files: \.qll?$
language: system
entry: codeql query format --in-place
- id: sync-files
name: Fix files required to be identical
language: system
entry: python3 config/sync-files.py --latest
pass_filenames: false
- id: qhelp
name: Check query help generation
files: \.qhelp$
language: system
entry: python3 misc/scripts/check-qhelp.py

View File

@@ -42,7 +42,11 @@ If you have an idea for a query that you would like to share with other CodeQL u
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on how to install the hook.
If you prefer, you can either:
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
2. use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted.
See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on the two approaches.
4. **Compilation**
@@ -63,6 +67,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
## Using your personal data
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.

View File

@@ -1,3 +1,9 @@
## 0.0.10
### New Features
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.
## 0.0.9
## 0.0.8

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Many queries now support structured bindings, as structured bindings are now handled in the IR translation.

View File

@@ -1,4 +1,5 @@
---
category: feature
---
## 0.0.10
### New Features
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.10

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.0.10-dev
version: 0.0.11-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -241,8 +241,8 @@ private module Cached {
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(i) and
result = resolveCall(call).getParameter(i)
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
)
or
// For compatibility, send flow into a `Variable` if there is flow to any

View File

@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
or
instr instanceof InheritanceConversionInstruction and
(
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
}
private predicate variableAddressValueNumber(
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
// 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
unique( | | instr.getIRVariable().getAST()) = ast
}
private predicate initializeParameterValueNumber(
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
unique( | | instr.getDerivedClass()) = derivedClass
}
private predicate loadTotalOverlapValueNumber(

View File

@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
or
instr instanceof InheritanceConversionInstruction and
(
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
}
private predicate variableAddressValueNumber(
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
// 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
unique( | | instr.getIRVariable().getAST()) = ast
}
private predicate initializeParameterValueNumber(
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
unique( | | instr.getDerivedClass()) = derivedClass
}
private predicate loadTotalOverlapValueNumber(

View File

@@ -71,7 +71,8 @@ newtype TInstructionTag =
AsmTag() or
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or
ThisAddressTag() or
ThisLoadTag()
ThisLoadTag() or
StructuredBindingAccessTag()
class InstructionTag extends TInstructionTag {
final string toString() { result = "Tag" }
@@ -221,4 +222,6 @@ string getInstructionTagId(TInstructionTag tag) {
tag = ThisAddressTag() and result = "ThisAddress"
or
tag = ThisLoadTag() and result = "ThisLoad"
or
tag = StructuredBindingAccessTag() and result = "StructuredBindingAccess"
}

View File

@@ -3,6 +3,7 @@ private import semmle.code.cpp.ir.implementation.IRType
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.IRUtilities
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
@@ -813,7 +814,9 @@ abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr {
}
class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
TranslatedNonFieldVariableAccess() { not expr instanceof FieldAccess }
TranslatedNonFieldVariableAccess() {
not expr instanceof FieldAccess and not isNonReferenceStructuredBinding(expr.getTarget())
}
override Instruction getFirstInstruction() {
if exists(this.getQualifier())
@@ -860,6 +863,71 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
}
}
/**
* The IR translation of a variable access of a structured binding, where the type
* of the structured binding is not of a reference type, e.g., `x0` and `x1`
* in `auto [x0, x1] = xs` where `xs` is an array. Although the type of the
* structured binding is a non-reference type, the structured binding behaves
* like a reference. Hence, the translation requires a `VariableAddress` followed
* by a `Load` instead of only a `VariableAddress` as produced by
* `TranslatedVariableAccess`.
*/
class TranslatedStructuredBindingVariableAccess extends TranslatedNonConstantExpr {
override VariableAccess expr;
TranslatedStructuredBindingVariableAccess() { isNonReferenceStructuredBinding(expr.getTarget()) }
override Instruction getFirstInstruction() {
// Structured bindings cannot be qualified.
result = this.getInstruction(StructuredBindingAccessTag())
}
override TranslatedElement getChild(int id) {
// Structured bindings cannot be qualified.
none()
}
override Instruction getResult() { result = this.getInstruction(LoadTag()) }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = StructuredBindingAccessTag() and
kind instanceof GotoEdge and
result = this.getInstruction(LoadTag())
or
tag = LoadTag() and
kind instanceof GotoEdge and
result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) { none() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = StructuredBindingAccessTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(this.getLValueReferenceType())
or
tag = LoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(this.getLValueReferenceType())
}
private LValueReferenceType getLValueReferenceType() {
// The extractor ensures `result` exists when `isNonReferenceStructuredBinding(expr.getTarget())` holds.
result.getBaseType() = expr.getUnspecifiedType()
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = LoadTag() and
operandTag instanceof AddressOperandTag and
result = this.getInstruction(StructuredBindingAccessTag())
}
override IRVariable getInstructionVariable(InstructionTag tag) {
tag = StructuredBindingAccessTag() and
result = getIRUserVariable(expr.getEnclosingFunction(), expr.getTarget())
}
}
class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
override FunctionAccess expr;

View File

@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
or
instr instanceof InheritanceConversionInstruction and
(
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
}
private predicate variableAddressValueNumber(
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
// 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
unique( | | instr.getIRVariable().getAST()) = ast
}
private predicate initializeParameterValueNumber(
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
unique( | | instr.getDerivedClass()) = derivedClass
}
private predicate loadTotalOverlapValueNumber(

View File

@@ -11,6 +11,15 @@ private Type getDecayedType(Type type) {
result.(PointerType).getBaseType() = type.(ArrayType).getBaseType()
}
/**
* Holds if the sepcified variable is a structured binding with a non-reference
* type.
*/
predicate isNonReferenceStructuredBinding(Variable v) {
v.isStructuredBinding() and
not v.getUnspecifiedType() instanceof ReferenceType
}
/**
* Get the actual type of the specified variable, as opposed to the declared type.
* This returns the type of the variable after any pointer decay is applied, and
@@ -30,7 +39,12 @@ Type getVariableType(Variable v) {
result = v.getInitializer().getExpr().getType()
or
not exists(v.getInitializer()) and result = v.getType()
else result = v.getType()
else
if isNonReferenceStructuredBinding(v)
then
// The extractor ensures `r` exists when `isNonReferenceStructuredBinding(v)` holds.
exists(LValueReferenceType r | r.getBaseType() = v.getUnspecifiedType() | result = r)
else result = v.getType()
)
}

View File

@@ -1,3 +1,9 @@
## 0.0.10
### Deprecated Classes
* The `CodeDuplication.Copy`, `CodeDuplication.DuplicateBlock`, and `CodeDuplication.SimilarBlock` classes have been deprecated.
## 0.0.9
### New Queries

View File

@@ -3,7 +3,7 @@
* @description Exposing system data or debugging information helps
* an adversary learn about the system and form an
* attack plan.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @security-severity 6.5
* @precision medium
@@ -14,7 +14,9 @@
import cpp
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.OutputWrite
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import DataFlow::PathGraph
/**
* An element that should not be exposed to an adversary.
@@ -24,42 +26,19 @@ abstract class SystemData extends Element {
* Gets an expression that is part of this `SystemData`.
*/
abstract Expr getAnExpr();
/**
* Gets an expression whose value originates from, or is used by,
* this `SystemData`.
*/
Expr getAnExprIndirect() {
// direct SystemData
result = this.getAnExpr() or
// flow via global or member variable (conservative approximation)
result = this.getAnAffectedVar().getAnAccess() or
// flow via stack variable
definitionUsePair(_, this.getAnExprIndirect(), result) or
useUsePair(_, this.getAnExprIndirect(), result) or
useUsePair(_, result, this.getAnExprIndirect()) or
// flow from assigned value to assignment expression
result.(AssignExpr).getRValue() = this.getAnExprIndirect()
}
/**
* Gets a global or member variable that may be affected by this system
* data (conservative approximation).
*/
private Variable getAnAffectedVar() {
(
result.getAnAssignedValue() = this.getAnExprIndirect() or
result.getAnAccess() = this.getAnExprIndirect()
) and
not result instanceof LocalScopeVariable
}
}
/**
* Data originating from the environment.
*/
class EnvData extends SystemData {
EnvData() { this instanceof EnvironmentRead }
EnvData() {
// identify risky looking environment variables only
this.(EnvironmentRead)
.getEnvironmentVariable()
.toLowerCase()
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
}
override Expr getAnExpr() { result = this }
}
@@ -91,11 +70,6 @@ class SQLConnectInfo extends SystemData {
}
private predicate posixSystemInfo(FunctionCall source, Element use) {
// long sysconf(int name)
// - various OS / system values and limits
source.getTarget().hasName("sysconf") and
use = source
or
// size_t confstr(int name, char *buf, size_t len)
// - various OS / system strings, such as the libc version
// int statvfs(const char *__path, struct statvfs *__buf)
@@ -311,70 +285,31 @@ class RegQuery extends SystemData {
override Expr getAnExpr() { regQuery(this, result) }
}
/**
* Somewhere data is output.
*/
abstract class DataOutput extends Element {
/**
* Get an expression containing data that is output.
*/
abstract Expr getASource();
}
class ExposedSystemDataConfiguration extends TaintTracking::Configuration {
ExposedSystemDataConfiguration() { this = "ExposedSystemDataConfiguration" }
/**
* Data that is output via standard output or standard error.
*/
class StandardOutput extends DataOutput instanceof OutputWrite {
override Expr getASource() { result = OutputWrite.super.getASource() }
}
override predicate isSource(DataFlow::Node source) {
source.asConvertedExpr() = any(SystemData sd).getAnExpr()
}
private predicate socketCallOrIndirect(FunctionCall call) {
// direct socket call
// int socket(int domain, int type, int protocol);
call.getTarget().getName() = "socket"
or
exists(ReturnStmt rtn |
// indirect socket call
call.getTarget() = rtn.getEnclosingFunction() and
(
socketCallOrIndirect(rtn.getExpr()) or
socketCallOrIndirect(rtn.getExpr().(VariableAccess).getTarget().getAnAssignedValue())
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall fc, FunctionInput input, int arg |
fc.getTarget().(RemoteFlowSinkFunction).hasRemoteFlowSink(input, _) and
input.isParameterDeref(arg) and
fc.getArgument(arg).getAChild*() = sink.asExpr()
)
)
}
}
private predicate socketFileDescriptor(Expr e) {
exists(Variable var, FunctionCall socket |
socketCallOrIndirect(socket) and
var.getAnAssignedValue() = socket and
e = var.getAnAccess()
)
}
private predicate socketOutput(FunctionCall call, Expr data) {
(
// ssize_t send(int sockfd, const void *buf, size_t len, int flags);
// ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
// const struct sockaddr *dest_addr, socklen_t addrlen);
// ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
// int write(int handle, void *buffer, int nbyte);
call.getTarget().hasGlobalName(["send", "sendto", "sendmsg", "write"]) and
data = call.getArgument(1) and
socketFileDescriptor(call.getArgument(0))
)
}
/**
* Data that is output via a socket.
*/
class SocketOutput extends DataOutput {
SocketOutput() { socketOutput(this, _) }
override Expr getASource() { socketOutput(this, result) }
}
from SystemData sd, DataOutput ow
from ExposedSystemDataConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where
sd.getAnExprIndirect() = ow.getASource() or
sd.getAnExprIndirect() = ow.getASource().getAChild*()
select ow, "This operation exposes system data from $@.", sd, sd.toString()
config.hasFlowPath(source, sink) and
not exists(
DataFlow::Node alt // remove duplicate results on conversions
|
config.hasFlow(source.getNode(), alt) and
alt.asConvertedExpr() = sink.getNode().asExpr() and
alt != sink.getNode()
)
select sink, source, sink, "This operation exposes system data from $@.", source,
source.getNode().toString()

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/system-data-exposure` query has been modernized and has converted to a `path-problem` query. There are now fewer false positive results.

View File

@@ -1,4 +1,5 @@
---
category: deprecated
---
## 0.0.10
### Deprecated Classes
* The `CodeDuplication.Copy`, `CodeDuplication.DuplicateBlock`, and `CodeDuplication.SimilarBlock` classes have been deprecated.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.10

View File

@@ -2,7 +2,6 @@
# These queries are infeasible to compute on large projects:
- exclude:
query path:
- Security/CWE/CWE-497/ExposedSystemData.ql
- Critical/DescriptorMayNotBeClosed.ql
- Critical/DescriptorNeverClosed.ql
- Critical/FileMayNotBeClosed.ql

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.0.10-dev
version: 0.0.11-dev
groups:
- cpp
- queries

File diff suppressed because it is too large Load Diff

View File

@@ -1471,31 +1471,43 @@ void array_structured_binding() {
// explicit reference version
{
auto& unnamed_local_variable = xs;
auto& x0 = xs[0];
auto& x1 = xs[1];
auto& x0 = unnamed_local_variable[0];
auto& x1 = unnamed_local_variable[1];
x1 = 3;
int &rx1 = x1;
int x = x1;
}
}
struct StructuredBindingDataMemberMemberStruct {
int x = 5;
};
struct StructuredBindingDataMemberStruct {
typedef int ArrayType[2];
typedef int &RefType;
int i = 1;
double d = 2.0;
unsigned int b : 3;
int& r = i;
int* p = &i;
ArrayType xs = {1, 2};
RefType r_alt = i;
StructuredBindingDataMemberMemberStruct m;
};
void data_member_structured_binding() {
StructuredBindingDataMemberStruct s;
// structured binding use
{
auto [i, d, b, r] = s;
auto [i, d, b, r, p, xs, r_alt, m] = s;
d = 4.0;
double& rd = d;
int v = i;
r = 5;
*p = 6;
int& rr = r;
int* pr = &r;
int w = r;
}
// explicit reference version
@@ -1505,58 +1517,67 @@ void data_member_structured_binding() {
auto& d = unnamed_local_variable.d;
// no equivalent for b
auto& r = unnamed_local_variable.r;
auto& p = unnamed_local_variable.p;
d = 4.0;
double& rd = d;
int v = i;
r = 5;
*p = 6;
int& rr = r;
int* pr = &r;
int w = r;
}
}
struct StructuredBindingTuple;
namespace std {
template<typename T>
struct tuple_size;
template<>
struct tuple_size<StructuredBindingTuple> {
static const unsigned int value = 3;
};
template<int, typename T>
struct tuple_element;
template<>
struct tuple_element<0, StructuredBindingTuple> {
using type = int;
};
template<>
struct tuple_element<1, StructuredBindingTuple> {
using type = double;
};
template<>
struct tuple_element<2, StructuredBindingTuple> {
using type = int&;
};
}
struct StructuredBindingTuple {
struct StructuredBindingTupleRefGet {
int i = 1;
double d = 2.2;
int& r = i;
template<int i>
typename std::tuple_element<i, StructuredBindingTuple>::type& get();
typename std::tuple_element<i, StructuredBindingTupleRefGet>::type& get();
};
template<>
std::tuple_element<0, StructuredBindingTuple>::type& StructuredBindingTuple::get<0>() { return i; }
template<>
std::tuple_element<1, StructuredBindingTuple>::type& StructuredBindingTuple::get<1>() { return d; }
template<>
std::tuple_element<2, StructuredBindingTuple>::type& StructuredBindingTuple::get<2>() { return r; }
struct std::tuple_size<StructuredBindingTupleRefGet> {
static const unsigned int value = 3;
};
void tuple_structured_binding() {
StructuredBindingTuple t;
template<>
struct std::tuple_element<0, StructuredBindingTupleRefGet> {
using type = int;
};
template<>
struct std::tuple_element<1, StructuredBindingTupleRefGet> {
using type = double;
};
template<>
struct std::tuple_element<2, StructuredBindingTupleRefGet> {
using type = int&;
};
template<>
std::tuple_element<0, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<0>() {
return i;
}
template<>
std::tuple_element<1, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<1>() {
return d;
}
template<>
std::tuple_element<2, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<2>() {
return r;
}
void tuple_structured_binding_ref_get() {
StructuredBindingTupleRefGet t;
// structured binding use
{
auto [i, d, r] = t;
@@ -1582,4 +1603,70 @@ void tuple_structured_binding() {
}
}
struct StructuredBindingTupleNoRefGet {
int i = 1;
int& r = i;
template<int i>
typename std::tuple_element<i, StructuredBindingTupleNoRefGet>::type get();
};
template<>
struct std::tuple_size<StructuredBindingTupleNoRefGet> {
static const unsigned int value = 3;
};
template<>
struct std::tuple_element<0, StructuredBindingTupleNoRefGet> {
using type = int;
};
template<>
struct std::tuple_element<1, StructuredBindingTupleNoRefGet> {
using type = int&;
};
template<>
struct std::tuple_element<2, StructuredBindingTupleNoRefGet> {
using type = int&&;
};
template<>
std::tuple_element<0, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<0>() {
return i;
}
template<>
std::tuple_element<1, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<1>() {
return r;
}
template<>
std::tuple_element<2, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<2>() {
return 5;
}
void tuple_structured_binding_no_ref_get() {
StructuredBindingTupleNoRefGet t;
//structured binding use
{
auto&& [i, r, rv] = t;
i = 4;
int& ri = i;
int v = i;
r = 5;
int& rr = r;
int w = r;
}
// explicit reference version
{
auto&& unnamed_local_variable = t;
auto&& i = unnamed_local_variable.get<0>();
auto& r = unnamed_local_variable.get<1>();
auto&& rv = unnamed_local_variable.get<2>();
i = 4;
int& ri = i;
int v = i;
r = 5;
int& rr = r;
int w = r;
}
}
// semmle-extractor-options: -std=c++17 --clang

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ containsLoopOfForwardEdges
lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
| ir.cpp:1486:8:1486:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1486:8:1486:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1,4 @@
| tests.c:70:9:70:15 | call to fprintf | This operation exposes system data from $@. | tests.c:54:13:54:22 | call to LogonUserA | call to LogonUserA |
edges
nodes
subpaths
#select

View File

@@ -1,7 +0,0 @@
| tests.c:29:9:29:14 | call to printf | tests.c:29:16:29:21 | %s\n |
| tests.c:29:9:29:14 | call to printf | tests.c:29:24:29:27 | line |
| tests.c:43:13:43:21 | call to printLine | tests.c:43:23:43:38 | fgets() failed |
| tests.c:62:13:62:21 | call to printLine | tests.c:62:23:62:52 | User logged in successfully. |
| tests.c:67:13:67:21 | call to printLine | tests.c:67:23:67:40 | Unable to login. |
| tests.c:70:9:70:15 | call to fprintf | tests.c:70:25:70:67 | User attempted access with password: %s\n |
| tests.c:70:9:70:15 | call to fprintf | tests.c:70:70:70:77 | password |

View File

@@ -1,4 +0,0 @@
import semmle.code.cpp.security.OutputWrite
from OutputWrite ow
select ow, ow.getASource()

View File

@@ -67,6 +67,6 @@ void CWE535_Info_Exposure_Shell_Error__w32_char_01_bad()
printLine("Unable to login.");
}
/* FLAW: Write sensitive data to stderr */
fprintf(stderr, "User attempted access with password: %s\n", password);
fprintf(stderr, "User attempted access with password: %s\n", password); // [NOT DETECTED]
}
}

View File

@@ -1,2 +1,47 @@
| tests2.cpp:27:12:27:12 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:27:15:27:20 | call to getenv | call to getenv |
| tests2.cpp:28:25:28:25 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:28:28:28:33 | call to getenv | call to getenv |
edges
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... |
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... |
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... |
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
| tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 |
| tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw |
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | tests2.cpp:109:14:109:15 | c1 [read] [ptr] |
| tests2.cpp:107:6:107:8 | ptr [post update] | tests2.cpp:107:3:107:4 | c1 [post update] [ptr] |
| tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:107:6:107:8 | ptr [post update] |
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | tests2.cpp:109:14:109:19 | (const char *)... |
nodes
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:64:13:64:26 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:65:13:65:30 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:79:14:79:19 | (const char *)... | semmle.label | (const char *)... |
| tests2.cpp:89:42:89:45 | str1 | semmle.label | str1 |
| tests2.cpp:91:14:91:17 | str1 | semmle.label | str1 |
| tests2.cpp:99:8:99:15 | call to getpwuid | semmle.label | call to getpwuid |
| tests2.cpp:100:14:100:15 | pw | semmle.label | pw |
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | semmle.label | c1 [post update] [ptr] |
| tests2.cpp:107:6:107:8 | ptr [post update] | semmle.label | ptr [post update] |
| tests2.cpp:107:12:107:17 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | semmle.label | c1 [read] [ptr] |
| tests2.cpp:109:14:109:19 | (const char *)... | semmle.label | (const char *)... |
subpaths
#select
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:63:13:63:18 | call to getenv | call to getenv |
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:64:13:64:18 | call to getenv | call to getenv |
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:65:13:65:18 | call to getenv | call to getenv |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:79:14:79:19 | (const char *)... | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:91:14:91:17 | str1 | tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 | This operation exposes system data from $@. | tests2.cpp:89:42:89:45 | str1 | str1 |
| tests2.cpp:100:14:100:15 | pw | tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw | This operation exposes system data from $@. | tests2.cpp:99:8:99:15 | call to getpwuid | call to getpwuid |
| tests2.cpp:109:14:109:19 | (const char *)... | tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:109:14:109:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:107:12:107:17 | call to getenv | call to getenv |

View File

@@ -1,5 +0,0 @@
| tests2.cpp:27:12:27:12 | call to operator<< | tests2.cpp:27:15:27:20 | call to getenv |
| tests2.cpp:28:12:28:12 | call to operator<< | tests2.cpp:28:15:28:23 | PATH = |
| tests2.cpp:28:25:28:25 | call to operator<< | tests2.cpp:28:28:28:33 | call to getenv |
| tests2.cpp:28:43:28:43 | call to operator<< | tests2.cpp:28:46:28:48 | . |
| tests2.cpp:29:12:29:12 | call to operator<< | tests2.cpp:29:15:29:28 | PATHPATHPATH |

View File

@@ -1,4 +0,0 @@
import semmle.code.cpp.security.OutputWrite
from OutputWrite ow
select ow, ow.getASource()

View File

@@ -3,6 +3,7 @@
// library functions etc
char *getenv(const char *name);
char *strcpy(char *s1, const char *s2);
namespace std
{
@@ -20,11 +21,92 @@ namespace std
extern ostream cout;
}
int socket(int p1, int p2, int p3);
void send(int sock, const char *buffer, int p3, int p4);
const char *mysql_get_client_info();
void mysql_real_connect(int p1, int p2, int p3, const char *password, int p5, int p6, int p7, int p8);
struct container
{
char *ptr;
};
struct passwd
{
// ...
char *pw_passwd;
// ...
};
passwd *getpwuid(int uid);
int val();
// test cases
const char *global1 = mysql_get_client_info();
const char *global2 = "abc";
void test1()
{
std::cout << getenv("HOME"); // BAD: outputs HOME environment variable
std::cout << "PATH = " << getenv("PATH") << "."; // BAD: outputs PATH environment variable
int sock = socket(val(), val(), val());
// tests for a strict implementation of CWE-497
std::cout << getenv("HOME"); // BAD: outputs HOME environment variable [NOT DETECTED]
std::cout << "PATH = " << getenv("PATH") << "."; // BAD: outputs PATH environment variable [NOT DETECTED]
std::cout << "PATHPATHPATH"; // GOOD: not system data
// tests for a more pragmatic implementation of CWE-497
send(sock, getenv("HOME"), val(), val()); // BAD
send(sock, getenv("PATH"), val(), val()); // BAD
send(sock, getenv("USERNAME"), val(), val()); // BAD
send(sock, getenv("HARMLESS"), val(), val()); // GOOD: harmless information
send(sock, "HOME", val(), val()); // GOOD: not system data
send(sock, "PATH", val(), val()); // GOOD: not system data
send(sock, "USERNAME", val(), val()); // GOOD: not system data
send(sock, "HARMLESS", val(), val()); // GOOD: not system data
// tests for `mysql_get_client_info`, including via a global
{
char buffer[256];
strcpy(buffer, mysql_get_client_info());
send(sock, mysql_get_client_info(), val(), val()); // BAD
send(sock, buffer, val(), val()); // BAD
send(sock, global1, val(), val()); // BAD [NOT DETECTED]
send(sock, global2, val(), val()); // GOOD: not system data
}
// tests for `mysql_real_connect`
{
const char *str1 = "123456";
const char *str2 = "abcdef";
mysql_real_connect(sock, val(), val(), str1, val(), val(), val(), val());
send(sock, str1, val(), val()); // BAD
send(sock, str2, val(), val()); // GOOD: not system data
}
// tests for `getpwuid`
{
passwd *pw;
pw = getpwuid(val());
send(sock, pw->pw_passwd, val(), val()); // BAD
}
// tests for containers
{
container c1, c2;
c1.ptr = getenv("MY_SECRET_TOKEN");
c2.ptr = "";
send(sock, c1.ptr, val(), val()); // BAD
send(sock, c2.ptr, val(), val()); // GOOD: not system data
}
}

View File

@@ -399,6 +399,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution;
actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors;
actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS"] = buildless;
actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions;
actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore;
actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null;

View File

@@ -10,7 +10,8 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public class AutobuildOptions
{
private const string prefix = "LGTM_INDEX_";
private const string lgtmPrefix = "LGTM_INDEX_";
private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
public int SearchDepth { get; } = 3;
public string RootDirectory { get; }
@@ -36,20 +37,21 @@ namespace Semmle.Autobuild.Shared
public AutobuildOptions(IBuildActions actions, Language language)
{
RootDirectory = actions.GetCurrentDirectory();
VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION");
MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
MsBuildPlatform = actions.GetEnvironmentVariable(prefix + "MSBUILD_PLATFORM");
MsBuildConfiguration = actions.GetEnvironmentVariable(prefix + "MSBUILD_CONFIGURATION");
MsBuildTarget = actions.GetEnvironmentVariable(prefix + "MSBUILD_TARGET");
DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
DotNetVersion = actions.GetEnvironmentVariable(prefix + "DOTNET_VERSION");
BuildCommand = actions.GetEnvironmentVariable(prefix + "BUILD_COMMAND");
Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty<string>());
VsToolsVersion = actions.GetEnvironmentVariable(lgtmPrefix + "VSTOOLS_VERSION");
MsBuildArguments = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
MsBuildPlatform = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_PLATFORM");
MsBuildConfiguration = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_CONFIGURATION");
MsBuildTarget = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_TARGET");
DotNetArguments = actions.GetEnvironmentVariable(lgtmPrefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
DotNetVersion = actions.GetEnvironmentVariable(lgtmPrefix + "DOTNET_VERSION");
BuildCommand = actions.GetEnvironmentVariable(lgtmPrefix + "BUILD_COMMAND");
Solution = actions.GetEnvironmentVariable(lgtmPrefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty<string>());
IgnoreErrors = actions.GetEnvironmentVariable(prefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
Buildless = actions.GetEnvironmentVariable(prefix + "BUILDLESS").AsBool("buildless", false);
AllSolutions = actions.GetEnvironmentVariable(prefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
IgnoreErrors = actions.GetEnvironmentVariable(lgtmPrefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
AllSolutions = actions.GetEnvironmentVariable(lgtmPrefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
NugetRestore = actions.GetEnvironmentVariable(lgtmPrefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
Language = language;
}
@@ -62,21 +64,12 @@ namespace Semmle.Autobuild.Shared
if (value is null)
return defaultValue;
switch (value.ToLower())
return value.ToLower() switch
{
case "on":
case "yes":
case "true":
case "enabled":
return true;
case "off":
case "no":
case "false":
case "disabled":
return false;
default:
throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid.");
}
"on" or "yes" or "true" or "enabled" => true,
"off" or "no" or "false" or "disabled" => false,
_ => throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid."),
};
}
public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue)

View File

@@ -16,3 +16,28 @@ file_types:
extensions:
- .cs
legacy_qltest_extraction: true
options:
trap:
title: Options pertaining to TRAP.
type: object
properties:
compression:
title: Controls compression for the TRAP files written by the extractor.
description: >
This option is only intended for use in debugging the extractor. Accepted
values are 'brotli' (the default, to write brotli-compressed TRAP), 'gzip', and 'none'
(to write uncompressed TRAP).
type: string
pattern: "^(none|gzip|brotli)$"
buildless:
title: Whether to use buildless (standalone) extraction.
description: >
A value indicating, which type of extraction the autobuilder should perform.
If 'true', then the standalone extractor will be used, otherwise tracing extraction
will be performed.
The default is 'false'.
Note that buildless extraction will generally yield less accurate analysis results,
and should only be used in cases where it is not possible to build
the code (for example if it uses inaccessible dependencies).
type: string
pattern: "^(false|true)$"

View File

@@ -46,7 +46,9 @@ namespace Semmle.Extraction.CSharp
var argsList = new List<string>(arguments);
if (!string.IsNullOrEmpty(extractionOptions))
{
argsList.AddRange(extractionOptions.Split(' '));
}
options.ParseArguments(argsList);
return options;

View File

@@ -33,6 +33,7 @@ namespace Semmle.Extraction.Tests
Assert.False(options.ClrTracer);
Assert.False(options.PDB);
Assert.False(options.Fast);
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
}
[Fact]
@@ -205,5 +206,25 @@ namespace Semmle.Extraction.Tests
File.Delete(file);
}
}
[Fact]
public void CompressionTests()
{
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "gzip");
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
Assert.Equal(TrapWriter.CompressionMode.Gzip, options.TrapCompression);
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "brotli");
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "none");
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
Assert.Equal(TrapWriter.CompressionMode.None, options.TrapCompression);
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", null);
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using Semmle.Util.Logging;
using Semmle.Util;
@@ -49,10 +50,11 @@ namespace Semmle.Extraction
/// </summary>
public bool QlTest { get; private set; } = false;
/// <summary>
/// The compression algorithm used for trap files.
/// </summary>
public TrapWriter.CompressionMode TrapCompression { get; set; } = TrapWriter.CompressionMode.Brotli;
public TrapWriter.CompressionMode TrapCompression { get; private set; } = TrapWriter.CompressionMode.Brotli;
public virtual bool HandleOption(string key, string value)
{
@@ -64,6 +66,13 @@ namespace Semmle.Extraction
case "verbosity":
Verbosity = (Verbosity)int.Parse(value);
return true;
case "trap_compression":
if (Enum.TryParse<TrapWriter.CompressionMode>(value, true, out var mode))
{
TrapCompression = mode;
return true;
}
return false;
default:
return false;
}
@@ -102,9 +111,6 @@ namespace Semmle.Extraction
case "qltest":
QlTest = value;
return true;
case "brotli":
TrapCompression = value ? TrapWriter.CompressionMode.Brotli : TrapWriter.CompressionMode.Gzip;
return true;
default:
return false;
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Semmle.Util
@@ -39,8 +40,26 @@ namespace Semmle.Util
public static class OptionsExtensions
{
public static void ParseArguments(this ICommandLineOptions options, IReadOnlyList<string> arguments)
private static string? GetExtractorOption(string name) =>
Environment.GetEnvironmentVariable($"CODEQL_EXTRACTOR_CSHARP_OPTION_{name.ToUpper()}");
private static List<string> GetExtractorOptions()
{
var extractorOptions = new List<string>();
var trapCompression = GetExtractorOption("trap_compression");
if (!string.IsNullOrEmpty(trapCompression))
{
extractorOptions.Add($"--trap_compression:{trapCompression}");
}
return extractorOptions;
}
public static void ParseArguments(this ICommandLineOptions options, IReadOnlyList<string> providedArguments)
{
var arguments = GetExtractorOptions();
arguments.AddRange(providedArguments);
for (var i = 0; i < arguments.Count; ++i)
{
var arg = arguments[i];

View File

@@ -1,3 +1,5 @@
## 1.0.4
## 1.0.3
## 1.0.2

View File

@@ -0,0 +1 @@
## 1.0.4

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.3
lastReleaseVersion: 1.0.4

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.0.4-dev
version: 1.0.5-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,5 @@
## 1.0.4
## 1.0.3
## 1.0.2

View File

@@ -0,0 +1 @@
## 1.0.4

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.3
lastReleaseVersion: 1.0.4

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.0.4-dev
version: 1.0.5-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,5 @@
## 0.0.10
## 0.0.9
### Major Analysis Improvements

View File

@@ -0,0 +1 @@
## 0.0.10

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.10

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.0.10-dev
version: 0.0.11-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -1,3 +1,10 @@
## 0.0.10
### Query Metadata Changes
* The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.
## 0.0.9
## 0.0.8

View File

@@ -204,6 +204,8 @@ class ExplicitUpcast extends ExplicitCast {
this = any(LocalVariableDeclAndInitExpr decl | decl.isImplicitlyTyped()).getInitializer()
or
exists(LambdaExpr c | c.canReturn(this))
or
dest instanceof DynamicType
}
}

View File

@@ -1,5 +0,0 @@
---
category: queryMetadata
---
The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The C# extractor now accepts an extractor option `trap.compression`, which is used to decide the compression format for TRAP files. The legal values are `brotli` (default), `gzip` or `none`.
The option is added via `codeql database create --language=csharp -Otrap.compression=value ...`.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The C# extractor now accepts an extractor option `buildless`, which is used to decide what type of extraction that should be performed. If `true` then buildless (standalone) extraction will be performed. Otherwise tracing extraction will be performed (default).
The option is added via `codeql database create --language=csharp -Obuildless=true ...`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Casts to `dynamic` are excluded from the useless upcasts check (`cs/useless-upcast`).

View File

@@ -0,0 +1,6 @@
## 0.0.10
### Query Metadata Changes
* The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.10

View File

@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
or
instr instanceof InheritanceConversionInstruction and
(
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
}
private predicate variableAddressValueNumber(
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
// 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
unique( | | instr.getIRVariable().getAST()) = ast
}
private predicate initializeParameterValueNumber(
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
unique( | | instr.getDerivedClass()) = derivedClass
}
private predicate loadTotalOverlapValueNumber(

View File

@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
or
instr instanceof InheritanceConversionInstruction and
(
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
}
private predicate variableAddressValueNumber(
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
// 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
unique( | | instr.getIRVariable().getAST()) = ast
}
private predicate initializeParameterValueNumber(
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
unique( | | instr.getDerivedClass()) = derivedClass
}
private predicate loadTotalOverlapValueNumber(

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.0.10-dev
version: 0.0.11-dev
groups:
- csharp
- queries

View File

@@ -17,8 +17,8 @@ class A : I1, I2
class B : A
{
public static bool operator==(B b1, B b2) { return false; }
public static bool operator!=(B b1, B b2) { return true; }
public static bool operator ==(B b1, B b2) { return false; }
public static bool operator !=(B b1, B b2) { return true; }
public void M(B b) { }
}
@@ -68,11 +68,11 @@ class Tests
((I2)a).Foo(); // GOOD: Cast to an interface
o = a==(A)b; // GOOD: EQExpr
o = a == (A)b; // GOOD: EQExpr
o = b==(B)b; // GOOD: Operator call
o = b == (B)b; // GOOD: Operator call
var act = (Action) (() => { }); // GOOD
var act = (Action)(() => { }); // GOOD
var objects = args.Select(arg => (object)arg); // GOOD
@@ -126,9 +126,9 @@ static class IExtensions
static class StaticMethods
{
public static void M1(A _) { }
public static void M1(B _) { }
public static void M2(B _) { }
public static void M1(A _) { }
public static void M1(B _) { }
public static void M2(B _) { }
}
class Constructors : I2
@@ -162,4 +162,12 @@ class Constructors : I2
new Sub((Sub)ss); // BAD
}
}
class Dynamic
{
void M(object o)
{
var s0 = ((dynamic)o).ToString(); // GOOD
}
}
}

View File

@@ -23,7 +23,7 @@
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py``
Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
TypeScript [8]_,"2.6-4.5",Standard TypeScript compiler,"``.ts``, ``.tsx``"
TypeScript [8]_,"2.6-4.6",Standard TypeScript compiler,"``.ts``, ``.tsx``"
.. container:: footnote-group

View File

@@ -78,6 +78,8 @@ To avoid these problems, a data-flow ``Configuration`` comes with a mechanism fo
*/
final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) {
There is also a ``Configuration.hasPartialFlowRev`` for exploring flow backwards from a sink.
As noted in the documentation for ``hasPartialFlow`` (for example, in the
`CodeQL for Java documentation <https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/dataflow/internal/DataFlowImpl2.qll/predicate.DataFlowImpl2$Configuration$hasPartialFlow.3.html>`__) you must first enable this by adding an override of ``explorationLimit``. For example:
@@ -87,6 +89,8 @@ As noted in the documentation for ``hasPartialFlow`` (for example, in the
This defines the exploration radius within which ``hasPartialFlow`` returns results.
To get good performance when using ``hasPartialFlow`` it is important to ensure the ``isSink`` predicate of the configuration has no results. Likewise, when using ``hasPartialFlowRev`` the ``isSource`` predicate of the configuration should have no results.
It is also useful to focus on a single source at a time as the starting point for the flow exploration. This is most easily done by adding a temporary restriction in the ``isSource`` predicate.
To do quick evaluations of partial flow it is often easiest to add a predicate to the query that is solely intended for quick evaluation (right-click the predicate name and choose "CodeQL: Quick Evaluation"). A good starting point is something like:
@@ -113,4 +117,4 @@ Further reading
----------------
- :ref:`About data flow analysis <about-data-flow-analysis>`
- :ref:`Creating path queries <creating-path-queries>`
- :ref:`Creating path queries <creating-path-queries>`

View File

@@ -1,6 +1,44 @@
# CodeQL pre-commit-hook setup
As stated in [CONTRIBUTING](../CONTRIBUTING.md) all CodeQL files must be formatted according to our [CodeQL style guide](ql-style-guide.md). You can use our pre-commit hook to avoid committing incorrectly formatted code. To use it, simply copy the [pre-commit](../misc/scripts/pre-commit) script to `.git/hooks/pre-commit` and make sure that:
As stated in [CONTRIBUTING](../CONTRIBUTING.md) all CodeQL files must be formatted according to our [CodeQL style guide](ql-style-guide.md). You can use a pre-commit hook to avoid committing incorrectly formatted code, as well as prevent some other easily checkable errors.
## Using the `pre-commit` framework
Preferably, you can use the [pre-commit framework](https://pre-commit.com/). There are some pre-commit hooks already configured on [`.pre-commit-config.yaml`](../.pre-commit-config.yaml). In order to install them you need to follow pre-commit's [installation instructions](https://pre-commit.com/#installation) and then run `pre-commit install`. Typically (assuming you have [`pip`](https://pip.pypa.io/en/stable/installation/) installed):
```
python3 -m pip install pre-commit
pre-commit install
```
Also, make sure that the CodeQL CLI has been added to your `PATH`.
By default, pre-commit will check and fix:
* trailing whitespaces;
* absence of or duplicate newlines at end of files;
* QL formatting;
* files out of sync (see [`config/sync-files.py`](../config/sync-files.py)).
It will additionally check:
* `.qhelp` files for query help generation.
It will run the checks only on files changed by the commit (except for the file sync check) and it will skip all files under `test` directories unless they are `.ql`, `.qll` or `.qlref` files.
If you want to change any behaviour (for example, you want to skip the out-of-sync file check, or you want to avoid auto-fixing formatting or file syncing), you can copy the configuration file to a separate location, modify it and use that. For example
```
cp .pre-commit-config.yaml ~/my-codeql-pre-commit-config.yaml
pre-commit install --config ~/my-codeql-pre-commit-config.yaml
# edit ~/my-codeql-pre-commit-config.yaml to your liking
```
You can for example:
* change `--in-place` to `--check-only` in the `codeql-format` hook to have it report formatting problems instead of auto-fixing them;
* remove `--latest` in the `sync-files` hook to do the same;
* remove any hook altogether.
## Manual approach
You can have the formatting check in place by copying the [pre-commit](../misc/scripts/pre-commit) script to `.git/hooks/pre-commit` and make sure that:
- The script is executable. On Linux and macOS this can be done using `chmod +x`.
- The CodeQL CLI has been added to your `PATH`.

View File

@@ -13,5 +13,5 @@
import java
from RefType type
where type.getASupertype+().hasQualifiedName("com.example", "Class")
where type.getAStrictAncestor().hasQualifiedName("com.example", "Class")
select type

View File

@@ -9,5 +9,5 @@
import java
from ThrowStmt throw
where throw.getThrownExceptionType().getASupertype*().hasQualifiedName("com.example", "AnException")
where throw.getThrownExceptionType().getAnAncestor().hasQualifiedName("com.example", "AnException")
select throw, "Don't throw com.example.AnException"

View File

@@ -1,3 +1,9 @@
## 0.0.10
### New Features
* Added predicates `ClassOrInterface.getAPermittedSubtype` and `isSealed` exposing information about sealed classes.
## 0.0.9
## 0.0.8

View File

@@ -0,0 +1,6 @@
---
category: feature
---
* Added `hasDescendant(RefType anc, Type sub)`
* Added `RefType.getADescendant()`
* Added `RefType.getAStrictAncestor()`

View File

@@ -1,4 +1,5 @@
---
category: feature
---
## 0.0.10
### New Features
* Added predicates `ClassOrInterface.getAPermittedSubtype` and `isSealed` exposing information about sealed classes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.10

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
name: codeql/java-all
version: 0.0.10-dev
version: 0.0.11-dev
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java

View File

@@ -236,7 +236,7 @@ private module ControlFlowGraphImpl {
*/
private predicate mustCatch(CatchClause c, ThrowableType thrown) {
thrown = thrownInBody(c.getTry()) and
hasSubtype*(c.getACaughtType(), thrown)
hasDescendant(c.getACaughtType(), thrown)
}
/**
@@ -250,7 +250,7 @@ private module ControlFlowGraphImpl {
*/
private predicate mayNotCatch(CatchClause c, ThrowableType thrown) {
thrown = thrownInBody(c.getTry()) and
not hasSubtype*(c.getACaughtType(), thrown)
not hasDescendant(c.getACaughtType(), thrown)
}
/**

View File

@@ -2093,7 +2093,7 @@ class Argument extends Expr {
p.isVarargs() and
ptyp = p.getType() and
(
hasSubtype*(ptyp, typ)
hasDescendant(ptyp, typ)
or
// If the types don't match then we'll guess based on whether there are type variables involved.
hasInstantiation(ptyp.(Array).getComponentType())

View File

@@ -18,7 +18,7 @@ class AnnotatedGeneratedClass extends GeneratedClass {
/** A Java class generated by an ANTLR scanner or parser class. */
class AntlrGenerated extends GeneratedClass {
AntlrGenerated() {
exists(RefType t | this.getASupertype+() = t |
exists(RefType t | this.getAStrictAncestor() = t |
// ANTLR v3
t.hasQualifiedName("org.antlr.runtime", "Lexer") or
t.hasQualifiedName("org.antlr.runtime", "Parser") or

View File

@@ -114,7 +114,7 @@ class TypeNumber extends RefType {
/** A (reflexive, transitive) subtype of `java.lang.Number`. */
class NumberType extends RefType {
NumberType() { exists(TypeNumber number | hasSubtype*(number, this)) }
NumberType() { exists(TypeNumber number | hasDescendant(number, this)) }
}
/** A numeric type, including both primitive and boxed types. */
@@ -436,13 +436,13 @@ class ArrayLengthField extends Field {
/** A (reflexive, transitive) subtype of `java.lang.Throwable`. */
class ThrowableType extends RefType {
ThrowableType() { exists(TypeThrowable throwable | hasSubtype*(throwable, this)) }
ThrowableType() { exists(TypeThrowable throwable | hasDescendant(throwable, this)) }
}
/** An unchecked exception. That is, a (reflexive, transitive) subtype of `java.lang.Error` or `java.lang.RuntimeException`. */
class UncheckedThrowableType extends RefType {
UncheckedThrowableType() {
exists(TypeError e | hasSubtype*(e, this)) or
exists(TypeRuntimeException e | hasSubtype*(e, this))
exists(TypeError e | hasDescendant(e, this)) or
exists(TypeRuntimeException e | hasDescendant(e, this))
}
}

View File

@@ -63,7 +63,7 @@ predicate catchesNFE(TryStmt t) {
exists(CatchClause cc, LocalVariableDeclExpr v |
t.getACatchClause() = cc and
cc.getVariable() = v and
v.getType().(RefType).getASubtype*() instanceof NumberFormatException
v.getType().(RefType).getADescendant() instanceof NumberFormatException
)
}

View File

@@ -295,7 +295,7 @@ class NewInstance extends MethodAccess {
// If we cast the result of this method, then this is either the type specified, or a
// sub-type of that type. Make sure we exclude overly generic types such as `Object`.
not overlyGenericType(cast.getType()) and
hasSubtype*(cast.getType(), result)
hasDescendant(cast.getType(), result)
)
}
}

View File

@@ -24,7 +24,7 @@ abstract class DeserializableField extends Field { }
*/
library class StandardSerializableField extends SerializableField, DeserializableField {
StandardSerializableField() {
this.getDeclaringType().getASupertype*() instanceof TypeSerializable and
this.getDeclaringType().getAnAncestor() instanceof TypeSerializable and
not this.isTransient()
}
}

View File

@@ -37,6 +37,16 @@ predicate hasSubtype(RefType t, Type sub) {
typeVarSubtypeBound(t, sub) and t != sub
}
/**
* Holds if reference type `anc` is a direct or indirect supertype of `sub`, including itself.
*/
cached
predicate hasDescendant(RefType anc, Type sub) {
anc = sub
or
exists(RefType mid | hasSubtype(anc, mid) and hasDescendant(mid, sub))
}
private predicate typeVarSubtypeBound(RefType t, TypeVariable tv) {
if tv.hasTypeBound() then t = tv.getATypeBound().getType() else t instanceof TypeObject
}
@@ -394,11 +404,17 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
/** Gets a direct subtype of this type. */
RefType getASubtype() { hasSubtype(this, result) }
/** Gets a direct or indirect descendant of this type, including itself. */
RefType getADescendant() { hasDescendant(this, result) }
/** Gets a direct supertype of this type. */
RefType getASupertype() { hasSubtype(result, this) }
/** Gets a direct or indirect supertype of this type, including itself. */
RefType getAnAncestor() { hasSubtype*(result, this) }
RefType getAnAncestor() { hasDescendant(result, this) }
/** Gets a direct or indirect supertype of this type, not including itself. */
RefType getAStrictAncestor() { result = this.getAnAncestor() and result != this }
/**
* Gets the source declaration of a direct supertype of this type, excluding itself.

View File

@@ -103,7 +103,7 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable {
int argument;
NumberTaintPreservingCallable() {
this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "Number") and
this.getDeclaringType().getAnAncestor().hasQualifiedName("java.lang", "Number") and
(
this instanceof Constructor and
argument = 0

View File

@@ -641,7 +641,7 @@ private module SsaImpl {
ssaDefReachesRank(v, def, b, lastRank(v, b))
or
exists(BasicBlock idom |
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
bbIDominates(pragma[only_bind_into](idom), b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
ssaDefReachesEndOfBlock(v, def, idom) and
not any(TrackedSsaDef other).definesAt(v, b, _)
)
@@ -768,12 +768,12 @@ private module SsaImpl {
*/
private predicate varBlockReaches(TrackedVar v, BasicBlock b1, BasicBlock b2) {
varOccursInBlock(v, b1) and
b2 = b1.getABBSuccessor() and
pragma[only_bind_into](b2) = b1.getABBSuccessor() and
blockPrecedesVar(v, b2)
or
exists(BasicBlock mid |
varBlockReaches(v, b1, mid) and
b2 = mid.getABBSuccessor() and
pragma[only_bind_into](b2) = mid.getABBSuccessor() and
not varOccursInBlock(v, mid) and
blockPrecedesVar(v, b2)
)

View File

@@ -285,7 +285,7 @@ private predicate downcastSuccessorAux(
*/
private predicate downcastSuccessor(VarAccess va, RefType t) {
exists(CastExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
downcastSuccessorAux(cast, v, t, t1, t2) and
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
t1.getASourceSupertype+() = t2 and
va = v.getAUse() and
dominates(cast, va) and
@@ -360,7 +360,7 @@ private predicate typeFlowJoin(int r, TypeFlowNode n, RefType t) {
) and
forall(TypeFlowNode mid | joinStepRank(r, mid, n) |
exists(RefType midtyp | exactType(mid, midtyp) or typeFlow(mid, midtyp) |
midtyp.getASupertype*() = t
pragma[only_bind_out](midtyp).getAnAncestor() = t
)
)
}
@@ -408,14 +408,14 @@ pragma[nomagic]
private predicate irrelevantBound(TypeFlowNode n, RefType t) {
exists(RefType bound |
typeFlow(n, bound) and
t = bound.getASupertype+() and
t = bound.getAStrictAncestor() and
typeBound(t) and
typeFlow(n, t) and
not t.getASupertype*() = bound
not t.getAnAncestor() = bound
or
n.getType() = bound and
n.getType() = pragma[only_bind_into](bound) and
typeFlow(n, t) and
t = bound.getASupertype*()
t = bound.getAnAncestor()
)
}

View File

@@ -231,7 +231,7 @@ private module SsaImpl {
ssaDefReachesRank(v, def, b, lastRank(v, b))
or
exists(BasicBlock idom |
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
bbIDominates(pragma[only_bind_into](idom), b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
ssaDefReachesEndOfBlock(v, def, idom) and
not any(TrackedSsaDef other).definesAt(v, b, _)
)
@@ -333,12 +333,12 @@ private module SsaImpl {
*/
private predicate varBlockReaches(BaseSsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
varOccursInBlock(v, b1) and
b2 = b1.getABBSuccessor() and
pragma[only_bind_into](b2) = b1.getABBSuccessor() and
blockPrecedesVar(v, b2)
or
exists(BasicBlock mid |
varBlockReaches(v, b1, mid) and
b2 = mid.getABBSuccessor() and
pragma[only_bind_into](b2) = mid.getABBSuccessor() and
not varOccursInBlock(v, mid) and
blockPrecedesVar(v, b2)
)

View File

@@ -283,7 +283,7 @@ private predicate taintPreservingQualifierToMethod(Method m) {
m.getName().matches("read%")
or
m instanceof GetterMethod and
m.getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and
m.getDeclaringType().getADescendant() instanceof SpringUntrustedDataType and
not m.getDeclaringType() instanceof TypeObject
or
m.(TaintPreservingCallable).returnsTaintFrom(-1)
@@ -607,7 +607,7 @@ private SrcRefType entrypointType() {
s instanceof DataFlow::ExplicitParameterNode and
t = pragma[only_bind_out](s).getType() and
not t instanceof TypeObject and
result = t.getASubtype*().getSourceDeclaration()
result = t.getADescendant().getSourceDeclaration()
)
or
result = entrypointType().getAField().getType().(RefType).getSourceDeclaration()

View File

@@ -33,7 +33,7 @@ Callable possibleLivenessCause(Callable c, string reason) {
or
c.hasName("<clinit>") and
reason = "class initialization" and
exists(RefType clintedType | c = clintedType.getASupertype*().getACallable() |
exists(RefType clintedType | c = clintedType.getAnAncestor().getACallable() |
result.getDeclaringType() = clintedType or
result.getAnAccessedField().getDeclaringType() = clintedType
)
@@ -155,7 +155,7 @@ library class SourceClassOrInterface extends ClassOrInterface {
*/
class LiveClass extends SourceClassOrInterface {
LiveClass() {
exists(Callable c | c.getDeclaringType().getASupertype*().getSourceDeclaration() = this |
exists(Callable c | c.getDeclaringType().getAnAncestor().getSourceDeclaration() = this |
isLive(c)
)
or

View File

@@ -93,7 +93,7 @@ class SerialVersionUIDField extends ReflectivelyReadField {
this.isStatic() and
this.isFinal() and
this.getType().hasName("long") and
this.getDeclaringType().getASupertype*() instanceof TypeSerializable
this.getDeclaringType().getAnAncestor() instanceof TypeSerializable
}
}

View File

@@ -142,7 +142,7 @@ class DeserializedClass extends ReflectivelyConstructedClass {
exists(CastExpr cast, ReadObjectMethod readObject |
cast.getExpr().(MethodAccess).getMethod() = readObject
|
hasSubtype*(cast.getType(), this)
hasDescendant(cast.getType(), this)
)
}
}
@@ -315,7 +315,7 @@ class FacesComponentReflectivelyConstructedClass extends ReflectivelyConstructed
* Entry point for EJB home interfaces.
*/
class EJBHome extends Interface, EntryPoint {
EJBHome() { this.getASupertype*().hasQualifiedName("javax.ejb", "EJBHome") }
EJBHome() { this.getAnAncestor().hasQualifiedName("javax.ejb", "EJBHome") }
override Callable getALiveCallable() { result = this.getACallable() }
}
@@ -324,7 +324,7 @@ class EJBHome extends Interface, EntryPoint {
* Entry point for EJB object interfaces.
*/
class EJBObject extends Interface, EntryPoint {
EJBObject() { this.getASupertype*().hasQualifiedName("javax.ejb", "EJBObject") }
EJBObject() { this.getAnAncestor().hasQualifiedName("javax.ejb", "EJBObject") }
override Callable getALiveCallable() { result = this.getACallable() }
}

View File

@@ -10,7 +10,7 @@ import semmle.code.java.frameworks.struts.StrutsActions
*/
class Struts1ActionEntryPoint extends EntryPoint, Class {
Struts1ActionEntryPoint() {
this.getASupertype*().hasQualifiedName("org.apache.struts.action", "Action")
this.getAnAncestor().hasQualifiedName("org.apache.struts.action", "Action")
}
override Callable getALiveCallable() {
@@ -22,7 +22,7 @@ class Struts1ActionEntryPoint extends EntryPoint, Class {
result.(Method).overrides+(methodFromAction)
)
or
this.getASupertype*().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and
this.getAnAncestor().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and
result.(Method).isPublic()
or
result.(Constructor).getNumberOfParameters() = 0

View File

@@ -47,7 +47,7 @@ class ServletListenerClass extends ReflectivelyConstructedClass {
*/
class ServletFilterClass extends ReflectivelyConstructedClass {
ServletFilterClass() {
this.getASupertype*().hasQualifiedName("javax.servlet", "Filter") and
this.getAnAncestor().hasQualifiedName("javax.servlet", "Filter") and
// If we have seen any `web.xml` files, this filter will be considered to be live only if it is
// referred to as a filter-class in at least one. If no `web.xml` files are found, we assume
// that XML extraction was not enabled, and therefore consider all filter classes as live.

View File

@@ -335,7 +335,7 @@ import Dispatch
private Expr variableTrackStep(Expr use) {
exists(Variable v |
use = v.getAnAccess() and
pragma[only_bind_out](use) = v.getAnAccess() and
use.getType() instanceof RefType and
not result instanceof NullLiteral and
not v.(LocalVariableDecl).getDeclExpr().hasImplicitInit()
@@ -358,6 +358,7 @@ private Expr variableTrackPath(Expr use) {
/**
* Gets an expression by tracking `use` backwards through variable assignments.
*/
pragma[inline]
Expr variableTrack(Expr use) {
result = variableTrackPath(use)
or

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