Skip to content

Commit 65cc988

Browse files
author
Robert Marsh
committed
Merge branch 'main' into rdmarsh2/cpp/explicit-conversion-perf
2 parents 083a4b2 + 9879c6c commit 65cc988

File tree

509 files changed

+22637
-10986
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

509 files changed

+22637
-10986
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"slevesque.vscode-zipexplorer"
55
],
66
"settings": {
7-
"codeQL.experimentalBqrsParsing": true
7+
"codeQL.runningQueries.memory": 2048
88
}
99
}

.github/workflows/labeler.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: "Pull Request Labeler"
2+
on:
3+
- pull_request_target
4+
5+
jobs:
6+
triage:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/labeler@v2
10+
with:
11+
repo-token: "${{ secrets.GITHUB_TOKEN }}"

change-notes/1.26/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The following changes in version 1.26 affect C/C++ analysis in all applications.
1313

1414
| **Query** | **Expected impact** | **Change** |
1515
|----------------------------|------------------------|------------------------------------------------------------------|
16+
| Declaration hides parameter (`cpp/declaration-hides-parameter`) | Fewer false positive results | False positives involving template functions have been fixed. |
1617
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
1718
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. |
1819
| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |

change-notes/1.26/analysis-csharp.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ The following changes in version 1.26 affect C# analysis in all applications.
2121
* Partial method bodies are extracted. Previously, partial method bodies were skipped completely.
2222
* Inferring the lengths of implicitely sized arrays is fixed. Previously, multidimensional arrays were always extracted with the same length for
2323
each dimension. With the fix, the array sizes `2` and `1` are extracted for `new int[,]{{1},{2}}`. Previously `2` and `2` were extracted.
24+
* The extractor is now assembly-insensitive by default. This means that two entities with the same
25+
fully-qualified name are now mapped to the same entity in the resulting database, regardless of
26+
whether they belong to different assemblies. Assembly sensitivity can be reenabled by passing
27+
`--assemblysensitivetrap` to the extractor.
2428

2529
## Changes to libraries
2630

change-notes/1.26/analysis-javascript.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@
2626

2727
| **Query** | **Expected impact** | **Change** |
2828
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
29+
| Potentially unsafe external link (`js/unsafe-external-link`) | Fewer results | This query no longer flags URLs constructed using a template system where only the hash or query part of the URL is dynamic. |
2930
| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. |
3031
| Ambiguous HTML id attribute (`js/duplicate-html-id`) | Results no longer shown | Precision tag reduced to "low". The query is no longer run by default. |
3132
| Unused loop iteration variable (`js/unused-loop-variable`) | Fewer results | This query no longer flags variables in a destructuring array assignment that are not the last variable in the destructed array. |
33+
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. |
3234

3335

3436
## Changes to libraries
37+
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.

cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,35 @@
1111

1212
import cpp
1313

