Merge branch 'main' into rdmarsh2/swift/keypath-write-flow

This commit is contained in:
Robert Marsh
2023-09-08 15:39:06 +00:00
73 changed files with 1186 additions and 324 deletions

View File

@@ -11,12 +11,12 @@
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Treat functions that reach the end of the function as returning in the IR.
They used to be treated as unreachable but it is allowed in C.

View File

@@ -254,9 +254,7 @@ class Node extends TIRDataFlowNode {
* after the `f` has returned.
*/
Expr asDefiningArgument(int index) {
// Subtract one because `DefinitionByReferenceNode` is defined to be in
// the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`.
this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and
this.(DefinitionByReferenceNode).getIndirectionIndex() = index and
result = this.(DefinitionByReferenceNode).getArgument()
}

View File

@@ -405,9 +405,6 @@ predicate hasUnreachedInstruction(IRFunction func) {
exists(Call c |
c.getEnclosingFunction() = func.getFunction() and
any(Options opt).exits(c.getTarget())
) and
not exists(TranslatedUnreachableReturnStmt return |
return.getEnclosingFunction().getFunction() = func.getFunction()
)
}

View File

@@ -442,29 +442,26 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
/**
* The IR translation of an implicit `return` statement generated by the extractor to handle control
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
* from continuing on to pollute other analysis. The assumption is that the developer is certain
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
* flow that reaches the end of a non-`void`-returning function body. Such control flow
* produces undefined behavior in C++ but not in C. However even in C using the return value is
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
*/
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
TranslatedUnreachableReturnStmt() {
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
TranslatedNoValueReturnStmt() {
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
}
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::Unreached and
resultType = getVoidType()
final override Instruction getInitializationSuccessor() {
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
override Instruction getChildSuccessor(TranslatedElement child) { none() }
final override TranslatedInitialization getInitialization() { none() }
final override IRVariable getIRVariable() {
result = this.getEnclosingFunction().getReturnVariable()
}
}
/**

View File

@@ -6207,10 +6207,12 @@
| ir.cpp:1286:25:1286:49 | ChiPartial | partial:m1286_7 |
| ir.cpp:1286:25:1286:49 | ChiTotal | total:m1286_4 |
| ir.cpp:1286:25:1286:49 | SideEffect | ~m1286_4 |
| ir.cpp:1289:5:1289:22 | Address | &:r1289_9 |
| ir.cpp:1289:5:1289:22 | Address | &:r1289_10 |
| ir.cpp:1289:5:1289:22 | ChiPartial | partial:m1289_3 |
| ir.cpp:1289:5:1289:22 | ChiTotal | total:m1289_2 |
| ir.cpp:1289:5:1289:22 | Load | m1291_4 |
| ir.cpp:1289:5:1289:22 | Load | m1289_9 |
| ir.cpp:1289:5:1289:22 | Phi | from 2:m1291_4 |
| ir.cpp:1289:5:1289:22 | Phi | from 3:m1293_2 |
| ir.cpp:1289:5:1289:22 | SideEffect | m1289_3 |
| ir.cpp:1289:29:1289:29 | Address | &:r1289_5 |
| ir.cpp:1289:36:1289:36 | Address | &:r1289_7 |
@@ -6221,6 +6223,7 @@
| ir.cpp:1291:16:1291:16 | Address | &:r1291_2 |
| ir.cpp:1291:16:1291:16 | Load | m1289_8 |
| ir.cpp:1291:16:1291:16 | StoreValue | r1291_3 |
| ir.cpp:1293:1:1293:1 | Address | &:r1293_1 |
| ir.cpp:1295:6:1295:15 | ChiPartial | partial:m1295_3 |
| ir.cpp:1295:6:1295:15 | ChiTotal | total:m1295_2 |
| ir.cpp:1295:6:1295:15 | SideEffect | ~m1296_8 |
@@ -8393,16 +8396,23 @@
| ir.cpp:1747:39:1747:39 | ChiTotal | total:m1747_20 |
| ir.cpp:1747:39:1747:39 | SideEffect | ~m1747_4 |
| ir.cpp:1747:39:1747:39 | SideEffect | ~m1747_15 |
| ir.cpp:1750:5:1750:34 | Address | &:r1750_5 |
| ir.cpp:1750:5:1750:34 | ChiPartial | partial:m1750_3 |
| ir.cpp:1750:5:1750:34 | ChiTotal | total:m1750_2 |
| ir.cpp:1750:5:1750:34 | Load | m1755_2 |
| ir.cpp:1750:5:1750:34 | SideEffect | ~m1754_10 |
| ir.cpp:1751:51:1751:51 | Address | &:r1751_1 |
| ir.cpp:1751:51:1751:51 | Address | &:r1751_1 |
| ir.cpp:1751:51:1751:51 | Address | &:r1751_3 |
| ir.cpp:1751:51:1751:51 | Address | &:r1751_3 |
| ir.cpp:1751:51:1751:51 | Load | m1751_2 |
| ir.cpp:1751:51:1751:51 | SideEffect | m1751_4 |
| ir.cpp:1752:48:1752:48 | Address | &:r1752_1 |
| ir.cpp:1752:48:1752:48 | Address | &:r1752_1 |
| ir.cpp:1752:48:1752:48 | Address | &:r1752_3 |
| ir.cpp:1752:48:1752:48 | Address | &:r1752_3 |
| ir.cpp:1752:48:1752:48 | Load | m1752_2 |
| ir.cpp:1752:48:1752:48 | SideEffect | m1752_4 |
| ir.cpp:1753:40:1753:41 | Address | &:r1753_1 |
| ir.cpp:1753:40:1753:41 | Address | &:r1753_1 |
| ir.cpp:1753:40:1753:41 | Arg(this) | this:r1753_1 |
@@ -8435,6 +8445,7 @@
| ir.cpp:1754:42:1754:42 | SideEffect | ~m1752_4 |
| ir.cpp:1754:42:1754:42 | Unary | r1754_5 |
| ir.cpp:1754:42:1754:42 | Unary | r1754_6 |
| ir.cpp:1755:1:1755:1 | Address | &:r1755_1 |
| ir.cpp:1757:6:1757:22 | ChiPartial | partial:m1757_3 |
| ir.cpp:1757:6:1757:22 | ChiTotal | total:m1757_2 |
| ir.cpp:1757:6:1757:22 | SideEffect | m1757_3 |
@@ -9588,22 +9599,27 @@
| ir.cpp:2021:23:2021:40 | SideEffect | ~m2021_27 |
| ir.cpp:2021:23:2021:40 | Unary | r2021_20 |
| ir.cpp:2021:23:2021:40 | Unary | r2021_28 |
| ir.cpp:2026:14:2026:22 | Address | &:r2026_7 |
| ir.cpp:2026:14:2026:22 | ChiPartial | partial:m2026_3 |
| ir.cpp:2026:14:2026:22 | ChiTotal | total:m2026_2 |
| ir.cpp:2026:14:2026:22 | Load | m2031_2 |
| ir.cpp:2026:14:2026:22 | SideEffect | ~m2028_6 |
| ir.cpp:2026:37:2026:37 | Address | &:r2026_5 |
| ir.cpp:2027:16:2027:16 | Address | &:r2027_1 |
| ir.cpp:2028:3:2028:3 | Address | &:r2028_9 |
| ir.cpp:2028:3:2028:3 | Address | &:r2028_10 |
| ir.cpp:2028:7:2028:7 | Address | &:r2028_1 |
| ir.cpp:2028:7:2028:7 | Left | r2028_2 |
| ir.cpp:2028:7:2028:7 | Load | m2026_6 |
| ir.cpp:2028:7:2028:13 | Condition | r2028_4 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_7 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_11 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_13 |
| ir.cpp:2028:7:2030:28 | Load | m2028_6 |
| ir.cpp:2028:7:2030:28 | Phi | from 2:m2028_12 |
| ir.cpp:2028:7:2030:28 | Phi | from 3:m2028_14 |
| ir.cpp:2028:7:2030:28 | StoreValue | r2028_8 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_8 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_12 |
| ir.cpp:2028:7:2030:28 | Address | &:r2028_14 |
| ir.cpp:2028:7:2030:28 | Load | m2028_7 |
| ir.cpp:2028:7:2030:28 | Phi | from 2:m2028_13 |
| ir.cpp:2028:7:2030:28 | Phi | from 2:~m2029_6 |
| ir.cpp:2028:7:2030:28 | Phi | from 3:m2028_15 |
| ir.cpp:2028:7:2030:28 | Phi | from 3:~m2030_6 |
| ir.cpp:2028:7:2030:28 | StoreValue | r2028_9 |
| ir.cpp:2028:11:2028:13 | Right | r2028_3 |
| ir.cpp:2029:6:2029:20 | CallTarget | func:r2029_1 |
| ir.cpp:2029:6:2029:20 | ChiPartial | partial:m2029_5 |
@@ -9626,6 +9642,7 @@
| ir.cpp:2030:22:2030:22 | Arg(0) | 0:r2030_3 |
| ir.cpp:2030:22:2030:22 | Load | m2026_6 |
| ir.cpp:2030:26:2030:27 | Unary | r2030_7 |
| ir.cpp:2031:1:2031:1 | Address | &:r2031_1 |
| ir.cpp:2033:6:2033:17 | ChiPartial | partial:m2033_3 |
| ir.cpp:2033:6:2033:17 | ChiTotal | total:m2033_2 |
| ir.cpp:2033:6:2033:17 | SideEffect | ~m2036_6 |
@@ -9721,8 +9738,11 @@
| ir.cpp:2051:32:2051:32 | Address | &:r2051_7 |
| ir.cpp:2051:32:2051:32 | Load | m2051_6 |
| ir.cpp:2051:32:2051:32 | SideEffect | m2051_8 |
| ir.cpp:2056:5:2056:18 | Address | &:r2056_5 |
| ir.cpp:2056:5:2056:18 | ChiPartial | partial:m2056_3 |
| ir.cpp:2056:5:2056:18 | ChiTotal | total:m2056_2 |
| ir.cpp:2056:5:2056:18 | Load | m2066_2 |
| ir.cpp:2056:5:2056:18 | SideEffect | ~m2065_6 |
| ir.cpp:2058:12:2058:13 | Address | &:r2058_1 |
| ir.cpp:2058:17:2058:27 | Address | &:r2058_4 |
| ir.cpp:2058:17:2058:27 | Address | &:r2058_8 |
@@ -9796,6 +9816,7 @@
| ir.cpp:2065:12:2065:12 | Address | &:r2065_2 |
| ir.cpp:2065:12:2065:12 | Arg(0) | 0:r2065_3 |
| ir.cpp:2065:12:2065:12 | Load | m2064_15 |
| ir.cpp:2066:1:2066:1 | Address | &:r2066_1 |
| ir.cpp:2070:6:2070:26 | ChiPartial | partial:m2070_3 |
| ir.cpp:2070:6:2070:26 | ChiTotal | total:m2070_2 |
| ir.cpp:2070:6:2070:26 | SideEffect | ~m2072_5 |

View File

@@ -12,13 +12,15 @@ unnecessaryPhiInstruction
memoryOperandDefinitionIsUnmodeled
operandAcrossFunctions
instructionWithoutUniqueBlock
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
containsLoopOfForwardEdges
missingIRType
multipleIRTypes
lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
| ir.cpp:1486:8:1486:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1486:8:1486:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
| ir.cpp:1751:51:1751:51 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
| ir.cpp:1752:48:1752:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
| try_except.c:39:15:39:15 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:32:6:32:6 | void h(int) | void h(int) |
@@ -35,8 +37,4 @@ nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
multipleIRTypes
missingCppType

View File

@@ -7201,21 +7201,26 @@ ir.cpp:
# 1290| r1290_1(glval<bool>) = VariableAddress[b] :
# 1290| r1290_2(bool) = Load[b] : &:r1290_1, ~m?
# 1290| v1290_3(void) = ConditionalBranch : r1290_2
#-----| False -> Block 2
#-----| True -> Block 1
#-----| False -> Block 3
#-----| True -> Block 2
# 1291| Block 1
# 1291| r1291_1(glval<int>) = VariableAddress[#return] :
# 1291| r1291_2(glval<int>) = VariableAddress[x] :
# 1291| r1291_3(int) = Load[x] : &:r1291_2, ~m?
# 1291| mu1291_4(int) = Store[#return] : &:r1291_1, r1291_3
# 1289| Block 1
# 1289| r1289_8(glval<int>) = VariableAddress[#return] :
# 1289| v1289_9(void) = ReturnValue : &:r1289_8, ~m?
# 1289| v1289_10(void) = AliasedUse : ~m?
# 1289| v1289_11(void) = ExitFunction :
# 1293| Block 2
# 1293| v1293_1(void) = Unreached :
# 1291| Block 2
# 1291| r1291_1(glval<int>) = VariableAddress[#return] :
# 1291| r1291_2(glval<int>) = VariableAddress[x] :
# 1291| r1291_3(int) = Load[x] : &:r1291_2, ~m?
# 1291| mu1291_4(int) = Store[#return] : &:r1291_1, r1291_3
#-----| Goto -> Block 1
# 1293| Block 3
# 1293| r1293_1(glval<int>) = VariableAddress[#return] :
# 1293| mu1293_2(int) = Uninitialized[#return] : &:r1293_1
#-----| Goto -> Block 1
# 1295| void returnVoid(int, int)
# 1295| Block 0
@@ -9526,15 +9531,14 @@ ir.cpp:
# 1754| mu1754_9(unknown) = ^CallSideEffect : ~m?
# 1754| v1754_10(void) = ^BufferReadSideEffect[0] : &:r1754_7, ~m?
# 1754| mu1754_11(CopyConstructorTestVirtualClass) = ^IndirectMayWriteSideEffect[-1] : &:r1754_1
# 1755| v1755_1(void) = Unreached :
# 1751| Block 1
# 1751| v1751_5(void) = ReturnIndirection[x] : &:r1751_3, ~m?
# 1752| v1752_5(void) = ReturnIndirection[y] : &:r1752_3, ~m?
# 1750| r1750_4(glval<int>) = VariableAddress[#return] :
# 1750| v1750_5(void) = ReturnValue : &:r1750_4, ~m?
# 1750| v1750_6(void) = AliasedUse : ~m?
# 1750| v1750_7(void) = ExitFunction :
# 1755| r1755_1(glval<int>) = VariableAddress[#return] :
# 1755| mu1755_2(int) = Uninitialized[#return] : &:r1755_1
# 1751| v1751_5(void) = ReturnIndirection[x] : &:r1751_3, ~m?
# 1752| v1752_5(void) = ReturnIndirection[y] : &:r1752_3, ~m?
# 1750| r1750_4(glval<int>) = VariableAddress[#return] :
# 1750| v1750_5(void) = ReturnValue : &:r1750_4, ~m?
# 1750| v1750_6(void) = AliasedUse : ~m?
# 1750| v1750_7(void) = ExitFunction :
# 1757| void if_initialization(int)
# 1757| Block 0
@@ -10199,24 +10203,32 @@ ir.cpp:
# 1900| r1900_3(int) = Constant[10] :
# 1900| r1900_4(bool) = CompareLT : r1900_2, r1900_3
# 1900| v1900_5(void) = ConditionalBranch : r1900_4
#-----| False -> Block 2
#-----| True -> Block 1
#-----| False -> Block 3
#-----| True -> Block 2
# 1901| Block 1
# 1901| r1901_1(glval<int>) = VariableAddress[#return] :
# 1901| r1901_2(glval<int>) = VariableAddress[x] :
# 1901| r1901_3(int) = Load[x] : &:r1901_2, ~m?
# 1901| mu1901_4(int) = Store[#return] : &:r1901_1, r1901_3
# 1899| Block 1
# 1899| r1899_6(glval<int>) = VariableAddress[#return] :
# 1899| v1899_7(void) = ReturnValue : &:r1899_6, ~m?
# 1899| v1899_8(void) = AliasedUse : ~m?
# 1899| v1899_9(void) = ExitFunction :
# 1903| Block 2
# 1901| Block 2
# 1901| r1901_1(glval<int>) = VariableAddress[#return] :
# 1901| r1901_2(glval<int>) = VariableAddress[x] :
# 1901| r1901_3(int) = Load[x] : &:r1901_2, ~m?
# 1901| mu1901_4(int) = Store[#return] : &:r1901_1, r1901_3
#-----| Goto -> Block 1
# 1903| Block 3
# 1903| r1903_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 1903| v1903_2(void) = Call[noreturnFunc] : func:r1903_1
# 1903| mu1903_3(unknown) = ^CallSideEffect : ~m?
# 1905| v1905_1(void) = Unreached :
# 1899| v1899_10(void) = Unreached :
# 1905| Block 4
# 1905| r1905_1(glval<int>) = VariableAddress[#return] :
# 1905| mu1905_2(int) = Uninitialized[#return] : &:r1905_1
#-----| Goto -> Block 1
# 1907| int noreturnTest2(int)
# 1907| Block 0
@@ -11069,23 +11081,22 @@ ir.cpp:
# 2028| r2028_3(unsigned int) = Constant[100] :
# 2028| r2028_4(bool) = CompareLT : r2028_2, r2028_3
# 2028| v2028_5(void) = ConditionalBranch : r2028_4
#-----| False -> Block 4
#-----| True -> Block 3
#-----| False -> Block 3
#-----| True -> Block 2
# 2026| Block 1
# 2026| r2026_6(glval<unsigned int>) = VariableAddress[#return] :
# 2026| v2026_7(void) = ReturnValue : &:r2026_6, ~m?
# 2026| v2026_8(void) = AliasedUse : ~m?
# 2026| v2026_9(void) = ExitFunction :
# 2028| Block 2
# 2028| Block 1
# 2028| r2028_6(glval<unsigned int>) = VariableAddress[#temp2028:7] :
# 2028| r2028_7(unsigned int) = Load[#temp2028:7] : &:r2028_6, ~m?
# 2028| r2028_8(glval<unsigned int>) = VariableAddress[y] :
# 2028| mu2028_9(unsigned int) = Store[y] : &:r2028_8, r2028_7
# 2031| v2031_1(void) = Unreached :
# 2031| r2031_1(glval<unsigned int>) = VariableAddress[#return] :
# 2031| mu2031_2(unsigned int) = Uninitialized[#return] : &:r2031_1
# 2026| r2026_6(glval<unsigned int>) = VariableAddress[#return] :
# 2026| v2026_7(void) = ReturnValue : &:r2026_6, ~m?
# 2026| v2026_8(void) = AliasedUse : ~m?
# 2026| v2026_9(void) = ExitFunction :
# 2029| Block 3
# 2029| Block 2
# 2029| r2029_1(glval<unknown>) = FunctionAddress[CommaTestHelper] :
# 2029| r2029_2(glval<unsigned int>) = VariableAddress[x] :
# 2029| r2029_3(unsigned int) = Load[x] : &:r2029_2, ~m?
@@ -11096,9 +11107,9 @@ ir.cpp:
# 2029| r2029_8(unsigned int) = CopyValue : r2029_7
# 2028| r2028_10(glval<unsigned int>) = VariableAddress[#temp2028:7] :
# 2028| mu2028_11(unsigned int) = Store[#temp2028:7] : &:r2028_10, r2029_8
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 2030| Block 4
# 2030| Block 3
# 2030| r2030_1(glval<unknown>) = FunctionAddress[CommaTestHelper] :
# 2030| r2030_2(glval<unsigned int>) = VariableAddress[x] :
# 2030| r2030_3(unsigned int) = Load[x] : &:r2030_2, ~m?
@@ -11109,7 +11120,7 @@ ir.cpp:
# 2030| r2030_8(unsigned int) = Convert : r2030_7
# 2028| r2028_12(glval<unsigned int>) = VariableAddress[#temp2028:7] :
# 2028| mu2028_13(unsigned int) = Store[#temp2028:7] : &:r2028_12, r2030_8
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 2033| void NewDeleteMem()
# 2033| Block 0
@@ -11295,13 +11306,12 @@ ir.cpp:
# 2065| r2065_3(Derived2 *) = Load[d] : &:r2065_2, ~m?
# 2065| v2065_4(void) = Call[?] : func:r2065_1, 0:r2065_3
# 2065| mu2065_5(unknown) = ^CallSideEffect : ~m?
# 2066| v2066_1(void) = Unreached :
# 2056| Block 1
# 2056| r2056_4(glval<int>) = VariableAddress[#return] :
# 2056| v2056_5(void) = ReturnValue : &:r2056_4, ~m?
# 2056| v2056_6(void) = AliasedUse : ~m?
# 2056| v2056_7(void) = ExitFunction :
# 2066| r2066_1(glval<int>) = VariableAddress[#return] :
# 2066| mu2066_2(int) = Uninitialized[#return] : &:r2066_1
# 2056| r2056_4(glval<int>) = VariableAddress[#return] :
# 2056| v2056_5(void) = ReturnValue : &:r2056_4, ~m?
# 2056| v2056_6(void) = AliasedUse : ~m?
# 2056| v2056_7(void) = ExitFunction :
# 2070| void test_constant_folding()
# 2070| Block 0

View File

@@ -34,13 +34,15 @@ unnecessaryPhiInstruction
memoryOperandDefinitionIsUnmodeled
operandAcrossFunctions
instructionWithoutUniqueBlock
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
containsLoopOfForwardEdges
missingIRType
multipleIRTypes
lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
| VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor<int>(int, int*) | void CallDestructor<int>(int, int*) |
| misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) |
| ms_assume.cpp:11:30:11:33 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ms_assume.cpp:11:12:11:12 | int f(int, char*[]) | int f(int, char*[]) |
| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
| ms_try_except.cpp:19:17:19:21 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
@@ -58,8 +60,4 @@ thisArgumentIsNonPointer
| pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
| pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
multipleIRTypes
missingCppType

View File

@@ -1,9 +1,7 @@
{
"recommendations": [
"formulahendry.dotnet-test-explorer",
"gaoshan0621.csharp-format-usings",
"github.vscode-codeql",
"hbenl.vscode-test-explorer",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
],

View File

@@ -557,7 +557,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestLinuxBuildlessExtractionSuccess()
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 0;
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 0;
actions.FileExists["csharp.log"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -571,7 +571,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestLinuxBuildlessExtractionFailed()
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 10;
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 10;
actions.FileExists["csharp.log"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -585,7 +585,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestLinuxBuildlessExtractionSolution()
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0;
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln"] = 0;
actions.FileExists["csharp.log"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -873,7 +873,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestSkipNugetBuildless()
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0;
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --skip-nuget"] = 0;
actions.FileExists["csharp.log"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";

View File

@@ -8,12 +8,12 @@
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj" />

View File

@@ -48,8 +48,11 @@ namespace Semmle.Autobuild.CSharp
attempt = new BuildCommandRule(DotNetRule.WithDotNet).Analyse(this, false) & CheckExtractorRun(true);
break;
case CSharpBuildStrategy.Buildless:
// No need to check that the extractor has been executed in buildless mode
attempt = new StandaloneBuildRule().Analyse(this, false);
attempt = DotNetRule.WithDotNet(this, (dotNetPath, env) =>
{
// No need to check that the extractor has been executed in buildless mode
return new StandaloneBuildRule(dotNetPath).Analyse(this, false);
});
break;
case CSharpBuildStrategy.MSBuild:
attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true);

View File

@@ -70,7 +70,16 @@ namespace Semmle.Autobuild.CSharp
});
}
private static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
/// <summary>
/// Returns a script that attempts to download relevant version(s) of the
/// .NET Core SDK, followed by running the script generated by <paramref name="f"/>.
///
/// The arguments to <paramref name="f"/> are the path to the directory in which the
/// .NET Core SDK(s) were installed and any additional required environment
/// variables needed by the installed .NET Core (<code>null</code> when no variables
/// are needed).
/// </summary>
public static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
{
var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet");
var installScript = DownloadDotNet(builder, installDir);

View File

@@ -3,9 +3,9 @@
<TargetFramework>net7.0</TargetFramework>
<AssemblyName>Semmle.Autobuild.CSharp</AssemblyName>
<RootNamespace>Semmle.Autobuild.CSharp</RootNamespace>
<ApplicationIcon />
<ApplicationIcon/>
<OutputType>Exe</OutputType>
<StartupObject />
<StartupObject/>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
@@ -14,8 +14,8 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />

View File

@@ -8,6 +8,13 @@ namespace Semmle.Autobuild.CSharp
/// </summary>
internal class StandaloneBuildRule : IBuildRule<CSharpAutobuildOptions>
{
private readonly string? dotNetPath;
internal StandaloneBuildRule(string? dotNetPath)
{
this.dotNetPath = dotNetPath;
}
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
{
BuildScript GetCommand(string? solution)
@@ -28,13 +35,17 @@ namespace Semmle.Autobuild.CSharp
if (solution is not null)
cmd.QuoteArgument(solution);
cmd.Argument("--references:.");
if (!builder.Options.NugetRestore)
{
cmd.Argument("--skip-nuget");
}
if (!string.IsNullOrEmpty(this.dotNetPath))
{
cmd.Argument("--dotnet");
cmd.QuoteArgument(this.dotNetPath);
}
return cmd.Script;
}

View File

@@ -11,7 +11,7 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />

View File

@@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.DiaSymReader" Version="2.0.0" />
<PackageReference Include="Microsoft.DiaSymReader" Version="1.4.0" />
<PackageReference Include="Microsoft.DiaSymReader.Native" Version="1.7.0" />
<PackageReference Include="Microsoft.DiaSymReader.PortablePdb" Version="1.6.0"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -15,14 +15,24 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// <summary>
/// Locate all reference files and index them.
/// </summary>
/// <param name="dirs">Directories to search.</param>
/// <param name="paths">
/// Paths to search. Directories are searched recursively. Files are added directly to the
/// assembly cache.
/// </param>
/// <param name="progressMonitor">Callback for progress.</param>
public AssemblyCache(IEnumerable<string> dirs, ProgressMonitor progressMonitor)
public AssemblyCache(IEnumerable<string> paths, ProgressMonitor progressMonitor)
{
foreach (var dir in dirs)
foreach (var path in paths)
{
progressMonitor.FindingFiles(dir);
AddReferenceDirectory(dir);
if (File.Exists(path))
{
pendingDllsToIndex.Enqueue(path);
}
else
{
progressMonitor.FindingFiles(path);
AddReferenceDirectory(path);
}
}
IndexReferences();
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@@ -31,6 +31,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private readonly FileContent fileContent;
private readonly TemporaryDirectory packageDirectory;
private TemporaryDirectory? razorWorkingDirectory;
private readonly Git git;
/// <summary>
@@ -48,7 +49,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
try
{
this.dotnet = new DotNet(progressMonitor);
this.dotnet = new DotNet(options, progressMonitor);
}
catch
{
@@ -68,7 +69,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
? new[] { options.SolutionFile }
: allFiles.SelectFileNamesByExtension(".sln");
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
// If DLL reference paths are specified on the command-line, use those to discover
// assemblies. Otherwise (the default), query the git CLI to determine which DLL files
// are tracked as part of the repository.
this.git = new Git(this.progressMonitor);
var dllDirNames = options.DllDirs.Count == 0 ? this.git.ListFiles("*.dll") : options.DllDirs.Select(Path.GetFullPath).ToList();
// Find DLLs in the .Net / Asp.Net Framework
if (options.ScanNetFrameworkDlls)
@@ -98,13 +103,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
progressMonitor.MissingNuGet();
}
// TODO: remove the below when the required SDK is installed
using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories)))
{
Restore(solutions);
Restore(allProjects);
DownloadMissingPackages(allFiles);
}
Restore(solutions);
Restore(allProjects);
DownloadMissingPackages(allFiles);
}
assemblyCache = new AssemblyCache(dllDirNames, progressMonitor);
@@ -157,7 +158,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
progressMonitor.LogInfo($"Found {views.Length} cshtml and razor files.");
// TODO: use SDK specified in global.json
var sdk = new Sdk(dotnet).GetNewestSdk();
if (sdk != null)
{

View File

@@ -51,6 +51,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// The number of threads to use.
/// </summary>
int Threads { get; }
/// <summary>
/// The path to the local ".dotnet" directory, if any.
/// </summary>
string? DotNetPath { get; }
}
public class DependencyOptions : IDependencyOptions
@@ -73,5 +78,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
Excludes.Any(path.Contains);
public int Threads { get; set; } = EnvironmentVariables.GetDefaultNumberOfThreads();
public string? DotNetPath { get; set; } = null;
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Semmle.Util;
namespace Semmle.Extraction.CSharp.DependencyFetching
@@ -10,12 +11,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// </summary>
internal class DotNet : IDotNet
{
private const string dotnet = "dotnet";
private readonly ProgressMonitor progressMonitor;
private readonly string dotnet;
public DotNet(ProgressMonitor progressMonitor)
public DotNet(IDependencyOptions options, ProgressMonitor progressMonitor)
{
this.progressMonitor = progressMonitor;
this.dotnet = Path.Combine(options.DotNetPath ?? string.Empty, "dotnet");
Info();
}
@@ -29,7 +31,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private static ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput) =>
private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput) =>
new ProcessStartInfo(dotnet, args)
{
UseShellExecute = false,
@@ -39,7 +41,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private bool RunCommand(string args)
{
progressMonitor.RunningProcess($"{dotnet} {args}");
using var proc = Process.Start(MakeDotnetStartInfo(args, redirectStandardOutput: false));
using var proc = Process.Start(this.MakeDotnetStartInfo(args, redirectStandardOutput: false));
proc?.WaitForExit();
var exitCode = proc?.ExitCode ?? -1;
if (exitCode != 0)
@@ -77,7 +79,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private IList<string> GetListed(string args, string artifact)
{
progressMonitor.RunningProcess($"{dotnet} {args}");
var pi = MakeDotnetStartInfo(args, redirectStandardOutput: true);
var pi = this.MakeDotnetStartInfo(args, redirectStandardOutput: true);
var exitCode = pi.ReadOutput(out var artifacts);
if (exitCode != 0)
{

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Utilities for querying information from the git CLI.
/// </summary>
internal class Git
{
private readonly ProgressMonitor progressMonitor;
private const string git = "git";
public Git(ProgressMonitor progressMonitor)
{
this.progressMonitor = progressMonitor;
}
/// <summary>
/// Lists all files matching <paramref name="pattern"/> which are tracked in the
/// current git repository.
/// </summary>
/// <param name="pattern">The file pattern.</param>
/// <returns>A list of all tracked files which match <paramref name="pattern"/>.</returns>
/// <exception cref="Exception"></exception>
public List<string> ListFiles(string pattern)
{
var results = new List<string>();
var args = string.Join(' ', "ls-files", $"\"{pattern}\"");
progressMonitor.RunningProcess($"{git} {args}");
var pi = new ProcessStartInfo(git, args)
{
UseShellExecute = false,
RedirectStandardOutput = true
};
using var p = new Process() { StartInfo = pi };
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!string.IsNullOrWhiteSpace(e.Data))
{
results.Add(e.Data);
}
});
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
if (p.ExitCode != 0)
{
progressMonitor.CommandFailed(git, args, p.ExitCode);
throw new Exception($"{git} {args} failed");
}
return results;
}
}
}

View File

@@ -50,6 +50,9 @@ namespace Semmle.Extraction.CSharp.Standalone
case "references":
dependencies.DllDirs.Add(value);
return true;
case "dotnet":
dependencies.DotNetPath = value;
return true;
default:
return base.HandleOption(key, value);
}

View File

@@ -6,7 +6,7 @@
<RootNamespace>Semmle.Extraction.CSharp.Standalone</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors />
<WarningsAsErrors/>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -19,7 +19,7 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
<PackageReference Include="System.Security.Principal" Version="4.3.0" />

View File

@@ -250,41 +250,44 @@ namespace Semmle.Extraction.CSharp.Entities
/// </summary>
public void PopulateGenerics()
{
if (Symbol is null || !NeedsPopulation || !Context.ExtractGenerics(this))
return;
var members = new List<ISymbol>();
foreach (var member in Symbol.GetMembers())
members.Add(member);
foreach (var member in Symbol.GetTypeMembers())
members.Add(member);
// Mono extractor puts all BASE interface members as members of the current interface.
if (Symbol.TypeKind == TypeKind.Interface)
Context.PopulateLater(() =>
{
foreach (var baseInterface in Symbol.Interfaces)
if (Symbol is null || !NeedsPopulation || !Context.ExtractGenerics(this))
return;
var members = new List<ISymbol>();
foreach (var member in Symbol.GetMembers())
members.Add(member);
foreach (var member in Symbol.GetTypeMembers())
members.Add(member);
// Mono extractor puts all BASE interface members as members of the current interface.
if (Symbol.TypeKind == TypeKind.Interface)
{
foreach (var member in baseInterface.GetMembers())
members.Add(member);
foreach (var member in baseInterface.GetTypeMembers())
members.Add(member);
foreach (var baseInterface in Symbol.Interfaces)
{
foreach (var member in baseInterface.GetMembers())
members.Add(member);
foreach (var member in baseInterface.GetTypeMembers())
members.Add(member);
}
}
}
foreach (var member in members)
{
Context.CreateEntity(member);
}
foreach (var member in members)
{
Context.CreateEntity(member);
}
if (Symbol.BaseType is not null)
Create(Context, Symbol.BaseType).PopulateGenerics();
if (Symbol.BaseType is not null)
Create(Context, Symbol.BaseType).PopulateGenerics();
foreach (var i in Symbol.Interfaces)
{
Create(Context, i).PopulateGenerics();
}
foreach (var i in Symbol.Interfaces)
{
Create(Context, i).PopulateGenerics();
}
}, preserveDuplicationKey: false);
}
public void ExtractRecursive(TextWriter trapFile, IEntity parent)

View File

@@ -18,7 +18,7 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup>
</Project>

View File

@@ -8,12 +8,12 @@
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj" />

View File

@@ -153,9 +153,9 @@ namespace Semmle.Extraction
/// Enqueue the given action to be performed later.
/// </summary>
/// <param name="toRun">The action to run.</param>
public void PopulateLater(Action a)
public void PopulateLater(Action a, bool preserveDuplicationKey = true)
{
var key = GetCurrentTagStackKey();
var key = preserveDuplicationKey ? GetCurrentTagStackKey() : null;
if (key is not null)
{
// If we are currently executing with a duplication guard, then the same

View File

@@ -12,9 +12,9 @@
<DefineConstants>TRACE;DEBUG;DEBUG_LABELS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.7.0" />
<PackageReference Include="GitInfo" Version="3.3.1">
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
<PackageReference Include="GitInfo" Version="2.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@@ -6,12 +6,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />

View File

@@ -15,7 +15,7 @@
<ItemGroup>
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
</Project>

View File

@@ -8,10 +8,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>
</Project>

View File

@@ -10,10 +10,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
</ItemGroup>
</Project>

View File

@@ -40,8 +40,8 @@
| CSharp7.cs:87:18:87:34 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 1 | CSharp7.cs:87:30:87:33 | "X2" |
| CSharp7.cs:88:9:88:24 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 0 | CSharp7.cs:88:14:88:15 | String t2 |
| CSharp7.cs:88:9:88:24 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 1 | CSharp7.cs:88:22:88:23 | String t3 |
| CSharp7.cs:95:18:95:38 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:95:19:95:19 | 1 |
| CSharp7.cs:95:18:95:38 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:95:22:95:37 | "TupleExprNode1" |
| CSharp7.cs:95:18:95:38 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:95:19:95:19 | 1 |
| CSharp7.cs:95:18:95:38 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:95:22:95:37 | "TupleExprNode1" |
| CSharp7.cs:96:18:96:43 | (..., ...) | CSharp7.cs:96:18:96:43 | (Int32,(String,Int32)) | 0 | CSharp7.cs:96:19:96:19 | 1 |
| CSharp7.cs:96:18:96:43 | (..., ...) | CSharp7.cs:96:18:96:43 | (Int32,(String,Int32)) | 1 | CSharp7.cs:96:22:96:42 | (..., ...) |
| CSharp7.cs:96:22:96:42 | (..., ...) | file://:0:0:0:0 | (String,Int32) | 0 | CSharp7.cs:96:23:96:38 | "TupleExprNode2" |
@@ -82,11 +82,11 @@
| CSharp7.cs:223:9:223:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 1 | CSharp7.cs:223:17:223:17 | _ |
| CSharp7.cs:224:9:224:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 0 | CSharp7.cs:224:10:224:10 | _ |
| CSharp7.cs:224:9:224:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 1 | CSharp7.cs:224:17:224:17 | Double y |
| CSharp7.cs:283:40:283:61 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:283:41:283:48 | access to property Key |
| CSharp7.cs:283:40:283:61 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:283:51:283:60 | access to property Value |
| CSharp7.cs:285:18:285:34 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:285:23:285:23 | Int32 a |
| CSharp7.cs:285:18:285:34 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:285:33:285:33 | String b |
| CSharp7.cs:287:18:287:31 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:287:23:287:23 | Int32 a |
| CSharp7.cs:287:18:287:31 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:287:30:287:30 | String b |
| CSharp7.cs:289:18:289:27 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:289:23:289:23 | Int32 a |
| CSharp7.cs:289:18:289:27 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:289:26:289:26 | String b |
| CSharp7.cs:283:40:283:61 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:283:41:283:48 | access to property Key |
| CSharp7.cs:283:40:283:61 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:283:51:283:60 | access to property Value |
| CSharp7.cs:285:18:285:34 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:285:23:285:23 | Int32 a |
| CSharp7.cs:285:18:285:34 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:285:33:285:33 | String b |
| CSharp7.cs:287:18:287:31 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:287:23:287:23 | Int32 a |
| CSharp7.cs:287:18:287:31 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:287:30:287:30 | String b |
| CSharp7.cs:289:18:289:27 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:289:23:289:23 | Int32 a |
| CSharp7.cs:289:18:289:27 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:289:26:289:26 | String b |

View File

@@ -1,10 +1,13 @@
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 0 | CSharp7.cs:96:19:96:19 | Item1 |
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 1 | CSharp7.cs:96:22:96:42 | Item2 |
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 1 | CSharp7.cs:102:22:102:46 | Item2 |
| (Int32,Double) | (int, double) | ValueTuple<int, double> | 2 | 0 | CSharp7.cs:213:6:213:8 | Item1 |
| (Int32,Double) | (int, double) | ValueTuple<int, double> | 2 | 1 | CSharp7.cs:213:11:213:16 | Item2 |
| (Int32,Int32) | (int, int) | ValueTuple<int, int> | 2 | 0 | CSharp7.cs:62:10:62:10 | Item1 |
| (Int32,Int32) | (int, int) | ValueTuple<int, int> | 2 | 1 | CSharp7.cs:62:17:62:17 | Item2 |
| (Int32,String) | (int, string) | ValueTuple<int, string> | 2 | 0 | CSharp7.cs:95:19:95:19 | Item1 |
| (Int32,String) | (int, string) | ValueTuple<int, string> | 2 | 1 | CSharp7.cs:95:22:95:37 | Item2 |
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 0 | CSharp7.cs:82:17:82:17 | Item1 |
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 0 | CSharp7.cs:101:19:101:38 | Item1 |
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 1 | CSharp7.cs:82:23:82:23 | Item2 |
| (String,String) | (string, string) | ValueTuple<string, string> | 2 | 0 | CSharp7.cs:87:19:87:27 | Item1 |
| (String,String) | (string, string) | ValueTuple<string, string> | 2 | 1 | CSharp7.cs:87:30:87:33 | Item2 |

View File

@@ -482,3 +482,14 @@ func EmitGoModVersionSupportedLowerEqualGoEnv(msg string) {
noLocation,
)
}
func EmitNewerSystemGoRequired(requiredVersion string) {
emitDiagnostic(
"go/autobuilder/newer-system-go-version-required",
"The Go version installed on the system is too old to support this project",
"At least Go version `"+requiredVersion+"` is required to build this project, but the version installed on the system is older. [Install a newer version](https://github.com/actions/setup-go#basic).",
severityError,
fullVisibility,
noLocation,
)
}

View File

@@ -91,6 +91,10 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error {
}
pkgs, err := packages.Load(cfg, patterns...)
if err != nil {
// the toolchain directive is only supported in Go 1.21 and above
if strings.Contains(err.Error(), "unknown directive: toolchain") {
diagnostics.EmitNewerSystemGoRequired("1.21.0")
}
return err
}
log.Println("Done running packages.Load.")

View File

@@ -35,22 +35,54 @@ newtype TApplicationModeEndpoint =
argExpr.isVararg() and
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
)
} or
TMethodReturnValue(Call call) { not call instanceof ConstructorCall } or
TOverriddenParameter(Parameter p, Method overriddenMethod) {
not p.getCallable().callsConstructor(_) and
p.getCallable().(Method).overrides(overriddenMethod)
}
/**
* An endpoint is a node that is a candidate for modeling.
*/
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
abstract predicate isArgOf(Call c, int idx);
/**
* Gets the callable to be modeled that this endpoint represents.
*/
abstract Callable getCallable();
Call getCall() { this.isArgOf(result, _) }
abstract Call getCall();
int getArgIndex() { this.isArgOf(_, result) }
/**
* Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
*
* For endpoints that are source candidates, this will be `none()`.
*/
abstract string getMaDInput();
/**
* Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
*
* For endpoints that are sink candidates, this will be `none()`.
*/
abstract string getMaDOutput();
abstract Top asTop();
/**
* Converts the endpoint to a node that can be used in a data flow graph.
*/
abstract DataFlow::Node asNode();
string getExtensibleType() {
if not exists(this.getMaDInput()) and exists(this.getMaDOutput())
then result = "sourceModel"
else
if exists(this.getMaDInput()) and not exists(this.getMaDOutput())
then result = "sinkModel"
else none() // if both exist, it would be a summaryModel (not yet supported)
}
abstract string toString();
}
@@ -63,7 +95,15 @@ class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
ExplicitArgument() { this = TExplicitArgument(call, arg) }
override predicate isArgOf(Call c, int idx) { c = call and this.asTop() = c.getArgument(idx) }
override Callable getCallable() { result = call.getCallee() }
override Call getCall() { result = call }
private int getArgIndex() { this.asTop() = call.getArgument(result) }
override string getMaDInput() { result = "Argument[" + this.getArgIndex() + "]" }
override string getMaDOutput() { none() }
override Top asTop() { result = arg.asExpr() }
@@ -78,9 +118,13 @@ class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
InstanceArgument() { this = TInstanceArgument(call, arg) }
override predicate isArgOf(Call c, int idx) {
c = call and this.asTop() = c.getQualifier() and idx = -1
}
override Callable getCallable() { result = call.getCallee() }
override Call getCall() { result = call }
override string getMaDInput() { result = "Argument[this]" }
override string getMaDOutput() { none() }
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
@@ -105,15 +149,78 @@ class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArra
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
override predicate isArgOf(Call c, int i) { c = call and i = idx }
override Callable getCallable() { result = call.getCallee() }
override Top asTop() { result = this.getCall() }
override Call getCall() { result = call }
override string getMaDInput() { result = "Argument[" + idx + "]" }
override string getMaDOutput() { none() }
override Top asTop() { result = call }
override DataFlow::Node asNode() { result = vararg }
override string toString() { result = vararg.toString() }
}
/**
* An endpoint that represents a method call. The `ReturnValue` of a method call
* may be a source.
*/
class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue {
Call call;
MethodReturnValue() { this = TMethodReturnValue(call) }
override Callable getCallable() { result = call.getCallee() }
override Call getCall() { result = call }
override string getMaDInput() { none() }
override string getMaDOutput() { result = "ReturnValue" }
override Top asTop() { result = call }
override DataFlow::Node asNode() { result.asExpr() = call }
override string toString() { result = call.toString() }
}
/**
* An endpoint that represents a parameter of an overridden method that may be
* a source.
*/
class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter {
Parameter p;
Method overriddenMethod;
OverriddenParameter() { this = TOverriddenParameter(p, overriddenMethod) }
override Callable getCallable() {
// NB: we're returning the overridden callable here. This means that the
// candidate model will be about the overridden method, not the overriding
// method. This is a more general model, that also applies to other
// subclasses of the overridden class.
result = overriddenMethod
}
override Call getCall() { none() }
private int getArgIndex() { p.getCallable().getParameter(result) = p }
override string getMaDInput() { none() }
override string getMaDOutput() { result = "Parameter[" + this.getArgIndex() + "]" }
override Top asTop() { result = p }
override DataFlow::Node asNode() { result.(DataFlow::ParameterNode).asParameter() = p }
override string toString() { result = p.toString() }
}
/**
* A candidates implementation.
*
@@ -161,6 +268,14 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
isCustomSink(e, kind) and provenance = "custom-sink"
}
predicate isSource(Endpoint e, string kind, string provenance) {
exists(string package, string type, string name, string signature, string ext, string output |
sourceSpec(e, package, type, name, signature, ext, output) and
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind,
provenance)
)
}
predicate isNeutral(Endpoint e) {
exists(string package, string type, string name, string signature |
sinkSpec(e, package, type, name, signature, _, _) and
@@ -168,13 +283,24 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
)
}
// XXX how to extend to support sources?
additional predicate sinkSpec(
Endpoint e, string package, string type, string name, string signature, string ext, string input
) {
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
ext = "" and
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex())
input = e.getMaDInput()
}
additional predicate sourceSpec(
Endpoint e, string package, string type, string name, string signature, string ext,
string output
) {
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
ext = "" and
output = e.getMaDOutput()
}
/**
@@ -229,11 +355,12 @@ class ApplicationModeMetadataExtractor extends string {
predicate hasMetadata(
Endpoint e, string package, string type, string subtypes, string name, string signature,
string input, string isVarargsArray
string input, string output, string isVarargsArray
) {
exists(Callable callable |
e.getCall().getCallee() = callable and
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex()) and
e.getCallable() = callable and
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
package = callable.getDeclaringType().getPackage().getName() and
// we're using the erased types because the MaD convention is to not specify type parameters.
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
@@ -266,8 +393,8 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
override predicate appliesToEndpoint(Endpoint e) {
not ApplicationCandidatesImpl::isSink(e, _, _) and
ApplicationModeGetCallable::getCallable(e).getName().matches("is%") and
ApplicationModeGetCallable::getCallable(e).getReturnType() instanceof BooleanType
e.getCallable().getName().matches("is%") and
e.getCallable().getReturnType() instanceof BooleanType
}
}
@@ -313,9 +440,13 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
IsMaDTaintStepCharacteristic() { this = "taint step" }
override predicate appliesToEndpoint(Endpoint e) {
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _) or
e.getExtensibleType() = "sinkModel" and
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _)
or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _)
or
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _)
or
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
}
}
@@ -326,8 +457,8 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
* The reason is that we would expect data/taint flow into the method implementation to uncover
* any sinks that are present there.
*/
private class ArgumentToLocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
ArgumentToLocalCall() { this = "argument to local call" }
private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
LocalCall() { this = "local call" }
override predicate appliesToEndpoint(Endpoint e) {
ApplicationModeGetCallable::getCallable(e).fromSource()
@@ -354,6 +485,7 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
NonPublicMethodCharacteristic() { this = "non-public method" }
override predicate appliesToEndpoint(Endpoint e) {
e.getExtensibleType() = "sinkModel" and
not ApplicationModeGetCallable::getCallable(e).isPublic()
}
}
@@ -376,6 +508,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
}
override predicate appliesToEndpoint(Endpoint e) {
e.getExtensibleType() = "sinkModel" and
not ApplicationCandidatesImpl::isSink(e, _, _) and
exists(Endpoint otherSink |
ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and
@@ -393,7 +526,10 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
FunctionValueCharacteristic() { this = "function value" }
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
override predicate appliesToEndpoint(Endpoint e) {
e.getExtensibleType() = "sinkModel" and
e.asNode().asExpr() instanceof FunctionalExpr
}
}
/**
@@ -407,7 +543,10 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
{
CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
override predicate appliesToEndpoint(Endpoint e) {
e.getExtensibleType() = "sinkModel" and
not this.isKnownOutNodeForStep(e)
}
/**
* Holds if the node `n` is known as the predecessor in a modeled flow step.

View File

@@ -25,18 +25,20 @@ private import AutomodelJavaUtil
bindingset[limit]
private Endpoint getSampleForSignature(
int limit, string package, string type, string subtypes, string name, string signature,
string input, string isVarargs
string input, string output, string isVarargs, string extensibleType
) {
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
num_endpoints =
count(Endpoint e |
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
e.getExtensibleType() = extensibleType and
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
)
|
result =
rank[n](Endpoint e, Location loc |
loc = e.asTop().getLocation() and
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
e.getExtensibleType() = extensibleType and
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
|
e
order by
@@ -55,37 +57,39 @@ private Endpoint getSampleForSignature(
from
Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package,
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
DollarAtString input, DollarAtString isVarargsArray, DollarAtString alreadyAiModeled
DollarAtString input, DollarAtString output, DollarAtString isVarargsArray,
DollarAtString alreadyAiModeled, DollarAtString extensibleType
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
) and
endpoint =
getSampleForSignature(9, package, type, subtypes, name, signature, input, isVarargsArray) and
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
isVarargsArray, extensibleType) and
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
// types, and we don't need to reexamine it.
(
not CharacteristicsImpl::isSink(endpoint, _, _) and alreadyAiModeled = ""
not CharacteristicsImpl::isModeled(endpoint, _, _, _) and alreadyAiModeled = ""
or
alreadyAiModeled.matches("%ai-%") and
CharacteristicsImpl::isSink(endpoint, _, alreadyAiModeled)
CharacteristicsImpl::isModeled(endpoint, _, _, alreadyAiModeled)
) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
includeAutomodelCandidate(package, type, name, signature) and
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
// a non-sink, and we surface only endpoints that have at least one such sink type.
message =
strictconcat(AutomodelEndpointTypes::SinkType sinkType |
not CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
not CharacteristicsImpl::isKnownAs(endpoint, sinkType, _) and
CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
|
sinkType, ", "
)
select endpoint.asNode(),
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
@@ -93,5 +97,7 @@ select endpoint.asNode(),
name, "name", // method name
signature, "signature", //
input, "input", //
output, "output", //
isVarargsArray, "isVarargsArray", //
alreadyAiModeled, "alreadyAiModeled"
alreadyAiModeled, "alreadyAiModeled", //
extensibleType, "extensibleType"

View File

@@ -44,7 +44,7 @@ from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
DollarAtString isVarargsArray
DollarAtString output, DollarAtString isVarargsArray
where
endpoint = getSampleForCharacteristic(characteristic, 100) and
confidence >= SharedCharacteristics::highConfidence() and
@@ -52,7 +52,7 @@ where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
not erroneousEndpoints(endpoint, _, _, _, _, false) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
// treated by the actual query as a sanitizer, since the final logic is something like
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
@@ -65,7 +65,7 @@ where
) and
message = characteristic
select endpoint.asNode(),
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
@@ -73,4 +73,5 @@ select endpoint.asNode(),
name, "name", //
signature, "signature", //
input, "input", //
output, "output", //
isVarargsArray, "isVarargsArray" //

View File

@@ -13,19 +13,20 @@ private import AutomodelEndpointTypes
private import AutomodelJavaUtil
from
Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta,
Endpoint endpoint, EndpointType endpointType, ApplicationModeMetadataExtractor meta,
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
DollarAtString signature, DollarAtString input, DollarAtString isVarargsArray
DollarAtString signature, DollarAtString input, DollarAtString output,
DollarAtString isVarargsArray
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
not erroneousEndpoints(endpoint, _, _, _, _, false) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
// Extract positive examples of sinks belonging to the existing ATM query configurations.
CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
select endpoint.asNode(),
sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
endpointType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
@@ -33,4 +34,5 @@ select endpoint.asNode(),
name, "name", //
signature, "signature", //
input, "input", //
output, "output", //
isVarargsArray, "isVarargsArray"

View File

@@ -28,12 +28,6 @@ abstract class SinkType extends EndpointType {
SinkType() { any() }
}
/** A class for source types that can be predicted by a classifier. */
abstract class SourceType extends EndpointType {
bindingset[this]
SourceType() { any() }
}
/** The `Negative` class for non-sinks. */
class NegativeSinkType extends SinkType {
NegativeSinkType() { this = "non-sink" }
@@ -58,3 +52,14 @@ class RequestForgerySinkType extends SinkType {
class CommandInjectionSinkType extends SinkType {
CommandInjectionSinkType() { this = "command-injection" }
}
/** A class for source types that can be predicted by a classifier. */
abstract class SourceType extends EndpointType {
bindingset[this]
SourceType() { any() }
}
/** A source of remote data. */
class RemoteSourceType extends SourceType {
RemoteSourceType() { this = "remote" }
}

View File

@@ -117,6 +117,10 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
)
}
predicate isSource(Endpoint e, string kind, string provenance) {
none() // TODO: implement
}
predicate isNeutral(Endpoint e) {
exists(string package, string type, string name, string signature |
sinkSpec(e, package, type, name, signature, _, _) and

View File

@@ -40,7 +40,7 @@ where
// a non-sink, and we surface only endpoints that have at least one such sink type.
message =
strictconcat(AutomodelEndpointTypes::SinkType sinkType |
not CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
not CharacteristicsImpl::isKnownAs(endpoint, sinkType, _) and
CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
|
sinkType, ", "

View File

@@ -22,7 +22,7 @@ where
not erroneousEndpoints(endpoint, _, _, _, _, false) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, parameterName) and
// Extract positive examples of sinks belonging to the existing ATM query configurations.
CharacteristicsImpl::isKnownSink(endpoint, sinkType, _)
CharacteristicsImpl::isKnownAs(endpoint, sinkType, _)
select endpoint,
sinkType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //

View File

@@ -39,6 +39,9 @@ predicate isKnownKind(string kind, AutomodelEndpointTypes::EndpointType type) {
or
kind = "command-injection" and
type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
or
kind = "remote" and
type instanceof AutomodelEndpointTypes::RemoteSourceType
}
/** Gets the models-as-data description for the method argument with the index `index`. */

View File

@@ -62,6 +62,11 @@ signature module CandidateSig {
*/
predicate isSink(Endpoint e, string kind, string provenance);
/**
* Holds if `e` is a sink with the label `kind`, and provenance `provenance`.
*/
predicate isSource(Endpoint e, string kind, string provenance);
/**
* Holds if `e` is not a sink of any kind.
*/
@@ -91,17 +96,23 @@ module SharedCharacteristics<CandidateSig Candidate> {
predicate isNeutral = Candidate::isNeutral/1;
predicate isModeled(Candidate::Endpoint e, string kind, string extensibleKind, string provenance) {
Candidate::isSink(e, kind, provenance) and extensibleKind = "sinkModel"
or
Candidate::isSource(e, kind, provenance) and extensibleKind = "sourceModel"
}
/**
* Holds if `sink` is a known sink of type `endpointType`.
* Holds if `endpoint` is modeled as `endpointType` (endpoint type must not be negative).
*/
predicate isKnownSink(
Candidate::Endpoint sink, Candidate::EndpointType endpointType,
predicate isKnownAs(
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
EndpointCharacteristic characteristic
) {
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
// known sink for the class.
not endpointType instanceof Candidate::NegativeEndpointType and
characteristic.appliesToEndpoint(sink) and
characteristic.appliesToEndpoint(endpoint) and
characteristic.hasImplications(endpointType, true, maximalConfidence())
}
@@ -209,6 +220,25 @@ module SharedCharacteristics<CandidateSig Candidate> {
}
}
/**
* A high-confidence characteristic that indicates that an endpoint is a source of a specified type. These endpoints can
* be used as positive samples for training or for a few-shot prompt.
*/
abstract class SourceCharacteristic extends EndpointCharacteristic {
bindingset[this]
SourceCharacteristic() { any() }
abstract Candidate::EndpointType getSourceType();
final override predicate hasImplications(
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
) {
endpointType = this.getSourceType() and
isPositiveIndicator = true and
confidence = maximalConfidence()
}
}
/**
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
* used as negative samples for training or for a few-shot prompt.
@@ -292,6 +322,25 @@ module SharedCharacteristics<CandidateSig Candidate> {
override Candidate::EndpointType getSinkType() { result = endpointType }
}
private class KnownSourceCharacteristic extends SourceCharacteristic {
string madKind;
Candidate::EndpointType endpointType;
string provenance;
KnownSourceCharacteristic() {
Candidate::isKnownKind(madKind, endpointType) and
// bind "this" to a unique string differing from that of the SinkType classes
this = madKind + "_" + provenance + "_characteristic" and
Candidate::isSource(_, madKind, provenance)
}
override predicate appliesToEndpoint(Candidate::Endpoint e) {
Candidate::isSource(e, madKind, provenance)
}
override Candidate::EndpointType getSourceType() { result = endpointType }
}
/**
* A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
*/

View File

@@ -1,4 +1,13 @@
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
| Test.java:34:4:34:11 | openPath | command-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled |
| Test.java:53:4:53:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
| PluginImpl.java:5:27:5:37 | name | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:27:5:37 | name | CallContext | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| PluginImpl.java:5:40:5:51 | value | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:40:5:51 | value | CallContext | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[1]:1:1:1:1 | Parameter[1] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:18:3:18:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:18:3:18:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
| Test.java:23:3:23:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:23:3:23:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
| Test.java:23:3:23:16 | get(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:23:3:23:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:27:3:31:3 | copy(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:35:10:37:3 | newInputStream(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:36:4:36:11 | openPath | command-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
| Test.java:42:4:42:22 | get(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:42:4:42:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:53:3:58:3 | walk(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
| Test.java:55:4:55:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
| Test.java:62:3:62:3 | c | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:62:3:62:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
| Test.java:67:30:67:47 | writer | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:67:30:67:47 | writer | CallContext | file://java.lang:1:1:1:1 | java.lang | package | file://Throwable:1:1:1:1 | Throwable | type | file://true:1:1:1:1 | true | subtypes | file://printStackTrace:1:1:1:1 | printStackTrace | name | file://(PrintWriter):1:1:1:1 | (PrintWriter) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |

View File

@@ -1,2 +1,4 @@
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:52:4:52:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:47:10:49:3 | compareTo(...) | known sanitizer\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:47:10:49:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:48:4:48:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:47:10:49:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:54:4:54:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:66:7:66:18 | this <constr(this)> | exception\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:66:7:66:18 | super(...) | CallContext | file://java.lang:1:1:1:1 | java.lang | package | file://Exception:1:1:1:1 | Exception | type | file://true:1:1:1:1 | true | subtypes | file://Exception:1:1:1:1 | Exception | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |

View File

@@ -1,3 +1,4 @@
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:28:4:28:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:29:4:29:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:36:4:36:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:62:3:62:20 | getInputStream(...) | remote\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:62:3:62:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray |

View File

@@ -0,0 +1,8 @@
import hudson.Plugin;
public class PluginImpl extends Plugin {
@Override
public void configure(String name, String value) {
// ...
}
}

View File

@@ -1,6 +1,7 @@
package com.github.codeql.test;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -9,6 +10,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.io.File;
import java.nio.file.FileVisitOption;
import java.net.URLConnection;
class Test {
public static void main(String[] args) throws Exception {
@@ -18,11 +20,11 @@ class Test {
}
public static void callSupplier(Supplier<String> supplier) {
supplier.get(); // Argument[this] is a candidate
supplier.get(); // Argument[this] is a sink candidate; the call is a source candidate
}
public static void copyFiles(Path source, Path target, CopyOption option) throws Exception {
Files.copy(
Files.copy( // the call is a source candidate
source, // positive example (known sink)
target, // positive example (known sink)
option // no candidate (not modeled, but source and target are modeled)
@@ -30,29 +32,39 @@ class Test {
}
public static InputStream getInputStream(Path openPath) throws Exception {
return Files.newInputStream(
return Files.newInputStream( // the call is a source candidate
openPath // positive example (known sink), candidate ("only" ai-modeled, and useful as a candidate in regression testing)
);
}
public static InputStream getInputStream(String openPath) throws Exception {
return Test.getInputStream(
Paths.get(openPath) // no candidate (argument to local call)
return Test.getInputStream( // the call is not a source candidate (argument to local call)
Paths.get(openPath) // no sink candidate (argument to local call); the call is a source candidate
);
}
public static int compareFiles(File f1, File f2) {
return f1.compareTo(
return f1.compareTo( // compareTo call is a known sanitizer
f2 // negative example (modeled as not a sink)
);
}
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
Files.walk(
Files.walk( // the call is a source candidate
p, // negative example (modeled as a taint step)
o, // the implicit varargs array is a candidate
o // not a candidate (only the first arg corresponding to a varargs array
// is extracted)
);
}
public static void WebSocketExample(URLConnection c) throws Exception {
c.getInputStream(); // the call is a source example, c is a sink candidate
}
}
class OverrideTest extends Exception {
public void printStackTrace(PrintWriter writer) { // writer is a source candidate because it overrides an existing method
return;
}
}

View File

@@ -0,0 +1,5 @@
package hudson;
public class Plugin {
public void configure(String name, String value) {}
}

View File

@@ -4396,6 +4396,117 @@ private module StdlibPrivate {
preservesValue = true
}
}
// ---------------------------------------------------------------------------
// asyncio
// ---------------------------------------------------------------------------
/** Provides models for the `asyncio` module. */
module AsyncIO {
/**
* A call to the `asyncio.create_subprocess_exec` function (also accessible via the `subprocess` module of `asyncio`)
*
* See https://docs.python.org/3/library/asyncio-subprocess.html#creating-subprocesses
*/
private class CreateSubprocessExec extends SystemCommandExecution::Range,
FileSystemAccess::Range, API::CallNode
{
CreateSubprocessExec() {
this = API::moduleImport("asyncio").getMember("create_subprocess_exec").getACall()
or
this =
API::moduleImport("asyncio")
.getMember("subprocess")
.getMember("create_subprocess_exec")
.getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "program").asSink() }
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
override predicate isShellInterpreted(DataFlow::Node arg) {
none() // this is a safe API.
}
}
/**
* A call to the `asyncio.create_subprocess_shell` function (also accessible via the `subprocess` module of `asyncio`)
*
* See https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_shell
*/
private class CreateSubprocessShell extends SystemCommandExecution::Range,
FileSystemAccess::Range, API::CallNode
{
CreateSubprocessShell() {
this = API::moduleImport("asyncio").getMember("create_subprocess_shell").getACall()
or
this =
API::moduleImport("asyncio")
.getMember("subprocess")
.getMember("create_subprocess_shell")
.getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "cmd").asSink() }
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
}
/**
* Get an asyncio event loop (an object with basetype `AbstractEventLoop`).
*
* See https://docs.python.org/3/library/asyncio-eventloop.html
*/
private API::Node getAsyncioEventLoop() {
result = API::moduleImport("asyncio").getMember("get_running_loop").getReturn()
or
result = API::moduleImport("asyncio").getMember("get_event_loop").getReturn() // deprecated in Python 3.10.0 and later
or
result = API::moduleImport("asyncio").getMember("new_event_loop").getReturn()
}
/**
* A call to `subprocess_exec` on an event loop instance.
*
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec
*/
private class EventLoopSubprocessExec extends API::CallNode, SystemCommandExecution::Range,
FileSystemAccess::Range
{
EventLoopSubprocessExec() {
this = getAsyncioEventLoop().getMember("subprocess_exec").getACall()
}
override DataFlow::Node getCommand() { result = this.getArg(1) }
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
override predicate isShellInterpreted(DataFlow::Node arg) {
none() // this is a safe API.
}
}
/**
* A call to `subprocess_shell` on an event loop instance.
*
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell
*/
private class EventLoopSubprocessShell extends API::CallNode, SystemCommandExecution::Range,
FileSystemAccess::Range
{
EventLoopSubprocessShell() {
this = getAsyncioEventLoop().getMember("subprocess_shell").getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(1, "cmd").asSink() }
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
}
}
}
// ---------------------------------------------------------------------------

View File

@@ -2,4 +2,33 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.qhelp" /></qhelp>
<overview>
<p>If sensitive data is written to a log entry it could be exposed to an attacker
who gains access to the logs.</p>
<p>Potential attackers can obtain sensitive user data when the log output is displayed. Additionally that data may
expose system information such as full path names, system information, and sometimes usernames and passwords.</p>
</overview>
<recommendation>
<p>
Sensitive data should not be logged.
</p>
</recommendation>
<example>
<p>In the example the entire process environment is logged using `print`. Regular users of the production deployed application
should not have access to this much information about the environment configuration.
</p>
<sample src="examples/CleartextLogging.py" />
<p> In the second example the data that is logged is not sensitive.</p>
<sample src="examples/CleartextLoggingGood.py" />
</example>
<references>
<li>OWASP: <a href="https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/">Insertion of Sensitive Information into Log File</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,3 @@
# BAD: Logging cleartext sensitive data
import os
print(f"[INFO] Environment: {os.environ}")

View File

@@ -0,0 +1,3 @@
not_sensitive_data = {'a': 1, 'b': 2}
# GOOD: it is fine to log data that is not sensitive
print(f"[INFO] Some object contains: {not_sensitive_data}")

View File

@@ -8,8 +8,8 @@ def full_ssrf():
target = request.args["target"]
# BAD: user has full control of URL
resp = request.get("https://" + target + ".example.com/data/")
resp = requests.get("https://" + target + ".example.com/data/")
# GOOD: `subdomain` is controlled by the server.
subdomain = "europe" if target == "EU" else "world"
resp = request.get("https://" + subdomain + ".example.com/data/")
resp = requests.get("https://" + subdomain + ".example.com/data/")

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Extended the `py/command-line-injection` query with sinks from Python's `asyncio` module.

View File

@@ -158,3 +158,21 @@ safe_cmd = "ls {}".format(shlex.quote(tainted))
wrong_use = shlex.quote("ls {}".format(tainted))
# still dangerous, for example
cmd = "sh -c " + wrong_use
########################################
# Program/shell command execution via asyncio
import asyncio
from asyncio import subprocess
asyncio.run(asyncio.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
asyncio.run(subprocess.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
loop = asyncio.new_event_loop()
loop.run_until_complete(loop.subprocess_exec(asyncio.SubprocessProtocol, "executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
asyncio.run(asyncio.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
asyncio.run(subprocess.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
loop = asyncio.get_running_loop()
loop.run_until_complete(loop.subprocess_shell(asyncio.SubprocessProtocol, "shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"

BIN
ql/Cargo.lock generated

Binary file not shown.

View File

@@ -7,6 +7,6 @@ edition = "2018"
[dependencies]
lazy_static = "1.4.0"
chrono = "0.4.29"
chrono = "0.4.30"
rayon = "1.7.0"
regex = "1.9.5"

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added flow steps through `Dictionary` keys and values.

View File

@@ -9,6 +9,7 @@ private import codeql.swift.dataflow.FlowSummary as FlowSummary
private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
private import codeql.swift.frameworks.StandardLibrary.Array
private import codeql.swift.frameworks.StandardLibrary.Dictionary
/** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
@@ -139,6 +140,9 @@ private module Cached {
any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr(),
any(SubscriptExpr se).getBase()
])
} or
TDictionarySubscriptNode(SubscriptExpr e) {
e.getBase().getType().getCanonicalType() instanceof CanonicalDictionaryType
}
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
@@ -332,6 +336,28 @@ import Cached
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
/**
* The intermediate node for a dictionary subscript operation `dict[key]`. In a write, this is used
* as the destination of the `storeStep`s that add `TupleContent`s and the source of the storeStep
* that adds `CollectionContent`. In a read, this is the destination of the `readStep` that pops
* `CollectionContent` and the source of the `readStep` that pops `TupleContent[0]`
*/
private class DictionarySubscriptNode extends NodeImpl, TDictionarySubscriptNode {
SubscriptExpr expr;
DictionarySubscriptNode() { this = TDictionarySubscriptNode(expr) }
override DataFlowCallable getEnclosingCallable() {
result.asSourceCallable() = expr.getEnclosingCallable()
}
override string toStringImpl() { result = "DictionarySubscriptNode" }
override Location getLocationImpl() { result = expr.getLocation() }
SubscriptExpr getExpr() { result = expr }
}
private module ParameterNodes {
abstract class ParameterNodeImpl extends NodeImpl {
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
@@ -835,6 +861,32 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c instanceof OptionalSomeContentSet
)
or
// assignment to a dictionary value via subscript operator, with intermediate step
// `dict[key] = value`
exists(AssignExpr assign, SubscriptExpr subscript |
subscript = assign.getDest() and
(
subscript.getArgument(0).getExpr() = node1.asExpr() and
node2.(DictionarySubscriptNode).getExpr() = subscript and
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 0))
or
assign.getSource() = node1.asExpr() and
node2.(DictionarySubscriptNode).getExpr() = subscript and
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
or
node1.(DictionarySubscriptNode).getExpr() = subscript and
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = subscript.getBase() and
c.isSingleton(any(Content::CollectionContent cc))
)
)
or
// creation of a dictionary `[key: value, ...]`
exists(DictionaryExpr dict |
node1.asExpr() = dict.getAnElement() and
node2.asExpr() = dict and
c.isSingleton(any(Content::CollectionContent cc))
)
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}
@@ -926,6 +978,17 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
)
)
or
// read of a dictionary value via subscript operator
exists(SubscriptExpr subscript |
subscript.getBase() = node1.asExpr() and
node2.(DictionarySubscriptNode).getExpr() = subscript and
c.isSingleton(any(Content::CollectionContent cc))
or
subscript = node2.asExpr() and
node1.(DictionarySubscriptNode).getExpr() = subscript and
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
)
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}
@@ -936,7 +999,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
* in `x.f = newValue`.
*/
predicate clearsContent(Node n, ContentSet c) {
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() and
(
c.isSingleton(any(Content::FieldContent fc)) or
c.isSingleton(any(Content::TupleContent tc)) or
c.isSingleton(any(Content::EnumContent ec))
)
}
/**

View File

@@ -9,7 +9,7 @@ private import codeql.swift.dataflow.ExternalFlow
* An instance of the `Array` type.
*/
class ArrayType extends Type {
ArrayType() { this.getName().matches("Array<%") or this.getName().matches("[%]") }
ArrayType() { this.getCanonicalType().getName().matches("Array<%") }
}
/**

View File

@@ -0,0 +1,27 @@
/**
* Provides models for the Swift `Dictionary` class.
*/
import swift
private import codeql.swift.dataflow.ExternalFlow
/**
* An instance of the `Dictionary` type.
*/
class CanonicalDictionaryType extends BoundGenericType {
CanonicalDictionaryType() { this.getName().matches("Dictionary<%") }
}
/**
* A model for `Dictionary` and related class members that permit data flow.
*/
private class DictionarySummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";Dictionary;true;updateValue(_:forKey:);;;Argument[0];Argument[-1].CollectionElement.TupleElement[1];value",
";Dictionary;true;updateValue(_:forKey:);;;Argument[1];Argument[-1].CollectionElement.TupleElement[0];value",
";Dictionary;true;updateValue(_:forKey:);;;Argument[-1].CollectionElement.TupleElement[1];ReturnValue.OptionalSome;value"
]
}
}

View File

@@ -442,17 +442,60 @@ edges
| test.swift:766:29:766:29 | KeyPathComponent [x] | test.swift:766:13:766:29 | exit #keyPath(...) |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:767:15:767:28 | \\...[...] |
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:17:773:17 | [post] KeyPathComponent [x] |
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] |
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] |
| test.swift:774:3:774:3 | [post] s2 [s, x] | test.swift:775:13:775:13 | s2 [s, x] |
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) |
| test.swift:774:3:774:16 | \\...[...] | test.swift:774:3:774:3 | [post] s2 [s, x] |
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:775:13:775:16 | .s [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:775:13:775:18 | .x |
| test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] | test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] |
| test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] |
| test.swift:774:16:774:23 | call to source() | test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] | test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:776:15:776:22 | ...[...] |
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] |
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] |
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] |
| test.swift:786:18:786:28 | (...) [Tuple element at index 1] | test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] |
| test.swift:786:21:786:28 | call to source() | test.swift:786:18:786:28 | (...) [Tuple element at index 1] |
| test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] | test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:787:15:787:22 | ...[...] |
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] | test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] |
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] | test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] |
| test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] | test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] | test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] |
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] |
| test.swift:789:11:789:18 | call to source() | test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] |
| test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] | test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] |
| test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] | test.swift:791:15:791:36 | ...! [Tuple element at index 0] |
| test.swift:791:15:791:36 | ...! [Tuple element at index 0] | test.swift:791:15:791:38 | .0 |
| test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] | test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] |
| test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] | test.swift:792:15:792:36 | ...! [Tuple element at index 1] |
| test.swift:792:15:792:36 | ...! [Tuple element at index 1] | test.swift:792:15:792:38 | .1 |
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] |
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] |
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] |
| test.swift:799:18:799:27 | (...) [Tuple element at index 1] | test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] |
| test.swift:799:20:799:27 | call to source() | test.swift:799:18:799:27 | (...) [Tuple element at index 1] |
| test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] | test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] |
| test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] |
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] | test.swift:800:15:800:53 | ...! |
| test.swift:800:44:800:51 | call to source() | test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] |
| test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] | test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] |
| test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] |
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] | test.swift:801:15:801:53 | ...! |
| test.swift:801:33:801:40 | call to source() | test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] |
| test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] | test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] |
| test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] | test.swift:802:15:802:36 | ...! [Tuple element at index 0] |
| test.swift:802:15:802:36 | ...! [Tuple element at index 0] | test.swift:802:15:802:38 | .0 |
| test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] |
| test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] | test.swift:803:15:803:36 | ...! [Tuple element at index 1] |
| test.swift:803:15:803:36 | ...! [Tuple element at index 1] | test.swift:803:15:803:38 | .1 |
| test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:17:811:17 | [post] KeyPathComponent [x] |
| test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] |
| test.swift:811:17:811:17 | [post] KeyPathComponent [x] | test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] |
| test.swift:812:3:812:3 | [post] s2 [s, x] | test.swift:813:13:813:13 | s2 [s, x] |
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) |
| test.swift:812:3:812:16 | \\...[...] | test.swift:812:3:812:3 | [post] s2 [s, x] |
| test.swift:812:20:812:27 | call to source() | test.swift:812:3:812:16 | \\...[...] |
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:813:13:813:16 | .s [x] |
| test.swift:813:13:813:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
| test.swift:813:13:813:16 | .s [x] | test.swift:813:13:813:18 | .x |
nodes
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
| file://:0:0:0:0 | .s [x] | semmle.label | .s [x] |
@@ -936,16 +979,63 @@ nodes
| test.swift:766:29:766:29 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | semmle.label | s2 [s, some:0, x] |
| test.swift:767:15:767:28 | \\...[...] | semmle.label | \\...[...] |
| test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
| test.swift:774:3:774:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
| test.swift:774:3:774:16 | \\...[...] | semmle.label | \\...[...] |
| test.swift:774:20:774:27 | call to source() | semmle.label | call to source() |
| test.swift:775:13:775:13 | s2 [s, x] | semmle.label | s2 [s, x] |
| test.swift:775:13:775:16 | .s [x] | semmle.label | .s [x] |
| test.swift:775:13:775:18 | .x | semmle.label | .x |
| test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] | semmle.label | [post] dict1 [Collection element, Tuple element at index 1] |
| test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:774:16:774:23 | call to source() | semmle.label | call to source() |
| test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] | semmle.label | dict1 [Collection element, Tuple element at index 1] |
| test.swift:776:15:776:22 | ...[...] | semmle.label | ...[...] |
| test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | semmle.label | [...] [Collection element, Tuple element at index 1] |
| test.swift:786:18:786:28 | (...) [Tuple element at index 1] | semmle.label | (...) [Tuple element at index 1] |
| test.swift:786:21:786:28 | call to source() | semmle.label | call to source() |
| test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
| test.swift:787:15:787:22 | ...[...] | semmle.label | ...[...] |
| test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] | semmle.label | [post] dict3 [Collection element, Tuple element at index 0] |
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] | semmle.label | [post] dict3 [Collection element, Tuple element at index 1] |
| test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] | semmle.label | DictionarySubscriptNode [Tuple element at index 0] |
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
| test.swift:789:11:789:18 | call to source() | semmle.label | call to source() |
| test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] | semmle.label | dict3 [Collection element, Tuple element at index 0] |
| test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] | semmle.label | call to randomElement() [some:0, Tuple element at index 0] |
| test.swift:791:15:791:36 | ...! [Tuple element at index 0] | semmle.label | ...! [Tuple element at index 0] |
| test.swift:791:15:791:38 | .0 | semmle.label | .0 |
| test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
| test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] | semmle.label | call to randomElement() [some:0, Tuple element at index 1] |
| test.swift:792:15:792:36 | ...! [Tuple element at index 1] | semmle.label | ...! [Tuple element at index 1] |
| test.swift:792:15:792:38 | .1 | semmle.label | .1 |
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | semmle.label | [...] [Collection element, Tuple element at index 1] |
| test.swift:799:18:799:27 | (...) [Tuple element at index 1] | semmle.label | (...) [Tuple element at index 1] |
| test.swift:799:20:799:27 | call to source() | semmle.label | call to source() |
| test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] | semmle.label | [post] dict4 [Collection element, Tuple element at index 0] |
| test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] | semmle.label | call to updateValue(_:forKey:) [some:0] |
| test.swift:800:15:800:53 | ...! | semmle.label | ...! |
| test.swift:800:44:800:51 | call to source() | semmle.label | call to source() |
| test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] | semmle.label | [post] dict4 [Collection element, Tuple element at index 1] |
| test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] | semmle.label | call to updateValue(_:forKey:) [some:0] |
| test.swift:801:15:801:53 | ...! | semmle.label | ...! |
| test.swift:801:33:801:40 | call to source() | semmle.label | call to source() |
| test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] | semmle.label | dict4 [Collection element, Tuple element at index 0] |
| test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] | semmle.label | call to randomElement() [some:0, Tuple element at index 0] |
| test.swift:802:15:802:36 | ...! [Tuple element at index 0] | semmle.label | ...! [Tuple element at index 0] |
| test.swift:802:15:802:38 | .0 | semmle.label | .0 |
| test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
| test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] | semmle.label | call to randomElement() [some:0, Tuple element at index 1] |
| test.swift:803:15:803:36 | ...! [Tuple element at index 1] | semmle.label | ...! [Tuple element at index 1] |
| test.swift:803:15:803:38 | .1 | semmle.label | .1 |
| test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
| test.swift:811:11:811:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
| test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
| test.swift:811:17:811:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
| test.swift:812:3:812:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
| test.swift:812:3:812:16 | \\...[...] | semmle.label | \\...[...] |
| test.swift:812:20:812:27 | call to source() | semmle.label | call to source() |
| test.swift:813:13:813:13 | s2 [s, x] | semmle.label | s2 [s, x] |
| test.swift:813:13:813:16 | .s [x] | semmle.label | .s [x] |
| test.swift:813:13:813:18 | .x | semmle.label | .x |
subpaths
| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y |
| test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... |
@@ -1000,10 +1090,10 @@ subpaths
| test.swift:764:18:764:25 | call to source() | test.swift:617:8:617:11 | x | test.swift:617:3:619:3 | self[return] [x] | test.swift:764:13:764:26 | call to S.init(x:) [x] |
| test.swift:765:29:765:29 | s [some:0, x] | test.swift:655:8:655:12 | s [some:0, x] | test.swift:655:3:657:3 | self[return] [s, some:0, x] | test.swift:765:14:765:30 | call to S2_Optional.init(s:) [s, some:0, x] |
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] | test.swift:766:13:766:29 | exit #keyPath(...) | test.swift:767:15:767:28 | \\...[...] |
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | test.swift:774:3:774:3 | [post] s2 [s, x] |
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | test.swift:774:3:774:3 | [post] s2 [s, x] |
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:775:13:775:16 | .s [x] |
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:775:13:775:18 | .x |
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] | test.swift:812:3:812:3 | [post] s2 [s, x] |
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | test.swift:812:3:812:3 | [post] s2 [s, x] |
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:813:13:813:16 | .s [x] |
| test.swift:813:13:813:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:813:13:813:18 | .x |
#select
| test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 | result |
| test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:9:15:9:15 | t1 | result |
@@ -1105,4 +1195,13 @@ subpaths
| test.swift:756:15:756:21 | ...! | test.swift:746:14:746:21 | call to source() | test.swift:756:15:756:21 | ...! | result |
| test.swift:757:15:757:19 | .v3 | test.swift:747:14:747:21 | call to source() | test.swift:757:15:757:19 | .v3 | result |
| test.swift:767:15:767:28 | \\...[...] | test.swift:764:18:764:25 | call to source() | test.swift:767:15:767:28 | \\...[...] | result |
| test.swift:775:13:775:18 | .x | test.swift:774:20:774:27 | call to source() | test.swift:775:13:775:18 | .x | result |
| test.swift:776:15:776:22 | ...[...] | test.swift:774:16:774:23 | call to source() | test.swift:776:15:776:22 | ...[...] | result |
| test.swift:787:15:787:22 | ...[...] | test.swift:786:21:786:28 | call to source() | test.swift:787:15:787:22 | ...[...] | result |
| test.swift:791:15:791:38 | .0 | test.swift:789:11:789:18 | call to source() | test.swift:791:15:791:38 | .0 | result |
| test.swift:792:15:792:38 | .1 | test.swift:786:21:786:28 | call to source() | test.swift:792:15:792:38 | .1 | result |
| test.swift:800:15:800:53 | ...! | test.swift:799:20:799:27 | call to source() | test.swift:800:15:800:53 | ...! | result |
| test.swift:801:15:801:53 | ...! | test.swift:799:20:799:27 | call to source() | test.swift:801:15:801:53 | ...! | result |
| test.swift:802:15:802:38 | .0 | test.swift:800:44:800:51 | call to source() | test.swift:802:15:802:38 | .0 | result |
| test.swift:803:15:803:38 | .1 | test.swift:799:20:799:27 | call to source() | test.swift:803:15:803:38 | .1 | result |
| test.swift:803:15:803:38 | .1 | test.swift:801:33:801:40 | call to source() | test.swift:803:15:803:38 | .1 | result |
| test.swift:813:13:813:18 | .x | test.swift:812:20:812:27 | call to source() | test.swift:813:13:813:18 | .x | result |

