diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index b836f12c96f4..76a1299cb536 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -994,6 +994,18 @@ private module Cached { ) or unary_compares_eq(test.(BuiltinExpectCallValueNumber).getCondition(), op, k, areEqual, value) + or + exists(BinaryLogicalOperation logical, Expr operand, boolean b | + test.getAnInstruction().getUnconvertedResultExpression() = logical and + op.getDef().getUnconvertedResultExpression() = operand and + logical.impliesValue(operand, b, value.(BooleanValue).getValue()) + | + k = 1 and + areEqual = b + or + k = 0 and + areEqual = b.booleanNot() + ) } /** Rearrange various simple comparisons into `left == right + k` form. */ diff --git a/cpp/ql/test/library-tests/controlflow/guards/Guards.expected b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected index a56d8964c095..48bfb7aaf966 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/Guards.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected @@ -69,3 +69,11 @@ | test.cpp:168:8:168:8 | b | | test.cpp:176:7:176:8 | ! ... | | test.cpp:176:8:176:8 | c | +| test.cpp:182:6:182:16 | ! ... | +| test.cpp:182:8:182:9 | b1 | +| test.cpp:182:8:182:15 | ... && ... | +| test.cpp:182:14:182:15 | b2 | +| test.cpp:193:6:193:16 | ! ... | +| test.cpp:193:8:193:9 | b1 | +| test.cpp:193:8:193:15 | ... \|\| ... | +| test.cpp:193:14:193:15 | b2 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected index f2ce7f611f39..2ff93603a267 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected @@ -545,8 +545,12 @@ | 182 | ! ... == 1 when ... && ... is false | | 182 | ... && ... != 0 when ! ... is false | | 182 | ... && ... != 0 when ... && ... is true | +| 182 | ... && ... != 1 when ! ... is true | +| 182 | ... && ... != 1 when ... && ... is false | | 182 | ... && ... == 0 when ! ... is true | | 182 | ... && ... == 0 when ... && ... is false | +| 182 | ... && ... == 1 when ! ... is false | +| 182 | ... && ... == 1 when ... && ... is true | | 182 | ... < ... != 0 when ... && ... is true | | 182 | ... < ... != 0 when ... < ... is true | | 182 | ... < ... != 1 when ... < ... is false | @@ -559,6 +563,22 @@ | 182 | ... >= ... == 0 when ... >= ... is false | | 182 | ... >= ... == 1 when ... && ... is true | | 182 | ... >= ... == 1 when ... >= ... is true | +| 182 | b1 != 0 when ! ... is false | +| 182 | b1 != 0 when ... && ... is true | +| 182 | b1 != 0 when b1 is true | +| 182 | b1 != 1 when b1 is false | +| 182 | b1 == 0 when b1 is false | +| 182 | b1 == 1 when ! ... is false | +| 182 | b1 == 1 when ... && ... is true | +| 182 | b1 == 1 when b1 is true | +| 182 | b2 != 0 when ! ... is false | +| 182 | b2 != 0 when ... && ... is true | +| 182 | b2 != 0 when b2 is true | +| 182 | b2 != 1 when b2 is false | +| 182 | b2 == 0 when b2 is false | +| 182 | b2 == 1 when ! ... is false | +| 182 | b2 == 1 when ... && ... is true | +| 182 | b2 == 1 when b2 is true | | 182 | foo < 1.0+0 when ... && ... is true | | 182 | foo < 1.0+0 when ... < ... is true | | 182 | foo < 9.999999999999999547e-07+0 when ... >= ... is false | @@ -577,6 +597,38 @@ | 190 | c != 0 when c is true | | 190 | c == 0 when ! ... is true | | 190 | c == 0 when c is false | +| 193 | ! ... != 0 when ! ... is true | +| 193 | ! ... != 0 when ... \|\| ... is false | +| 193 | ! ... != 1 when ! ... is false | +| 193 | ! ... != 1 when ... \|\| ... is true | +| 193 | ! ... == 0 when ! ... is false | +| 193 | ! ... == 0 when ... \|\| ... is true | +| 193 | ! ... == 1 when ! ... is true | +| 193 | ! ... == 1 when ... \|\| ... is false | +| 193 | ... \|\| ... != 0 when ! ... is false | +| 193 | ... \|\| ... != 0 when ... \|\| ... is true | +| 193 | ... \|\| ... != 1 when ! ... is true | +| 193 | ... \|\| ... != 1 when ... \|\| ... is false | +| 193 | ... \|\| ... == 0 when ! ... is true | +| 193 | ... \|\| ... == 0 when ... \|\| ... is false | +| 193 | ... \|\| ... == 1 when ! ... is false | +| 193 | ... \|\| ... == 1 when ... \|\| ... is true | +| 193 | b1 != 0 when b1 is true | +| 193 | b1 != 1 when ! ... is true | +| 193 | b1 != 1 when ... \|\| ... is false | +| 193 | b1 != 1 when b1 is false | +| 193 | b1 == 0 when ! ... is true | +| 193 | b1 == 0 when ... \|\| ... is false | +| 193 | b1 == 0 when b1 is false | +| 193 | b1 == 1 when b1 is true | +| 193 | b2 != 0 when b2 is true | +| 193 | b2 != 1 when ! ... is true | +| 193 | b2 != 1 when ... \|\| ... is false | +| 193 | b2 != 1 when b2 is false | +| 193 | b2 == 0 when ! ... is true | +| 193 | b2 == 0 when ... \|\| ... is false | +| 193 | b2 == 0 when b2 is false | +| 193 | b2 == 1 when b2 is true | | 198 | ! ... != 0 when ! ... is true | | 198 | ! ... != 0 when b is false | | 198 | ! ... != 1 when ! ... is false | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected index 2857bdf511bf..cee020adadd9 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected @@ -130,3 +130,19 @@ | test.cpp:168:8:168:8 | b | false | 168 | 170 | | test.cpp:176:7:176:8 | ! ... | true | 176 | 178 | | test.cpp:176:8:176:8 | c | false | 176 | 178 | +| test.cpp:182:6:182:16 | ! ... | false | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | true | 182 | 184 | +| test.cpp:182:8:182:9 | b1 | true | 181 | 182 | +| test.cpp:182:8:182:9 | b1 | true | 182 | 182 | +| test.cpp:182:8:182:15 | ... && ... | false | 182 | 184 | +| test.cpp:182:8:182:15 | ... && ... | true | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | true | 185 | 188 | +| test.cpp:182:14:182:15 | b2 | true | 181 | 182 | +| test.cpp:193:6:193:16 | ! ... | false | 197 | 199 | +| test.cpp:193:6:193:16 | ! ... | true | 193 | 196 | +| test.cpp:193:8:193:9 | b1 | false | 192 | 193 | +| test.cpp:193:8:193:9 | b1 | false | 193 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | false | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | false | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | true | 197 | 199 | +| test.cpp:193:14:193:15 | b2 | false | 192 | 193 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected index 73a809b3175c..288b49ceb687 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected @@ -636,3 +636,79 @@ unary | test.cpp:176:8:176:8 | c | test.cpp:176:7:176:8 | ! ... | == | 1 | 176 | 178 | | test.cpp:176:8:176:8 | c | test.cpp:176:8:176:8 | c | != | 1 | 176 | 178 | | test.cpp:176:8:176:8 | c | test.cpp:176:8:176:8 | c | == | 0 | 176 | 178 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:6:182:16 | ! ... | != | 0 | 182 | 184 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:6:182:16 | ! ... | != | 1 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:6:182:16 | ! ... | == | 0 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:6:182:16 | ! ... | == | 1 | 182 | 184 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:9 | b1 | != | 0 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:9 | b1 | == | 1 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:15 | ... && ... | != | 0 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:15 | ... && ... | != | 1 | 182 | 184 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:15 | ... && ... | == | 0 | 182 | 184 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:8:182:15 | ... && ... | == | 1 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:14:182:15 | b2 | != | 0 | 185 | 188 | +| test.cpp:182:6:182:16 | ! ... | test.cpp:182:14:182:15 | b2 | == | 1 | 185 | 188 | +| test.cpp:182:8:182:9 | b1 | test.cpp:182:8:182:9 | b1 | != | 0 | 181 | 182 | +| test.cpp:182:8:182:9 | b1 | test.cpp:182:8:182:9 | b1 | != | 0 | 182 | 182 | +| test.cpp:182:8:182:9 | b1 | test.cpp:182:8:182:9 | b1 | == | 1 | 181 | 182 | +| test.cpp:182:8:182:9 | b1 | test.cpp:182:8:182:9 | b1 | == | 1 | 182 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | != | 0 | 182 | 184 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | != | 1 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | != | 1 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | == | 0 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | == | 0 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:6:182:16 | ! ... | == | 1 | 182 | 184 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:9 | b1 | != | 0 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:9 | b1 | != | 0 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:9 | b1 | == | 1 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:9 | b1 | == | 1 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | != | 0 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | != | 0 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | != | 1 | 182 | 184 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | == | 0 | 182 | 184 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | == | 1 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:8:182:15 | ... && ... | == | 1 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:14:182:15 | b2 | != | 0 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:14:182:15 | b2 | != | 0 | 185 | 188 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:14:182:15 | b2 | == | 1 | 181 | 182 | +| test.cpp:182:8:182:15 | ... && ... | test.cpp:182:14:182:15 | b2 | == | 1 | 185 | 188 | +| test.cpp:182:14:182:15 | b2 | test.cpp:182:14:182:15 | b2 | != | 0 | 181 | 182 | +| test.cpp:182:14:182:15 | b2 | test.cpp:182:14:182:15 | b2 | == | 1 | 181 | 182 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:6:193:16 | ! ... | != | 0 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:6:193:16 | ! ... | != | 1 | 197 | 199 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:6:193:16 | ! ... | == | 0 | 197 | 199 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:6:193:16 | ! ... | == | 1 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:9 | b1 | != | 1 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:9 | b1 | == | 0 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:15 | ... \|\| ... | != | 0 | 197 | 199 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:15 | ... \|\| ... | != | 1 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:15 | ... \|\| ... | == | 0 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:8:193:15 | ... \|\| ... | == | 1 | 197 | 199 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:14:193:15 | b2 | != | 1 | 193 | 196 | +| test.cpp:193:6:193:16 | ! ... | test.cpp:193:14:193:15 | b2 | == | 0 | 193 | 196 | +| test.cpp:193:8:193:9 | b1 | test.cpp:193:8:193:9 | b1 | != | 1 | 192 | 193 | +| test.cpp:193:8:193:9 | b1 | test.cpp:193:8:193:9 | b1 | != | 1 | 193 | 193 | +| test.cpp:193:8:193:9 | b1 | test.cpp:193:8:193:9 | b1 | == | 0 | 192 | 193 | +| test.cpp:193:8:193:9 | b1 | test.cpp:193:8:193:9 | b1 | == | 0 | 193 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | != | 0 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | != | 0 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | != | 1 | 197 | 199 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | == | 0 | 197 | 199 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | == | 1 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:6:193:16 | ! ... | == | 1 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:9 | b1 | != | 1 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:9 | b1 | != | 1 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:9 | b1 | == | 0 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:9 | b1 | == | 0 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | != | 0 | 197 | 199 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | != | 1 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | != | 1 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | == | 0 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | == | 0 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:8:193:15 | ... \|\| ... | == | 1 | 197 | 199 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:14:193:15 | b2 | != | 1 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:14:193:15 | b2 | != | 1 | 193 | 196 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:14:193:15 | b2 | == | 0 | 192 | 193 | +| test.cpp:193:8:193:15 | ... \|\| ... | test.cpp:193:14:193:15 | b2 | == | 0 | 193 | 196 | +| test.cpp:193:14:193:15 | b2 | test.cpp:193:14:193:15 | b2 | != | 1 | 192 | 193 | +| test.cpp:193:14:193:15 | b2 | test.cpp:193:14:193:15 | b2 | == | 0 | 192 | 193 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/test.cpp b/cpp/ql/test/library-tests/controlflow/guards/test.cpp index 3bdcdcc01ff5..7349671633d4 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/test.cpp +++ b/cpp/ql/test/library-tests/controlflow/guards/test.cpp @@ -176,4 +176,26 @@ void test_with_negated_binary_relational(int a, int b) { if (!c) { } +} + +void test_logical_and(bool b1, bool b2) { + if(!(b1 && b2)) { + use(b1); + use(b2); + } else { + // b1 = true and b2 = true + use(b1); + use(b2); + } +} + +void test_logical_or(bool b1, bool b2) { + if(!(b1 || b2)) { + // b1 = false and b2 = false + use(b1); + use(b2); + } else { + use(b1); + use(b2); + } } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp index 20beabf8c932..a67fa4cdb425 100644 --- a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp @@ -68,3 +68,10 @@ int templateClassContext :: val = MACRO_TEMPLATECLASSCONTEXT_REFERENCED; #define INSTANTIATION templateClassContext tcci; + +#define BAR + +#if defined(BAR) && \ + defined(BAR) +#warning BAR defined +#endif diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h index 288442716b52..780caa66d9ff 100644 --- a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h @@ -4,15 +4,15 @@ //#pragma byte_order(big_endian) #warning "Not in Kansas any more" -//#define MULTILINE \ +#define MULTILINE \ /* Hello */ \ world \ /* from */ \ a long \ /* macro */ -//#undef \ +#undef \ MULTILINE -//#include \ - \ +#include \ + "pp.h" \ \ diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected index cff0dd489eaa..dd8696b31650 100644 --- a/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected @@ -27,6 +27,13 @@ | pp.cpp:0:0:0:0 | pp.cpp | 60 | 3 | 60 | 21 | Macro | IN_TEMPLATE | | | pp.cpp:0:0:0:0 | pp.cpp | 61 | 1 | 61 | 7 | PreprocessorEndif | N/A | N/A | | pp.cpp:0:0:0:0 | pp.cpp | 69 | 1 | 69 | 21 | Macro | INSTANTIATION | | +| pp.cpp:0:0:0:0 | pp.cpp | 72 | 1 | 72 | 11 | Macro | BAR | | +| pp.cpp:0:0:0:0 | pp.cpp | 74 | 1 | 74 | 21 | PreprocessorIf | defined(BAR) && \\ | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 76 | 1 | 76 | 20 | PreprocessorWarning | BAR defined | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 77 | 1 | 77 | 6 | PreprocessorEndif | N/A | N/A | | pp.h:0:0:0:0 | pp.h | 1 | 1 | 1 | 12 | PreprocessorPragma | once | N/A | | pp.h:0:0:0:0 | pp.h | 3 | 1 | 3 | 27 | PreprocessorLine | 33 "emerald_city.h" | N/A | | pp.h:0:0:0:0 | pp.h | 5 | 1 | 5 | 33 | PreprocessorWarning | "Not in Kansas any more" | N/A | +| pp.h:0:0:0:0 | pp.h | 7 | 1 | 11 | 8 | Macro | MULTILINE | world a long | +| pp.h:0:0:0:0 | pp.h | 13 | 1 | 14 | 11 | PreprocessorUndef | MULTILINE | N/A | +| pp.h:0:0:0:0 | pp.h | 16 | 1 | 17 | 8 | Include | "pp.h" | N/A | diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 44738dfc03b3..2855bd9644c4 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -14,6 +14,7 @@ Microsoft.Android.Build,,1,14,,,,,,,,,,,,,1,,,,,,12,2 Microsoft.Apple.Build,,,7,,,,,,,,,,,,,,,,,,,7, Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,,,28,,,,,,,,,, Microsoft.AspNetCore.Components,2,4,2,,,,,,,2,,,,,,,,,4,,,1,1 +Microsoft.AspNetCore.Http,,,1,,,,,,,,,,,,,,,,,,,1, Microsoft.AspNetCore.Mvc,,,2,,,,,,,,,,,,,,,,,,,,2 Microsoft.AspNetCore.WebUtilities,,,2,,,,,,,,,,,,,,,,,,,2, Microsoft.CSharp,,,2,,,,,,,,,,,,,,,,,,,2, diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 1d3285094a23..b2786d66767d 100644 --- a/csharp/documentation/library-coverage/coverage.rst +++ b/csharp/documentation/library-coverage/coverage.rst @@ -9,6 +9,6 @@ C# framework & library support Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194, System,"``System.*``, ``System``",47,10819,54,5 - Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``JsonToItemsTaskFactory``, ``Microsoft.Android.Build``, ``Microsoft.Apple.Build``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NET.Sdk.WebAssembly``, ``Microsoft.NET.WebAssembly.Webcil``, ``Microsoft.VisualBasic``, ``Microsoft.WebAssembly.Build.Tasks``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",61,2074,152,4 - Totals,,108,12900,400,9 + Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``JsonToItemsTaskFactory``, ``Microsoft.Android.Build``, ``Microsoft.Apple.Build``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NET.Sdk.WebAssembly``, ``Microsoft.NET.WebAssembly.Webcil``, ``Microsoft.VisualBasic``, ``Microsoft.WebAssembly.Build.Tasks``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",61,2075,152,4 + Totals,,108,12901,400,9 diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs index f424e98a7a51..32c00f8a729e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs @@ -14,7 +14,12 @@ internal sealed class ImplicitToString : Expression /// private static IMethodSymbol? GetToStringMethod(ITypeSymbol? type) { - return type? + if (type is null) + { + return null; + } + + var toString = type .GetMembers() .OfType() .Where(method => @@ -22,6 +27,8 @@ internal sealed class ImplicitToString : Expression method.Parameters.Length == 0 ) .FirstOrDefault(); + + return toString ?? GetToStringMethod(type.BaseType); } private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base(new ExpressionInfo(info.Context, AnnotatedTypeSymbol.CreateNotAnnotated(toString.ReturnType), info.Location, ExprKind.METHOD_INVOCATION, info.Parent, info.Child, isCompilerGenerated: true, info.ExprValue)) diff --git a/csharp/ql/test/library-tests/implicittostring/implicitToString.cs b/csharp/ql/test/library-tests/implicittostring/implicitToString.cs index d75044246f8e..8d74d23791ad 100644 --- a/csharp/ql/test/library-tests/implicittostring/implicitToString.cs +++ b/csharp/ql/test/library-tests/implicittostring/implicitToString.cs @@ -10,6 +10,10 @@ public override string ToString() } } + public class Container2 : Container { } + + public class Container3 { } + public class FormattableContainer : IFormattable { public string ToString(string format, IFormatProvider formatProvider) @@ -40,5 +44,11 @@ public void M() y = "Hello" + formattableContainer; // Implicit call to ToString(). y = $"Hello {formattableContainer}"; // Implicit call to ToString(string, IFormatProvider). We don't handle this. y = $"Hello {formattableContainer:D}"; // Implicit call to ToString(string, IFormatProvider). We don't handle this. + + var container2 = new Container2(); + y = "Hello" + container2; // Implicit Container.ToString call. + + var container3 = new Container3(); + y = "Hello" + container3; // Implicit Object.ToString call. } } diff --git a/csharp/ql/test/library-tests/implicittostring/implicitToString.expected b/csharp/ql/test/library-tests/implicittostring/implicitToString.expected index 4878b12a4fdb..14efebf23207 100644 --- a/csharp/ql/test/library-tests/implicittostring/implicitToString.expected +++ b/csharp/ql/test/library-tests/implicittostring/implicitToString.expected @@ -1,4 +1,6 @@ -| implicitToString.cs:31:27:31:35 | call to method ToString | -| implicitToString.cs:33:22:33:30 | call to method ToString | -| implicitToString.cs:35:22:35:30 | call to method ToString | -| implicitToString.cs:40:23:40:42 | call to method ToString | +| implicitToString.cs:35:27:35:35 | call to method ToString | Container | +| implicitToString.cs:37:22:37:30 | call to method ToString | Container | +| implicitToString.cs:39:22:39:30 | call to method ToString | Container | +| implicitToString.cs:44:23:44:42 | call to method ToString | FormattableContainer | +| implicitToString.cs:49:23:49:32 | call to method ToString | Container | +| implicitToString.cs:52:23:52:32 | call to method ToString | Object | diff --git a/csharp/ql/test/library-tests/implicittostring/implicitToString.ql b/csharp/ql/test/library-tests/implicittostring/implicitToString.ql index 4eb518d84c39..fee24b480c9c 100644 --- a/csharp/ql/test/library-tests/implicittostring/implicitToString.ql +++ b/csharp/ql/test/library-tests/implicittostring/implicitToString.ql @@ -2,4 +2,4 @@ import csharp from MethodCall c where c.isImplicit() -select c +select c, c.getTarget().getDeclaringType().toString() diff --git a/csharp/ql/test/query-tests/Nullness/Implications.expected b/csharp/ql/test/query-tests/Nullness/Implications.expected index 45ffbc1d92b0..dbb6ab23a9aa 100644 --- a/csharp/ql/test/query-tests/Nullness/Implications.expected +++ b/csharp/ql/test/query-tests/Nullness/Implications.expected @@ -908,8 +908,8 @@ | D.cs:253:13:253:14 | access to local variable o2 | null | D.cs:249:18:249:38 | ... ? ... : ... | null | | D.cs:266:13:266:27 | ... is ... | true | D.cs:266:13:266:17 | access to local variable other | non-null | | D.cs:310:21:310:26 | ... + ... | non-null | D.cs:310:21:310:22 | "" | non-null | -| D.cs:310:21:310:26 | ... + ... | non-null | D.cs:310:26:310:26 | access to parameter a | non-null | -| D.cs:310:21:310:26 | ... + ... | null | D.cs:310:26:310:26 | access to parameter a | null | +| D.cs:310:21:310:26 | ... + ... | non-null | D.cs:310:26:310:26 | call to method ToString | non-null | +| D.cs:310:21:310:26 | ... + ... | null | D.cs:310:26:310:26 | call to method ToString | null | | D.cs:312:17:312:23 | !... | false | D.cs:312:18:312:23 | access to local variable s_null | true | | D.cs:312:17:312:23 | !... | true | D.cs:312:18:312:23 | access to local variable s_null | false | | D.cs:318:16:318:62 | ... && ... | true | D.cs:318:16:318:36 | ... == ... | true | diff --git a/rust/ql/integration-tests/hello-project/summary.expected b/rust/ql/integration-tests/hello-project/summary.expected index 4b584a4c7953..20f4b216b64c 100644 --- a/rust/ql/integration-tests/hello-project/summary.expected +++ b/rust/ql/integration-tests/hello-project/summary.expected @@ -14,6 +14,11 @@ | Macro calls - resolved | 2 | | Macro calls - total | 2 | | Macro calls - unresolved | 0 | -| Sensitive data | 0 | +| Taint edges - number of edges | 2 | +| Taint reach - nodes tainted | 0 | +| Taint reach - per million nodes | 0 | +| Taint sinks - cryptographic operations | 0 | +| Taint sinks - query sinks | 0 | | Taint sources - active | 0 | -| Taint sources - total | 0 | +| Taint sources - disabled | 0 | +| Taint sources - sensitive data | 0 | diff --git a/rust/ql/integration-tests/hello-workspace/summary.cargo.expected b/rust/ql/integration-tests/hello-workspace/summary.cargo.expected index eb8f861f9358..e0d5ce240a39 100644 --- a/rust/ql/integration-tests/hello-workspace/summary.cargo.expected +++ b/rust/ql/integration-tests/hello-workspace/summary.cargo.expected @@ -14,6 +14,11 @@ | Macro calls - resolved | 2 | | Macro calls - total | 2 | | Macro calls - unresolved | 0 | -| Sensitive data | 0 | +| Taint edges - number of edges | 2 | +| Taint reach - nodes tainted | 0 | +| Taint reach - per million nodes | 0 | +| Taint sinks - cryptographic operations | 0 | +| Taint sinks - query sinks | 0 | | Taint sources - active | 0 | -| Taint sources - total | 0 | +| Taint sources - disabled | 0 | +| Taint sources - sensitive data | 0 | diff --git a/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected b/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected index eb8f861f9358..e0d5ce240a39 100644 --- a/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected +++ b/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected @@ -14,6 +14,11 @@ | Macro calls - resolved | 2 | | Macro calls - total | 2 | | Macro calls - unresolved | 0 | -| Sensitive data | 0 | +| Taint edges - number of edges | 2 | +| Taint reach - nodes tainted | 0 | +| Taint reach - per million nodes | 0 | +| Taint sinks - cryptographic operations | 0 | +| Taint sinks - query sinks | 0 | | Taint sources - active | 0 | -| Taint sources - total | 0 | +| Taint sources - disabled | 0 | +| Taint sources - sensitive data | 0 | diff --git a/rust/ql/src/queries/summary/CryptographicOperations.ql b/rust/ql/src/queries/summary/CryptographicOperations.ql new file mode 100644 index 000000000000..3b4c03c40fd2 --- /dev/null +++ b/rust/ql/src/queries/summary/CryptographicOperations.ql @@ -0,0 +1,59 @@ +/** + * @name Cryptographic Operations + * @description List all cryptographic operations found in the database. + * @kind problem + * @problem.severity info + * @id rust/summary/cryptographic-operations + * @tags summary + */ + +import rust +import codeql.rust.Concepts +import codeql.rust.security.WeakSensitiveDataHashingExtensions + +/** + * Gets the type of cryptographic algorithm `alg`. + */ +string getAlgorithmType(Cryptography::CryptographicAlgorithm alg) { + alg instanceof Cryptography::EncryptionAlgorithm and result = "EncryptionAlgorithm" + or + alg instanceof Cryptography::HashingAlgorithm and result = "HashingAlgorithm" + or + alg instanceof Cryptography::PasswordHashingAlgorithm and result = "PasswordHashingAlgorithm" +} + +/** + * Gets a feature of cryptographic algorithm `alg`. + */ +string getAlgorithmFeature(Cryptography::CryptographicAlgorithm alg) { + alg.isWeak() and result = "WEAK" +} + +/** + * Gets a description of cryptographic algorithm `alg`. + */ +string describeAlgorithm(Cryptography::CryptographicAlgorithm alg) { + result = + getAlgorithmType(alg) + " " + alg.getName() + " " + concat(getAlgorithmFeature(alg), ", ") +} + +/** + * Gets a feature of cryptographic operation `op`. + */ +string getOperationFeature(Cryptography::CryptographicOperation op) { + result = "inputs:" + strictcount(op.getAnInput()).toString() or + result = "blockmodes:" + strictcount(op.getBlockMode()).toString() +} + +/** + * Gets a description of cryptographic operation `op`. + */ +string describeOperation(Cryptography::CryptographicOperation op) { + result = describeAlgorithm(op.getAlgorithm()) + " " + concat(getOperationFeature(op), ", ") + or + not exists(op.getAlgorithm()) and + result = "(unknown) " + concat(getOperationFeature(op), ", ") +} + +from Cryptography::CryptographicOperation operation +select operation, describeOperation(operation) diff --git a/rust/ql/src/queries/summary/QuerySinkCounts.ql b/rust/ql/src/queries/summary/QuerySinkCounts.ql new file mode 100644 index 000000000000..6525c3263a11 --- /dev/null +++ b/rust/ql/src/queries/summary/QuerySinkCounts.ql @@ -0,0 +1,17 @@ +/** + * @name Query Sink Counts + * @description Lists the number of query sinks of each type found in the database. Query sinks are + * flow sinks that are used as possible locations for query results. Cryptographic + * operations are excluded. + * @kind metric + * @id rust/summary/query-sink-counts + * @tags summary + */ + +import rust +import codeql.rust.dataflow.DataFlow +import Stats + +from string kind, int num +where num = strictcount(DataFlow::Node n | getAQuerySinkKind(n) = kind) +select kind, num diff --git a/rust/ql/src/queries/summary/QuerySinks.ql b/rust/ql/src/queries/summary/QuerySinks.ql new file mode 100644 index 000000000000..09cd7fcb2991 --- /dev/null +++ b/rust/ql/src/queries/summary/QuerySinks.ql @@ -0,0 +1,17 @@ +/** + * @name Query Sinks + * @description Lists query sinks that are found in the database. Query sinks are flow sinks that + * are used as possible locations for query results. Cryptographic operations are + * excluded (see `rust/summary/cryptographic-operations` instead). + * @kind problem + * @problem.severity info + * @id rust/summary/query-sinks + * @tags summary + */ + +import rust +import codeql.rust.dataflow.DataFlow +import Stats + +from DataFlow::Node n +select n, "Sink for " + strictconcat(getAQuerySinkKind(n), ", ") + "." diff --git a/rust/ql/src/queries/summary/Stats.qll b/rust/ql/src/queries/summary/Stats.qll index c2993b47899f..8bdb25381bc6 100644 --- a/rust/ql/src/queries/summary/Stats.qll +++ b/rust/ql/src/queries/summary/Stats.qll @@ -3,11 +3,13 @@ */ import rust +private import codeql.rust.dataflow.DataFlow private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.dataflow.internal.TaintTrackingImpl private import codeql.rust.AstConsistency as AstConsistency private import codeql.rust.controlflow.internal.CfgConsistency as CfgConsistency private import codeql.rust.dataflow.internal.DataFlowConsistency as DataFlowConsistency +private import codeql.rust.security.SqlInjectionExtensions /** * Gets a count of the total number of lines of code in the database. @@ -41,3 +43,25 @@ int getTotalCfgInconsistencies() { int getTotalDataFlowInconsistencies() { result = sum(string type | | DataFlowConsistency::getInconsistencyCounts(type)) } + +/** + * Gets the total number of taint edges in the database. + */ +int getTaintEdgesCount() { + result = + count(DataFlow::Node a, DataFlow::Node b | + RustTaintTracking::defaultAdditionalTaintStep(a, b, _) + ) +} + +/** + * Gets a kind of query for which `n` is a sink (if any). + */ +string getAQuerySinkKind(DataFlow::Node n) { + (n instanceof SqlInjection::Sink and result = "SqlInjection") +} + +/** + * Gets a count of the total number of query sinks in the database. + */ +int getQuerySinksCount() { result = count(DataFlow::Node n | exists(getAQuerySinkKind(n))) } diff --git a/rust/ql/src/queries/summary/SummaryStats.ql b/rust/ql/src/queries/summary/SummaryStats.ql index 005233f87cf3..4e14045428f2 100644 --- a/rust/ql/src/queries/summary/SummaryStats.ql +++ b/rust/ql/src/queries/summary/SummaryStats.ql @@ -9,8 +9,10 @@ import rust import codeql.rust.Concepts import codeql.rust.security.SensitiveData +import codeql.rust.security.WeakSensitiveDataHashingExtensions import codeql.rust.Diagnostics import Stats +import TaintReach from string key, int value where @@ -54,9 +56,21 @@ where or key = "Macro calls - unresolved" and value = count(MacroCall mc | not mc.hasExpanded()) or - key = "Taint sources - total" and value = count(ThreatModelSource s) - or key = "Taint sources - active" and value = count(ActiveThreatModelSource s) or - key = "Sensitive data" and value = count(SensitiveData d) + key = "Taint sources - disabled" and + value = count(ThreatModelSource s | not s instanceof ActiveThreatModelSource) + or + key = "Taint sources - sensitive data" and value = count(SensitiveData d) + or + key = "Taint edges - number of edges" and value = getTaintEdgesCount() + or + key = "Taint reach - nodes tainted" and value = getTaintedNodesCount() + or + key = "Taint reach - per million nodes" and value = getTaintReach().floor() + or + key = "Taint sinks - query sinks" and value = getQuerySinksCount() + or + key = "Taint sinks - cryptographic operations" and + value = count(Cryptography::CryptographicOperation o) select key, value order by key diff --git a/rust/ql/src/queries/summary/TaintReach.qll b/rust/ql/src/queries/summary/TaintReach.qll new file mode 100644 index 000000000000..0f00fe6f7c6e --- /dev/null +++ b/rust/ql/src/queries/summary/TaintReach.qll @@ -0,0 +1,31 @@ +/** + * Taint reach computation. Taint reach is the proportion of all dataflow nodes that can be reached + * via taint flow from any active thread model source. It's usually expressed per million nodes. + */ + +import rust +private import codeql.rust.Concepts +private import codeql.rust.dataflow.DataFlow +private import codeql.rust.dataflow.TaintTracking + +/** + * A taint configuration for taint reach (flow to any node from any modeled source). + */ +private module TaintReachConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof ActiveThreatModelSource } + + predicate isSink(DataFlow::Node node) { any() } +} + +private module TaintReachFlow = TaintTracking::Global; + +/** + * Gets the total number of data flow nodes that taint reaches (from any source). + */ +int getTaintedNodesCount() { result = count(DataFlow::Node n | TaintReachFlow::flowTo(n)) } + +/** + * Gets the proportion of data flow nodes that taint reaches (from any source), + * expressed as a count per million nodes. + */ +float getTaintReach() { result = (getTaintedNodesCount() * 1000000.0) / count(DataFlow::Node n) } diff --git a/rust/ql/test/query-tests/diagnostics/SummaryStats.expected b/rust/ql/test/query-tests/diagnostics/SummaryStats.expected index 78bfeb9322c7..5223601d625f 100644 --- a/rust/ql/test/query-tests/diagnostics/SummaryStats.expected +++ b/rust/ql/test/query-tests/diagnostics/SummaryStats.expected @@ -14,6 +14,11 @@ | Macro calls - resolved | 8 | | Macro calls - total | 9 | | Macro calls - unresolved | 1 | -| Sensitive data | 0 | +| Taint edges - number of edges | 2 | +| Taint reach - nodes tainted | 0 | +| Taint reach - per million nodes | 0 | +| Taint sinks - cryptographic operations | 0 | +| Taint sinks - query sinks | 0 | | Taint sources - active | 0 | -| Taint sources - total | 0 | +| Taint sources - disabled | 0 | +| Taint sources - sensitive data | 0 |