diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 3f4a29c59c6..1e79068390e 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -15030,25 +15030,28 @@ ir.cpp: # 1999| Value = [Literal] 5 # 1999| ValueCategory = prvalue # 2000| getStmt(3): [ExprStmt] ExprStmt -# 2000| getExpr(): [ConditionalExpr] ... ? ... : ... +# 2000| getExpr(): [AssignExpr] ... = ... # 2000| Type = [IntType] int # 2000| ValueCategory = lvalue -# 2000| getCondition(): [VariableAccess] a -# 2000| Type = [BoolType] bool -# 2000| ValueCategory = prvalue(load) -# 2000| getThen(): [VariableAccess] x +# 2000| getLValue(): [ConditionalExpr] ... ? ... : ... # 2000| Type = [IntType] int # 2000| ValueCategory = lvalue -# 2000| getElse(): [AssignExpr] ... = ... -# 2000| Type = [IntType] int -# 2000| ValueCategory = lvalue -# 2000| getLValue(): [VariableAccess] y +# 2000| getCondition(): [VariableAccess] a +# 2000| Type = [BoolType] bool +# 2000| ValueCategory = prvalue(load) +# 2000| getThen(): [VariableAccess] x # 2000| Type = [IntType] int # 2000| ValueCategory = lvalue -# 2000| getRValue(): [Literal] 7 +# 2000| getElse(): [VariableAccess] y # 2000| Type = [IntType] int -# 2000| Value = [Literal] 7 -# 2000| ValueCategory = prvalue +# 2000| ValueCategory = lvalue +# 2000| getRValue(): [Literal] 7 +# 2000| Type = [IntType] int +# 2000| Value = [Literal] 7 +# 2000| ValueCategory = prvalue +# 2000| getLValue().getFullyConverted(): [ParenthesisExpr] (...) +# 2000| Type = [IntType] int +# 2000| ValueCategory = lvalue # 2001| getStmt(4): [ReturnStmt] return ... # 2003| [CopyAssignmentOperator] TernaryPodObj& TernaryPodObj::operator=(TernaryPodObj const&) # 2003| : @@ -15151,31 +15154,34 @@ ir.cpp: # 2010| getExpr(): [AssignExpr] ... = ... # 2010| Type = [Struct] TernaryPodObj # 2010| ValueCategory = lvalue -# 2010| getLValue(): [VariableAccess] z +# 2010| getLValue(): [AssignExpr] ... = ... # 2010| Type = [Struct] TernaryPodObj # 2010| ValueCategory = lvalue -# 2010| getRValue(): [ConditionalExpr] ... ? ... : ... -# 2010| Type = [Struct] TernaryPodObj -# 2010| ValueCategory = prvalue -# 2010| getCondition(): [VariableAccess] a -# 2010| Type = [BoolType] bool -# 2010| ValueCategory = prvalue(load) -# 2010| getThen(): [VariableAccess] x +# 2010| getLValue(): [VariableAccess] z # 2010| Type = [Struct] TernaryPodObj -# 2010| ValueCategory = prvalue(load) -# 2010| getElse(): [AssignExpr] ... = ... +# 2010| ValueCategory = lvalue +# 2010| getRValue(): [ConditionalExpr] ... ? ... : ... # 2010| Type = [Struct] TernaryPodObj # 2010| ValueCategory = prvalue -# 2010| getLValue(): [VariableAccess] y -# 2010| Type = [Struct] TernaryPodObj -# 2010| ValueCategory = lvalue -# 2010| getRValue(): [Literal] 0 -# 2010| Type = [Struct] TernaryPodObj -# 2010| Value = [Literal] 0 -# 2010| ValueCategory = prvalue -# 2010| getRValue().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2010| getCondition(): [VariableAccess] a +# 2010| Type = [BoolType] bool +# 2010| ValueCategory = prvalue(load) +# 2010| getThen(): [VariableAccess] x # 2010| Type = [Struct] TernaryPodObj # 2010| ValueCategory = prvalue(load) +# 2010| getElse(): [VariableAccess] y +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue(load) +# 2010| getRValue(): [Literal] 0 +# 2010| Type = [Struct] TernaryPodObj +# 2010| Value = [Literal] 0 +# 2010| ValueCategory = prvalue +# 2010| getLValue().getFullyConverted(): [ParenthesisExpr] (...) +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = lvalue +# 2010| getRValue().getFullyConverted(): [TemporaryObjectExpr] temporary object +# 2010| Type = [Struct] TernaryPodObj +# 2010| ValueCategory = prvalue(load) # 2011| getStmt(4): [ReturnStmt] return ... # 2013| [CopyAssignmentOperator] TernaryNonPodObj& TernaryNonPodObj::operator=(TernaryNonPodObj const&) # 2013| : @@ -15339,38 +15345,38 @@ ir.cpp: # 2021| getExpr(): [FunctionCall] call to operator= # 2021| Type = [LValueReferenceType] TernaryNonPodObj & # 2021| ValueCategory = prvalue -# 2021| getQualifier(): [VariableAccess] z -# 2021| Type = [Struct] TernaryNonPodObj -# 2021| ValueCategory = lvalue -# 2021| getArgument(0): [ConditionalExpr] ... ? ... : ... -# 2021| Type = [Struct] TernaryNonPodObj -# 2021| ValueCategory = lvalue -# 2021| getCondition(): [VariableAccess] a -# 2021| Type = [BoolType] bool -# 2021| ValueCategory = prvalue(load) -# 2021| getThen(): [VariableAccess] x +# 2021| getQualifier(): [FunctionCall] call to operator= +# 2021| Type = [LValueReferenceType] TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getQualifier(): [VariableAccess] z # 2021| Type = [Struct] TernaryNonPodObj # 2021| ValueCategory = lvalue -# 2021| getElse(): [FunctionCall] call to operator= -# 2021| Type = [LValueReferenceType] TernaryNonPodObj & -# 2021| ValueCategory = prvalue -# 2021| getQualifier(): [VariableAccess] y +# 2021| getArgument(0): [ConditionalExpr] ... ? ... : ... +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getCondition(): [VariableAccess] a +# 2021| Type = [BoolType] bool +# 2021| ValueCategory = prvalue(load) +# 2021| getThen(): [VariableAccess] x # 2021| Type = [Struct] TernaryNonPodObj # 2021| ValueCategory = lvalue -# 2021| getArgument(0): [ConstructorCall] call to TernaryNonPodObj -# 2021| Type = [VoidType] void -# 2021| ValueCategory = prvalue -# 2021| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) -# 2021| Type = [LValueReferenceType] const TernaryNonPodObj & -# 2021| ValueCategory = prvalue -# 2021| getExpr(): [CStyleCast] (const TernaryNonPodObj)... -# 2021| Conversion = [GlvalueConversion] glvalue conversion -# 2021| Type = [SpecifiedType] const TernaryNonPodObj -# 2021| ValueCategory = lvalue -# 2021| getExpr(): [TemporaryObjectExpr] temporary object -# 2021| Type = [Struct] TernaryNonPodObj -# 2021| ValueCategory = lvalue -# 2021| getElse().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) +# 2021| getElse(): [VariableAccess] y +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 2021| Type = [LValueReferenceType] const TernaryNonPodObj & +# 2021| ValueCategory = prvalue +# 2021| getExpr(): [CStyleCast] (const TernaryNonPodObj)... +# 2021| Conversion = [GlvalueConversion] glvalue conversion +# 2021| Type = [SpecifiedType] const TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getArgument(0): [ConstructorCall] call to TernaryNonPodObj +# 2021| Type = [VoidType] void +# 2021| ValueCategory = prvalue +# 2021| getQualifier().getFullyConverted(): [ParenthesisExpr] (...) +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue +# 2021| getExpr(): [ReferenceDereferenceExpr] (reference dereference) # 2021| Type = [Struct] TernaryNonPodObj # 2021| ValueCategory = lvalue # 2021| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) @@ -15380,6 +15386,9 @@ ir.cpp: # 2021| Conversion = [GlvalueConversion] glvalue conversion # 2021| Type = [SpecifiedType] const TernaryNonPodObj # 2021| ValueCategory = lvalue +# 2021| getExpr(): [TemporaryObjectExpr] temporary object +# 2021| Type = [Struct] TernaryNonPodObj +# 2021| ValueCategory = lvalue # 2021| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) # 2021| Type = [Struct] TernaryNonPodObj # 2021| ValueCategory = lvalue diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 2f78d82a48e..4568026c7f4 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1997,7 +1997,7 @@ void TernaryTestInt(bool a, int x, int y, int z) { z = a ? x : y; z = a ? x : 5; z = a ? 3 : 5; - a ? x : y = 7; + (a ? x : y) = 7; } struct TernaryPodObj { @@ -2007,7 +2007,7 @@ void TernaryTestPodObj(bool a, TernaryPodObj x, TernaryPodObj y, TernaryPodObj z z = a ? x : y; z = a ? x : TernaryPodObj(); z = a ? TernaryPodObj() : TernaryPodObj(); - z = a ? x : y = TernaryPodObj(); + (z = a ? x : y) = TernaryPodObj(); } struct TernaryNonPodObj { @@ -2018,7 +2018,7 @@ void TernaryTestNonPodObj(bool a, TernaryNonPodObj x, TernaryNonPodObj y, Ternar z = a ? x : y; z = a ? x : TernaryNonPodObj(); z = a ? TernaryNonPodObj() : TernaryNonPodObj(); - z = a ? x : y = TernaryNonPodObj(); + (z = a ? x : y) = TernaryNonPodObj(); } // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index 8ef33ff286d..f06eceb9965 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -9078,7 +9078,7 @@ | ir.cpp:1993:13:1993:32 | StoreValue | r1993_2 | | ir.cpp:1996:6:1996:19 | ChiPartial | partial:m1996_3 | | ir.cpp:1996:6:1996:19 | ChiTotal | total:m1996_2 | -| ir.cpp:1996:6:1996:19 | SideEffect | m1996_3 | +| ir.cpp:1996:6:1996:19 | SideEffect | ~m2000_9 | | ir.cpp:1996:26:1996:26 | Address | &:r1996_5 | | ir.cpp:1996:33:1996:33 | Address | &:r1996_7 | | ir.cpp:1996:40:1996:40 | Address | &:r1996_9 | @@ -9128,20 +9128,21 @@ | ir.cpp:1999:9:1999:17 | StoreValue | r1999_6 | | ir.cpp:1999:13:1999:13 | StoreValue | r1999_9 | | ir.cpp:1999:17:1999:17 | StoreValue | r1999_12 | -| ir.cpp:2000:5:2000:5 | Address | &:r2000_1 | -| ir.cpp:2000:5:2000:5 | Condition | r2000_2 | -| ir.cpp:2000:5:2000:5 | Load | m1996_6 | -| ir.cpp:2000:5:2000:17 | Address | &:r2000_5 | -| ir.cpp:2000:5:2000:17 | Address | &:r2000_8 | -| ir.cpp:2000:5:2000:17 | Address | &:r2000_14 | -| ir.cpp:2000:5:2000:17 | Load | m2000_4 | -| ir.cpp:2000:5:2000:17 | Phi | from 11:m2000_9 | -| ir.cpp:2000:5:2000:17 | Phi | from 12:m2000_15 | -| ir.cpp:2000:9:2000:9 | StoreValue | r2000_7 | -| ir.cpp:2000:13:2000:13 | Address | &:r2000_11 | -| ir.cpp:2000:13:2000:13 | Unary | r2000_11 | -| ir.cpp:2000:13:2000:17 | StoreValue | r2000_13 | -| ir.cpp:2000:17:2000:17 | StoreValue | r2000_10 | +| ir.cpp:2000:5:2000:19 | ChiPartial | partial:m2000_8 | +| ir.cpp:2000:5:2000:19 | ChiTotal | total:m1996_4 | +| ir.cpp:2000:6:2000:6 | Address | &:r2000_2 | +| ir.cpp:2000:6:2000:6 | Condition | r2000_3 | +| ir.cpp:2000:6:2000:6 | Load | m1996_6 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_6 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_7 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_11 | +| ir.cpp:2000:6:2000:14 | Address | &:r2000_14 | +| ir.cpp:2000:6:2000:14 | Load | m2000_5 | +| ir.cpp:2000:6:2000:14 | Phi | from 11:m2000_12 | +| ir.cpp:2000:6:2000:14 | Phi | from 12:m2000_15 | +| ir.cpp:2000:10:2000:10 | StoreValue | r2000_10 | +| ir.cpp:2000:14:2000:14 | StoreValue | r2000_13 | +| ir.cpp:2000:19:2000:19 | StoreValue | r2000_1 | | ir.cpp:2006:6:2006:22 | ChiPartial | partial:m2006_3 | | ir.cpp:2006:6:2006:22 | ChiTotal | total:m2006_2 | | ir.cpp:2006:6:2006:22 | SideEffect | m2006_3 | @@ -9218,28 +9219,30 @@ | ir.cpp:2009:31:2009:45 | Load | m2009_20 | | ir.cpp:2009:31:2009:45 | StoreValue | r2009_19 | | ir.cpp:2009:31:2009:45 | StoreValue | r2009_21 | -| ir.cpp:2010:5:2010:5 | Address | &:r2010_7 | -| ir.cpp:2010:9:2010:9 | Address | &:r2010_1 | -| ir.cpp:2010:9:2010:9 | Condition | r2010_2 | -| ir.cpp:2010:9:2010:9 | Load | m2006_6 | -| ir.cpp:2010:9:2010:35 | Address | &:r2010_5 | -| ir.cpp:2010:9:2010:35 | Address | &:r2010_11 | -| ir.cpp:2010:9:2010:35 | Address | &:r2010_20 | -| ir.cpp:2010:9:2010:35 | Load | m2010_4 | -| ir.cpp:2010:9:2010:35 | Phi | from 11:m2010_12 | -| ir.cpp:2010:9:2010:35 | Phi | from 12:m2010_21 | -| ir.cpp:2010:9:2010:35 | StoreValue | r2010_6 | -| ir.cpp:2010:13:2010:13 | Address | &:r2010_9 | -| ir.cpp:2010:13:2010:13 | Load | m2006_8 | -| ir.cpp:2010:13:2010:13 | StoreValue | r2010_10 | -| ir.cpp:2010:17:2010:17 | Address | &:r2010_17 | -| ir.cpp:2010:17:2010:35 | StoreValue | r2010_19 | -| ir.cpp:2010:21:2010:35 | Address | &:r2010_13 | -| ir.cpp:2010:21:2010:35 | Address | &:r2010_13 | -| ir.cpp:2010:21:2010:35 | Load | m2010_15 | -| ir.cpp:2010:21:2010:35 | StoreValue | r2010_14 | -| ir.cpp:2010:21:2010:35 | StoreValue | r2010_16 | -| ir.cpp:2010:21:2010:35 | Unary | r2010_16 | +| ir.cpp:2010:6:2010:6 | Address | &:r2010_11 | +| ir.cpp:2010:6:2010:6 | Unary | r2010_11 | +| ir.cpp:2010:6:2010:18 | Address | &:r2010_13 | +| ir.cpp:2010:10:2010:10 | Address | &:r2010_5 | +| ir.cpp:2010:10:2010:10 | Condition | r2010_6 | +| ir.cpp:2010:10:2010:10 | Load | m2006_6 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_9 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_17 | +| ir.cpp:2010:10:2010:18 | Address | &:r2010_21 | +| ir.cpp:2010:10:2010:18 | Load | m2010_8 | +| ir.cpp:2010:10:2010:18 | Phi | from 11:m2010_18 | +| ir.cpp:2010:10:2010:18 | Phi | from 12:m2010_22 | +| ir.cpp:2010:10:2010:18 | StoreValue | r2010_10 | +| ir.cpp:2010:14:2010:14 | Address | &:r2010_15 | +| ir.cpp:2010:14:2010:14 | Load | m2006_8 | +| ir.cpp:2010:14:2010:14 | StoreValue | r2010_16 | +| ir.cpp:2010:18:2010:18 | Address | &:r2010_19 | +| ir.cpp:2010:18:2010:18 | Load | m2006_10 | +| ir.cpp:2010:18:2010:18 | StoreValue | r2010_20 | +| ir.cpp:2010:23:2010:37 | Address | &:r2010_1 | +| ir.cpp:2010:23:2010:37 | Address | &:r2010_1 | +| ir.cpp:2010:23:2010:37 | Load | m2010_3 | +| ir.cpp:2010:23:2010:37 | StoreValue | r2010_2 | +| ir.cpp:2010:23:2010:37 | StoreValue | r2010_4 | | ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | | ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | | ir.cpp:2013:8:2013:8 | Address | &:r2013_5 | @@ -9280,7 +9283,7 @@ | ir.cpp:2014:13:2014:29 | SideEffect | m2014_8 | | ir.cpp:2017:6:2017:25 | ChiPartial | partial:m2017_3 | | ir.cpp:2017:6:2017:25 | ChiTotal | total:m2017_2 | -| ir.cpp:2017:6:2017:25 | SideEffect | ~m2021_14 | +| ir.cpp:2017:6:2017:25 | SideEffect | ~m2021_32 | | ir.cpp:2017:32:2017:32 | Address | &:r2017_5 | | ir.cpp:2017:52:2017:52 | Address | &:r2017_7 | | ir.cpp:2017:72:2017:72 | Address | &:r2017_9 | @@ -9423,60 +9426,58 @@ | ir.cpp:2020:34:2020:51 | Load | m2020_40 | | ir.cpp:2020:34:2020:51 | SideEffect | ~m2019_16 | | ir.cpp:2020:34:2020:51 | StoreValue | r2020_41 | -| ir.cpp:2021:5:2021:5 | Address | &:r2021_1 | -| ir.cpp:2021:5:2021:5 | Address | &:r2021_1 | -| ir.cpp:2021:5:2021:5 | Arg(this) | this:r2021_1 | -| ir.cpp:2021:5:2021:5 | ChiPartial | partial:m2021_17 | -| ir.cpp:2021:5:2021:5 | ChiTotal | total:m2020_20 | -| ir.cpp:2021:5:2021:5 | SideEffect | m2020_20 | -| ir.cpp:2021:7:2021:7 | CallTarget | func:r2021_2 | -| ir.cpp:2021:7:2021:7 | ChiPartial | partial:m2021_13 | -| ir.cpp:2021:7:2021:7 | ChiTotal | total:m2021_6 | -| ir.cpp:2021:7:2021:7 | SideEffect | ~m2021_6 | -| ir.cpp:2021:7:2021:7 | Unary | r2021_12 | -| ir.cpp:2021:9:2021:9 | Address | &:r2021_3 | -| ir.cpp:2021:9:2021:9 | Condition | r2021_4 | -| ir.cpp:2021:9:2021:9 | Load | m2017_6 | -| ir.cpp:2021:9:2021:38 | Address | &:r2021_8 | -| ir.cpp:2021:9:2021:38 | Address | &:r2021_11 | -| ir.cpp:2021:9:2021:38 | Address | &:r2021_21 | -| ir.cpp:2021:9:2021:38 | Address | &:r2021_43 | -| ir.cpp:2021:9:2021:38 | Arg(0) | 0:r2021_11 | -| ir.cpp:2021:9:2021:38 | Load | m2021_7 | -| ir.cpp:2021:9:2021:38 | Phi | from 11:m2021_22 | -| ir.cpp:2021:9:2021:38 | Phi | from 11:~m2020_16 | -| ir.cpp:2021:9:2021:38 | Phi | from 12:m2021_44 | -| ir.cpp:2021:9:2021:38 | Phi | from 12:~m2021_37 | -| ir.cpp:2021:9:2021:38 | SideEffect | ~m2021_14 | -| ir.cpp:2021:9:2021:38 | Unary | r2021_9 | -| ir.cpp:2021:9:2021:38 | Unary | r2021_10 | -| ir.cpp:2021:13:2021:13 | StoreValue | r2021_20 | -| ir.cpp:2021:17:2021:17 | Address | &:r2021_23 | -| ir.cpp:2021:17:2021:17 | Address | &:r2021_23 | -| ir.cpp:2021:17:2021:17 | Arg(this) | this:r2021_23 | -| ir.cpp:2021:17:2021:17 | ChiPartial | partial:m2021_40 | -| ir.cpp:2021:17:2021:17 | ChiTotal | total:m2017_10 | -| ir.cpp:2021:17:2021:17 | SideEffect | m2017_10 | -| ir.cpp:2021:19:2021:19 | CallTarget | func:r2021_24 | -| ir.cpp:2021:19:2021:19 | ChiPartial | partial:m2021_36 | -| ir.cpp:2021:19:2021:19 | ChiTotal | total:m2021_30 | -| ir.cpp:2021:19:2021:19 | SideEffect | ~m2021_30 | -| ir.cpp:2021:19:2021:19 | Unary | r2021_35 | -| ir.cpp:2021:19:2021:39 | StoreValue | r2021_42 | -| ir.cpp:2021:21:2021:38 | Address | &:r2021_25 | -| ir.cpp:2021:21:2021:38 | Address | &:r2021_25 | -| ir.cpp:2021:21:2021:38 | Address | &:r2021_34 | -| ir.cpp:2021:21:2021:38 | Arg(0) | 0:r2021_34 | -| ir.cpp:2021:21:2021:38 | Arg(this) | this:r2021_25 | -| ir.cpp:2021:21:2021:38 | CallTarget | func:r2021_27 | -| ir.cpp:2021:21:2021:38 | ChiPartial | partial:m2021_29 | -| ir.cpp:2021:21:2021:38 | ChiPartial | partial:m2021_31 | -| ir.cpp:2021:21:2021:38 | ChiTotal | total:m2020_16 | -| ir.cpp:2021:21:2021:38 | ChiTotal | total:m2021_26 | -| ir.cpp:2021:21:2021:38 | SideEffect | ~m2020_16 | -| ir.cpp:2021:21:2021:38 | SideEffect | ~m2021_32 | -| ir.cpp:2021:21:2021:38 | Unary | r2021_25 | -| ir.cpp:2021:21:2021:38 | Unary | r2021_33 | +| ir.cpp:2021:5:2021:19 | ChiPartial | partial:m2021_35 | +| ir.cpp:2021:5:2021:19 | ChiTotal | total:m2021_17 | +| ir.cpp:2021:5:2021:19 | SideEffect | m2021_17 | +| ir.cpp:2021:6:2021:6 | Address | &:r2021_1 | +| ir.cpp:2021:6:2021:6 | Address | &:r2021_1 | +| ir.cpp:2021:6:2021:6 | Arg(this) | this:r2021_1 | +| ir.cpp:2021:6:2021:6 | ChiPartial | partial:m2021_16 | +| ir.cpp:2021:6:2021:6 | ChiTotal | total:m2020_20 | +| ir.cpp:2021:6:2021:6 | SideEffect | m2020_20 | +| ir.cpp:2021:8:2021:8 | CallTarget | func:r2021_2 | +| ir.cpp:2021:8:2021:8 | ChiPartial | partial:m2021_12 | +| ir.cpp:2021:8:2021:8 | ChiTotal | total:m2020_16 | +| ir.cpp:2021:8:2021:8 | SideEffect | ~m2020_16 | +| ir.cpp:2021:8:2021:8 | Unary | r2021_11 | +| ir.cpp:2021:8:2021:19 | Address | &:r2021_18 | +| ir.cpp:2021:8:2021:19 | Address | &:r2021_18 | +| ir.cpp:2021:8:2021:19 | Arg(this) | this:r2021_18 | +| ir.cpp:2021:10:2021:10 | Address | &:r2021_3 | +| ir.cpp:2021:10:2021:10 | Condition | r2021_4 | +| ir.cpp:2021:10:2021:10 | Load | m2017_6 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_7 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_10 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_39 | +| ir.cpp:2021:10:2021:18 | Address | &:r2021_42 | +| ir.cpp:2021:10:2021:18 | Arg(0) | 0:r2021_10 | +| ir.cpp:2021:10:2021:18 | Load | m2021_6 | +| ir.cpp:2021:10:2021:18 | Phi | from 11:m2021_40 | +| ir.cpp:2021:10:2021:18 | Phi | from 12:m2021_43 | +| ir.cpp:2021:10:2021:18 | SideEffect | ~m2021_13 | +| ir.cpp:2021:10:2021:18 | Unary | r2021_8 | +| ir.cpp:2021:10:2021:18 | Unary | r2021_9 | +| ir.cpp:2021:14:2021:14 | StoreValue | r2021_38 | +| ir.cpp:2021:18:2021:18 | StoreValue | r2021_41 | +| ir.cpp:2021:21:2021:21 | CallTarget | func:r2021_19 | +| ir.cpp:2021:21:2021:21 | ChiPartial | partial:m2021_31 | +| ir.cpp:2021:21:2021:21 | ChiTotal | total:m2021_25 | +| ir.cpp:2021:21:2021:21 | SideEffect | ~m2021_25 | +| ir.cpp:2021:21:2021:21 | Unary | r2021_30 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_20 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_20 | +| ir.cpp:2021:23:2021:40 | Address | &:r2021_29 | +| ir.cpp:2021:23:2021:40 | Arg(0) | 0:r2021_29 | +| ir.cpp:2021:23:2021:40 | Arg(this) | this:r2021_20 | +| ir.cpp:2021:23:2021:40 | CallTarget | func:r2021_22 | +| ir.cpp:2021:23:2021:40 | ChiPartial | partial:m2021_24 | +| ir.cpp:2021:23:2021:40 | ChiPartial | partial:m2021_26 | +| ir.cpp:2021:23:2021:40 | ChiTotal | total:m2021_13 | +| ir.cpp:2021:23:2021:40 | ChiTotal | total:m2021_21 | +| ir.cpp:2021:23:2021:40 | SideEffect | ~m2021_13 | +| 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 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index bd3d387c5bb..a6b527b95b6 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -10529,9 +10529,10 @@ ir.cpp: # 1999| r1999_5(int) = Load[#temp1999:9] : &:r1999_4, ~m? # 1999| r1999_6(glval) = VariableAddress[z] : # 1999| mu1999_7(int) = Store[z] : &:r1999_6, r1999_5 -# 2000| r2000_1(glval) = VariableAddress[a] : -# 2000| r2000_2(bool) = Load[a] : &:r2000_1, ~m? -# 2000| v2000_3(void) = ConditionalBranch : r2000_2 +# 2000| r2000_1(int) = Constant[7] : +# 2000| r2000_2(glval) = VariableAddress[a] : +# 2000| r2000_3(bool) = Load[a] : &:r2000_2, ~m? +# 2000| v2000_4(void) = ConditionalBranch : r2000_3 #-----| False -> Block 12 #-----| True -> Block 11 @@ -10548,26 +10549,24 @@ ir.cpp: #-----| Goto -> Block 7 # 2000| Block 10 -# 2000| r2000_4(glval) = VariableAddress[#temp2000:5] : -# 2000| r2000_5(glval) = Load[#temp2000:5] : &:r2000_4, ~m? +# 2000| r2000_5(glval) = VariableAddress[#temp2000:6] : +# 2000| r2000_6(glval) = Load[#temp2000:6] : &:r2000_5, ~m? +# 2000| mu2000_7(int) = Store[?] : &:r2000_6, r2000_1 # 2001| v2001_1(void) = NoOp : # 1996| v1996_12(void) = ReturnVoid : # 1996| v1996_13(void) = AliasedUse : ~m? # 1996| v1996_14(void) = ExitFunction : # 2000| Block 11 -# 2000| r2000_6(glval) = VariableAddress[x] : -# 2000| r2000_7(glval) = VariableAddress[#temp2000:5] : -# 2000| mu2000_8(glval) = Store[#temp2000:5] : &:r2000_7, r2000_6 +# 2000| r2000_8(glval) = VariableAddress[x] : +# 2000| r2000_9(glval) = VariableAddress[#temp2000:6] : +# 2000| mu2000_10(glval) = Store[#temp2000:6] : &:r2000_9, r2000_8 #-----| Goto -> Block 10 # 2000| Block 12 -# 2000| r2000_9(int) = Constant[7] : -# 2000| r2000_10(glval) = VariableAddress[y] : -# 2000| mu2000_11(int) = Store[y] : &:r2000_10, r2000_9 -# 2000| r2000_12(glval) = CopyValue : r2000_10 -# 2000| r2000_13(glval) = VariableAddress[#temp2000:5] : -# 2000| mu2000_14(glval) = Store[#temp2000:5] : &:r2000_13, r2000_12 +# 2000| r2000_11(glval) = VariableAddress[y] : +# 2000| r2000_12(glval) = VariableAddress[#temp2000:6] : +# 2000| mu2000_13(glval) = Store[#temp2000:6] : &:r2000_12, r2000_11 #-----| Goto -> Block 10 # 2006| void TernaryTestPodObj(bool, TernaryPodObj, TernaryPodObj, TernaryPodObj) @@ -10649,15 +10648,19 @@ ir.cpp: #-----| Goto -> Block 4 # 2009| Block 7 -# 2009| r2009_5(glval) = VariableAddress[#temp2009:9] : -# 2009| r2009_6(TernaryPodObj) = Load[#temp2009:9] : &:r2009_5, ~m? -# 2009| mu2009_7(TernaryPodObj) = Store[#temp2009:9] : &:r2009_1, r2009_6 -# 2009| r2009_8(TernaryPodObj) = Load[#temp2009:9] : &:r2009_1, ~m? -# 2009| r2009_9(glval) = VariableAddress[z] : -# 2009| mu2009_10(TernaryPodObj) = Store[z] : &:r2009_9, r2009_8 -# 2010| r2010_1(glval) = VariableAddress[a] : -# 2010| r2010_2(bool) = Load[a] : &:r2010_1, ~m? -# 2010| v2010_3(void) = ConditionalBranch : r2010_2 +# 2009| r2009_5(glval) = VariableAddress[#temp2009:9] : +# 2009| r2009_6(TernaryPodObj) = Load[#temp2009:9] : &:r2009_5, ~m? +# 2009| mu2009_7(TernaryPodObj) = Store[#temp2009:9] : &:r2009_1, r2009_6 +# 2009| r2009_8(TernaryPodObj) = Load[#temp2009:9] : &:r2009_1, ~m? +# 2009| r2009_9(glval) = VariableAddress[z] : +# 2009| mu2009_10(TernaryPodObj) = Store[z] : &:r2009_9, r2009_8 +# 2010| r2010_1(glval) = VariableAddress[#temp2010:23] : +# 2010| r2010_2(TernaryPodObj) = Constant[0] : +# 2010| mu2010_3(TernaryPodObj) = Store[#temp2010:23] : &:r2010_1, r2010_2 +# 2010| r2010_4(TernaryPodObj) = Load[#temp2010:23] : &:r2010_1, ~m? +# 2010| r2010_5(glval) = VariableAddress[a] : +# 2010| r2010_6(bool) = Load[a] : &:r2010_5, ~m? +# 2010| v2010_7(void) = ConditionalBranch : r2010_6 #-----| False -> Block 12 #-----| True -> Block 11 @@ -10680,32 +10683,29 @@ ir.cpp: #-----| Goto -> Block 7 # 2010| Block 10 -# 2010| r2010_4(glval) = VariableAddress[#temp2010:9] : -# 2010| r2010_5(TernaryPodObj) = Load[#temp2010:9] : &:r2010_4, ~m? -# 2010| r2010_6(glval) = VariableAddress[z] : -# 2010| mu2010_7(TernaryPodObj) = Store[z] : &:r2010_6, r2010_5 -# 2011| v2011_1(void) = NoOp : -# 2006| v2006_12(void) = ReturnVoid : -# 2006| v2006_13(void) = AliasedUse : ~m? -# 2006| v2006_14(void) = ExitFunction : +# 2010| r2010_8(glval) = VariableAddress[#temp2010:10] : +# 2010| r2010_9(TernaryPodObj) = Load[#temp2010:10] : &:r2010_8, ~m? +# 2010| r2010_10(glval) = VariableAddress[z] : +# 2010| mu2010_11(TernaryPodObj) = Store[z] : &:r2010_10, r2010_9 +# 2010| r2010_12(glval) = CopyValue : r2010_10 +# 2010| mu2010_13(TernaryPodObj) = Store[?] : &:r2010_12, r2010_4 +# 2011| v2011_1(void) = NoOp : +# 2006| v2006_12(void) = ReturnVoid : +# 2006| v2006_13(void) = AliasedUse : ~m? +# 2006| v2006_14(void) = ExitFunction : # 2010| Block 11 -# 2010| r2010_8(glval) = VariableAddress[x] : -# 2010| r2010_9(TernaryPodObj) = Load[x] : &:r2010_8, ~m? -# 2010| r2010_10(glval) = VariableAddress[#temp2010:9] : -# 2010| mu2010_11(TernaryPodObj) = Store[#temp2010:9] : &:r2010_10, r2010_9 +# 2010| r2010_14(glval) = VariableAddress[x] : +# 2010| r2010_15(TernaryPodObj) = Load[x] : &:r2010_14, ~m? +# 2010| r2010_16(glval) = VariableAddress[#temp2010:10] : +# 2010| mu2010_17(TernaryPodObj) = Store[#temp2010:10] : &:r2010_16, r2010_15 #-----| Goto -> Block 10 # 2010| Block 12 -# 2010| r2010_12(glval) = VariableAddress[#temp2010:21] : -# 2010| r2010_13(TernaryPodObj) = Constant[0] : -# 2010| mu2010_14(TernaryPodObj) = Store[#temp2010:21] : &:r2010_12, r2010_13 -# 2010| r2010_15(TernaryPodObj) = Load[#temp2010:21] : &:r2010_12, ~m? -# 2010| r2010_16(glval) = VariableAddress[y] : -# 2010| mu2010_17(TernaryPodObj) = Store[y] : &:r2010_16, r2010_15 -# 2010| r2010_18(TernaryPodObj) = CopyValue : r2010_15 -# 2010| r2010_19(glval) = VariableAddress[#temp2010:9] : -# 2010| mu2010_20(TernaryPodObj) = Store[#temp2010:9] : &:r2010_19, r2010_18 +# 2010| r2010_18(glval) = VariableAddress[y] : +# 2010| r2010_19(TernaryPodObj) = Load[y] : &:r2010_18, ~m? +# 2010| r2010_20(glval) = VariableAddress[#temp2010:10] : +# 2010| mu2010_21(TernaryPodObj) = Store[#temp2010:10] : &:r2010_20, r2010_19 #-----| Goto -> Block 10 # 2013| TernaryNonPodObj& TernaryNonPodObj::operator=(TernaryNonPodObj const&) @@ -10931,46 +10931,46 @@ ir.cpp: #-----| Goto -> Block 7 # 2021| Block 10 -# 2021| r2021_6(glval) = VariableAddress[#temp2021:9] : -# 2021| r2021_7(glval) = Load[#temp2021:9] : &:r2021_6, ~m? -# 2021| r2021_8(glval) = Convert : r2021_7 -# 2021| r2021_9(TernaryNonPodObj &) = CopyValue : r2021_8 -# 2021| r2021_10(TernaryNonPodObj &) = Call[operator=] : func:r2021_2, this:r2021_1, 0:r2021_9 -# 2021| mu2021_11(unknown) = ^CallSideEffect : ~m? -# 2021| v2021_12(void) = ^IndirectReadSideEffect[-1] : &:r2021_1, ~m? -# 2021| v2021_13(void) = ^BufferReadSideEffect[0] : &:r2021_9, ~m? -# 2021| mu2021_14(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_1 -# 2021| r2021_15(glval) = CopyValue : r2021_10 -# 2022| v2022_1(void) = NoOp : -# 2017| v2017_12(void) = ReturnVoid : -# 2017| v2017_13(void) = AliasedUse : ~m? -# 2017| v2017_14(void) = ExitFunction : +# 2021| r2021_6(glval) = VariableAddress[#temp2021:10] : +# 2021| r2021_7(glval) = Load[#temp2021:10] : &:r2021_6, ~m? +# 2021| r2021_8(glval) = Convert : r2021_7 +# 2021| r2021_9(TernaryNonPodObj &) = CopyValue : r2021_8 +# 2021| r2021_10(TernaryNonPodObj &) = Call[operator=] : func:r2021_2, this:r2021_1, 0:r2021_9 +# 2021| mu2021_11(unknown) = ^CallSideEffect : ~m? +# 2021| v2021_12(void) = ^IndirectReadSideEffect[-1] : &:r2021_1, ~m? +# 2021| v2021_13(void) = ^BufferReadSideEffect[0] : &:r2021_9, ~m? +# 2021| mu2021_14(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_1 +# 2021| r2021_15(glval) = CopyValue : r2021_10 +# 2021| r2021_16(glval) = FunctionAddress[operator=] : +# 2021| r2021_17(glval) = VariableAddress[#temp2021:23] : +# 2021| mu2021_18(TernaryNonPodObj) = Uninitialized[#temp2021:23] : &:r2021_17 +# 2021| r2021_19(glval) = FunctionAddress[TernaryNonPodObj] : +# 2021| v2021_20(void) = Call[TernaryNonPodObj] : func:r2021_19, this:r2021_17 +# 2021| mu2021_21(unknown) = ^CallSideEffect : ~m? +# 2021| mu2021_22(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_17 +# 2021| r2021_23(glval) = Convert : r2021_17 +# 2021| r2021_24(TernaryNonPodObj &) = CopyValue : r2021_23 +# 2021| r2021_25(TernaryNonPodObj &) = Call[operator=] : func:r2021_16, this:r2021_15, 0:r2021_24 +# 2021| mu2021_26(unknown) = ^CallSideEffect : ~m? +# 2021| v2021_27(void) = ^IndirectReadSideEffect[-1] : &:r2021_15, ~m? +# 2021| v2021_28(void) = ^BufferReadSideEffect[0] : &:r2021_24, ~m? +# 2021| mu2021_29(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_15 +# 2021| r2021_30(glval) = CopyValue : r2021_25 +# 2022| v2022_1(void) = NoOp : +# 2017| v2017_12(void) = ReturnVoid : +# 2017| v2017_13(void) = AliasedUse : ~m? +# 2017| v2017_14(void) = ExitFunction : # 2021| Block 11 -# 2021| r2021_16(glval) = VariableAddress[x] : -# 2021| r2021_17(glval) = VariableAddress[#temp2021:9] : -# 2021| mu2021_18(glval) = Store[#temp2021:9] : &:r2021_17, r2021_16 +# 2021| r2021_31(glval) = VariableAddress[x] : +# 2021| r2021_32(glval) = VariableAddress[#temp2021:10] : +# 2021| mu2021_33(glval) = Store[#temp2021:10] : &:r2021_32, r2021_31 #-----| Goto -> Block 10 # 2021| Block 12 -# 2021| r2021_19(glval) = VariableAddress[y] : -# 2021| r2021_20(glval) = FunctionAddress[operator=] : -# 2021| r2021_21(glval) = VariableAddress[#temp2021:21] : -# 2021| mu2021_22(TernaryNonPodObj) = Uninitialized[#temp2021:21] : &:r2021_21 -# 2021| r2021_23(glval) = FunctionAddress[TernaryNonPodObj] : -# 2021| v2021_24(void) = Call[TernaryNonPodObj] : func:r2021_23, this:r2021_21 -# 2021| mu2021_25(unknown) = ^CallSideEffect : ~m? -# 2021| mu2021_26(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_21 -# 2021| r2021_27(glval) = Convert : r2021_21 -# 2021| r2021_28(TernaryNonPodObj &) = CopyValue : r2021_27 -# 2021| r2021_29(TernaryNonPodObj &) = Call[operator=] : func:r2021_20, this:r2021_19, 0:r2021_28 -# 2021| mu2021_30(unknown) = ^CallSideEffect : ~m? -# 2021| v2021_31(void) = ^IndirectReadSideEffect[-1] : &:r2021_19, ~m? -# 2021| v2021_32(void) = ^BufferReadSideEffect[0] : &:r2021_28, ~m? -# 2021| mu2021_33(TernaryNonPodObj) = ^IndirectMayWriteSideEffect[-1] : &:r2021_19 -# 2021| r2021_34(glval) = CopyValue : r2021_29 -# 2021| r2021_35(glval) = VariableAddress[#temp2021:9] : -# 2021| mu2021_36(glval) = Store[#temp2021:9] : &:r2021_35, r2021_34 +# 2021| r2021_34(glval) = VariableAddress[y] : +# 2021| r2021_35(glval) = VariableAddress[#temp2021:10] : +# 2021| mu2021_36(glval) = Store[#temp2021:10] : &:r2021_35, r2021_34 #-----| Goto -> Block 10 perf-regression.cpp: diff --git a/java/ql/lib/change-notes/2023-07-19-inputstream-wrapper-steps.md b/java/ql/lib/change-notes/2023-07-19-inputstream-wrapper-steps.md new file mode 100644 index 00000000000..aaeacf93e34 --- /dev/null +++ b/java/ql/lib/change-notes/2023-07-19-inputstream-wrapper-steps.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added more dataflow steps for `java.io.InputStream`s that wrap other `java.io.InputStream`s. diff --git a/java/ql/lib/ext/generated/struts2.model.yml b/java/ql/lib/ext/generated/struts2.model.yml index f157c44806e..74e1101a960 100644 --- a/java/ql/lib/ext/generated/struts2.model.yml +++ b/java/ql/lib/ext/generated/struts2.model.yml @@ -4849,15 +4849,6 @@ extensions: - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(Throwable,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] - ["org.apache.struts2", "StrutsException", true, "StrutsException", "(Throwable,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] - ["org.apache.struts2", "StrutsException", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "Order", "(String,String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "Order", "(String,String,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "getClientName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "getId", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "setClientName", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] - - ["org.demo.rest.example", "Order", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] - - ["org.demo.rest.example", "OrdersController", true, "create", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] - - ["org.demo.rest.example", "OrdersController", true, "setId", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] - addsTo: pack: codeql/java-all extensible: neutralModel @@ -6886,19 +6877,5 @@ extensions: - ["org.apache.struts2", "ServletActionContext", "setResponse", "(HttpServletResponse)", "summary", "df-generated"] - ["org.apache.struts2", "ServletActionContext", "setServletContext", "(ServletContext)", "summary", "df-generated"] - ["org.apache.struts2", "ServletCache", "clear", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "IndexController", "index", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "Order", "getAmount", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "Order", "setAmount", "(int)", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "deleteConfirm", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "destroy", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "edit", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "editNew", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "index", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "show", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersController", "update", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersService", "get", "(String)", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersService", "getAll", "()", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersService", "remove", "(String)", "summary", "df-generated"] - - ["org.demo.rest.example", "OrdersService", "save", "(Order)", "summary", "df-generated"] - ["org.w3c.dom", "Document", "getElementsByTagName", "(String)", "summary", "df-generated"] - ["org.w3c.dom", "Document", "getElementsByTagNameNS", "(String,String)", "summary", "df-generated"] diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 156cbbc0f93..6372b22c8f4 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -177,6 +177,11 @@ class TypeObjectInputStream extends RefType { TypeObjectInputStream() { this.hasQualifiedName("java.io", "ObjectInputStream") } } +/** The class `java.io.InputStream`. */ +class TypeInputStream extends RefType { + TypeInputStream() { this.hasQualifiedName("java.io", "InputStream") } +} + /** The class `java.nio.file.Paths`. */ class TypePaths extends Class { TypePaths() { this.hasQualifiedName("java.nio.file", "Paths") } diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll index 1619965f0f0..fef69bec7fd 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll @@ -20,11 +20,11 @@ private module Frameworks { private import semmle.code.java.frameworks.Guice private import semmle.code.java.frameworks.IoJsonWebToken private import semmle.code.java.frameworks.jackson.JacksonSerializability + private import semmle.code.java.frameworks.InputStream private import semmle.code.java.frameworks.Properties private import semmle.code.java.frameworks.Protobuf private import semmle.code.java.frameworks.ratpack.RatpackExec private import semmle.code.java.frameworks.stapler.Stapler - private import semmle.code.java.JDK } /** diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll index c061f559251..53c0a83a536 100644 --- a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll @@ -757,7 +757,7 @@ private predicate baseBound(Expr e, int b, boolean upper) { or exists(Method read | e.(MethodAccess).getMethod().overrides*(read) and - read.getDeclaringType().hasQualifiedName("java.io", "InputStream") and + read.getDeclaringType() instanceof TypeInputStream and read.hasName("read") and read.getNumberOfParameters() = 0 | diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index c992f92ee8a..6f8dbb1771b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -239,7 +239,7 @@ private class BulkData extends RefType { this.(Array).getElementType().(PrimitiveType).hasName(["byte", "char"]) or exists(RefType t | this.getASourceSupertype*() = t | - t.hasQualifiedName("java.io", "InputStream") or + t instanceof TypeInputStream or t.hasQualifiedName("java.nio", "ByteBuffer") or t.hasQualifiedName("java.lang", "Readable") or t.hasQualifiedName("java.io", "DataInput") or @@ -259,7 +259,7 @@ private class BulkData extends RefType { private predicate inputStreamWrapper(Constructor c, int argi) { not c.fromSource() and c.getParameterType(argi) instanceof BulkData and - c.getDeclaringType().getASourceSupertype+().hasQualifiedName("java.io", "InputStream") + c.getDeclaringType().getASourceSupertype+() instanceof TypeInputStream } /** An object construction that preserves the data flow status of any of its arguments. */ diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index c22f77725a1..64f26685b68 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -102,7 +102,7 @@ private module Dispatch { or t instanceof Interface and not t.fromSource() or - t.hasQualifiedName("java.io", "InputStream") + t instanceof TypeInputStream or t.hasQualifiedName("java.io", "Serializable") or diff --git a/java/ql/lib/semmle/code/java/frameworks/InputStream.qll b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll new file mode 100644 index 00000000000..8f37ecc24ea --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll @@ -0,0 +1,90 @@ +/** Provides definitions related to `java.io.InputStream`. */ + +import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.SSA +private import semmle.code.java.dataflow.TaintTracking + +/** + * A jump taint step from an update of the `bytes[]` parameter in an override of the `InputStream.read` method + * to a class instance expression of the type extending `InputStream`. + * + * This models how a subtype of `InputStream` could be tainted by the definition of its methods, which will + * normally only happen in nested classes. + */ +private class InputStreamWrapperCapturedJumpStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(InputStreamRead m, NestedClass wrapper | + m.getDeclaringType() = wrapper and + wrapper.getASourceSupertype+() instanceof TypeInputStream + | + n1.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess() and + n2.asExpr() + .(ClassInstanceExpr) + .getConstructedType() + .getASourceSupertype*() + .getSourceDeclaration() = wrapper + ) + } +} + +/** + * A local taint step from the definition of a captured variable, the capturer of which + * updates the `bytes[]` parameter in an override of the `InputStream.read` method, + * to a class instance expression of the type extending `InputStream`. + * + * This models how a subtype of `InputStream` could be tainted by capturing tainted variables in + * the definition of its methods. + */ +private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(InputStreamRead m, NestedClass wrapper, SsaVariable captured, SsaImplicitInit capturer | + wrapper.getASourceSupertype+() instanceof TypeInputStream and + m.getDeclaringType() = wrapper and + capturer.captures(captured) and + TaintTracking::localTaint(DataFlow::exprNode(capturer.getAFirstUse()), + any(DataFlow::PostUpdateNode pun | + pun.getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess() + )) and + n2.asExpr() + .(ClassInstanceExpr) + .getConstructedType() + .getASourceSupertype*() + .getSourceDeclaration() = wrapper + | + n1.asExpr() = captured.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() + or + captured.(SsaImplicitInit).isParameterDefinition(n1.asParameter()) + ) + } +} + +/** + * A taint step from an `InputStream` argument of the constructor of an `InputStream` subtype + * to the call of the constructor, only if the argument is assigned to a class field. + * + * This models how it's assumed that an `InputStream` wrapper is tainted by the wrapped stream, + * and is a workaround to low `fieldFlowBranchLimit`s in dataflow configurations. + */ +private class InputStreamWrapperConstructorStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(ClassInstanceExpr cc, Argument a, AssignExpr ae, int pos | + cc.getConstructedType().getASourceSupertype+() instanceof TypeInputStream and + cc.getArgument(pragma[only_bind_into](pos)) = a and + cc.getCallee().getParameter(pragma[only_bind_into](pos)).getAnAccess() = ae.getRhs() and + ae.getDest().(FieldWrite).getField().getType().(RefType).getASourceSupertype*() instanceof + TypeInputStream + | + n1.asExpr() = a and + n2.asExpr() = cc + ) + } +} + +private class InputStreamRead extends Method { + InputStreamRead() { + this.hasName("read") and + this.getDeclaringType().getASourceSupertype*() instanceof TypeInputStream + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll index 8df603c5d6a..461a7dc8208 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll @@ -317,7 +317,7 @@ class SystemSetInputStreamMethod extends Method { SystemSetInputStreamMethod() { this.hasName("setIn") and this.getNumberOfParameters() = 1 and - this.getParameter(0).getType().(RefType).hasQualifiedName("java.io", "InputStream") and + this.getParameter(0).getType() instanceof TypeInputStream and this.getDeclaringType() .getAnAncestor() .getSourceDeclaration() diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll index d40d1608969..f2611d6e2d1 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll @@ -237,7 +237,7 @@ class SpringRequestMappingParameter extends Parameter { private predicate isExplicitlyTaintedInput() { // InputStream or Reader parameters allow access to the body of a request - this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "InputStream") or + this.getType().(RefType).getAnAncestor() instanceof TypeInputStream or this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader") or // The SpringServletInputAnnotations allow access to the URI, request parameters, cookie values and the body of the request this.getAnAnnotation() instanceof SpringServletInputAnnotation or diff --git a/java/ql/test/library-tests/dataflow/stream-read/A.java b/java/ql/test/library-tests/dataflow/stream-read/A.java new file mode 100644 index 00000000000..779f95bcefa --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/A.java @@ -0,0 +1,139 @@ +import java.io.InputStream; +import java.io.IOException; + +public class A { + + private static InputStream source() { + return null; + } + + private static void sink(Object s) {} + + static class MyStream extends InputStream { + private InputStream wrapped; + + MyStream(InputStream wrapped) { + this.wrapped = wrapped; + } + + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return wrapped.read(b); + } + } + + public static void testSeveralWrappers() { + InputStream src = source(); + InputStream wrapper1 = new MyStream(src); + sink(wrapper1); // $ hasTaintFlow + InputStream wrapper2 = new MyStream(wrapper1); + sink(wrapper2); // $ hasTaintFlow + InputStream wrapper3 = new MyStream(wrapper2); + sink(wrapper3); // $ hasTaintFlow + + InputStream wrapper4 = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return wrapper3.read(b); + + } + }; + sink(wrapper4); // $ hasTaintFlow + } + + public static void testAnonymous() throws Exception { + InputStream wrapper = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + InputStream in = source(); + return in.read(b); + } + }; + sink(wrapper); // $ hasTaintFlow + } + + public static void testAnonymousVarCapture() throws Exception { + InputStream in = source(); + InputStream wrapper = new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + + } + }; + sink(wrapper); // $ hasTaintFlow + } + + public static InputStream wrapStream(InputStream in) { + return new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + } + }; + } + + public static void testWrapCall() { + sink(wrapStream(null)); // $ SPURIOUS: hasTaintFlow + sink(wrapStream(source())); // $ hasTaintFlow + } + + public static void testLocal() { + + class LocalInputStream extends InputStream { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + InputStream in = source(); + return in.read(b); + } + } + sink(new LocalInputStream()); // $ hasTaintFlow + } + + public static void testLocalVarCapture() { + InputStream in = source(); + + class LocalInputStream extends InputStream { + @Override + public int read() throws IOException { + return 0; + } + + @Override + public int read(byte[] b) throws IOException { + return in.read(b); + } + } + sink(new LocalInputStream()); // $ hasTaintFlow + } +} diff --git a/java/ql/test/library-tests/dataflow/stream-read/test.expected b/java/ql/test/library-tests/dataflow/stream-read/test.expected new file mode 100644 index 00000000000..48de9172b36 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/stream-read/test.ql b/java/ql/test/library-tests/dataflow/stream-read/test.ql new file mode 100644 index 00000000000..50e3f8d2f7d --- /dev/null +++ b/java/ql/test/library-tests/dataflow/stream-read/test.ql @@ -0,0 +1,2 @@ +import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/swift/ql/lib/change-notes/2023-07-17-nsuseractivity-referrer-url.md b/swift/ql/lib/change-notes/2023-07-17-nsuseractivity-referrer-url.md new file mode 100644 index 00000000000..03e90b39c05 --- /dev/null +++ b/swift/ql/lib/change-notes/2023-07-17-nsuseractivity-referrer-url.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- + +* Added taint flow for `NSUserActivity.referrerURL`. diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll index 109d1acec9c..97f0529159f 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/CustomUrlSchemes.qll @@ -83,7 +83,7 @@ private class UserActivityUrlInheritTaint extends TaintInheritingContent, { UserActivityUrlInheritTaint() { this.getField().getEnclosingDecl().asNominalTypeDecl().getName() = "NSUserActivity" and - this.getField().getName() = "webpageURL" + this.getField().getName() = ["webpageURL", "referrerURL"] } } diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll new file mode 100644 index 00000000000..26e8782a799 --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll @@ -0,0 +1,85 @@ +/** + * Provides classes and predicates for reasoning about system + * commands built from user-controlled sources (that is, command injection + * vulnerabilities). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.ExternalFlow + +/** + * A dataflow sink for command injection vulnerabilities. + */ +abstract class CommandInjectionSink extends DataFlow::Node { } + +/** + * A barrier for command injection vulnerabilities. + */ +abstract class CommandInjectionBarrier extends DataFlow::Node { } + +/** + * A unit class for adding additional flow steps. + */ +class CommandInjectionAdditionalFlowStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a flow + * step for paths related to command injection vulnerabilities. + */ + abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); +} + +private class ProcessSink2 extends CommandInjectionSink instanceof DataFlow::Node { + ProcessSink2() { + exists(AssignExpr assign, ProcessHost s | + assign.getDest() = s and + this.asExpr() = assign.getSource() + ) + or + exists(AssignExpr assign, ProcessHost s, ArrayExpr a | + assign.getDest() = s and + a = assign.getSource() and + this.asExpr() = a.getAnElement() + ) + } +} + +private class ProcessHost extends MemberRefExpr { + ProcessHost() { this.getBase() instanceof ProcessRef } +} + +/** An expression of type `Process`. */ +private class ProcessRef extends Expr { + ProcessRef() { + this.getType() instanceof ProcessType or + this.getType() = any(OptionalType t | t.getBaseType() instanceof ProcessType) + } +} + +/** The type `Process`. */ +private class ProcessType extends NominalType { + ProcessType() { this.getFullName() = "Process" } +} + +/** + * A `DataFlow::Node` that is written into a `Process` object. + */ +private class ProcessSink extends CommandInjectionSink instanceof DataFlow::Node { + ProcessSink() { + // any write into a class derived from `Process` is a sink. For + // example in `Process.launchPath = sensitive` the post-update node corresponding + // with `Process.launchPath` is a sink. + exists(NominalType t, Expr e | + t.getABaseType*().getUnderlyingType().getName() = "Process" and + e.getFullyConverted() = this.asExpr() and + e.getFullyConverted().getType() = t + ) + } +} + +/** + * A sink defined in a CSV model. + */ +private class DefaultCommandInjectionSink extends CommandInjectionSink { + DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } +} diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll new file mode 100644 index 00000000000..4b67932209d --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll @@ -0,0 +1,31 @@ +/** + * Provides a taint-tracking configuration for reasoning about system + * commands built from user-controlled sources (that is, Command injection + * vulnerabilities). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import codeql.swift.security.CommandInjectionExtensions + +/** + * A taint configuration for tainted data that reaches a Command Injection sink. + */ +module CommandInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + predicate isSink(DataFlow::Node node) { node instanceof CommandInjectionSink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof CommandInjectionBarrier } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + any(CommandInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + } +} + +/** + * Detect taint flow of tainted data that reaches a Command Injection sink. + */ +module CommandInjectionFlow = TaintTracking::Global; diff --git a/swift/ql/src/change-notes/2023-07-12-command-injection.md b/swift/ql/src/change-notes/2023-07-12-command-injection.md new file mode 100644 index 00000000000..2befc7592b9 --- /dev/null +++ b/swift/ql/src/change-notes/2023-07-12-command-injection.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added new query "Command injection" (`swift/command-line-injection`). The query finds places where user input is used to execute system commands without proper escaping. \ No newline at end of file diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp new file mode 100644 index 00000000000..ab9562af12c --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp @@ -0,0 +1,45 @@ + + + + +

