Skip to content

Commit a83879f

Browse files
committed
C++: Make sure that arguments of const pointer-type (as opposed to arguments of pointer to const-type) has an outgoing argument node.
1 parent 8b01dfe commit a83879f

2 files changed

Lines changed: 67 additions & 18 deletions

File tree

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ private newtype TIRDataFlowNode =
3939
} or
4040
TSsaPhiNode(Ssa::PhiNode phi) or
4141
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
42-
Ssa::isModifiableByCall(operand) and
43-
indirectionIndex = [1 .. Ssa::countIndirectionsForCppType(operand.getLanguageType())]
42+
Ssa::isModifiableByCall(operand, indirectionIndex)
4443
} or
4544
TRawIndirectOperand(Operand op, int indirectionIndex) {
4645
Ssa::hasRawIndirectOperand(op, indirectionIndex)
@@ -374,11 +373,13 @@ class OperandNode extends Node, Node0 {
374373
}
375374

376375
/**
376+
* INTERNAL: Do not use.
377+
*
377378
* Returns `t`, but stripped of the outermost pointer, reference, etc.
378379
*
379380
* For example, `stripPointers(int*&)` is `int*` and `stripPointers(int*)` is `int`.
380381
*/
381-
private Type stripPointer(Type t) {
382+
Type stripPointer(Type t) {
382383
result = any(Ssa::Indirection ind | ind.getType() = t).getBaseType()
383384
or
384385
// These types have a sensible base type, but don't receive additional
@@ -665,19 +666,24 @@ private class PostIndirectReturnOutNode extends IndirectReturnOutNode, PostUpdat
665666
override Node getPreUpdateNode() { result = this }
666667
}
667668

668-
private Type getTypeImpl(Type t, int indirectionIndex) {
669+
/**
670+
* INTERNAL: Do not use.
671+
*
672+
* Returns `t`, but stripped of the outer-most `indirectionIndex` number of indirections.
673+
*/
674+
Type getTypeImpl(Type t, int indirectionIndex) {
669675
indirectionIndex = 0 and
670676
result = t
671677
or
672678
indirectionIndex > 0 and
673679
exists(Type stripped |
674-
stripped = stripPointer(t) and
680+
stripped = stripPointer(t.stripTopLevelSpecifiers()) and
675681
// We need to avoid the case where `stripPointer(t) = t` (which can happen on
676682
// iterators that specify a `value_type` that is the iterator itself). Such a type
677683
// would create an infinite loop otherwise. For these cases we simply don't produce
678684
// a result for `getType`.
679685
stripped.getUnspecifiedType() != t.getUnspecifiedType() and
680-
result = getTypeImpl(stripPointer(t), indirectionIndex - 1)
686+
result = getTypeImpl(stripped, indirectionIndex - 1)
681687
)
682688
}
683689

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
44
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
55
private import DataFlowImplCommon as DataFlowImplCommon
66
private import DataFlowUtil
7+
private import semmle.code.cpp.models.interfaces.PointerWrapper
78
private import DataFlowPrivate
89

910
/**
@@ -137,7 +138,7 @@ abstract class Indirection extends Type {
137138
Type baseType;
138139

139140
/** Gets the type of this indirection. */
140-
final Type getType() { result = super.getUnspecifiedType() }
141+
final Type getType() { result = this }
141142

142143
/**
143144
* Gets the number of indirections supported by this type.
@@ -166,7 +167,7 @@ abstract class Indirection extends Type {
166167
*
167168
* For example, the base type of `int*&` is `int*`, and the base type of `int*` is `int`.
168169
*/
169-
final Type getBaseType() { result = baseType.getUnspecifiedType() }
170+
final Type getBaseType() { result = baseType }
170171

171172
/** Holds if there should be an additional taint step from `node1` to `node2`. */
172173
predicate isAdditionalTaintStep(Node node1, Node node2) { none() }
@@ -181,7 +182,9 @@ abstract class Indirection extends Type {
181182
private class PointerOrReferenceTypeIndirection extends Indirection instanceof PointerOrReferenceType {
182183
PointerOrReferenceTypeIndirection() { baseType = PointerOrReferenceType.super.getBaseType() }
183184

184-
override int getNumberOfIndirections() { result = 1 + countIndirections(this.getBaseType()) }
185+
override int getNumberOfIndirections() {
186+
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
187+
}
185188

186189
override predicate isAdditionalDereference(Instruction deref, Operand address) { none() }
187190

@@ -198,7 +201,9 @@ private module IteratorIndirections {
198201
not this instanceof PointerOrReferenceTypeIndirection and baseType = super.getValueType()
199202
}
200203

201-
override int getNumberOfIndirections() { result = 1 + countIndirections(this.getBaseType()) }
204+
override int getNumberOfIndirections() {
205+
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
206+
}
202207

203208
override predicate isAdditionalDereference(Instruction deref, Operand address) {
204209
exists(CallInstruction call |
@@ -352,8 +357,9 @@ class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
352357
* Holds if the value pointed to by `operand` can potentially be
353358
* modified be the caller.
354359
*/
355-
predicate isModifiableByCall(ArgumentOperand operand) {
360+
predicate isModifiableByCall(ArgumentOperand operand, int indirectionIndex) {
356361
exists(CallInstruction call, int index, CppType type |
362+
indirectionIndex = [1 .. countIndirectionsForCppType(type)] and
357363
type = getLanguageType(operand) and
358364
call.getArgumentOperand(index) = operand and
359365
if index = -1
@@ -385,13 +391,50 @@ predicate isModifiableByCall(ArgumentOperand operand) {
385391
else any()
386392
else
387393
// An argument is modifiable if it's a non-const pointer or reference type.
388-
exists(Type t, boolean isGLValue | type.hasType(t, isGLValue) |
389-
// If t is a glvalue it means that t is always a pointer-like type.
390-
isGLValue = true
391-
or
392-
t instanceof PointerOrReferenceType and
393-
not SideEffects::isConstPointerLike(t)
394-
)
394+
isModifiableAt(type, indirectionIndex)
395+
)
396+
}
397+
398+
/**
399+
* Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
400+
* of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
401+
* value of `t` to a function.
402+
*/
403+
private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
404+
indirectionIndex = [1 .. countIndirectionsForCppType(cppType)] and
405+
(
406+
exists(PointerOrReferenceType pointerType, Type base, Type t |
407+
cppType.hasType(t, _) and
408+
pointerType = t.getUnderlyingType() and
409+
base = getTypeImpl(pointerType, indirectionIndex)
410+
|
411+
// The value cannot be modified if it has a const specifier,
412+
not base.isConst()
413+
or
414+
// but in the case of a class type, it may be the case that
415+
// one of the members were modified.
416+
exists(base.stripType().(Cpp::Class).getAField())
417+
)
418+
or
419+
// If the `indirectionIndex`'th dereference of a type can be modified
420+
// then so can the `indirectionIndex + 1`'th dereference.
421+
isModifiableAtImpl(cppType, indirectionIndex - 1)
422+
)
423+
}
424+
425+
/**
426+
* Holds if `t` is a type with at least `indirectionIndex` number of indirections,
427+
* and the `indirectionIndex` indirection can be modified by passing a value of
428+
* type `t` to a function function.
429+
*/
430+
bindingset[indirectionIndex]
431+
private predicate isModifiableAt(CppType cppType, int indirectionIndex) {
432+
isModifiableAtImpl(cppType, indirectionIndex)
433+
or
434+
exists(PointerWrapper pw, Type t |
435+
cppType.hasType(t, _) and
436+
t.stripType() = pw and
437+
not pw.pointsToConst()
395438
)
396439
}
397440

0 commit comments

Comments
 (0)