14+
/**
15+
* Gets the template that a function `f` is constructed from, or just `f` if it
16+
* is not from a template instantiation.
17+
*/
18+
Function getConstructedFrom(Function f) {
19+
f.isConstructedFrom(result)
20+
or
21+
not f.isConstructedFrom(_) and
22+
result = f
23+
}
24+
1425
/**
1526
* Gets the parameter of `f` with name `name`, which has to come from the
1627
* _definition_ of `f` and not a prototype declaration.
1728
* We also exclude names from functions that have multiple definitions.
1829
* This should not happen in a single application but since we
1930
* have a system wide view it is likely to happen for instance for
2031
* the main function.
32+
*
33+
* Note: we use `getConstructedFrom` to ensure that we look at template
34+
* functions rather than their instantiations. We get better results this way
35+
* as the instantiation is artificial and may have inherited parameter names
36+
* from the declaration rather than the definition.
2137
*/
2238
ParameterDeclarationEntry functionParameterNames(Function f, string name) {
2339
exists(FunctionDeclarationEntry fe |
2440
result.getFunctionDeclarationEntry() = fe and
25-
fe.getFunction() = f and
41+
getConstructedFrom(f).getDefinition() = fe and
2642
fe.getLocation() = f.getDefinitionLocation() and
27-
result.getFile() = fe.getFile() and // Work around CPP-331
2843
strictcount(f.getDefinitionLocation()) = 1 and
2944
result.getName() = name
3045
)

cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ predicate dereferenceThis(Expr e) {
4545
or
4646
// `*this = ...` (where `=` is not overloaded, so an `AssignExpr`)
4747
dereferenceThis(e.(AssignExpr).getLValue())
48+
or
49+
// `e ? ... : ... `
50+
exists(ConditionalExpr cond |
51+
cond = e and
52+
dereferenceThis(cond.getThen()) and
53+
dereferenceThis(cond.getElse())
54+
)
55+
or
56+
// `..., ... `
57+
dereferenceThis(e.(CommaExpr).getRightOperand())
4858
}
4959

5060
/**

cpp/ql/src/semmle/code/cpp/exprs/Expr.qll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,3 +1275,31 @@ class SpaceshipExpr extends BinaryOperation, @spaceshipexpr {
12751275

12761276
override string getOperator() { result = "<=>" }
12771277
}
1278+
1279+
/**
1280+
* A C/C++ `co_await` expression.
1281+
* ```
1282+
* co_await foo();
1283+
* ```
1284+
*/
1285+
class CoAwaitExpr extends UnaryOperation, @co_await {
1286+
override string getAPrimaryQlClass() { result = "CoAwaitExpr" }
1287+
1288+
override string getOperator() { result = "co_await" }
1289+
1290+
override int getPrecedence() { result = 16 }
1291+
}
1292+
1293+
/**
1294+
* A C/C++ `co_yield` expression.
1295+
* ```
1296+
* co_yield 1;
1297+
* ```
1298+
*/
1299+
class CoYieldExpr extends UnaryOperation, @co_yield {
1300+
override string getAPrimaryQlClass() { result = "CoYieldExpr" }
1301+
1302+
override string getOperator() { result = "co_yield" }
1303+
1304+
override int getPrecedence() { result = 2 }
1305+
}

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ private import semmle.code.cpp.ir.IR
1111
private import semmle.code.cpp.controlflow.IRGuards
1212
private import semmle.code.cpp.models.interfaces.DataFlow
1313

14+
cached
1415
private newtype TIRDataFlowNode =
1516
TInstructionNode(Instruction i) or
1617
TOperandNode(Operand op) or
@@ -533,11 +534,11 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
533534
* data flow. It may have less flow than the `localFlowStep` predicate.
534535
*/
535536
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
536-
// Instruction -> Instruction flow
537-
simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
538-
or
539537
// Operand -> Instruction flow
540-
simpleOperandLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
538+
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
539+
or
540+
// Instruction -> Operand flow
541+
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
541542
}
542543

543544
pragma[noinline]
@@ -549,26 +550,20 @@ private predicate getFieldSizeOfClass(Class c, Type type, int size) {
549550
)
550551
}
551552

552-
private predicate simpleOperandLocalFlowStep(Operand opFrom, Instruction iTo) {
553-
// Certain dataflow steps (for instance `PostUpdateNode.getPreUpdateNode()`) generates flow to
554-
// operands, so we include dataflow from those operands to the "result" of the instruction (i.e., to
555-
// the instruction itself).
556-
exists(PostUpdateNode post |
557-
opFrom = post.getPreUpdateNode().asOperand() and
558-
iTo.getAnOperand() = opFrom
553+
private predicate isSingleFieldClass(Type type, Class cTo) {
554+
exists(int size |
555+
cTo.getSize() = size and
556+
getFieldSizeOfClass(cTo, type, size)
559557
)
560558
}
561559

562-
cached
563-
private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
564-
iTo.(CopyInstruction).getSourceValue() = iFrom
560+
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
561+
// Propagate flow from an instruction to its exact uses.
562+
opTo.getDef() = iFrom
565563
or
566-
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom
567-
or
568-
// A read side effect is almost never exact since we don't know exactly how
569-
// much memory the callee will read.
570-
iTo.(ReadSideEffectInstruction).getSideEffectOperand().getAnyDef() = iFrom and
571-
not iFrom.isResultConflated()
564+
opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and
565+
not iFrom.isResultConflated() and
566+
iFrom = opTo.getAnyDef()
572567
or
573568
// Loading a single `int` from an `int *` parameter is not an exact load since
574569
// the parameter may point to an entire array rather than a single `int`. The
@@ -582,20 +577,38 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
582577
// leads to a phi node.
583578
exists(InitializeIndirectionInstruction init |
584579
iFrom = init and
585-
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = init and
580+
opTo.(LoadOperand).getAnyDef() = init and
586581
// Check that the types match. Otherwise we can get flow from an object to
587582
// its fields, which leads to field conflation when there's flow from other
588583
// fields to the object elsewhere.
589584
init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
590-
iTo.getResultType().getUnspecifiedType()
585+
opTo.getType().getUnspecifiedType()
586+
)
587+
or
588+
// Flow from stores to structs with a single field to a load of that field.
589+
exists(LoadInstruction load |
590+
load.getSourceValueOperand() = opTo and
591+
opTo.getAnyDef() = iFrom and
592+
isSingleFieldClass(iFrom.getResultType(), opTo.getType())
591593
)
594+
}
595+
596+
cached
597+
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
598+
iTo.(CopyInstruction).getSourceValueOperand() = opFrom
599+
or
600+
iTo.(PhiInstruction).getAnInputOperand() = opFrom
601+
or
602+
// A read side effect is almost never exact since we don't know exactly how
603+
// much memory the callee will read.
604+
iTo.(ReadSideEffectInstruction).getSideEffectOperand() = opFrom
592605
or
593606
// Treat all conversions as flow, even conversions between different numeric types.
594-
iTo.(ConvertInstruction).getUnary() = iFrom
607+
iTo.(ConvertInstruction).getUnaryOperand() = opFrom
595608
or
596-
iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
609+
iTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom
597610
or
598-
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
611+
iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
599612
or
600613
// A chi instruction represents a point where a new value (the _partial_
601614
// operand) may overwrite an old value (the _total_ operand), but the alias
@@ -608,7 +621,7 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
608621
//
609622
// Flow through the partial operand belongs in the taint-tracking libraries
610623
// for now.
611-
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
624+
iTo.getAnOperand().(ChiTotalOperand) = opFrom
612625
or
613626
// Add flow from write side-effects to non-conflated chi instructions through their
614627
// partial operands. From there, a `readStep` will find subsequent reads of that field.
@@ -623,24 +636,16 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
623636
// Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
624637
// `setX`, which will be melded into `p` through a chi instruction.
625638
exists(ChiInstruction chi | chi = iTo |
626-
chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and
639+
opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
640+
chi.getPartialOperand() = opFrom and
627641
not chi.isResultConflated()
628642
)
629643
or
630-
// Flow from stores to structs with a single field to a load of that field.
631-
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
632-
exists(int size, Type type, Class cTo |
633-
type = iFrom.getResultType() and
634-
cTo = iTo.getResultType() and
635-
cTo.getSize() = size and
636-
getFieldSizeOfClass(cTo, type, size)
637-
)
638-
or
639644
// Flow through modeled functions
640-
modelFlow(iFrom, iTo)
645+
modelFlow(opFrom, iTo)
641646
}
642647