+Constructing a system command with unsanitized user input is dangerous, +since a malicious user may be able to craft input that executes arbitrary code. +

+
+ + +

+If possible, use hard-coded string literals to specify the command to run. Instead of interpreting +user input directly as command names, examine the input and then choose among hard-coded string +literals. +

+

+If this is not possible, then add sanitization code to verify that the user input is safe before +using it. +

+
+ + +

+The following examples execute code from user input without +sanitizing it first: +

+ +

+If user input is used to construct a command it should be checked +first. This ensures that the user cannot insert characters that have special +meanings. +

+ +
+ + +
  • +OWASP: +Command Injection. +
  • +
    +
    \ No newline at end of file diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql new file mode 100644 index 00000000000..148676e1ac3 --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql @@ -0,0 +1,22 @@ +/** + * @name System command built from user-controlled sources + * @description Building a system command from user-controlled sources is vulnerable to insertion of malicious code by the user. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id swift/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.CommandInjectionQuery +import CommandInjectionFlow::PathGraph + +from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode +where CommandInjectionFlow::flowPath(sourceNode, sinkNode) +select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.", + sourceNode.getNode(), "user-provided value" diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift new file mode 100644 index 00000000000..ffdaaf907ca --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionBad.swift @@ -0,0 +1,5 @@ +var task = Process() +task.launchPath = "/bin/bash" +task.arguments = ["-c", userControlledString] // BAD + +task.launch() \ No newline at end of file diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift new file mode 100644 index 00000000000..3482718eeac --- /dev/null +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift @@ -0,0 +1,13 @@ +func validateCommand(_ command: String) -> String? { + let allowedCommands = ["ls -l", "pwd", "echo"] + if allowedCommands.contains(command) { + return command + } + return nil +} + +var task = Process() +task.launchPath = "/bin/bash" +task.arguments = ["-c", validateCommand(userControlledString)] // GOOD + +task.launch() \ No newline at end of file diff --git a/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected new file mode 100644 index 00000000000..ac216c004f2 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/flowsources/CONSISTENCY/CfgConsistency.expected @@ -0,0 +1,3 @@ +deadEnd +| customurlschemes.swift:94:59:94:76 | options | +| customurlschemes.swift:133:59:133:76 | options | diff --git a/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql b/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql index 71e6055b7d4..e4ea2e0e56e 100644 --- a/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql +++ b/swift/ql/test/library-tests/dataflow/flowsources/FlowSourcesInline.ql @@ -1,6 +1,21 @@ import swift import TestUtilities.InlineExpectationsTest import FlowConfig +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.DataFlow + +module TestConfiguration implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src instanceof FlowSource } + + predicate isSink(DataFlow::Node sink) { + exists(CallExpr sinkCall | + sinkCall.getStaticTarget().getName().matches("sink%") and + sinkCall.getAnArgument().getExpr() = sink.asExpr() + ) + } +} + +module TestFlow = TaintTracking::Global; string describe(FlowSource source) { source instanceof RemoteFlowSource and result = "remote" @@ -9,7 +24,7 @@ string describe(FlowSource source) { } module FlowSourcesTest implements TestSig { - string getARelevantTag() { result = "source" } + string getARelevantTag() { result = ["source", "tainted"] } predicate hasActualResult(Location location, string element, string tag, string value) { exists(FlowSource source | @@ -19,6 +34,16 @@ module FlowSourcesTest implements TestSig { tag = "source" and value = describe(source) ) + or + exists(DataFlow::Node sink | + // this is not really what the "flowsources" test is about, but sometimes it's helpful to + // have sinks and confirm that taint reaches obvious points in the flow source test code. + TestFlow::flowTo(sink) and + location = sink.getLocation() and + element = sink.toString() and + tag = "tainted" and + value = "" + ) } } diff --git a/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift b/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift index 4a300dcd217..eb50c15824b 100644 --- a/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift +++ b/swift/ql/test/library-tests/dataflow/flowsources/customurlschemes.swift @@ -26,12 +26,24 @@ protocol UIApplicationDelegate { } class UIScene { - class ConnectionOptions {} + class ConnectionOptions { + var userActivities: Set { get { return Set() } } + var urlContexts: Set { get { return Set() } } + } } class UISceneSession {} -class NSUserActivity {} +class NSUserActivity: Hashable { + static func == (lhs: NSUserActivity, rhs: NSUserActivity) -> Bool { + return true; + } + + func hash(into hasher: inout Hasher) {} + + var webpageURL: URL? { get { return nil } set { } } + var referrerURL: URL? { get { return nil } set { } } +} class UIOpenURLContext: Hashable { static func == (lhs: UIOpenURLContext, rhs: UIOpenURLContext) -> Bool { @@ -39,6 +51,8 @@ class UIOpenURLContext: Hashable { } func hash(into hasher: inout Hasher) {} + + var url: URL { get { return URL() } } } protocol UISceneDelegate { @@ -48,6 +62,8 @@ protocol UISceneDelegate { func scene(_: UIScene, openURLContexts: Set) } +func sink(arg: Any) {} + // --- tests --- class AppDelegate: UIApplicationDelegate { @@ -64,28 +80,88 @@ class AppDelegate: UIApplicationDelegate { } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { - launchOptions?[.url] // $ source=remote + _ = launchOptions?[.url] // $ source=remote return true } func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { - launchOptions?[.url] // $ source=remote + _ = launchOptions?[.url] // $ source=remote return true } } class SceneDelegate : UISceneDelegate { - func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) {} // $ source=remote - func scene(_: UIScene, continue: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, didUpdate: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, openURLContexts: Set) {} // $ source=remote + func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) { // $ source=remote + for userActivity in options.userActivities { + let x = userActivity.webpageURL + sink(arg: x) // $ MISSING: tainted + let y = userActivity.referrerURL + sink(arg: y) // $ MISSING: tainted + } + + for urlContext in options.urlContexts { + let z = urlContext.url + sink(arg: z) // $ MISSING: tainted + } + } + + func scene(_: UIScene, continue: NSUserActivity) { // $ source=remote + let x = `continue`.webpageURL + sink(arg: x) // $ tainted + let y = `continue`.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, didUpdate: NSUserActivity) { // $ source=remote + let x = didUpdate.webpageURL + sink(arg: x) // $ tainted + let y = didUpdate.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, openURLContexts: Set) { // $ source=remote + for openURLContext in openURLContexts { + let x = openURLContext.url + sink(arg: x) // $ MISSING: tainted + } + } } class Extended {} extension Extended : UISceneDelegate { - func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) {} // $ source=remote - func scene(_: UIScene, continue: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, didUpdate: NSUserActivity) {} // $ source=remote - func scene(_: UIScene, openURLContexts: Set) {} // $ source=remote + func scene(_: UIScene, willConnectTo: UISceneSession, options: UIScene.ConnectionOptions) { // $ source=remote + for userActivity in options.userActivities { + let x = userActivity.webpageURL + sink(arg: x) // $ MISSING: tainted + let y = userActivity.referrerURL + sink(arg: y) // $ MISSING: tainted + } + + for urlContext in options.urlContexts { + let z = urlContext.url + sink(arg: z) // $ MISSING: tainted + } + } + + func scene(_: UIScene, continue: NSUserActivity) { // $ source=remote + let x = `continue`.webpageURL + sink(arg: x) // $ tainted + let y = `continue`.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, didUpdate: NSUserActivity) { // $ source=remote + let x = didUpdate.webpageURL + sink(arg: x) // $ tainted + let y = didUpdate.referrerURL + sink(arg: y) // $ tainted + } + + func scene(_: UIScene, openURLContexts: Set) { // $ source=remote + for openURLContext in openURLContexts { + let x = openURLContext.url + sink(arg: x) // $ MISSING: tainted + } + } }