mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into shared-taint-tracking
This commit is contained in:
@@ -22,7 +22,6 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
@@ -562,4 +561,4 @@
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
{
|
||||
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
|
||||
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The `PrintAST` library now also prints global and namespace variables and their initializers.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* The `PrintAST` library now also prints global and namespace variables and their initializers.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
|
||||
@@ -1,5 +1,14 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The `PrintAST` library now also prints global and namespace variables and their initializers.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.1
|
||||
lastReleaseVersion: 0.9.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.8.2-dev
|
||||
version: 0.9.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -1078,7 +1078,7 @@ private IRVariable getIRVariableForParameterNode(ParameterNode p) {
|
||||
|
||||
/** Holds if `v` is the source variable corresponding to the parameter represented by `p`. */
|
||||
pragma[nomagic]
|
||||
private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceIRVariable v) {
|
||||
private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceVariable v) {
|
||||
v.getIRVariable() = getIRVariableForParameterNode(p) and
|
||||
exists(Position pos | p.isParameterOf(_, pos) |
|
||||
pos instanceof DirectPosition and
|
||||
|
||||
@@ -781,26 +781,12 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDef
|
||||
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate indirectReturnOutNodeOperand0(CallInstruction call, Operand operand, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
|
||||
operandForFullyConvertedCall(operand, call)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate indirectReturnOutNodeInstruction0(
|
||||
CallInstruction call, Instruction instr, int indirectionIndex
|
||||
) {
|
||||
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
|
||||
instructionForFullyConvertedCall(instr, call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and
|
||||
* `operand` represents a use of the fully converted value of `call`.
|
||||
*/
|
||||
private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) {
|
||||
indirectReturnOutNodeOperand0(call, operand, indirectionIndex) and
|
||||
operandForFullyConvertedCall(operand, call) and
|
||||
hasOperandAndIndex(node, operand, indirectionIndex)
|
||||
}
|
||||
|
||||
@@ -813,7 +799,7 @@ private predicate hasOperand(Node node, CallInstruction call, int indirectionInd
|
||||
private predicate hasInstruction(
|
||||
Node node, CallInstruction call, int indirectionIndex, Instruction instr
|
||||
) {
|
||||
indirectReturnOutNodeInstruction0(call, instr, indirectionIndex) and
|
||||
instructionForFullyConvertedCall(instr, call) and
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
@@ -1534,6 +1520,25 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand.getDef() = instr`, but there exists a `StoreInstruction` that
|
||||
* writes to an address that is equivalent to the value computed by `instr` in
|
||||
* between `instr` and `operand`, and therefore there should not be flow from `*instr`
|
||||
* to `*operand`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate isStoredToBetween(Instruction instr, Operand operand) {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) and
|
||||
exists(StoreInstruction store, IRBlock block, int storeIndex, int instrIndex, int operandIndex |
|
||||
store.getDestinationAddress() = instr and
|
||||
block.getInstruction(storeIndex) = store and
|
||||
block.getInstruction(instrIndex) = instr and
|
||||
block.getInstruction(operandIndex) = operand.getUse() and
|
||||
instrIndex < storeIndex and
|
||||
storeIndex < operandIndex
|
||||
)
|
||||
}
|
||||
|
||||
private predicate indirectionInstructionFlow(
|
||||
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
|
||||
) {
|
||||
@@ -1543,7 +1548,8 @@ private module Cached {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand))
|
||||
|
|
||||
hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex))
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex)) and
|
||||
not isStoredToBetween(instr, operand)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,32 +10,35 @@ private import ssa0.SsaInternals as SsaInternals0
|
||||
import SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
int getMaxIndirectionForIRVariable(IRVariable var) {
|
||||
exists(Type type, boolean isGLValue |
|
||||
var.getLanguageType().hasType(type, isGLValue) and
|
||||
if isGLValue = true
|
||||
then result = 1 + getMaxIndirectionsForType(type)
|
||||
else result = getMaxIndirectionsForType(type)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar, int ind) {
|
||||
ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())]
|
||||
} or
|
||||
TCallVariable(AllocationInstruction call, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))]
|
||||
TMkSourceVariable(SsaInternals0::SourceVariable base, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
class SourceVariable extends TSourceVariable {
|
||||
SsaInternals0::SourceVariable base;
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
SourceVariable() { any() }
|
||||
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
||||
|
||||
/** Gets the IR variable associated with this `SourceVariable`, if any. */
|
||||
IRVariable getIRVariable() { result = base.(BaseIRVariable).getIRVariable() }
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without any
|
||||
* indirections) of this source variable.
|
||||
*/
|
||||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() {
|
||||
ind = 0 and
|
||||
result = this.getBaseVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getBaseVariable().toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of loads performed on the base source variable
|
||||
@@ -43,65 +46,19 @@ private module SourceVariables {
|
||||
*/
|
||||
int getIndirection() { result = ind }
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without any
|
||||
* indirections) of this source variable.
|
||||
*/
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
|
||||
/** Holds if this variable is a glvalue. */
|
||||
predicate isGLValue() { none() }
|
||||
predicate isGLValue() { ind = 0 }
|
||||
|
||||
/**
|
||||
* Gets the type of this source variable. If `isGLValue()` holds, then
|
||||
* the type of this source variable should be thought of as "pointer
|
||||
* to `getType()`".
|
||||
*/
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var, ind) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = this.getIRVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getIRVariable().toString() + " indirection"
|
||||
DataFlowType getType() {
|
||||
if this.isGLValue()
|
||||
then result = base.getType()
|
||||
else result = getTypeImpl(base.getType(), ind - 1)
|
||||
}
|
||||
|
||||
override predicate isGLValue() { ind = 0 }
|
||||
|
||||
override DataFlowType getType() {
|
||||
if ind = 0 then result = var.getType() else result = getTypeImpl(var.getType(), ind - 1)
|
||||
}
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call, ind) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = "Call"
|
||||
or
|
||||
ind > 0 and
|
||||
result = "Call indirection"
|
||||
}
|
||||
|
||||
override DataFlowType getType() { result = getTypeImpl(call.getResultType(), ind) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +94,9 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
|
||||
cached
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address, int indirectionIndex) {
|
||||
exists(Instruction base | isDef(_, _, address, base, _, indirectionIndex) |
|
||||
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, base, _, indirectionIndex) and
|
||||
(
|
||||
// We only include the definition if the SSA pruning stage
|
||||
// concluded that the definition is live after the write.
|
||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
||||
@@ -148,8 +106,8 @@ private newtype TDefOrUseImpl =
|
||||
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
|
||||
)
|
||||
} or
|
||||
TUseImpl(Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, _, _, indirectionIndex) and
|
||||
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, base, _, indirectionIndex) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
} or
|
||||
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
@@ -236,7 +194,7 @@ abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the base of this definition or use.
|
||||
* This is always a `VariableAddressInstruction` or an `AllocationInstruction`.
|
||||
* This is always a `VariableAddressInstruction` or an `CallInstruction`.
|
||||
*/
|
||||
abstract BaseSourceVariableInstruction getBase();
|
||||
|
||||
@@ -308,15 +266,17 @@ abstract class DefImpl extends DefOrUseImpl {
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDefImpl {
|
||||
DirectDef() { this = TDefImpl(address, ind) }
|
||||
BaseSourceVariableInstruction base;
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
DirectDef() { this = TDefImpl(base, address, ind) }
|
||||
|
||||
override int getIndirection() { isDef(_, _, address, _, result, ind) }
|
||||
override BaseSourceVariableInstruction getBase() { result = base }
|
||||
|
||||
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
override int getIndirection() { isDef(_, _, address, base, result, ind) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, _, _, ind) }
|
||||
override Node0Impl getValue() { isDef(_, result, address, base, _, _) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
|
||||
}
|
||||
|
||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
||||
@@ -359,6 +319,7 @@ abstract class UseImpl extends DefOrUseImpl {
|
||||
|
||||
abstract private class OperandBasedUse extends UseImpl {
|
||||
Operand operand;
|
||||
BaseSourceVariableInstruction base;
|
||||
|
||||
bindingset[ind]
|
||||
OperandBasedUse() { any() }
|
||||
@@ -366,50 +327,44 @@ abstract private class OperandBasedUse extends UseImpl {
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
|
||||
// predicate's implementation.
|
||||
exists(BaseSourceVariableInstruction base | base = this.getBase() |
|
||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op, int indirectionIndex, int indirection |
|
||||
indirectionIndex = this.getIndirectionIndex() and
|
||||
indirection = this.getIndirection() and
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, indirection, indirectionIndex) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
)
|
||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op, int indirectionIndex, int indirection |
|
||||
indirectionIndex = this.getIndirectionIndex() and
|
||||
indirection = this.getIndirection() and
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, indirection, indirectionIndex) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override BaseSourceVariableInstruction getBase() { result = base }
|
||||
|
||||
final Operand getOperand() { result = operand }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
}
|
||||
|
||||
private class DirectUse extends OperandBasedUse, TUseImpl {
|
||||
DirectUse() { this = TUseImpl(operand, ind) }
|
||||
DirectUse() { this = TUseImpl(base, operand, ind) }
|
||||
|
||||
override int getIndirection() { isUse(_, operand, _, result, ind) }
|
||||
override int getIndirection() { isUse(_, operand, base, result, ind) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, ind) }
|
||||
|
||||
override predicate isCertain() { isUse(true, operand, _, _, ind) }
|
||||
override predicate isCertain() { isUse(true, operand, base, _, ind) }
|
||||
|
||||
override Node getNode() { nodeHasOperand(result, operand, ind) }
|
||||
}
|
||||
|
||||
private class IteratorUse extends OperandBasedUse, TIteratorUse {
|
||||
BaseSourceVariableInstruction container;
|
||||
IteratorUse() { this = TIteratorUse(operand, base, ind) }
|
||||
|
||||
IteratorUse() { this = TIteratorUse(operand, container, ind) }
|
||||
|
||||
override int getIndirection() { isIteratorUse(container, operand, result, ind) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
override int getIndirection() { isIteratorUse(base, operand, result, ind) }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
@@ -146,14 +147,6 @@ int countIndirectionsForCppType(LanguageType langType) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CallInstruction` that calls an allocation function such
|
||||
* as `malloc` or `operator new`.
|
||||
*/
|
||||
class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
private predicate isIndirectionType(Type t) { t instanceof Indirection }
|
||||
|
||||
private predicate hasUnspecifiedBaseType(Indirection t, Type base) {
|
||||
@@ -368,17 +361,22 @@ newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
// Each allocation gets its own source variable
|
||||
TBaseCallVariable(AllocationInstruction call)
|
||||
TBaseCallVariable(CallInstruction call) { not call.getResultIRType() instanceof IRVoidType }
|
||||
|
||||
abstract class BaseSourceVariable extends TBaseSourceVariable {
|
||||
abstract private class AbstractBaseSourceVariable extends TBaseSourceVariable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the type of this base source variable. */
|
||||
abstract DataFlowType getType();
|
||||
final DataFlowType getType() { this.getLanguageType().hasUnspecifiedType(result, _) }
|
||||
|
||||
/** Gets the `CppType` of this base source variable. */
|
||||
abstract CppType getLanguageType();
|
||||
}
|
||||
|
||||
class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
final class BaseSourceVariable = AbstractBaseSourceVariable;
|
||||
|
||||
class BaseIRVariable extends AbstractBaseSourceVariable, TBaseIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getIRVariable() { result = var }
|
||||
@@ -387,19 +385,19 @@ class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override DataFlowType getType() { result = var.getType() }
|
||||
override CppType getLanguageType() { result = var.getLanguageType() }
|
||||
}
|
||||
|
||||
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
|
||||
AllocationInstruction call;
|
||||
class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
|
||||
CallInstruction call;
|
||||
|
||||
BaseCallVariable() { this = TBaseCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCallInstruction() { result = call }
|
||||
CallInstruction getCallInstruction() { result = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override DataFlowType getType() { result = call.getResultType() }
|
||||
override CppType getLanguageType() { result = getResultLanguageType(call) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,8 +497,7 @@ private class BaseIRVariableInstruction extends BaseSourceVariableInstruction,
|
||||
override BaseIRVariable getBaseSourceVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
}
|
||||
|
||||
private class BaseAllocationInstruction extends BaseSourceVariableInstruction, AllocationInstruction
|
||||
{
|
||||
private class BaseCallInstruction extends BaseSourceVariableInstruction, CallInstruction {
|
||||
override BaseCallVariable getBaseSourceVariable() { result.getCallInstruction() = this }
|
||||
}
|
||||
|
||||
@@ -868,7 +865,7 @@ private module Cached {
|
||||
* to a specific address.
|
||||
*/
|
||||
private predicate isCertainAddress(Operand operand) {
|
||||
operand.getDef() instanceof VariableAddressInstruction
|
||||
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
|
||||
or
|
||||
operand.getType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
@@ -15,15 +15,12 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
class SourceVariable instanceof BaseSourceVariable {
|
||||
string toString() { result = BaseSourceVariable.super.toString() }
|
||||
|
||||
class SourceVariable extends BaseSourceVariable {
|
||||
/**
|
||||
* Gets the base source variable of this `SourceVariable`.
|
||||
*/
|
||||
BaseSourceVariable getBaseVariable() { result = this }
|
||||
}
|
||||
|
||||
class SourceIRVariable = BaseIRVariable;
|
||||
|
||||
class CallVariable = BaseCallVariable;
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
@@ -3,24 +3,10 @@
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import codeql.util.Unit
|
||||
private import Reason as Reason
|
||||
private import RangeAnalysisStage
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
|
||||
module CppLangImplConstant implements LangSig<FloatDelta> {
|
||||
private module Param implements Reason::ParamSig {
|
||||
class TypeReasonImpl = Unit;
|
||||
}
|
||||
|
||||
class SemReason = Reason::Make<Param>::SemReason;
|
||||
|
||||
class SemNoReason = Reason::Make<Param>::SemNoReason;
|
||||
|
||||
class SemCondReason = Reason::Make<Param>::SemCondReason;
|
||||
|
||||
class SemTypeReason = Reason::Make<Param>::SemTypeReason;
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
|
||||
@@ -61,23 +61,18 @@ private newtype TSemReason =
|
||||
guard = any(ConstantStage::SemCondReason reason).getCond()
|
||||
or
|
||||
guard = any(RelativeStage::SemCondReason reason).getCond()
|
||||
} or
|
||||
TSemTypeReason()
|
||||
}
|
||||
|
||||
private ConstantStage::SemReason constantReason(SemReason reason) {
|
||||
ConstantStage::SemReason constantReason(SemReason reason) {
|
||||
result instanceof ConstantStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(ConstantStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
or
|
||||
result instanceof ConstantStage::SemTypeReason and reason instanceof SemTypeReason
|
||||
}
|
||||
|
||||
private RelativeStage::SemReason relativeReason(SemReason reason) {
|
||||
RelativeStage::SemReason relativeReason(SemReason reason) {
|
||||
result instanceof RelativeStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(RelativeStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
or
|
||||
result instanceof RelativeStage::SemTypeReason and reason instanceof SemTypeReason
|
||||
}
|
||||
|
||||
import Public
|
||||
@@ -116,12 +111,4 @@ module Public {
|
||||
|
||||
override string toString() { result = this.getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* based on type-information.
|
||||
*/
|
||||
class SemTypeReason extends SemReason, TSemTypeReason {
|
||||
override string toString() { result = "TypeReason" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,25 +7,9 @@ private import RangeAnalysisStage
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.IntDelta
|
||||
private import RangeAnalysisImpl
|
||||
private import codeql.util.Unit
|
||||
private import Reason as Reason
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
module CppLangImplRelative implements LangSig<FloatDelta> {
|
||||
private module Param implements Reason::ParamSig {
|
||||
class TypeReasonImpl extends Unit {
|
||||
TypeReasonImpl() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
class SemReason = Reason::Make<Param>::SemReason;
|
||||
|
||||
class SemNoReason = Reason::Make<Param>::SemNoReason;
|
||||
|
||||
class SemCondReason = Reason::Make<Param>::SemCondReason;
|
||||
|
||||
class SemTypeReason = Reason::Make<Param>::SemTypeReason;
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
|
||||
@@ -113,37 +113,6 @@ signature module DeltaSig {
|
||||
}
|
||||
|
||||
signature module LangSig<DeltaSig D> {
|
||||
/** A reason for an inferred bound. */
|
||||
class SemReason {
|
||||
/**
|
||||
* Returns `this` if `reason` is not a `SemTypeReason`. Otherwise,
|
||||
* this predicate returns `SemTypeReason`.
|
||||
*
|
||||
* This predicate ensures that we propagate `SemTypeReason` all the way
|
||||
* to the top-level of a call to `semBounded` if the inferred bound is
|
||||
* based on type-information.
|
||||
*/
|
||||
bindingset[this, reason]
|
||||
SemReason combineWith(SemReason reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason;
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason {
|
||||
SemGuard getCond();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* based on type-information.
|
||||
*/
|
||||
class SemTypeReason extends SemReason;
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
@@ -280,14 +249,6 @@ module RangeStage<
|
||||
DeltaSig D, BoundSig<D> Bounds, OverflowSig<D> OverflowParam, LangSig<D> LangParam,
|
||||
UtilSig<D> UtilParam>
|
||||
{
|
||||
class SemReason = LangParam::SemReason;
|
||||
|
||||
class SemCondReason = LangParam::SemCondReason;
|
||||
|
||||
class SemNoReason = LangParam::SemNoReason;
|
||||
|
||||
class SemTypeReason = LangParam::SemTypeReason;
|
||||
|
||||
private import Bounds
|
||||
private import LangParam
|
||||
private import UtilParam
|
||||
@@ -548,6 +509,36 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) { possibleReason(guard) }
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = this.getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
@@ -560,13 +551,13 @@ module RangeStage<
|
||||
semSsaUpdateStep(v, e, delta) and
|
||||
pos.hasReadOfVar(v) and
|
||||
(upper = true or upper = false) and
|
||||
reason instanceof SemNoReason
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason.(SemCondReason).getCond() = guard
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -579,20 +570,10 @@ module RangeStage<
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason.(SemCondReason).getCond() = guard
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
@@ -606,27 +587,6 @@ module RangeStage<
|
||||
delta = D::fromInt(0) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = D::fromInt(-1)
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = D::fromInt(0)
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = D::fromInt(1)
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = D::fromInt(0)
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = D::fromInt(-1) and
|
||||
@@ -709,7 +669,7 @@ module RangeStage<
|
||||
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = D::fromFloat(D::toFloat(d1) + D::toFloat(d2)) and
|
||||
(if r1 instanceof SemNoReason then reason = r2 else reason = r1.combineWith(r2))
|
||||
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
|
||||
)
|
||||
or
|
||||
exists(D::Delta d, SemReason r1, SemReason r2 |
|
||||
@@ -723,9 +683,9 @@ module RangeStage<
|
||||
upper = false and delta = D::fromFloat(D::toFloat(d) + 1)
|
||||
) and
|
||||
(
|
||||
reason = r1.combineWith(r2)
|
||||
reason = r1
|
||||
or
|
||||
reason = r2.combineWith(r1) and not r2 instanceof SemNoReason
|
||||
reason = r2 and not r2 instanceof SemNoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -795,7 +755,7 @@ module RangeStage<
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge0 = false and
|
||||
origdelta = D::fromFloat(0) and
|
||||
reason instanceof SemNoReason
|
||||
reason = TSemNoReason()
|
||||
|
|
||||
if semBackEdge(phi, inp, edge)
|
||||
then
|
||||
@@ -1053,13 +1013,13 @@ module RangeStage<
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason instanceof SemNoReason
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
baseBound(e, delta, upper) and
|
||||
b instanceof SemZeroBound and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason instanceof SemNoReason
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
@@ -1113,9 +1073,9 @@ module RangeStage<
|
||||
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
|
||||
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
|
||||
(
|
||||
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1.combineWith(r2)
|
||||
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
|
||||
or
|
||||
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2.combineWith(r1)
|
||||
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
|
||||
)
|
||||
|
|
||||
upper = true and delta = D::fromFloat(D::toFloat(d1).maximum(D::toFloat(d2)))
|
||||
@@ -1141,15 +1101,26 @@ module RangeStage<
|
||||
delta = D::fromFloat(D::toFloat(dLeft) + D::toFloat(dRight)) and
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
||||
|
|
||||
b = bLeft and
|
||||
origdelta = odLeft and
|
||||
reason = rLeft.combineWith(rRight) and
|
||||
bRight instanceof SemZeroBound
|
||||
b = bLeft and origdelta = odLeft and reason = rLeft and bRight instanceof SemZeroBound
|
||||
or
|
||||
b = bRight and
|
||||
origdelta = odRight and
|
||||
reason = rRight.combineWith(rLeft) and
|
||||
bLeft instanceof SemZeroBound
|
||||
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight |
|
||||
boundedSubOperandLeft(e, upper, b, dLeft, fbeLeft, origdelta, reason) and
|
||||
boundedSubOperandRight(e, upper, dRight, fbeRight) and
|
||||
// when `upper` is `true` we have:
|
||||
// left <= b + dLeft
|
||||
// right >= 0 + dRight
|
||||
// left - right <= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
// and when `upper` is `false` we have:
|
||||
// left >= b + dLeft
|
||||
// right <= 0 + dRight
|
||||
// left - right >= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
delta = D::fromFloat(D::toFloat(dLeft) - D::toFloat(dRight)) and
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
@@ -1165,9 +1136,9 @@ module RangeStage<
|
||||
(
|
||||
if D::toFloat(d1).abs() > D::toFloat(d2).abs()
|
||||
then (
|
||||
d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1.combineWith(r2)
|
||||
d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
|
||||
) else (
|
||||
d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2.combineWith(r1)
|
||||
d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
|
||||
)
|
||||
)
|
||||
|
|
||||
@@ -1183,14 +1154,11 @@ module RangeStage<
|
||||
boundedMulOperand(e, upper, true, dLeft, fbeLeft, odLeft, rLeft) and
|
||||
boundedMulOperand(e, upper, false, dRight, fbeRight, odRight, rRight) and
|
||||
delta = D::fromFloat(D::toFloat(dLeft) * D::toFloat(dRight)) and
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight) and
|
||||
b instanceof SemZeroBound
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
||||
|
|
||||
origdelta = odLeft and
|
||||
reason = rLeft.combineWith(rRight)
|
||||
b instanceof SemZeroBound and origdelta = odLeft and reason = rLeft
|
||||
or
|
||||
origdelta = odRight and
|
||||
reason = rRight.combineWith(rLeft)
|
||||
b instanceof SemZeroBound and origdelta = odRight and reason = rRight
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1219,6 +1187,37 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `left <= b + delta` if `upper` is `true`
|
||||
* and `left >= b + delta` is `upper` is `false`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandLeft(
|
||||
SemSubExpr sub, boolean upper, SemBound b, D::Delta delta, boolean fromBackEdge,
|
||||
D::Delta origdelta, SemReason reason
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `right <= 0 + delta` if `upper` is `false`
|
||||
* and `right >= 0 + delta` is `upper` is `true`.
|
||||
*
|
||||
* Note that the boolean value of `upper` is flipped compared to many other predicates in
|
||||
* this file. This ensures a clean join at the call-site.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandRight(
|
||||
SemSubExpr sub, boolean upper, D::Delta delta, boolean fromBackEdge
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getRightOperand(), any(SemZeroBound zb), delta, upper.booleanNot(), fromBackEdge, _,
|
||||
_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate boundedRemExpr(
|
||||
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* Provides a `Make` parameterized module for constructing a `Reason` type that is used
|
||||
* when implementing the `LangSig` module.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
|
||||
/** The necessary parameters that must be implemented to instantiate `Make`. */
|
||||
signature module ParamSig {
|
||||
class TypeReasonImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module that constructs a `Reason` type when provided with an implementation
|
||||
* of `ParamSig`.
|
||||
*/
|
||||
module Make<ParamSig Param> {
|
||||
private import Param
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) or
|
||||
TSemTypeReason(TypeReasonImpl trc)
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
|
||||
bindingset[this, reason]
|
||||
abstract SemReason combineWith(SemReason reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
|
||||
override SemReason combineWith(SemReason reason) { result = reason }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = this.getCond().toString() }
|
||||
|
||||
bindingset[this, reason]
|
||||
override SemReason combineWith(SemReason reason) {
|
||||
// Since we end up reporting a `SemReason` for the inferred bound we often pick somewhat
|
||||
// arbitrarily between two `SemReason`s during the analysis. This isn't an issue for most reasons
|
||||
// since they're mainly used for constructing alert messages. However, the `SemTypeReason` is
|
||||
// supposed to be used in query logic to filter out bounds inferred by type-based analysis if
|
||||
// the query author chooses to do so. So we need to ensure that if _any_ of the bounds that
|
||||
// contribute to the final bound depends on type information then the `SemReason` we report must
|
||||
// be a `SemTypeReason`. So when we need to combine this `SemCondReason` with a `SemTypeReason`
|
||||
// the result should always be a `SemTypeReason`.
|
||||
if reason instanceof SemTypeReason then result instanceof SemTypeReason else result = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* based on type-information.
|
||||
*/
|
||||
class SemTypeReason extends SemReason, TSemTypeReason {
|
||||
TypeReasonImpl impl;
|
||||
|
||||
SemTypeReason() { this = TSemTypeReason(impl) }
|
||||
|
||||
override string toString() { result = "TypeReason" }
|
||||
|
||||
bindingset[this, reason]
|
||||
override SemReason combineWith(SemReason reason) { result = this and exists(reason) }
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
* but because there's a strict comparison that compares `n` against the size of the allocation this
|
||||
* snippet is fine.
|
||||
*/
|
||||
module SizeBarrier {
|
||||
private module SizeBarrier {
|
||||
private module SizeBarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for the second
|
||||
@@ -104,35 +104,60 @@ module SizeBarrier {
|
||||
hasSize(_, source, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `testIsTrue`.
|
||||
*/
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
|
||||
// in a guard such as `left < right + k`.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `large` expression
|
||||
// in a guard such as `small <= large + k`.
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k + 1, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
}
|
||||
|
||||
private import DataFlow::Global<SizeBarrierConfig>
|
||||
module SizeBarrierFlow = DataFlow::Global<SizeBarrierConfig>;
|
||||
|
||||
private int getAFlowStateForNode(DataFlow::Node node) {
|
||||
private int getASizeAddend(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
flow(source, node) and
|
||||
SizeBarrierFlow::flow(source, node) and
|
||||
hasSize(_, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `edge`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
IRGuardCondition g, Operand small, DataFlow::Node large, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getAFlowStateForNode(nRight) and
|
||||
k <= state
|
||||
SizeBarrierFlow::flowTo(large) and
|
||||
SizeBarrierConfig::isSink(DataFlow::operandNode(small), large, g, k, edge)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction `instr` that is guarded by a check such as `instr <= small + delta` where
|
||||
* `small <= _ + k` and `small` is the "small side" of of a relational comparison that checks
|
||||
* whether `small <= size` where `size` is the size of an allocation.
|
||||
*/
|
||||
Instruction getABarrierInstruction0(int delta, int k) {
|
||||
exists(
|
||||
IRGuardCondition g, ValueNumber value, Operand small, boolean edge, DataFlow::Node large
|
||||
|
|
||||
// We know:
|
||||
// 1. result <= value + delta (by `bounded`)
|
||||
// 2. value <= large + k (by `operandGuardChecks`).
|
||||
// So:
|
||||
// result <= value + delta (by 1.)
|
||||
// <= large + k + delta (by 2.)
|
||||
small = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](small), large,
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
k < getASizeAddend(large)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,13 +165,14 @@ module SizeBarrier {
|
||||
* Gets an instruction that is guarded by a guard condition which ensures that
|
||||
* the value of the instruction is upper-bounded by size of some allocation.
|
||||
*/
|
||||
bindingset[state]
|
||||
pragma[inline_late]
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
|
||||
pragma[only_bind_into](state), pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
exists(int delta, int k |
|
||||
state > k + delta and
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
result = getABarrierInstruction0(delta, k)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -155,14 +181,16 @@ module SizeBarrier {
|
||||
* the value of the node is upper-bounded by size of some allocation.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode(int state) {
|
||||
result.asOperand() = getABarrierInstruction(state).getAUse()
|
||||
exists(DataFlow::Node source, int delta, int k |
|
||||
SizeBarrierFlow::flow(source, result) and
|
||||
hasSize(_, source, state) and
|
||||
result.asInstruction() = SizeBarrier::getABarrierInstruction0(delta, k) and
|
||||
state > k + delta
|
||||
// so now we have:
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
|
||||
* `getABarrierNode` for the definition of what it means to be guarded).
|
||||
*/
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
}
|
||||
|
||||
private module InterestingPointerAddInstruction {
|
||||
|
||||
@@ -66,11 +66,14 @@
|
||||
* module. Since the node we are tracking is not necessarily _equal_ to the pointer-arithmetic instruction, but rather satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`, we need to account for the delta when checking if a guard is sufficiently
|
||||
* strong to infer that a future dereference is safe. To do this, we check that the guard guarantees that a node `n` satisfies
|
||||
* `n < node + k` where `node` is a node we know is equal to the value of the dereference source (i.e., it satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`) and `k <= deltaDerefSourceAndPai`. Combining this we have
|
||||
* `n < node + k <= node + deltaDerefSourceAndPai <= pai + 2*deltaDerefSourceAndPai` (TODO: Oops. This math doesn't quite work out.
|
||||
* I think this is because we need to redefine the `BarrierConfig` to start flow at the pointer-arithmetic instruction instead of
|
||||
* at the dereference source. When combined with TODO above it's easy to show that this guard ensures that the dereference is safe).
|
||||
* `n < node + k` where `node` is a node such that `node <= pai`. Thus, we know that any node `m` such that `m <= n + delta` where
|
||||
* `delta + k <= 0` will be safe because:
|
||||
* ```
|
||||
* m <= n + delta
|
||||
* < node + k + delta
|
||||
* <= pai + k + delta
|
||||
* <= pai
|
||||
* ```
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
@@ -82,16 +85,19 @@ private import RangeAnalysisUtil
|
||||
|
||||
private module InvalidPointerToDerefBarrier {
|
||||
private module BarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
|
||||
invalidPointerToDerefSource(_, _, source, _)
|
||||
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
|
||||
invalidPointerToDerefSource(_, pai, _, _) and
|
||||
// source <= pai
|
||||
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
@@ -99,59 +105,82 @@ private module InvalidPointerToDerefBarrier {
|
||||
|
||||
private module BarrierFlow = DataFlow::Global<BarrierConfig>;
|
||||
|
||||
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
BarrierFlow::flow(source, node) and
|
||||
invalidPointerToDerefSource(_, _, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` ensures that `small < large + k` if `g` evaluates to `edge`.
|
||||
*
|
||||
* Additionally, it also holds that `large <= pai`. Thus, when `g` evaluates to `edge`
|
||||
* it holds that `small < pai + k`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
PointerArithmeticInstruction pai, IRGuardCondition g, Operand small, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getInvalidPointerToDerefSourceDelta(nRight) and
|
||||
k <= state
|
||||
exists(DataFlow::Node source, DataFlow::Node nSmall, DataFlow::Node nLarge |
|
||||
nSmall.asOperand() = small and
|
||||
BarrierConfig::isSource(source, pai) and
|
||||
BarrierFlow::flow(source, nLarge) and
|
||||
BarrierConfig::isSink(nSmall, nLarge, g, k, edge)
|
||||
)
|
||||
}
|
||||
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
/**
|
||||
* Gets an instruction `instr` such that `instr < pai`.
|
||||
*/
|
||||
Instruction getABarrierInstruction(PointerArithmeticInstruction pai) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge, int delta, int k |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state,
|
||||
pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
// value < pai + k
|
||||
operandGuardChecks(pai, pragma[only_bind_into](g), pragma[only_bind_into](use),
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
// result <= value + delta
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
delta + k <= 0
|
||||
// combining the above we have: result < pai + k + delta <= pai
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() }
|
||||
DataFlow::Node getABarrierNode(PointerArithmeticInstruction pai) {
|
||||
result.asOperand() = getABarrierInstruction(pai).getAUse()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
/**
|
||||
* Gets an address operand whose definition `instr` satisfies `instr < pai`.
|
||||
*/
|
||||
AddressOperand getABarrierAddressOperand(PointerArithmeticInstruction pai) {
|
||||
result.getDef() = getABarrierInstruction(pai)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a pointer-arithmetic operation found
|
||||
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
|
||||
*/
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, _, source, _) }
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends PointerArithmeticInstruction {
|
||||
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState pai) {
|
||||
invalidPointerToDerefSource(_, pai, source, _)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState pai) { none() }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
|
||||
or
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState pai) {
|
||||
// `node = getABarrierNode(pai)` ensures that node < pai, so this node is safe to dereference.
|
||||
// Note that this is the only place where the `FlowState` is used in this configuration.
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode(pai)
|
||||
}
|
||||
}
|
||||
|
||||
private import DataFlow::Global<InvalidPointerToDerefConfig>
|
||||
private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
|
||||
|
||||
/**
|
||||
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
|
||||
@@ -165,19 +194,14 @@ private predicate invalidPointerToDerefSource(
|
||||
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
|
||||
int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(int rhsSizeDelta |
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0 and
|
||||
// TODO: This condition will go away once #13725 is merged, and then we can make `SizeBarrier`
|
||||
// private to `AllocationToInvalidPointer.qll`.
|
||||
not derefSource.getBasicBlock() =
|
||||
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
|
||||
)
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
|
||||
// derefSource <= pai + deltaDerefSourceAndPai
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,15 +211,14 @@ private predicate invalidPointerToDerefSource(
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isInvalidPointerDerefSink(
|
||||
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
|
||||
DataFlow::Node sink, AddressOperand addr, Instruction i, string operation,
|
||||
int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(AddressOperand addr, Instruction s, IRBlock b |
|
||||
exists(Instruction s |
|
||||
s = sink.asInstruction() and
|
||||
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
|
||||
deltaDerefSinkAndDerefAddress >= 0 and
|
||||
i.getAnOperand() = addr and
|
||||
b = i.getBlock() and
|
||||
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
|
||||
i.getAnOperand() = addr
|
||||
|
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
@@ -221,9 +244,11 @@ private Instruction getASuccessor(Instruction instr) {
|
||||
instr.getBlock().getASuccessor+() = result.getBlock()
|
||||
}
|
||||
|
||||
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
|
||||
private predicate paiForDereferenceSink(
|
||||
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(DataFlow::Node derefSource |
|
||||
invalidPointerToDerefSource(_, pai, derefSource, _) and
|
||||
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink)
|
||||
)
|
||||
}
|
||||
@@ -235,13 +260,15 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
|
||||
*/
|
||||
private predicate derefSinkToOperation(
|
||||
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
|
||||
string description, int deltaDerefSinkAndDerefAddress
|
||||
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(Instruction operationInstr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
|
||||
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
|
||||
exists(Instruction operationInstr, AddressOperand addr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
|
||||
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
operationInstr = getASuccessor(derefSink.asInstruction()) and
|
||||
operation.asInstruction() = operationInstr
|
||||
operation.asInstruction() = operationInstr and
|
||||
not addr = InvalidPointerToDerefBarrier::getABarrierAddressOperand(pai)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,7 +287,8 @@ predicate operationIsOffBy(
|
||||
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
|
||||
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSinkAndDerefAddress) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ private Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
* Holds if `i <= b + delta`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
private predicate boundedImplCand(Instruction i, Instruction b, int delta) {
|
||||
exists(SemBound bound, IRFunction func |
|
||||
semBounded(getSemanticExpr(i), bound, delta, true, _) and
|
||||
b = getABoundIn(bound, func) and
|
||||
@@ -26,6 +26,15 @@ private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta` and `delta` is the smallest integer that satisfies
|
||||
* this condition.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
delta = min(int cand | boundedImplCand(i, b, cand))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta`.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -24,7 +24,7 @@ import semmle.code.cpp.security.BufferWrite
|
||||
from BufferWrite bw, int destSize
|
||||
where
|
||||
bw.hasExplicitLimit() and // has an explicit size limit
|
||||
destSize = getBufferSize(bw.getDest(), _) and
|
||||
destSize = max(getBufferSize(bw.getDest(), _)) and
|
||||
bw.getExplicitLimit() > destSize // but it's larger than the destination
|
||||
select bw,
|
||||
"This '" + bw.getBWDesc() + "' operation is limited to " + bw.getExplicitLimit() +
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/badly-bounded-write` query could report false positives when a pointer was first initialized with a literal and later assigned a dynamically allocated array. These false positives now no longer occur.
|
||||
3
cpp/ql/src/change-notes/released/0.7.2.md
Normal file
3
cpp/ql/src/change-notes/released/0.7.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.1
|
||||
lastReleaseVersion: 0.7.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.7.2-dev
|
||||
version: 0.7.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -69,12 +69,6 @@ edges
|
||||
| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end |
|
||||
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... |
|
||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 |
|
||||
| test.cpp:351:9:351:11 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
nodes
|
||||
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
|
||||
| test.cpp:34:10:34:12 | buf | semmle.label | buf |
|
||||
@@ -167,11 +161,6 @@ nodes
|
||||
| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
| test.cpp:351:9:351:11 | arr | semmle.label | arr |
|
||||
| test.cpp:351:9:351:14 | access to array | semmle.label | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | semmle.label | arr |
|
||||
| test.cpp:351:18:351:25 | access to array | semmle.label | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | semmle.label | arr |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
|
||||
@@ -194,6 +183,3 @@ subpaths
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:330:13:330:24 | Store: ... = ... | write |
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:331:13:331:24 | Store: ... = ... | write |
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:333:13:333:24 | Store: ... = ... | write |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
|
||||
@@ -348,7 +348,7 @@ int positiveRange(int x) {
|
||||
int arr[128];
|
||||
|
||||
for(int i=127-offset; i>= 0; i--) {
|
||||
arr[i] = arr[i+1] + arr[i+offset]; // GOOD [FALSE POSITIVE]
|
||||
arr[i] = arr[i+1] + arr[i+offset]; // GOOD
|
||||
}
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
@@ -129,7 +129,6 @@ edges
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
|
||||
| test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:357:24:357:30 | ... + ... |
|
||||
@@ -214,20 +213,21 @@ edges
|
||||
| test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... |
|
||||
| test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... |
|
||||
| test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... |
|
||||
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | test.cpp:701:15:701:16 | * ... |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:705:18:705:18 | q |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
|
||||
| test.cpp:711:13:711:26 | new[] | test.cpp:714:11:714:11 | q |
|
||||
| test.cpp:714:11:714:11 | q | test.cpp:705:18:705:18 | q |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
|
||||
nodes
|
||||
| test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
|
||||
@@ -320,8 +320,6 @@ nodes
|
||||
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:274:5:274:10 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:304:15:304:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:308:5:308:29 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:355:14:355:27 | new[] | semmle.label | new[] |
|
||||
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
|
||||
@@ -371,20 +369,19 @@ nodes
|
||||
| test.cpp:559:5:559:19 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:642:14:642:31 | new[] | semmle.label | new[] |
|
||||
| test.cpp:647:5:647:19 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:652:14:652:27 | new[] | semmle.label | new[] |
|
||||
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:662:3:662:11 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:667:14:667:31 | new[] | semmle.label | new[] |
|
||||
| test.cpp:675:7:675:23 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:695:13:695:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:701:15:701:16 | * ... | semmle.label | * ... |
|
||||
| test.cpp:705:18:705:18 | q | semmle.label | q |
|
||||
| test.cpp:705:18:705:18 | q | semmle.label | q |
|
||||
| test.cpp:706:12:706:13 | * ... | semmle.label | * ... |
|
||||
| test.cpp:711:13:711:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:714:11:714:11 | q | semmle.label | q |
|
||||
| test.cpp:730:12:730:28 | new[] | semmle.label | new[] |
|
||||
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:733:5:733:12 | ... = ... | semmle.label | ... = ... |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:6:14:6:15 | * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
|
||||
@@ -406,7 +403,6 @@ subpaths
|
||||
| test.cpp:254:9:254:16 | ... = ... | test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:16 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:248:24:248:30 | call to realloc | call to realloc | test.cpp:254:11:254:11 | i | i |
|
||||
| test.cpp:264:13:264:14 | * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
|
||||
| test.cpp:274:5:274:10 | ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
|
||||
| test.cpp:308:5:308:29 | ... = ... | test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:304:15:304:26 | new[] | new[] | test.cpp:308:8:308:10 | ... + ... | ... + ... |
|
||||
| test.cpp:358:14:358:26 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
|
||||
| test.cpp:359:14:359:32 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
|
||||
| test.cpp:384:13:384:16 | * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size |
|
||||
@@ -418,7 +414,6 @@ subpaths
|
||||
| test.cpp:548:5:548:19 | ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos |
|
||||
| test.cpp:559:5:559:19 | ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos |
|
||||
| test.cpp:647:5:647:19 | ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos |
|
||||
| test.cpp:662:3:662:11 | ... = ... | test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:652:14:652:27 | new[] | new[] | test.cpp:653:19:653:22 | size | size |
|
||||
| test.cpp:675:7:675:23 | ... = ... | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:667:14:667:31 | new[] | new[] | test.cpp:675:10:675:18 | ... ++ | ... ++ |
|
||||
| test.cpp:701:15:701:16 | * ... | test.cpp:695:13:695:26 | new[] | test.cpp:701:15:701:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:695:13:695:26 | new[] | new[] | test.cpp:696:19:696:22 | size | size |
|
||||
| test.cpp:706:12:706:13 | * ... | test.cpp:711:13:711:26 | new[] | test.cpp:706:12:706:13 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:711:13:711:26 | new[] | new[] | test.cpp:712:19:712:22 | size | size |
|
||||
| test.cpp:733:5:733:12 | ... = ... | test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:730:12:730:28 | new[] | new[] | test.cpp:732:21:732:25 | ... + ... | ... + ... |
|
||||
|
||||
@@ -305,7 +305,7 @@ void test21() {
|
||||
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
xs[i] = test21_get(i); // GOOD
|
||||
xs[i+1] = test21_get(i+1); // $ alloc=L304 alloc=L304-1 deref=L308 // GOOD [FALSE POSITIVE]
|
||||
xs[i+1] = test21_get(i+1); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ void test32(unsigned size) {
|
||||
xs++;
|
||||
if (xs >= end)
|
||||
return;
|
||||
xs[0] = 0; // $ deref=L656->L662+1 deref=L657->L662+1 GOOD [FALSE POSITIVE]
|
||||
xs[0] = 0; // GOOD
|
||||
}
|
||||
|
||||
void test33(unsigned size, unsigned src_pos)
|
||||
@@ -672,7 +672,7 @@ void test33(unsigned size, unsigned src_pos)
|
||||
while (dst_pos < size - 1) {
|
||||
dst_pos++;
|
||||
if (true)
|
||||
xs[dst_pos++] = 0; // $ alloc=L667+1 deref=L675 // GOOD [FALSE POSITIVE]
|
||||
xs[dst_pos++] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,3 +714,31 @@ void test35(unsigned long size, char* q)
|
||||
deref(q);
|
||||
}
|
||||
}
|
||||
|
||||
void test21_simple(bool b) {
|
||||
int n = 0;
|
||||
if (b) n = 2;
|
||||
|
||||
int* xs = new int[n];
|
||||
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
xs[i+1] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test36(unsigned size, unsigned n) {
|
||||
int* p = new int[size + 2];
|
||||
if(n < size + 1) {
|
||||
int* end = p + (n + 2); // $ alloc=L730+2
|
||||
*end = 0; // $ deref=L733 // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void test37(unsigned long n)
|
||||
{
|
||||
int *p = new int[n];
|
||||
for (unsigned long i = n; i != 0u; i--)
|
||||
{
|
||||
p[n - i] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
@@ -732,7 +732,7 @@ void test_does_not_write_source_to_dereference()
|
||||
{
|
||||
int x;
|
||||
does_not_write_source_to_dereference(&x);
|
||||
sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11
|
||||
sink(x); // $ ast=733:7 ir SPURIOUS: ast=726:11
|
||||
}
|
||||
|
||||
void sometimes_calls_sink_eq(int x, int n) {
|
||||
|
||||
@@ -134,7 +134,7 @@ void pointer_test() {
|
||||
sink(*p3); // $ ast,ir
|
||||
|
||||
*p3 = 0;
|
||||
sink(*p3); // $ SPURIOUS: ast,ir
|
||||
sink(*p3); // $ SPURIOUS: ast
|
||||
}
|
||||
|
||||
// --- return values ---
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
| file://:0:0:0:0 | There was an error during this compilation |
|
||||
| float128.cpp:1:39:1:39 | 128-bit floating-point types are not supported in this configuration |
|
||||
| float128.cpp:2:30:2:30 | 128-bit floating-point types are not supported in this configuration |
|
||||
@@ -1,4 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from Diagnostic d
|
||||
select d
|
||||
@@ -1,5 +1,5 @@
|
||||
typedef _Complex float __attribute__((mode(TC))) _Complex128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
|
||||
typedef float __attribute__((mode(TF))) _Float128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
|
||||
typedef _Complex float __attribute__((mode(TC))) _Complex128;
|
||||
typedef float __attribute__((mode(TF))) _Float128;
|
||||
|
||||
int main() {
|
||||
__float128 f = 1.0f;
|
||||
@@ -25,4 +25,3 @@ __float128 id(__float128 q)
|
||||
{
|
||||
return q;
|
||||
}
|
||||
// semmle-extractor-options: --expect_errors
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | <error-type> |
|
||||
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | <error-type> |
|
||||
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | float __complex__ |
|
||||
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | __float128 |
|
||||
| float128.cpp:13:29:13:54 | __is_floating_point_helper<T> | float128.cpp:10:8:10:17 | false_type |
|
||||
| float128.cpp:14:19:14:51 | __is_floating_point_helper<float> | float128.cpp:11:8:11:16 | true_type |
|
||||
| float128.cpp:15:19:15:52 | __is_floating_point_helper<double> | float128.cpp:11:8:11:16 | true_type |
|
||||
|
||||
@@ -95,3 +95,25 @@ void gotoLoop(bool b1, bool b2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_sub(int x, int y, int n) {
|
||||
if(x > 0 && x < 500) {
|
||||
if(y > 0 && y < 10) {
|
||||
range(x - y); // $ range=<=498 range=>=-8
|
||||
}
|
||||
|
||||
if(n > 0 && n < 100) {
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
range(n - i); // $ range=">=Phi: i-97" range=<=99 range=>=-97
|
||||
range(i - n); // $ range="<=Phi: i-1" range=">=Phi: i-99" range=<=97 range=>=-99
|
||||
}
|
||||
|
||||
for (int i = n; i != 0; i--)
|
||||
{
|
||||
range(n - i); // $ SPURIOUS: overflow=+
|
||||
range(i - n); // $ range=">=Phi: i-99"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
| tests2.cpp:17:3:17:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
|
||||
| tests2.cpp:22:3:22:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
|
||||
| tests2.cpp:27:3:27:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
|
||||
| tests2.cpp:31:3:31:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
|
||||
| tests2.cpp:36:3:36:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
|
||||
| tests2.cpp:41:3:41:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
|
||||
| tests2.cpp:46:3:46:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
|
||||
| tests2.cpp:18:3:18:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
|
||||
| tests2.cpp:23:3:23:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
|
||||
| tests2.cpp:28:3:28:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
|
||||
| tests2.cpp:32:3:32:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
|
||||
| tests2.cpp:37:3:37:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
|
||||
| tests2.cpp:42:3:42:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
|
||||
| tests2.cpp:47:3:47:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
|
||||
| tests.c:54:3:54:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:58:3:58:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:62:17:62:24 | buffer10 | This 'scanf string argument' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
|
||||
@@ -6,6 +6,7 @@ void *realloc(void *ptr, size_t size);
|
||||
void *calloc(size_t nmemb, size_t size);
|
||||
void free(void *ptr);
|
||||
wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
|
||||
// --- Semmle tests ---
|
||||
|
||||
@@ -46,3 +47,18 @@ void tests2() {
|
||||
wcscpy(buffer, L"12345678"); // BAD: buffer overflow
|
||||
delete [] buffer;
|
||||
}
|
||||
|
||||
char* dest1 = "a";
|
||||
char* dest2 = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
void test3() {
|
||||
const char src[] = "abcdefghijkl";
|
||||
dest1 = (char*)malloc(sizeof(src));
|
||||
if (!dest1)
|
||||
return;
|
||||
snprintf(dest1, sizeof(src), "%s", src); // GOOD
|
||||
dest2 = (char*)malloc(3);
|
||||
if (!dest2)
|
||||
return;
|
||||
snprintf(dest2, sizeof(src), "%s", src); // BAD [NOT DETECTED]: buffer overflow
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestWindowsCmdIgnoreErrors()
|
||||
{
|
||||
actions.RunProcess["cmd.exe /C ^\"build.cmd --skip-tests^\""] = 3;
|
||||
actions.RunProcess["cmd.exe /C ^\"build.cmd^ --skip-tests^\""] = 3;
|
||||
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
@@ -744,9 +744,9 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuild()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -775,9 +775,9 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuildMultipleSolutions()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.csproj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\test1.csproj"] = true;
|
||||
actions.FileExists[@"C:\Project\test2.csproj"] = true;
|
||||
@@ -820,7 +820,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuildFailed()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -846,8 +846,8 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestSkipNugetMsBuild()
|
||||
{
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -1037,7 +1037,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj -DisableParallelProcessing"] = 1;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\a\test.csproj"] = true;
|
||||
actions.FileExists[@"C:\Project\dirs.proj"] = true;
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Semmle.Autobuild.Shared
|
||||
}
|
||||
|
||||
private static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' };
|
||||
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' };
|
||||
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|', ' ' };
|
||||
|
||||
/// <summary>
|
||||
/// Appends the given argument to the command line.
|
||||
|
||||
@@ -8,14 +8,13 @@ using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Semmle.BuildAnalyser
|
||||
{
|
||||
/// <summary>
|
||||
/// Main implementation of the build analysis.
|
||||
/// </summary>
|
||||
internal sealed partial class BuildAnalysis : IDisposable
|
||||
internal sealed class BuildAnalysis : IDisposable
|
||||
{
|
||||
private readonly AssemblyCache assemblyCache;
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
@@ -29,6 +28,9 @@ namespace Semmle.BuildAnalyser
|
||||
private readonly Options options;
|
||||
private readonly DirectoryInfo sourceDir;
|
||||
private readonly DotNet dotnet;
|
||||
private readonly FileContent fileContent;
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs a C# build analysis.
|
||||
@@ -55,6 +57,9 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
this.progressMonitor.FindingFiles(options.SrcDir);
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
|
||||
|
||||
this.fileContent = new FileContent(packageDirectory, progressMonitor, () => GetFiles("*.*"));
|
||||
this.allSources = GetFiles("*.cs").ToArray();
|
||||
var allProjects = GetFiles("*.csproj");
|
||||
var solutions = options.SolutionFile is not null
|
||||
@@ -63,12 +68,19 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
|
||||
|
||||
// Find DLLs in the .Net Framework
|
||||
// Find DLLs in the .Net / Asp.Net Framework
|
||||
if (options.ScanNetFrameworkDlls)
|
||||
{
|
||||
var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet);
|
||||
progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}");
|
||||
var runtime = new Runtime(dotnet);
|
||||
var runtimeLocation = runtime.GetRuntime(options.UseSelfContainedDotnet);
|
||||
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
|
||||
dllDirNames.Add(runtimeLocation);
|
||||
|
||||
if (fileContent.UseAspNetDlls && runtime.GetAspRuntime() is string aspRuntime)
|
||||
{
|
||||
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspRuntime}");
|
||||
dllDirNames.Add(aspRuntime);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.UseMscorlib)
|
||||
@@ -76,8 +88,6 @@ namespace Semmle.BuildAnalyser
|
||||
UseReference(typeof(object).Assembly.Location);
|
||||
}
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
|
||||
|
||||
if (options.UseNuGet)
|
||||
{
|
||||
dllDirNames.Add(packageDirectory.DirInfo.FullName);
|
||||
@@ -187,6 +197,7 @@ namespace Semmle.BuildAnalyser
|
||||
{
|
||||
finalAssemblyList[r.Name] = r;
|
||||
}
|
||||
|
||||
// Update the used references list
|
||||
usedReferences.Clear();
|
||||
foreach (var r in finalAssemblyList.Select(r => r.Value.Filename))
|
||||
@@ -210,24 +221,18 @@ namespace Semmle.BuildAnalyser
|
||||
/// Store that a particular reference file is used.
|
||||
/// </summary>
|
||||
/// <param name="reference">The filename of the reference.</param>
|
||||
private void UseReference(string reference)
|
||||
{
|
||||
usedReferences[reference] = true;
|
||||
}
|
||||
private void UseReference(string reference) => usedReferences[reference] = true;
|
||||
|
||||
/// <summary>
|
||||
/// Store that a particular source file is used (by a project file).
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
private void UseSource(FileInfo sourceFile)
|
||||
{
|
||||
sources[sourceFile.FullName] = sourceFile.Exists;
|
||||
}
|
||||
private void UseSource(FileInfo sourceFile) => sources[sourceFile.FullName] = sourceFile.Exists;
|
||||
|
||||
/// <summary>
|
||||
/// The list of resolved reference files.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ReferenceFiles => this.usedReferences.Keys;
|
||||
public IEnumerable<string> ReferenceFiles => usedReferences.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// The list of source files used in projects.
|
||||
@@ -242,7 +247,7 @@ namespace Semmle.BuildAnalyser
|
||||
/// <summary>
|
||||
/// List of assembly IDs which couldn't be resolved.
|
||||
/// </summary>
|
||||
public IEnumerable<string> UnresolvedReferences => this.unresolvedReferences.Select(r => r.Key);
|
||||
public IEnumerable<string> UnresolvedReferences => unresolvedReferences.Select(r => r.Key);
|
||||
|
||||
/// <summary>
|
||||
/// List of source files which were mentioned in project files but
|
||||
@@ -256,12 +261,7 @@ namespace Semmle.BuildAnalyser
|
||||
/// </summary>
|
||||
/// <param name="id">The assembly ID.</param>
|
||||
/// <param name="projectFile">The project file making the reference.</param>
|
||||
private void UnresolvedReference(string id, string projectFile)
|
||||
{
|
||||
unresolvedReferences[id] = projectFile;
|
||||
}
|
||||
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
private void UnresolvedReference(string id, string projectFile) => unresolvedReferences[id] = projectFile;
|
||||
|
||||
/// <summary>
|
||||
/// Reads all the source files and references from the given list of projects.
|
||||
@@ -318,10 +318,8 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
}
|
||||
|
||||
private bool Restore(string target, string? pathToNugetConfig = null)
|
||||
{
|
||||
return dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
|
||||
}
|
||||
private bool Restore(string target, string? pathToNugetConfig = null) =>
|
||||
dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
|
||||
|
||||
private void Restore(IEnumerable<string> targets, string? pathToNugetConfig = null)
|
||||
{
|
||||
@@ -331,11 +329,9 @@ namespace Semmle.BuildAnalyser
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DownloadMissingPackages(IEnumerable<string> restoreTargets)
|
||||
{
|
||||
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName).Select(d => Path.GetFileName(d).ToLowerInvariant()).ToHashSet();
|
||||
var notYetDownloadedPackages = new HashSet<string>();
|
||||
|
||||
var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray();
|
||||
string? nugetConfig = null;
|
||||
if (nugetConfigs.Length > 1)
|
||||
@@ -352,46 +348,7 @@ namespace Semmle.BuildAnalyser
|
||||
nugetConfig = nugetConfigs.FirstOrDefault();
|
||||
}
|
||||
|
||||
var allFiles = GetFiles("*.*");
|
||||
foreach (var file in allFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var sr = new StreamReader(file);
|
||||
ReadOnlySpan<char> line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
var match = line.Slice(valueMatch.Index, valueMatch.Length);
|
||||
var includeIndex = match.IndexOf("Include", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (includeIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match = match.Slice(includeIndex + "Include".Length + 1);
|
||||
|
||||
var quoteIndex1 = match.IndexOf("\"");
|
||||
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
|
||||
|
||||
var packageName = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
|
||||
if (!alreadyDownloadedPackages.Contains(packageName))
|
||||
{
|
||||
notYetDownloadedPackages.Add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressMonitor.FailedToReadFile(file, ex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var package in notYetDownloadedPackages)
|
||||
foreach (var package in fileContent.NotYetDownloadedPackages)
|
||||
{
|
||||
progressMonitor.NugetInstall(package);
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
|
||||
@@ -434,12 +391,6 @@ namespace Semmle.BuildAnalyser
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
packageDirectory?.Dispose();
|
||||
}
|
||||
|
||||
[GeneratedRegex("<PackageReference .*Include=\"(.*?)\".*/>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex PackageReference();
|
||||
public void Dispose() => packageDirectory?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Semmle.BuildAnalyser
|
||||
bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null);
|
||||
bool New(string folder);
|
||||
bool AddPackage(string folder, string package);
|
||||
public IList<string> GetListedRuntimes();
|
||||
IList<string> GetListedRuntimes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,7 +78,8 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
public IList<string> GetListedRuntimes()
|
||||
{
|
||||
var args = "--list-runtimes";
|
||||
const string args = "--list-runtimes";
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
var pi = new ProcessStartInfo(dotnet, args)
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
@@ -90,6 +91,7 @@ namespace Semmle.BuildAnalyser
|
||||
progressMonitor.CommandFailed(dotnet, args, exitCode);
|
||||
return new List<string>();
|
||||
}
|
||||
progressMonitor.LogInfo($"Found runtimes: {string.Join("\n", runtimes)}");
|
||||
return runtimes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
using Semmle.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Semmle.BuildAnalyser
|
||||
{
|
||||
|
||||
// <summary>
|
||||
// This class is used to read a set of files and decide different properties about the
|
||||
// content (by reading the content of the files only once).
|
||||
// The implementation is lazy, so the properties are only calculated when
|
||||
// the first property is accessed.
|
||||
// </summary>
|
||||
internal partial class FileContent
|
||||
{
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
private readonly IUnsafeFileReader unsafeFileReader;
|
||||
private readonly Func<IEnumerable<string>> getFiles;
|
||||
private readonly Func<HashSet<string>> getAlreadyDownloadedPackages;
|
||||
private readonly HashSet<string> notYetDownloadedPackages = new HashSet<string>();
|
||||
private readonly Initializer initialize;
|
||||
|
||||
public HashSet<string> NotYetDownloadedPackages
|
||||
{
|
||||
get
|
||||
{
|
||||
initialize.Run();
|
||||
return notYetDownloadedPackages;
|
||||
}
|
||||
}
|
||||
|
||||
private bool useAspNetDlls = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if any file in the source directory indicates that ASP.NET is used.
|
||||
/// The following heuristic is used to decide, if ASP.NET is used:
|
||||
/// If any file in the source directory contains something like (this will most like be a .csproj file)
|
||||
/// <Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
/// <FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||
/// </summary>
|
||||
public bool UseAspNetDlls
|
||||
{
|
||||
get
|
||||
{
|
||||
initialize.Run();
|
||||
return useAspNetDlls;
|
||||
}
|
||||
}
|
||||
|
||||
internal FileContent(Func<HashSet<string>> getAlreadyDownloadedPackages,
|
||||
ProgressMonitor progressMonitor,
|
||||
Func<IEnumerable<string>> getFiles,
|
||||
IUnsafeFileReader unsafeFileReader)
|
||||
{
|
||||
this.getAlreadyDownloadedPackages = getAlreadyDownloadedPackages;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.getFiles = getFiles;
|
||||
this.unsafeFileReader = unsafeFileReader;
|
||||
this.initialize = new Initializer(DoInitialize);
|
||||
}
|
||||
|
||||
|
||||
public FileContent(TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor, Func<IEnumerable<string>> getFiles) : this(() => Directory.GetDirectories(packageDirectory.DirInfo.FullName)
|
||||
.Select(d => Path.GetFileName(d)
|
||||
.ToLowerInvariant())
|
||||
.ToHashSet(), progressMonitor, getFiles, new UnsafeFileReader())
|
||||
{ }
|
||||
|
||||
private static string GetGroup(ReadOnlySpan<char> input, ValueMatch valueMatch, string groupPrefix)
|
||||
{
|
||||
var match = input.Slice(valueMatch.Index, valueMatch.Length);
|
||||
var includeIndex = match.IndexOf(groupPrefix, StringComparison.InvariantCultureIgnoreCase);
|
||||
if (includeIndex == -1)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
match = match.Slice(includeIndex + groupPrefix.Length + 1);
|
||||
|
||||
var quoteIndex1 = match.IndexOf("\"");
|
||||
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
|
||||
|
||||
return match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static bool IsGroupMatch(ReadOnlySpan<char> line, Regex regex, string groupPrefix, string value)
|
||||
{
|
||||
foreach (var valueMatch in regex.EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
if (GetGroup(line, valueMatch, groupPrefix) == value.ToLowerInvariant())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DoInitialize()
|
||||
{
|
||||
var alreadyDownloadedPackages = getAlreadyDownloadedPackages();
|
||||
foreach (var file in getFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (ReadOnlySpan<char> line in unsafeFileReader.ReadLines(file))
|
||||
{
|
||||
|
||||
// Find the not yet downloaded packages.
|
||||
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
var packageName = GetGroup(line, valueMatch, "Include");
|
||||
if (!string.IsNullOrEmpty(packageName) && !alreadyDownloadedPackages.Contains(packageName))
|
||||
{
|
||||
notYetDownloadedPackages.Add(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if ASP.NET is used.
|
||||
if (!useAspNetDlls)
|
||||
{
|
||||
useAspNetDlls =
|
||||
IsGroupMatch(line, ProjectSdk(), "Sdk", "Microsoft.NET.Sdk.Web") ||
|
||||
IsGroupMatch(line, FrameworkReference(), "Include", "Microsoft.AspNetCore.App");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressMonitor.FailedToReadFile(file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("<PackageReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex PackageReference();
|
||||
|
||||
[GeneratedRegex("<FrameworkReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex FrameworkReference();
|
||||
|
||||
[GeneratedRegex("<(.*\\s)?Project.*\\sSdk=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex ProjectSdk();
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IUnsafeFileReader
|
||||
{
|
||||
IEnumerable<string> ReadLines(string file);
|
||||
}
|
||||
|
||||
internal class UnsafeFileReader : IUnsafeFileReader
|
||||
{
|
||||
public IEnumerable<string> ReadLines(string file)
|
||||
{
|
||||
using var sr = new StreamReader(file);
|
||||
string? line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,121 +12,101 @@ namespace Semmle.BuildAnalyser
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void FindingFiles(string dir)
|
||||
{
|
||||
logger.Log(Severity.Info, "Finding files in {0}...", dir);
|
||||
}
|
||||
public void Log(Severity severity, string message) =>
|
||||
logger.Log(severity, message);
|
||||
|
||||
public void LogInfo(string message) =>
|
||||
logger.Log(Severity.Info, message);
|
||||
|
||||
private void LogDebug(string message) =>
|
||||
logger.Log(Severity.Debug, message);
|
||||
|
||||
private void LogError(string message) =>
|
||||
logger.Log(Severity.Error, message);
|
||||
|
||||
public void FindingFiles(string dir) =>
|
||||
LogInfo($"Finding files in {dir}...");
|
||||
|
||||
public void IndexingReferences(int count)
|
||||
{
|
||||
logger.Log(Severity.Info, "Indexing...");
|
||||
logger.Log(Severity.Debug, "Indexing {0} DLLs...", count);
|
||||
LogInfo("Indexing...");
|
||||
LogDebug($"Indexing {count} DLLs...");
|
||||
}
|
||||
|
||||
public void UnresolvedReference(string id, string project)
|
||||
{
|
||||
logger.Log(Severity.Info, "Unresolved reference {0}", id);
|
||||
logger.Log(Severity.Debug, "Unresolved {0} referenced by {1}", id, project);
|
||||
LogInfo($"Unresolved reference {id}");
|
||||
LogDebug($"Unresolved {id} referenced by {project}");
|
||||
}
|
||||
|
||||
public void AnalysingSolution(string filename)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Analyzing {filename}...");
|
||||
}
|
||||
public void AnalysingSolution(string filename) =>
|
||||
LogInfo($"Analyzing {filename}...");
|
||||
|
||||
public void FailedProjectFile(string filename, string reason)
|
||||
{
|
||||
logger.Log(Severity.Info, "Couldn't read project file {0}: {1}", filename, reason);
|
||||
}
|
||||
public void FailedProjectFile(string filename, string reason) =>
|
||||
LogInfo($"Couldn't read project file {filename}: {reason}");
|
||||
|
||||
public void FailedNugetCommand(string exe, string args, string message)
|
||||
{
|
||||
logger.Log(Severity.Info, "Command failed: {0} {1}", exe, args);
|
||||
logger.Log(Severity.Info, " {0}", message);
|
||||
LogInfo($"Command failed: {exe} {args}");
|
||||
LogInfo($" {message}");
|
||||
}
|
||||
|
||||
public void NugetInstall(string package)
|
||||
{
|
||||
logger.Log(Severity.Info, "Restoring {0}...", package);
|
||||
}
|
||||
public void NugetInstall(string package) =>
|
||||
LogInfo($"Restoring {package}...");
|
||||
|
||||
public void ResolvedReference(string filename)
|
||||
{
|
||||
logger.Log(Severity.Info, "Resolved {0}", filename);
|
||||
}
|
||||
public void ResolvedReference(string filename) =>
|
||||
LogInfo($"Resolved {filename}");
|
||||
|
||||
public void Summary(int existingSources, int usedSources, int missingSources,
|
||||
int references, int unresolvedReferences,
|
||||
int resolvedConflicts, int totalProjects, int failedProjects,
|
||||
TimeSpan analysisTime)
|
||||
{
|
||||
logger.Log(Severity.Info, "");
|
||||
logger.Log(Severity.Info, "Build analysis summary:");
|
||||
logger.Log(Severity.Info, "{0, 6} source files in the filesystem", existingSources);
|
||||
logger.Log(Severity.Info, "{0, 6} source files in project files", usedSources);
|
||||
logger.Log(Severity.Info, "{0, 6} sources missing from project files", missingSources);
|
||||
logger.Log(Severity.Info, "{0, 6} resolved references", references);
|
||||
logger.Log(Severity.Info, "{0, 6} unresolved references", unresolvedReferences);
|
||||
logger.Log(Severity.Info, "{0, 6} resolved assembly conflicts", resolvedConflicts);
|
||||
logger.Log(Severity.Info, "{0, 6} projects", totalProjects);
|
||||
logger.Log(Severity.Info, "{0, 6} missing/failed projects", failedProjects);
|
||||
logger.Log(Severity.Info, "Build analysis completed in {0}", analysisTime);
|
||||
const int align = 6;
|
||||
LogInfo("");
|
||||
LogInfo("Build analysis summary:");
|
||||
LogInfo($"{existingSources,align} source files in the filesystem");
|
||||
LogInfo($"{usedSources,align} source files in project files");
|
||||
LogInfo($"{missingSources,align} sources missing from project files");
|
||||
LogInfo($"{references,align} resolved references");
|
||||
LogInfo($"{unresolvedReferences,align} unresolved references");
|
||||
LogInfo($"{resolvedConflicts,align} resolved assembly conflicts");
|
||||
LogInfo($"{totalProjects,align} projects");
|
||||
LogInfo($"{failedProjects,align} missing/failed projects");
|
||||
LogInfo($"Build analysis completed in {analysisTime}");
|
||||
}
|
||||
|
||||
public void Log(Severity severity, string message)
|
||||
{
|
||||
logger.Log(severity, message);
|
||||
}
|
||||
public void ResolvedConflict(string asm1, string asm2) =>
|
||||
LogDebug($"Resolved {asm1} as {asm2}");
|
||||
|
||||
public void ResolvedConflict(string asm1, string asm2)
|
||||
{
|
||||
logger.Log(Severity.Debug, "Resolved {0} as {1}", asm1, asm2);
|
||||
}
|
||||
public void MissingProject(string projectFile) =>
|
||||
LogInfo($"Solution is missing {projectFile}");
|
||||
|
||||
public void MissingProject(string projectFile)
|
||||
{
|
||||
logger.Log(Severity.Info, "Solution is missing {0}", projectFile);
|
||||
}
|
||||
public void CommandFailed(string exe, string arguments, int exitCode) =>
|
||||
LogError($"Command {exe} {arguments} failed with exit code {exitCode}");
|
||||
|
||||
public void CommandFailed(string exe, string arguments, int exitCode)
|
||||
{
|
||||
logger.Log(Severity.Error, $"Command {exe} {arguments} failed with exit code {exitCode}");
|
||||
}
|
||||
public void MissingNuGet() =>
|
||||
LogError("Missing nuget.exe");
|
||||
|
||||
public void MissingNuGet()
|
||||
{
|
||||
logger.Log(Severity.Error, "Missing nuget.exe");
|
||||
}
|
||||
public void MissingDotNet() =>
|
||||
LogError("Missing dotnet CLI");
|
||||
|
||||
public void MissingDotNet()
|
||||
{
|
||||
logger.Log(Severity.Error, "Missing dotnet CLI");
|
||||
}
|
||||
public void RunningProcess(string command) =>
|
||||
LogInfo($"Running {command}");
|
||||
|
||||
public void RunningProcess(string command)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Running {command}");
|
||||
}
|
||||
|
||||
public void FailedToRestoreNugetPackage(string package)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Failed to restore nuget package {package}");
|
||||
}
|
||||
public void FailedToRestoreNugetPackage(string package) =>
|
||||
LogInfo($"Failed to restore nuget package {package}");
|
||||
|
||||
public void FailedToReadFile(string file, Exception ex)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Failed to read file {file}");
|
||||
logger.Log(Severity.Debug, $"Failed to read file {file}, exception: {ex}");
|
||||
LogInfo($"Failed to read file {file}");
|
||||
LogDebug($"Failed to read file {file}, exception: {ex}");
|
||||
}
|
||||
|
||||
public void MultipleNugetConfig(string[] nugetConfigs)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
|
||||
}
|
||||
public void MultipleNugetConfig(string[] nugetConfigs) =>
|
||||
LogInfo($"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
|
||||
|
||||
internal void NoTopLevelNugetConfig()
|
||||
{
|
||||
logger.Log(Severity.Info, $"Could not find a top-level nuget.config file.");
|
||||
}
|
||||
internal void NoTopLevelNugetConfig() =>
|
||||
LogInfo("Could not find a top-level nuget.config file.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,17 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
private const string aspNetCoreApp = "Microsoft.AspNetCore.App";
|
||||
|
||||
private readonly IDotNet dotNet;
|
||||
private readonly Lazy<Dictionary<string, RuntimeVersion>> newestRuntimes;
|
||||
private Dictionary<string, RuntimeVersion> NewestRuntimes => newestRuntimes.Value;
|
||||
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
|
||||
|
||||
public Runtime(IDotNet dotNet) => this.dotNet = dotNet;
|
||||
public Runtime(IDotNet dotNet)
|
||||
{
|
||||
this.dotNet = dotNet;
|
||||
this.newestRuntimes = new(GetNewestRuntimes);
|
||||
}
|
||||
|
||||
internal sealed class RuntimeVersion : IComparable<RuntimeVersion>
|
||||
internal record RuntimeVersion : IComparable<RuntimeVersion>
|
||||
{
|
||||
private readonly string dir;
|
||||
private readonly Version version;
|
||||
@@ -71,15 +77,10 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
return c;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is not null && obj is RuntimeVersion other && other.FullPath == FullPath;
|
||||
|
||||
public override int GetHashCode() => FullPath.GetHashCode();
|
||||
|
||||
public override string ToString() => FullPath;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(\S+)\]$")]
|
||||
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(.+)\]$")]
|
||||
private static partial Regex RuntimeRegex();
|
||||
|
||||
/// <summary>
|
||||
@@ -97,7 +98,7 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
var match = RuntimeRegex().Match(r);
|
||||
if (match.Success)
|
||||
{
|
||||
runtimes.AddOrUpdate(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value));
|
||||
runtimes.AddOrUpdateToLatest(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -145,33 +146,42 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetRuntimes()
|
||||
/// <summary>
|
||||
/// Gets the .NET runtime location to use for extraction.
|
||||
/// </summary>
|
||||
public string GetRuntime(bool useSelfContained)
|
||||
{
|
||||
// Gets the newest version of the installed runtimes.
|
||||
var newestRuntimes = GetNewestRuntimes();
|
||||
if (useSelfContained)
|
||||
{
|
||||
return ExecutingRuntime;
|
||||
}
|
||||
|
||||
// Location of the newest .NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
|
||||
if (NewestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
|
||||
{
|
||||
yield return netCoreVersion.FullPath;
|
||||
return netCoreVersion.FullPath;
|
||||
}
|
||||
|
||||
// Location of the newest ASP.NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
|
||||
if (DesktopRuntimes.Any())
|
||||
{
|
||||
yield return aspNetCoreVersion.FullPath;
|
||||
return DesktopRuntimes.First();
|
||||
}
|
||||
|
||||
foreach (var r in DesktopRuntimes)
|
||||
yield return r;
|
||||
|
||||
// A bad choice if it's the self-contained runtime distributed in codeql dist.
|
||||
yield return ExecutingRuntime;
|
||||
return ExecutingRuntime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the .NET runtime location to use for extraction
|
||||
/// Gets the ASP.NET runtime location to use for extraction, if one exists.
|
||||
/// </summary>
|
||||
public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First();
|
||||
public string? GetAspRuntime()
|
||||
{
|
||||
// Location of the newest ASP.NET Core Runtime.
|
||||
if (NewestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
|
||||
{
|
||||
return aspNetCoreVersion.FullPath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
Normal file
95
csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Xunit;
|
||||
using Semmle.BuildAnalyser;
|
||||
using Semmle.Util.Logging;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Extraction.Tests
|
||||
{
|
||||
|
||||
internal class LoggerStub : ILogger
|
||||
{
|
||||
public void Log(Severity severity, string message) { }
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
internal class UnsafeFileReaderStub : IUnsafeFileReader
|
||||
{
|
||||
private readonly List<string> lines;
|
||||
|
||||
public UnsafeFileReaderStub(List<string> lines)
|
||||
{
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
public IEnumerable<string> ReadLines(string file)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestFileContent : FileContent
|
||||
{
|
||||
public TestFileContent(List<string> lines) : base(() => new HashSet<string>(),
|
||||
new ProgressMonitor(new LoggerStub()),
|
||||
() => new List<string>() { "test1.cs" },
|
||||
new UnsafeFileReaderStub(lines))
|
||||
{ }
|
||||
}
|
||||
|
||||
public class FileContentTests
|
||||
{
|
||||
[Fact]
|
||||
public void TestFileContent1()
|
||||
{
|
||||
// Setup
|
||||
var lines = new List<string>()
|
||||
{
|
||||
"<Project Sdk=\"Microsoft.NET.Sdk\">",
|
||||
"<PackageReference Include=\"DotNetAnalyzers.DocumentationAnalyzers\" Version=\"1.0.0-beta.59\" PrivateAssets=\"all\" />",
|
||||
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
|
||||
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">",
|
||||
"<FrameworkReference Include=\"My.Framework\"/>"
|
||||
};
|
||||
var fileContent = new TestFileContent(lines);
|
||||
|
||||
// Execute
|
||||
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
|
||||
var useAspNetDlls = fileContent.UseAspNetDlls;
|
||||
|
||||
// Verify
|
||||
Assert.False(useAspNetDlls);
|
||||
Assert.Equal(3, notYetDownloadedPackages.Count);
|
||||
Assert.Contains("DotNetAnalyzers.DocumentationAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestFileContent2()
|
||||
{
|
||||
// Setup
|
||||
var lines = new List<string>()
|
||||
{
|
||||
"<Project Sdk=\"Microsoft.NET.Sdk.Web\">",
|
||||
"<FrameworkReference Include=\"My.Framework\"/>",
|
||||
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
|
||||
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">"
|
||||
};
|
||||
var fileContent = new TestFileContent(lines);
|
||||
|
||||
// Execute
|
||||
var useAspNetDlls = fileContent.UseAspNetDlls;
|
||||
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
|
||||
|
||||
// Verify
|
||||
Assert.True(useAspNetDlls);
|
||||
Assert.Equal(2, notYetDownloadedPackages.Count);
|
||||
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,5 +100,36 @@ namespace Semmle.Extraction.Tests
|
||||
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-rc.4.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestRuntime4()
|
||||
{
|
||||
// Setup
|
||||
var listedRuntimes = new List<string>
|
||||
{
|
||||
@"Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.AspNetCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.AspNetCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.NETCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.NETCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
|
||||
@"Microsoft.WindowsDesktop.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
|
||||
@"Microsoft.WindowsDesktop.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]"
|
||||
};
|
||||
var dotnet = new DotNetStub(listedRuntimes);
|
||||
var runtime = new Runtime(dotnet);
|
||||
|
||||
// Execute
|
||||
var runtimes = runtime.GetNewestRuntimes();
|
||||
|
||||
// Verify
|
||||
Assert.Equal(3, runtimes.Count);
|
||||
|
||||
Assert.True(runtimes.TryGetValue("Microsoft.AspNetCore.App", out var aspNetCoreApp));
|
||||
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App/7.0.2", FixExpectedPathOnWindows(aspNetCoreApp.FullPath));
|
||||
|
||||
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
|
||||
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.NETCore.App/7.0.2", FixExpectedPathOnWindows(netCoreApp.FullPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ namespace Semmle.Util
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new value or replaces the existing value (if the new value is greater than the existing)
|
||||
/// in dictionary for the given key.
|
||||
/// in this dictionary for the given key.
|
||||
/// </summary>
|
||||
public static void AddOrUpdate<T1, T2>(this Dictionary<T1, T2> dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable<T2>
|
||||
public static void AddOrUpdateToLatest<T1, T2>(this Dictionary<T1, T2> dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable<T2>
|
||||
{
|
||||
if (!dict.TryGetValue(key, out var existing) || existing.CompareTo(value) < 0)
|
||||
{
|
||||
|
||||
28
csharp/extractor/Semmle.Util/Initializer.cs
Normal file
28
csharp/extractor/Semmle.Util/Initializer.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Semmle.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// An instance of this class is used to ensure that the provided
|
||||
/// action is executed only once and on the first call to `Run`.
|
||||
/// It is thread-safe.
|
||||
/// </summary>
|
||||
public class Initializer
|
||||
{
|
||||
private readonly Lazy<bool> doInit;
|
||||
|
||||
public Initializer(Action action)
|
||||
{
|
||||
doInit = new Lazy<bool>(() =>
|
||||
{
|
||||
action();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var _ = doInit.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.6.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.6.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.6.1
|
||||
lastReleaseVersion: 1.6.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.6.2-dev
|
||||
version: 1.6.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.6.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.6.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.6.1
|
||||
lastReleaseVersion: 1.6.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.6.2-dev
|
||||
version: 1.6.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| Program.cs:0:0:0:0 | Program.cs |
|
||||
@@ -0,0 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from File f
|
||||
where f.fromSource()
|
||||
select f
|
||||
@@ -0,0 +1 @@
|
||||
var dummy = "dummy";
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
|
||||
<RemoveDir Directories=".\bin" />
|
||||
<RemoveDir Directories=".\obj" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
from create_database_utils import *
|
||||
|
||||
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* Provides helper classes and methods related to LINQ.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import csharp
|
||||
private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
|
||||
private import semmle.code.csharp.frameworks.system.Collections as Collections
|
||||
|
||||
//#################### PREDICATES ####################
|
||||
private Stmt firstStmt(ForeachStmt fes) {
|
||||
@@ -29,13 +31,40 @@ predicate isIEnumerableType(ValueOrRefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of foreach statements where the iterable expression
|
||||
* supports the use of the LINQ extension methods on `IEnumerable<T>`.
|
||||
*/
|
||||
class ForeachStmtGenericEnumerable extends ForeachStmt {
|
||||
ForeachStmtGenericEnumerable() {
|
||||
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
|
||||
t.getABaseType*().getUnboundDeclaration() instanceof
|
||||
GenericCollections::SystemCollectionsGenericIEnumerableTInterface or
|
||||
t.(ArrayType).getRank() = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of foreach statements where the iterable expression
|
||||
* supports the use of the LINQ extension methods on `IEnumerable`.
|
||||
*/
|
||||
class ForeachStmtEnumerable extends ForeachStmt {
|
||||
ForeachStmtEnumerable() {
|
||||
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
|
||||
t.getABaseType*() instanceof Collections::SystemCollectionsIEnumerableInterface or
|
||||
t.(ArrayType).getRank() = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `foreach` statement `fes` could be converted to a `.All()` call.
|
||||
* That is, the `ForeachStmt` contains a single `if` with a condition that
|
||||
* accesses the loop variable and with a body that assigns `false` to a variable
|
||||
* and `break`s out of the `foreach`.
|
||||
*/
|
||||
predicate missedAllOpportunity(ForeachStmt fes) {
|
||||
predicate missedAllOpportunity(ForeachStmtGenericEnumerable fes) {
|
||||
exists(IfStmt is |
|
||||
// The loop contains an if statement with no else case, and nothing else.
|
||||
is = firstStmt(fes) and
|
||||
@@ -54,12 +83,12 @@ predicate missedAllOpportunity(ForeachStmt fes) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `foreach` statement `fes` could be converted to a `.Cast()` call.
|
||||
* Holds if the `foreach` statement `fes` can be converted to a `.Cast()` call.
|
||||
* That is, the loop variable is accessed only in the first statement of the
|
||||
* block, and the access is a cast. The first statement needs to be a
|
||||
* `LocalVariableDeclStmt`.
|
||||
* block, the access is a cast, and the first statement is a
|
||||
* local variable declaration statement `s`.
|
||||
*/
|
||||
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
predicate missedCastOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
@@ -71,12 +100,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `foreach` statement `fes` could be converted to an `.OfType()` call.
|
||||
* Holds if `foreach` statement `fes` can be converted to an `.OfType()` call.
|
||||
* That is, the loop variable is accessed only in the first statement of the
|
||||
* block, and the access is a cast with the `as` operator. The first statement
|
||||
* needs to be a `LocalVariableDeclStmt`.
|
||||
* block, the access is a cast with the `as` operator, and the first statement
|
||||
* is a local variable declaration statement `s`.
|
||||
*/
|
||||
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
predicate missedOfTypeOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
@@ -88,12 +117,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `foreach` statement `fes` could be converted to a `.Select()` call.
|
||||
* Holds if `foreach` statement `fes` can be converted to a `.Select()` call.
|
||||
* That is, the loop variable is accessed only in the first statement of the
|
||||
* block, and the access is not a cast. The first statement needs to be a
|
||||
* `LocalVariableDeclStmt`.
|
||||
* block, the access is not a cast, and the first statement is a
|
||||
* local variable declaration statement `s`.
|
||||
*/
|
||||
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
predicate missedSelectOpportunity(ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s) {
|
||||
s = firstStmt(fes) and
|
||||
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
|
||||
va = s.getAVariableDeclExpr().getAChildExpr*()
|
||||
@@ -107,7 +136,7 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
|
||||
* variable, and the body of the `if` is either a `continue` or there's nothing
|
||||
* else in the loop than the `if`.
|
||||
*/
|
||||
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) {
|
||||
predicate missedWhereOpportunity(ForeachStmtGenericEnumerable fes, IfStmt is) {
|
||||
// The very first thing the foreach loop does is test its iteration variable.
|
||||
is = firstStmt(fes) and
|
||||
exists(VariableAccess va |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query library for `cs/hardcoded-credentials` now excludes benign properties such as `UserNameClaimType` and `AllowedUserNameCharacters` from `Microsoft.AspNetCore.Identity` options classes.
|
||||
3
csharp/ql/lib/change-notes/released/0.7.2.md
Normal file
3
csharp/ql/lib/change-notes/released/0.7.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.1
|
||||
lastReleaseVersion: 0.7.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.7.2-dev
|
||||
version: 0.7.3-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -861,6 +861,12 @@ class YieldReturnStmt extends YieldStmt {
|
||||
override string getAPrimaryQlClass() { result = "YieldReturnStmt" }
|
||||
}
|
||||
|
||||
bindingset[cfe1, cfe2]
|
||||
pragma[inline_late]
|
||||
private predicate sameCallable(ControlFlowElement cfe1, ControlFlowElement cfe2) {
|
||||
cfe1.getEnclosingCallable() = cfe2.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `try` statement, for example
|
||||
*
|
||||
@@ -947,8 +953,7 @@ class TryStmt extends Stmt, @try_stmt {
|
||||
mid = this.getATriedElement() and
|
||||
not mid instanceof TryStmt and
|
||||
result = mid.getAChild() and
|
||||
pragma[only_bind_into](mid.getEnclosingCallable()) =
|
||||
pragma[only_bind_into](result.getEnclosingCallable())
|
||||
sameCallable(mid, result)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ private class CredentialVar extends Assignable {
|
||||
exists(string name | name = this.getName() |
|
||||
name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(puid|username|userid).*")
|
||||
name.regexpMatch("(?i).*(puid|username|userid)(?!.*(characters|claimtype)).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*")
|
||||
)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* language-features
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import Linq.Helpers
|
||||
|
||||
/*
|
||||
@@ -31,7 +30,7 @@ import Linq.Helpers
|
||||
* bool allEven = lst.All(i => i % 2 == 0);
|
||||
*/
|
||||
|
||||
from ForeachStmt fes
|
||||
from ForeachStmtGenericEnumerable fes
|
||||
where missedAllOpportunity(fes)
|
||||
select fes,
|
||||
"This foreach loop looks as if it might be testing whether every sequence element satisfies a predicate - consider using '.All(...)'."
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import csharp
|
||||
import Linq.Helpers
|
||||
|
||||
from ForeachStmt fes, LocalVariableDeclStmt s
|
||||
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
|
||||
where missedCastOpportunity(fes, s)
|
||||
select fes,
|
||||
"This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'.",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import csharp
|
||||
import Linq.Helpers
|
||||
|
||||
from ForeachStmt fes, LocalVariableDeclStmt s
|
||||
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
|
||||
where missedOfTypeOpportunity(fes, s)
|
||||
select fes,
|
||||
"This foreach loop immediately uses 'as' to $@ - consider using '.OfType(...)' instead.", s,
|
||||
|
||||
@@ -20,7 +20,7 @@ predicate oversized(LocalVariableDeclStmt s) {
|
||||
)
|
||||
}
|
||||
|
||||
from ForeachStmt fes, LocalVariableDeclStmt s
|
||||
from ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s
|
||||
where
|
||||
missedSelectOpportunity(fes, s) and
|
||||
not oversized(s)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import csharp
|
||||
import Linq.Helpers
|
||||
|
||||
from ForeachStmt fes, IfStmt is
|
||||
from ForeachStmtGenericEnumerable fes, IfStmt is
|
||||
where
|
||||
missedWhereOpportunity(fes, is) and
|
||||
not missedAllOpportunity(fes)
|
||||
|
||||
3
csharp/ql/src/change-notes/released/0.7.2.md
Normal file
3
csharp/ql/src/change-notes/released/0.7.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.1
|
||||
lastReleaseVersion: 0.7.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.7.2-dev
|
||||
version: 0.7.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class MissedCastOpportunity
|
||||
{
|
||||
public void M1(List<Animal> animals)
|
||||
{
|
||||
// BAD: Can be replaced with animals.Cast<Dog>().
|
||||
foreach (Animal a in animals)
|
||||
{
|
||||
Dog d = (Dog)a;
|
||||
d.Woof();
|
||||
}
|
||||
}
|
||||
|
||||
public void M2(NonEnumerableClass nec)
|
||||
{
|
||||
// GOOD: Not possible to use Linq here.
|
||||
foreach (Animal a in nec)
|
||||
{
|
||||
Dog d = (Dog)a;
|
||||
d.Woof();
|
||||
}
|
||||
}
|
||||
|
||||
public void M3(Animal[] animals)
|
||||
{
|
||||
// BAD: Can be replaced with animals.Cast<Dog>().
|
||||
foreach (Animal animal in animals)
|
||||
{
|
||||
Dog d = (Dog)animal;
|
||||
d.Woof();
|
||||
}
|
||||
}
|
||||
|
||||
public void M4(Array animals)
|
||||
{
|
||||
// BAD: Can be replaced with animals.Cast<Dog>().
|
||||
foreach (Animal animal in animals)
|
||||
{
|
||||
Dog d = (Dog)animal;
|
||||
d.Woof();
|
||||
}
|
||||
}
|
||||
|
||||
public void M5(IEnumerable animals)
|
||||
{
|
||||
// BAD: Can be replaced with animals.Cast<Dog>().
|
||||
foreach (object animal in animals)
|
||||
{
|
||||
Dog d = (Dog)animal;
|
||||
d.Woof();
|
||||
}
|
||||
}
|
||||
|
||||
public class NonEnumerableClass
|
||||
{
|
||||
public IEnumerator<Animal> GetEnumerator() => throw null;
|
||||
}
|
||||
|
||||
public class Animal { }
|
||||
|
||||
class Dog : Animal
|
||||
{
|
||||
private string name;
|
||||
|
||||
public Dog(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void Woof()
|
||||
{
|
||||
Console.WriteLine("Woof! My name is " + name + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
| MissedCastOpportunity.cs:10:9:14:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:12:13:12:27 | ... ...; | casts its iteration variable to another type |
|
||||
| MissedCastOpportunity.cs:30:9:34:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:32:13:32:32 | ... ...; | casts its iteration variable to another type |
|
||||
| MissedCastOpportunity.cs:40:9:44:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:42:13:42:32 | ... ...; | casts its iteration variable to another type |
|
||||
| MissedCastOpportunity.cs:50:9:54:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'. | MissedCastOpportunity.cs:52:13:52:32 | ... ...; | casts its iteration variable to another type |
|
||||
@@ -0,0 +1 @@
|
||||
Linq/MissedCastOpportunity.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class MissedWhereOpportunity
|
||||
{
|
||||
public void M1(List<int> lst)
|
||||
{
|
||||
// BAD: Can be replaced with lst.Where(e => e % 2 == 0)
|
||||
foreach (int i in lst)
|
||||
{
|
||||
if (i % 2 != 0)
|
||||
continue;
|
||||
Console.WriteLine(i);
|
||||
Console.WriteLine((i / 2));
|
||||
}
|
||||
|
||||
// BAD: Can be replaced with lst.Where(e => e % 2 == 0)
|
||||
foreach (int i in lst)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
Console.WriteLine((i / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void M2(NonEnumerableClass nec)
|
||||
{
|
||||
// GOOD: Linq can't be used here.
|
||||
foreach (int i in nec)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
Console.WriteLine((i / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void M3(int[] arr)
|
||||
{
|
||||
// BAD: Can be replaced with arr.Where(e => e % 2 == 0)
|
||||
foreach (var n in arr)
|
||||
{
|
||||
if (n % 2 == 0)
|
||||
{
|
||||
Console.WriteLine(n);
|
||||
Console.WriteLine((n / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void M4(Array arr)
|
||||
{
|
||||
// GOOD: Linq can't be used here.
|
||||
foreach (var element in arr)
|
||||
{
|
||||
if (element.GetHashCode() % 2 == 0)
|
||||
{
|
||||
Console.WriteLine(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void M5(IEnumerable<int> elements)
|
||||
{
|
||||
// BAD: Can be replaced with elements.Where(e => e.GetHashCode() % 2 == 0)
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (element.GetHashCode() % 2 == 0)
|
||||
{
|
||||
Console.WriteLine(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NonEnumerableClass
|
||||
{
|
||||
public IEnumerator<int> GetEnumerator() => throw null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
| MissedWhereOpportunity.cs:10:9:16:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:12:17:12:26 | ... != ... | implicitly filters its target sequence |
|
||||
| MissedWhereOpportunity.cs:19:9:26:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:21:17:21:26 | ... == ... | implicitly filters its target sequence |
|
||||
| MissedWhereOpportunity.cs:45:9:52:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:47:17:47:26 | ... == ... | implicitly filters its target sequence |
|
||||
| MissedWhereOpportunity.cs:70:9:76:9 | foreach (... ... in ...) ... | This foreach loop $@ - consider filtering the sequence explicitly using '.Where(...)'. | MissedWhereOpportunity.cs:72:17:72:46 | ... == ... | implicitly filters its target sequence |
|
||||
@@ -0,0 +1 @@
|
||||
Linq/MissedWhereOpportunity.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -1,8 +1,8 @@
|
||||
edges
|
||||
nodes
|
||||
| HardcodedCredentials.cs:54:48:54:63 | "Password=12345" | semmle.label | "Password=12345" |
|
||||
| HardcodedCredentials.cs:56:49:56:63 | "User Id=12345" | semmle.label | "User Id=12345" |
|
||||
| HardcodedCredentials.cs:55:48:55:63 | "Password=12345" | semmle.label | "Password=12345" |
|
||||
| HardcodedCredentials.cs:57:49:57:63 | "User Id=12345" | semmle.label | "User Id=12345" |
|
||||
subpaths
|
||||
#select
|
||||
| HardcodedCredentials.cs:54:48:54:63 | "Password=12345" | HardcodedCredentials.cs:54:48:54:63 | "Password=12345" | HardcodedCredentials.cs:54:48:54:63 | "Password=12345" | 'ConnectionString' property includes hard-coded credentials set in $@. | HardcodedCredentials.cs:54:30:54:64 | object creation of type SqlConnection | object creation of type SqlConnection |
|
||||
| HardcodedCredentials.cs:56:49:56:63 | "User Id=12345" | HardcodedCredentials.cs:56:49:56:63 | "User Id=12345" | HardcodedCredentials.cs:56:49:56:63 | "User Id=12345" | 'ConnectionString' property includes hard-coded credentials set in $@. | HardcodedCredentials.cs:56:31:56:64 | object creation of type SqlConnection | object creation of type SqlConnection |
|
||||
| HardcodedCredentials.cs:55:48:55:63 | "Password=12345" | HardcodedCredentials.cs:55:48:55:63 | "Password=12345" | HardcodedCredentials.cs:55:48:55:63 | "Password=12345" | 'ConnectionString' property includes hard-coded credentials set in $@. | HardcodedCredentials.cs:55:30:55:64 | object creation of type SqlConnection | object creation of type SqlConnection |
|
||||
| HardcodedCredentials.cs:57:49:57:63 | "User Id=12345" | HardcodedCredentials.cs:57:49:57:63 | "User Id=12345" | HardcodedCredentials.cs:57:49:57:63 | "User Id=12345" | 'ConnectionString' property includes hard-coded credentials set in $@. | HardcodedCredentials.cs:57:31:57:64 | object creation of type SqlConnection | object creation of type SqlConnection |
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Data.SqlClient;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
public class HardcodedHandler : IHttpHandler
|
||||
{
|
||||
@@ -72,6 +73,21 @@ public class HardcodedHandler : IHttpHandler
|
||||
|
||||
// BAD: Hard-coded user
|
||||
Membership.CreateUser("myusername", "mypassword");
|
||||
|
||||
var identityOptions = new IdentityOptions
|
||||
{
|
||||
User = new UserOptions
|
||||
{
|
||||
// GOOD: This is not a credential so hardcoding a string assignment is fine
|
||||
AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"
|
||||
}
|
||||
};
|
||||
|
||||
var claimsIdentityOptions = new ClaimsIdentityOptions
|
||||
{
|
||||
// GOOD: This is not a credential so hardcoding a string assignment is fine
|
||||
UserNameClaimType = "username"
|
||||
};
|
||||
}
|
||||
|
||||
class Foo
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
edges
|
||||
| HardcodedCredentials.cs:47:30:47:60 | array creation of type Byte[] : Byte[] | HardcodedCredentials.cs:50:13:50:23 | access to local variable rawCertData |
|
||||
| HardcodedCredentials.cs:48:30:48:60 | array creation of type Byte[] : Byte[] | HardcodedCredentials.cs:51:13:51:23 | access to local variable rawCertData |
|
||||
nodes
|
||||
| HardcodedCredentials.cs:15:25:15:36 | "myPa55word" | semmle.label | "myPa55word" |
|
||||
| HardcodedCredentials.cs:31:19:31:28 | "username" | semmle.label | "username" |
|
||||
| HardcodedCredentials.cs:45:39:45:53 | "myNewPa55word" | semmle.label | "myNewPa55word" |
|
||||
| HardcodedCredentials.cs:47:30:47:60 | array creation of type Byte[] : Byte[] | semmle.label | array creation of type Byte[] : Byte[] |
|
||||
| HardcodedCredentials.cs:50:13:50:23 | access to local variable rawCertData | semmle.label | access to local variable rawCertData |
|
||||
| HardcodedCredentials.cs:51:13:51:24 | "myPa55word" | semmle.label | "myPa55word" |
|
||||
| HardcodedCredentials.cs:74:31:74:42 | "myusername" | semmle.label | "myusername" |
|
||||
| HardcodedCredentials.cs:74:45:74:56 | "mypassword" | semmle.label | "mypassword" |
|
||||
| HardcodedCredentials.cs:16:25:16:36 | "myPa55word" | semmle.label | "myPa55word" |
|
||||
| HardcodedCredentials.cs:32:19:32:28 | "username" | semmle.label | "username" |
|
||||
| HardcodedCredentials.cs:46:39:46:53 | "myNewPa55word" | semmle.label | "myNewPa55word" |
|
||||
| HardcodedCredentials.cs:48:30:48:60 | array creation of type Byte[] : Byte[] | semmle.label | array creation of type Byte[] : Byte[] |
|
||||
| HardcodedCredentials.cs:51:13:51:23 | access to local variable rawCertData | semmle.label | access to local variable rawCertData |
|
||||
| HardcodedCredentials.cs:52:13:52:24 | "myPa55word" | semmle.label | "myPa55word" |
|
||||
| HardcodedCredentials.cs:75:31:75:42 | "myusername" | semmle.label | "myusername" |
|
||||
| HardcodedCredentials.cs:75:45:75:56 | "mypassword" | semmle.label | "mypassword" |
|
||||
| TestHardcodedCredentials.cs:21:31:21:42 | "myusername" | semmle.label | "myusername" |
|
||||
| TestHardcodedCredentials.cs:21:45:21:56 | "mypassword" | semmle.label | "mypassword" |
|
||||
| TestHardcodedCredentials.cs:26:19:26:28 | "username" | semmle.label | "username" |
|
||||
subpaths
|
||||
#select
|
||||
| HardcodedCredentials.cs:15:25:15:36 | "myPa55word" | HardcodedCredentials.cs:15:25:15:36 | "myPa55word" | HardcodedCredentials.cs:15:25:15:36 | "myPa55word" | The hard-coded value "myPa55word" flows to $@ which is compared against $@. | HardcodedCredentials.cs:15:25:15:36 | "myPa55word" | "myPa55word" | HardcodedCredentials.cs:15:13:15:20 | access to local variable password | access to local variable password |
|
||||
| HardcodedCredentials.cs:31:19:31:28 | "username" | HardcodedCredentials.cs:31:19:31:28 | "username" | HardcodedCredentials.cs:31:19:31:28 | "username" | The hard-coded value "username" flows to the $@ parameter in $@. | HardcodedCredentials.cs:31:19:31:28 | "username" | name | HardcodedCredentials.cs:29:31:43:13 | object creation of type MembershipUser | object creation of type MembershipUser |
|
||||
| HardcodedCredentials.cs:45:39:45:53 | "myNewPa55word" | HardcodedCredentials.cs:45:39:45:53 | "myNewPa55word" | HardcodedCredentials.cs:45:39:45:53 | "myNewPa55word" | The hard-coded value "myNewPa55word" flows to the $@ parameter in $@. | HardcodedCredentials.cs:45:39:45:53 | "myNewPa55word" | newPassword | HardcodedCredentials.cs:45:9:45:54 | call to method ChangePassword | call to method ChangePassword |
|
||||
| HardcodedCredentials.cs:47:30:47:60 | array creation of type Byte[] | HardcodedCredentials.cs:47:30:47:60 | array creation of type Byte[] : Byte[] | HardcodedCredentials.cs:50:13:50:23 | access to local variable rawCertData | This hard-coded value flows to the $@ parameter in $@. | HardcodedCredentials.cs:50:13:50:23 | access to local variable rawCertData | rawData | HardcodedCredentials.cs:49:33:51:25 | object creation of type X509Certificate2 | object creation of type X509Certificate2 |
|
||||
| HardcodedCredentials.cs:51:13:51:24 | "myPa55word" | HardcodedCredentials.cs:51:13:51:24 | "myPa55word" | HardcodedCredentials.cs:51:13:51:24 | "myPa55word" | The hard-coded value "myPa55word" flows to the $@ parameter in $@. | HardcodedCredentials.cs:51:13:51:24 | "myPa55word" | password | HardcodedCredentials.cs:49:33:51:25 | object creation of type X509Certificate2 | object creation of type X509Certificate2 |
|
||||
| HardcodedCredentials.cs:74:31:74:42 | "myusername" | HardcodedCredentials.cs:74:31:74:42 | "myusername" | HardcodedCredentials.cs:74:31:74:42 | "myusername" | The hard-coded value "myusername" flows to the $@ parameter in $@. | HardcodedCredentials.cs:74:31:74:42 | "myusername" | username | HardcodedCredentials.cs:74:9:74:57 | call to method CreateUser | call to method CreateUser |
|
||||
| HardcodedCredentials.cs:74:45:74:56 | "mypassword" | HardcodedCredentials.cs:74:45:74:56 | "mypassword" | HardcodedCredentials.cs:74:45:74:56 | "mypassword" | The hard-coded value "mypassword" flows to the $@ parameter in $@. | HardcodedCredentials.cs:74:45:74:56 | "mypassword" | password | HardcodedCredentials.cs:74:9:74:57 | call to method CreateUser | call to method CreateUser |
|
||||
| HardcodedCredentials.cs:16:25:16:36 | "myPa55word" | HardcodedCredentials.cs:16:25:16:36 | "myPa55word" | HardcodedCredentials.cs:16:25:16:36 | "myPa55word" | The hard-coded value "myPa55word" flows to $@ which is compared against $@. | HardcodedCredentials.cs:16:25:16:36 | "myPa55word" | "myPa55word" | HardcodedCredentials.cs:16:13:16:20 | access to local variable password | access to local variable password |
|
||||
| HardcodedCredentials.cs:32:19:32:28 | "username" | HardcodedCredentials.cs:32:19:32:28 | "username" | HardcodedCredentials.cs:32:19:32:28 | "username" | The hard-coded value "username" flows to the $@ parameter in $@. | HardcodedCredentials.cs:32:19:32:28 | "username" | name | HardcodedCredentials.cs:30:31:44:13 | object creation of type MembershipUser | object creation of type MembershipUser |
|
||||
| HardcodedCredentials.cs:46:39:46:53 | "myNewPa55word" | HardcodedCredentials.cs:46:39:46:53 | "myNewPa55word" | HardcodedCredentials.cs:46:39:46:53 | "myNewPa55word" | The hard-coded value "myNewPa55word" flows to the $@ parameter in $@. | HardcodedCredentials.cs:46:39:46:53 | "myNewPa55word" | newPassword | HardcodedCredentials.cs:46:9:46:54 | call to method ChangePassword | call to method ChangePassword |
|
||||
| HardcodedCredentials.cs:48:30:48:60 | array creation of type Byte[] | HardcodedCredentials.cs:48:30:48:60 | array creation of type Byte[] : Byte[] | HardcodedCredentials.cs:51:13:51:23 | access to local variable rawCertData | This hard-coded value flows to the $@ parameter in $@. | HardcodedCredentials.cs:51:13:51:23 | access to local variable rawCertData | rawData | HardcodedCredentials.cs:50:33:52:25 | object creation of type X509Certificate2 | object creation of type X509Certificate2 |
|
||||
| HardcodedCredentials.cs:52:13:52:24 | "myPa55word" | HardcodedCredentials.cs:52:13:52:24 | "myPa55word" | HardcodedCredentials.cs:52:13:52:24 | "myPa55word" | The hard-coded value "myPa55word" flows to the $@ parameter in $@. | HardcodedCredentials.cs:52:13:52:24 | "myPa55word" | password | HardcodedCredentials.cs:50:33:52:25 | object creation of type X509Certificate2 | object creation of type X509Certificate2 |
|
||||
| HardcodedCredentials.cs:75:31:75:42 | "myusername" | HardcodedCredentials.cs:75:31:75:42 | "myusername" | HardcodedCredentials.cs:75:31:75:42 | "myusername" | The hard-coded value "myusername" flows to the $@ parameter in $@. | HardcodedCredentials.cs:75:31:75:42 | "myusername" | username | HardcodedCredentials.cs:75:9:75:57 | call to method CreateUser | call to method CreateUser |
|
||||
| HardcodedCredentials.cs:75:45:75:56 | "mypassword" | HardcodedCredentials.cs:75:45:75:56 | "mypassword" | HardcodedCredentials.cs:75:45:75:56 | "mypassword" | The hard-coded value "mypassword" flows to the $@ parameter in $@. | HardcodedCredentials.cs:75:45:75:56 | "mypassword" | password | HardcodedCredentials.cs:75:9:75:57 | call to method CreateUser | call to method CreateUser |
|
||||
| TestHardcodedCredentials.cs:26:19:26:28 | "username" | TestHardcodedCredentials.cs:26:19:26:28 | "username" | TestHardcodedCredentials.cs:26:19:26:28 | "username" | The hard-coded value "username" flows to the $@ parameter in $@. | TestHardcodedCredentials.cs:26:19:26:28 | "username" | name | TestHardcodedCredentials.cs:24:31:38:13 | object creation of type MembershipUser | object creation of type MembershipUser |
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/System.Data.SqlClient/4.8.3/System.Data.SqlClient.csproj
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/Microsoft.VisualStudio.TestTools.UnitTesting.cs
|
||||
|
||||
@@ -397,6 +397,8 @@ The following components are supported:
|
||||
- **SyntheticGlobal[**\ `name`\ **]** selects the synthetic global with name `name`.
|
||||
- **ArrayElement** selects the elements of an array.
|
||||
- **Element** selects the elements of a collection-like container.
|
||||
- **WithoutElement** selects a collection-like container without its elements. This is for input only.
|
||||
- **WithElement** selects the elements of a collection-like container, but points to the container itself. This is for input only.
|
||||
- **MapKey** selects the element keys of a map.
|
||||
- **MapValue** selects the element values of a map.
|
||||
|
||||
|
||||
@@ -122,12 +122,19 @@ Global environments
|
||||
|
||||
The global module environment has a single entry ``QlBuiltins``.
|
||||
|
||||
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``, as well as any types defined in the database schema.
|
||||
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``.
|
||||
|
||||
The global predicate environment includes all the built-in classless predicates, as well as any extensional predicates declared in the database schema.
|
||||
The global predicate environment includes all the built-in classless predicates.
|
||||
|
||||
The three global signature environments are empty.
|
||||
|
||||
Database schema environments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The database schema type environment has entries for types declared in the database schema.
|
||||
|
||||
The database schema predicate environment has entries for extensional predicates declared in the database schema.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
Module environments
|
||||
@@ -146,7 +153,7 @@ These are defined as follows (with X denoting the type of entity we are currentl
|
||||
|
||||
2. for each module which the current module directly imports (excluding ``private`` imports - see "`Import directives <#import-directives>`__"): all entries from the *exported X environment* that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
3. if X is ``predicates``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
|
||||
3. if X is ``predicate``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
|
||||
|
||||
- The *visible X environment* of a module is the union of
|
||||
|
||||
@@ -160,7 +167,9 @@ These are defined as follows (with X denoting the type of entity we are currentl
|
||||
|
||||
5. if there is an enclosing module: all entries from the *visible X environment* of the enclosing module that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
6. all parameters of the current module that are of type X.
|
||||
6. if there is no enclosing module and X is either ``type`` or ``predicate``: all entries from the *database schema X environment* that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
7. all parameters of the current module that are of type X.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
|
||||
@@ -310,3 +310,4 @@ and the CodeQL library pack ``codeql/swift-all`` (`changelog <https://github.com
|
||||
`SQLite3 <https://sqlite.org/index.html>`__, Database
|
||||
`SQLite.swift <https://github.com/stephencelis/SQLite.swift>`__, Database
|
||||
`WebKit <https://developer.apple.com/documentation/webkit>`__, User interface library
|
||||
`UIKit <https://developer.apple.com/documentation/uikit>`__, User interface library
|
||||
|
||||
@@ -773,7 +773,7 @@ func installDependenciesAndBuild() {
|
||||
|
||||
goModVersion, goModVersionFound := tryReadGoDirective(buildInfo)
|
||||
|
||||
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 {
|
||||
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) > 0 {
|
||||
diagnostics.EmitNewerGoVersionNeeded()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ module github.com/github/codeql-go
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.8.0
|
||||
golang.org/x/tools v0.6.0
|
||||
golang.org/x/mod v0.12.0
|
||||
golang.org/x/tools v0.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
)
|
||||
|
||||
@@ -8,6 +8,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVD
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
@@ -23,6 +25,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9w
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -34,7 +38,11 @@ golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
|
||||
golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.6.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Logrus' `WithContext` methods are no longer treated as if they output the values stored in that context to a log message.
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### New Features
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user