Skip to content

Commit bc3e74f

Browse files
author
Robert Marsh
committed
Merge branch 'main' into rdmarsh2/cpp/ir-qualifier-flow
Fix test conflicts
2 parents 12be90a + d867172 commit bc3e74f

File tree

49 files changed

+1229
-202
lines changed

Some content is hidden

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

49 files changed

+1229
-202
lines changed

change-notes/1.26/analysis-cpp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The following changes in version 1.26 affect C/C++ analysis in all applications.
2323
* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`.
2424
* The models library now models many taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
2525
* The models library now models many more taint flows through `std::string`.
26-
* The models library now models some taint flows through `std::ostream`.
26+
* The models library now models many taint flows through `std::istream` and `std::ostream`.
2727
* The models library now models some taint flows through `std::shared_ptr`, `std::unique_ptr`, `std::make_shared` and `std::make_unique`.
2828
* The `SimpleRangeAnalysis` library now supports multiplications of the form
2929
`e1 * e2` and `x *= e2` when `e1` and `e2` are unsigned or constant.

cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,20 +279,62 @@ private predicate reachableRecursive(ControlFlowNode n) {
279279
reachableRecursive(n.getAPredecessor())
280280
}
281281

282+
/** Holds if `e` is a compile time constant with integer value `val`. */
282283
private predicate compileTimeConstantInt(Expr e, int val) {
283-
val = e.getFullyConverted().getValue().toInt() and
284-
not e instanceof StringLiteral and
285-
not exists(Expr e1 | e1.getConversion() = e) // only values for fully converted expressions
284+
(
285+
// If we have an integer value then we are done.
286+
if exists(e.getValue().toInt())
287+
then val = e.getValue().toInt()
288+
else
289+
// Otherwise, if we are a conversion of another expression with an
290+
// integer value, and that value can be converted into our type,
291+
// then we have that value.
292+
exists(Expr x, int valx |
293+
x.getConversion() = e and
294+
compileTimeConstantInt(x, valx) and
295+
val = convertIntToType(valx, e.getType().getUnspecifiedType())
296+
)
297+
) and
298+
// If our unconverted expression is a string literal `"123"`, then we
299+
// do not have integer value `123`.
300+
not e.getUnconverted() instanceof StringLiteral
286301
}
287302

288-
library class CompileTimeConstantInt extends Expr {
289-
CompileTimeConstantInt() { compileTimeConstantInt(this, _) }
303+
/**
304+
* Get `val` represented as type `t`, if that is possible without
305+
* overflow or underflows.
306+
*/
307+
bindingset[val, t]
308+
private int convertIntToType(int val, IntegralType t) {
309+
if t instanceof BoolType
310+
then if val = 0 then result = 0 else result = 1
311+
else
312+
if t.isUnsigned()
313+
then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none()
314+
else
315+
if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0
316+
then result = val
317+
else
318+
if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0
319+
then result = val
320+
else none()
321+
}
322+
323+
/**
324+
* INTERNAL: Do not use.
325+
* An expression that has been found to have an integer value at compile
326+
* time.
327+
*/
328+
class CompileTimeConstantInt extends Expr {
329+
int val;
330+
331+
CompileTimeConstantInt() { compileTimeConstantInt(this.getFullyConverted(), val) }
290332

291-
int getIntValue() { compileTimeConstantInt(this, result) }
333+
int getIntValue() { result = val }
292334
}
293335

294336
library class CompileTimeVariableExpr extends Expr {
295-
CompileTimeVariableExpr() { not compileTimeConstantInt(this, _) }
337+
CompileTimeVariableExpr() { not this instanceof CompileTimeConstantInt }
296338
}
297339

298340
/** A helper class for evaluation of expressions. */

cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
264264
t instanceof Union
265265
or
266266
t instanceof ArrayType
267-
or
268-
// Buffers of unknown size
269-
t instanceof UnknownType
270267
)
271268
or
272269
exists(BinaryInstruction bin |

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

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,17 @@ private class CollectionContent extends Content, TCollectionContent {
197197
}
198198

199199
private class ArrayContent extends Content, TArrayContent {
200-
override string toString() { result = "array" }
200+
ArrayContent() { this = TArrayContent() }
201+
202+
override string toString() { result = "array content" }
201203
}
202204

203-
private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) {
205+
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
204206
exists(StoreInstruction store, Class c |
205207
store = node2.asInstruction() and
206208
store.getSourceValue() = node1.asInstruction() and
207209
getWrittenField(store, f.(FieldContent).getAField(), c) and
208-
f.(FieldContent).hasOffset(c, _, _)
210+
f.hasOffset(c, _, _)
209211
)
210212
}
211213

@@ -218,7 +220,7 @@ private predicate getWrittenField(StoreInstruction store, Field f, Class c) {
218220
)
219221
}
220222

221-
private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
223+
private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
222224
exists(StoreInstruction store, ChiInstruction chi |
223225
node1.asInstruction() = store and
224226
node2.asInstruction() = chi and
@@ -227,23 +229,43 @@ private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
227229
c = chi.getResultType() and
228230
exists(int startBit, int endBit |
229231
chi.getUpdatedInterval(startBit, endBit) and
230-
f.(FieldContent).hasOffset(c, startBit, endBit)
232+
f.hasOffset(c, startBit, endBit)
231233
)
232234
or
233-
getWrittenField(store, f.(FieldContent).getAField(), c) and
234-
f.(FieldContent).hasOffset(c, _, _)
235+
getWrittenField(store, f.getAField(), c) and
236+
f.hasOffset(c, _, _)
235237
)
236238
)
237239
}
238240

241+
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
242+
a = TArrayContent() and
243+
exists(StoreInstruction store |
244+
node1.asInstruction() = store and
245+
(
246+
// `x[i] = taint()`
247+
// This matches the characteristic predicate in `ArrayStoreNode`.
248+
store.getDestinationAddress() instanceof PointerAddInstruction
249+
or
250+
// `*p = taint()`
251+
// This matches the characteristic predicate in `PointerStoreNode`.
252+
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
253+
) and
254+
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
255+
// and `PointerStoreNode` require it in their characteristic predicates.
256+
node2.asInstruction().(ChiInstruction).getPartial() = store
257+
)
258+
}
259+
239260
/**
240261
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
241262
* Thus, `node2` references an object with a field `f` that contains the
242263
* value of `node1`.
243264
*/
244265
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
245-
storeStepNoChi(node1, f, node2) or
246-
storeStepChi(node1, f, node2)
266+
fieldStoreStepNoChi(node1, f, node2) or
267+
fieldStoreStepChi(node1, f, node2) or
268+
arrayStoreStepChi(node1, f, node2)
247269
}
248270

249271
bindingset[result, i]
@@ -263,23 +285,41 @@ private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
263285
* Thus, `node1` references an object with a field `f` whose value ends up in
264286
* `node2`.
265287
*/
266-
predicate readStep(Node node1, Content f, Node node2) {
288+
private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
267289
exists(LoadInstruction load |
268290
node2.asInstruction() = load and
269291
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
270292
exists(Class c |
271293
c = load.getSourceValueOperand().getAnyDef().getResultType() and
272294
exists(int startBit, int endBit |
273295
load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
274-
f.(FieldContent).hasOffset(c, startBit, endBit)
296+
f.hasOffset(c, startBit, endBit)
275297
)
276298
or
277-
getLoadedField(load, f.(FieldContent).getAField(), c) and
278-
f.(FieldContent).hasOffset(c, _, _)
299+
getLoadedField(load, f.getAField(), c) and
300+
f.hasOffset(c, _, _)
279301
)
280302
)
281303
}
282304

305+
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
306+
a = TArrayContent() and
307+
exists(LoadInstruction load |
308+
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
309+
load = node2.asInstruction()
310+
)
311+
}
312+
313+
/**
314+
* Holds if data can flow from `node1` to `node2` via a read of `f`.
315+
* Thus, `node1` references an object with a field `f` whose value ends up in
316+
* `node2`.
317+
*/
318+
predicate readStep(Node node1, Content f, Node node2) {
319+
fieldReadStep(node1, f, node2) or
320+
arrayReadStep(node1, f, node2)
321+
}
322+
283323
/**
284324
* Holds if values stored inside content `c` are cleared at node `n`.
285325
*/

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

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,45 @@ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNod
389389
}
390390
}
391391

392+
/**
393+
* The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
394+
* `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
395+
*/
396+
private class ArrayStoreNode extends PartialDefinitionNode {
397+
override ChiInstruction instr;
398+
PointerAddInstruction add;
399+
400+
ArrayStoreNode() {
401+
not instr.isResultConflated() and
402+
exists(StoreInstruction store |
403+
instr.getPartial() = store and
404+
add = store.getDestinationAddress()
405+
)
406+
}
407+
408+
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
409+
410+
override Expr getDefinedExpr() { result = add.getLeft().getUnconvertedResultExpression() }
411+
}
412+
413+
/**
414+
* The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
415+
* `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
416+
*/
417+
private class PointerStoreNode extends PostUpdateNode {
418+
override ChiInstruction instr;
419+
420+
PointerStoreNode() {
421+
not instr.isResultConflated() and
422+
exists(StoreInstruction store |
423+
instr.getPartial() = store and
424+
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
425+
)
426+
}
427+
428+
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
429+
}
430+
392431
/**
393432
* A node that represents the value of a variable after a function call that
394433
* may have changed the variable because it's passed by reference.
@@ -545,6 +584,7 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
545584
* This is the local flow predicate that's used as a building block in global
546585
* data flow. It may have less flow than the `localFlowStep` predicate.
547586
*/
587+
cached
548588
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
549589
// Operand -> Instruction flow
550590
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
@@ -562,10 +602,11 @@ private predicate getFieldSizeOfClass(Class c, Type type, int size) {
562602
)
563603
}
564604

565-
private predicate isSingleFieldClass(Type type, Class cTo) {
566-
exists(int size |
567-
cTo.getSize() = size and
568-
getFieldSizeOfClass(cTo, type, size)
605+
private predicate isSingleFieldClass(Type type, Operand op) {
606+
exists(int size, Class c |
607+
c = op.getType().getUnderlyingType() and
608+
c.getSize() = size and
609+
getFieldSizeOfClass(c, type, size)
569610
)
570611
}
571612

@@ -601,11 +642,10 @@ private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
601642
exists(LoadInstruction load |
602643
load.getSourceValueOperand() = opTo and
603644
opTo.getAnyDef() = iFrom and
604-
isSingleFieldClass(iFrom.getResultType(), opTo.getType().getUnderlyingType())
645+
isSingleFieldClass(iFrom.getResultType(), opTo)
605646
)
606647
}
607648

608-
cached
609649
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
610650
iTo.(CopyInstruction).getSourceValueOperand() = opFrom
611651
or

0 commit comments

Comments
 (0)