643-
private predicate modelFlow(Instruction iFrom, Instruction iTo) {
648+
private predicate modelFlow(Operand opFrom, Instruction iTo) {
644649
exists(
645650
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
646651
|
@@ -665,17 +670,17 @@ private predicate modelFlow(Instruction iFrom, Instruction iTo) {
665670
(
666671
exists(int index |
667672
modelIn.isParameter(index) and
668-
iFrom = call.getPositionalArgument(index)
673+
opFrom = call.getPositionalArgumentOperand(index)
669674
)
670675
or
671676
exists(int index, ReadSideEffectInstruction read |
672677
modelIn.isParameterDeref(index) and
673678
read = getSideEffectFor(call, index) and
674-
iFrom = read.getSideEffectOperand().getAnyDef()
679+
opFrom = read.getSideEffectOperand()
675680
)
676681
or
677682
modelIn.isQualifierAddress() and
678-
iFrom = call.getThisArgument()
683+
opFrom = call.getThisArgumentOperand()
679684
// TODO: add read side effects for qualifiers
680685
)
681686
)

cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Provides implementation classes modeling `std::string` and other
3-
* instantiations of`std::basic_string`. See `semmle.code.cpp.models.Models`
3+
* instantiations of `std::basic_string`. See `semmle.code.cpp.models.Models`
44
* for usage information.
55
*/
66

@@ -82,6 +82,32 @@ class StdStringData extends TaintFunction {
8282
}
8383
}
8484

85+
/**
86+
* The `std::string` function `push_back`.
87+
*/
88+
class StdStringPush extends TaintFunction {
89+
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
90+
91+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
92+
// flow from parameter to qualifier
93+
input.isParameterDeref(0) and
94+
output.isQualifierObject()
95+
}
96+
}
97+
98+
/**
99+
* The `std::string` functions `front` and `back`.
100+
*/
101+
class StdStringFrontBack extends TaintFunction {
102+
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
103+
104+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
105+
// flow from object to returned reference
106+
input.isQualifierObject() and
107+
output.isReturnValueDeref()
108+
}
109+
}
110+
85111
/**
86112
* The `std::string` function `operator+`.
87113
*/
@@ -138,6 +164,11 @@ class StdStringAppend extends TaintFunction {
138164
output.isQualifierObject() or
139165
output.isReturnValueDeref()
140166
)
167+
or
168+
// reverse flow from returned reference to the qualifier (for writes to
169+
// the result)
170+
input.isReturnValueDeref() and
171+
output.isQualifierObject()
141172
}
142173
}
143174

@@ -173,6 +204,11 @@ class StdStringAssign extends TaintFunction {
173204
output.isQualifierObject() or
174205
output.isReturnValueDeref()
175206
)
207+
or
208+
// reverse flow from returned reference to the qualifier (for writes to
209+
// the result)
210+
input.isReturnValueDeref() and
211+
output.isQualifierObject()
176212
}
177213
}
178214

0 commit comments

Comments
 (0)