View File

@@ -937,16 +937,75 @@
| test.swift:766:13:766:29 | #keyPath(...) | test.swift:766:9:766:9 | f |
| test.swift:766:13:766:29 | enter #keyPath(...) | test.swift:766:26:766:26 | KeyPathComponent |
| test.swift:766:26:766:26 | [post] KeyPathComponent | test.swift:766:13:766:29 | [post] enter #keyPath(...) |
| test.swift:771:7:771:7 | SSA def(s2) | test.swift:772:13:772:13 | s2 |
| test.swift:771:7:771:7 | s2 | test.swift:771:7:771:7 | SSA def(s2) |
| test.swift:771:12:771:25 | call to S2.init(s:) | test.swift:771:7:771:7 | s2 |
| test.swift:772:13:772:13 | [post] s2 | test.swift:774:3:774:3 | s2 |
| test.swift:772:13:772:13 | s2 | test.swift:774:3:774:3 | s2 |
| test.swift:773:7:773:7 | SSA def(f) | test.swift:774:15:774:15 | f |
| test.swift:773:7:773:7 | f | test.swift:773:7:773:7 | SSA def(f) |
| test.swift:773:11:773:17 | #keyPath(...) | test.swift:773:7:773:7 | f |
| test.swift:773:11:773:17 | enter #keyPath(...) | test.swift:773:15:773:15 | KeyPathComponent |
| test.swift:773:15:773:15 | [post] KeyPathComponent | test.swift:773:11:773:17 | [post] enter #keyPath(...) |
| test.swift:774:3:774:3 | [post] s2 | test.swift:775:13:775:13 | s2 |
| test.swift:774:3:774:3 | s2 | test.swift:775:13:775:13 | s2 |
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |
| test.swift:771:9:771:9 | SSA def(dict1) | test.swift:772:15:772:15 | dict1 |
| test.swift:771:9:771:9 | dict1 | test.swift:771:9:771:9 | SSA def(dict1) |
| test.swift:771:17:771:31 | [...] | test.swift:771:9:771:9 | dict1 |
| test.swift:772:15:772:15 | &... | test.swift:774:5:774:5 | dict1 |
| test.swift:772:15:772:15 | [post] dict1 | test.swift:772:15:772:15 | &... |
| test.swift:772:15:772:15 | dict1 | test.swift:772:15:772:15 | &... |
| test.swift:774:5:774:5 | &... | test.swift:776:15:776:15 | dict1 |
| test.swift:774:5:774:5 | [post] dict1 | test.swift:774:5:774:5 | &... |
| test.swift:774:5:774:5 | dict1 | test.swift:774:5:774:5 | &... |
| test.swift:776:15:776:15 | [post] dict1 | test.swift:776:15:776:15 | &... |
| test.swift:776:15:776:15 | dict1 | test.swift:776:15:776:15 | &... |
| test.swift:778:9:778:9 | SSA def(dict2) | test.swift:779:15:779:15 | dict2 |
| test.swift:778:9:778:9 | dict2 | test.swift:778:9:778:9 | SSA def(dict2) |
| test.swift:778:17:778:29 | [...] | test.swift:778:9:778:9 | dict2 |
| test.swift:779:15:779:15 | &... | test.swift:781:25:781:25 | dict2 |
| test.swift:779:15:779:15 | [post] dict2 | test.swift:779:15:779:15 | &... |
| test.swift:779:15:779:15 | dict2 | test.swift:779:15:779:15 | &... |
| test.swift:781:10:781:10 | SSA def(key) | test.swift:782:19:782:19 | key |
| test.swift:781:10:781:10 | key | test.swift:781:10:781:10 | SSA def(key) |
| test.swift:781:15:781:15 | SSA def(value) | test.swift:783:19:783:19 | value |
| test.swift:781:15:781:15 | value | test.swift:781:15:781:15 | SSA def(value) |
| test.swift:786:9:786:9 | SSA def(dict3) | test.swift:787:15:787:15 | dict3 |
| test.swift:786:9:786:9 | dict3 | test.swift:786:9:786:9 | SSA def(dict3) |
| test.swift:786:17:786:29 | [...] | test.swift:786:9:786:9 | dict3 |
| test.swift:787:15:787:15 | &... | test.swift:789:5:789:5 | dict3 |
| test.swift:787:15:787:15 | [post] dict3 | test.swift:787:15:787:15 | &... |
| test.swift:787:15:787:15 | dict3 | test.swift:787:15:787:15 | &... |
| test.swift:789:5:789:5 | &... | test.swift:791:15:791:15 | dict3 |
| test.swift:789:5:789:5 | [post] dict3 | test.swift:789:5:789:5 | &... |
| test.swift:789:5:789:5 | dict3 | test.swift:789:5:789:5 | &... |
| test.swift:791:15:791:15 | [post] dict3 | test.swift:792:15:792:15 | dict3 |
| test.swift:791:15:791:15 | dict3 | test.swift:792:15:792:15 | dict3 |
| test.swift:791:15:791:35 | call to randomElement() | test.swift:791:15:791:36 | ...! |
| test.swift:792:15:792:15 | [post] dict3 | test.swift:794:25:794:25 | dict3 |
| test.swift:792:15:792:15 | dict3 | test.swift:794:25:794:25 | dict3 |
| test.swift:792:15:792:35 | call to randomElement() | test.swift:792:15:792:36 | ...! |
| test.swift:794:10:794:10 | SSA def(key) | test.swift:795:19:795:19 | key |
| test.swift:794:10:794:10 | key | test.swift:794:10:794:10 | SSA def(key) |
| test.swift:794:15:794:15 | SSA def(value) | test.swift:796:19:796:19 | value |
| test.swift:794:15:794:15 | value | test.swift:794:15:794:15 | SSA def(value) |
| test.swift:799:9:799:9 | SSA def(dict4) | test.swift:800:15:800:15 | dict4 |
| test.swift:799:9:799:9 | dict4 | test.swift:799:9:799:9 | SSA def(dict4) |
| test.swift:799:17:799:28 | [...] | test.swift:799:9:799:9 | dict4 |
| test.swift:800:15:800:15 | &... | test.swift:801:15:801:15 | dict4 |
| test.swift:800:15:800:15 | [post] dict4 | test.swift:800:15:800:15 | &... |
| test.swift:800:15:800:15 | dict4 | test.swift:800:15:800:15 | &... |
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) | test.swift:800:15:800:53 | ...! |
| test.swift:801:15:801:15 | &... | test.swift:802:15:802:15 | dict4 |
| test.swift:801:15:801:15 | [post] dict4 | test.swift:801:15:801:15 | &... |
| test.swift:801:15:801:15 | dict4 | test.swift:801:15:801:15 | &... |
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) | test.swift:801:15:801:53 | ...! |
| test.swift:802:15:802:15 | [post] dict4 | test.swift:803:15:803:15 | dict4 |
| test.swift:802:15:802:15 | dict4 | test.swift:803:15:803:15 | dict4 |
| test.swift:802:15:802:35 | call to randomElement() | test.swift:802:15:802:36 | ...! |
| test.swift:803:15:803:15 | [post] dict4 | test.swift:804:15:804:15 | dict4 |
| test.swift:803:15:803:15 | dict4 | test.swift:804:15:804:15 | dict4 |
| test.swift:803:15:803:35 | call to randomElement() | test.swift:803:15:803:36 | ...! |
| test.swift:804:15:804:15 | [post] dict4 | test.swift:805:15:805:15 | dict4 |
| test.swift:804:15:804:15 | dict4 | test.swift:805:15:805:15 | dict4 |
| test.swift:809:7:809:7 | SSA def(s2) | test.swift:810:13:810:13 | s2 |
| test.swift:809:7:809:7 | s2 | test.swift:809:7:809:7 | SSA def(s2) |
| test.swift:809:12:809:25 | call to S2.init(s:) | test.swift:809:7:809:7 | s2 |
| test.swift:810:13:810:13 | [post] s2 | test.swift:812:3:812:3 | s2 |
| test.swift:810:13:810:13 | s2 | test.swift:812:3:812:3 | s2 |
| test.swift:811:7:811:7 | SSA def(f) | test.swift:812:15:812:15 | f |
| test.swift:811:7:811:7 | f | test.swift:811:7:811:7 | SSA def(f) |
| test.swift:811:11:811:17 | #keyPath(...) | test.swift:811:7:811:7 | f |
| test.swift:811:11:811:17 | enter #keyPath(...) | test.swift:811:15:811:15 | KeyPathComponent |
| test.swift:811:15:811:15 | [post] KeyPathComponent | test.swift:811:11:811:17 | [post] enter #keyPath(...) |
| test.swift:812:3:812:3 | [post] s2 | test.swift:813:13:813:13 | s2 |
| test.swift:812:3:812:3 | s2 | test.swift:813:13:813:13 | s2 |
| test.swift:812:20:812:27 | call to source() | test.swift:812:3:812:16 | \\...[...] |

View File

@@ -767,10 +767,48 @@ func testOptionalKeyPathForce() {
sink(arg: s2[keyPath: f]) // $ flow=764
}
func testDictionary() {
var dict1 = [1:2, 3:4, 5:6]
sink(arg: dict1[1])
dict1[1] = source()
sink(arg: dict1[1]) // $ flow=774
var dict2 = [source(): 1]
sink(arg: dict2[1])
for (key, value) in dict2 {
sink(arg: key) // $ MISSING: flow=778
sink(arg: value)
}
var dict3 = [1: source()]
sink(arg: dict3[1]) // $ flow=786
dict3[source()] = 2
sink(arg: dict3.randomElement()!.0) // $ flow=789
sink(arg: dict3.randomElement()!.1) // $ flow=786
for (key, value) in dict3 {
sink(arg: key) // $ MISSING: flow=789
sink(arg: value) // $ MISSING: flow=786
}
var dict4 = [1:source()]
sink(arg: dict4.updateValue(1, forKey: source())!) // $ flow=799
sink(arg: dict4.updateValue(source(), forKey: 2)!) // $ SPURIOUS: flow=799
sink(arg: dict4.randomElement()!.0) // $ flow=800
sink(arg: dict4.randomElement()!.1) // $ flow=799 flow=801
sink(arg: dict4.keys.randomElement()) // $ MISSING: flow=800
sink(arg: dict4.values.randomElement()) // $ MISSING: flow=799 flow=801
}
func testNestedKeyPathWrite() {
var s2 = S2(s: S(x: 1))
sink(arg: s2.s.x)
var f = \S2.s.x
s2[keyPath: f] = source()
sink(arg: s2.s.x) // $ flow=774
sink(arg: s2.s.x) // $ flow=812
}

View File

@@ -161,7 +161,7 @@ func taintCollections(array: inout Array<Int>, contiguousArray: inout Contiguous
dictionary[0] = source2()
sink(arg: dictionary)
sink(arg: dictionary[0]!) // $ MISSING: tainted=162
sink(arg: dictionary[0]!) // $ tainted=162
dictionary.withContiguousStorageIfAvailable({
buffer in
sink(arg: buffer)