Skip to content

Commit 0a86642

Browse files
author
Dave Bartolomeo
committed
C++: Refactor some side effect generation code
This change was necessary for my upcoming changes to introduce side effect instructions for indirections of smart pointers. The code to decide which parameters have which side effects appeared in both the IPA constructor for `TTranslatedSideEffect` and in `TranslatedCall`. These two versions didn't quite agree, especially once the `SideEffectFunction` model provides its own side effects instead of the defaults. The relevant code has now been factored out into `SideEffects.qll`. This queries the model if one exists, and provides default side effects if no model exists. This fixes at least one existing issue, where we were emitting a buffer read side effect for `*this` instead of an indirect read side effect. This accounts for all of the IR diffs in the tests.
1 parent 20416ae commit 0a86642

File tree

8 files changed

+332
-296
lines changed

8 files changed

+332
-296
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* Predicates to compute the modeled side effects of calls during IR construction.
3+
*
4+
* These are used in `TranslatedElement.qll` to generate the `TTranslatedSideEffect` instances, and
5+
* also in `TranslatedCall.qll` to inject the actual side effect instructions.
6+
*/
7+
8+
private import cpp
9+
private import semmle.code.cpp.ir.implementation.Opcode
10+
private import semmle.code.cpp.models.interfaces.SideEffect
11+
12+
/**
13+
* Holds if the specified call has a side effect that does not come from a `SideEffectFunction`
14+
* model.
15+
*/
16+
private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buffer, boolean isWrite) {
17+
not call.getTarget() instanceof SideEffectFunction and
18+
(
19+
exists(MemberFunction mfunc |
20+
// A non-static member function, including a constructor or destructor, may write to `*this`,
21+
// and may also read from `*this` if it is not a constructor.
22+
i = -1 and
23+
mfunc = call.getTarget() and
24+
not mfunc.isStatic() and
25+
buffer = false and
26+
(
27+
isWrite = false and not mfunc instanceof Constructor
28+
or
29+
isWrite = true and not mfunc instanceof ConstMemberFunction
30+
)
31+
)
32+
or
33+
exists(Expr expr |
34+
// A pointer-like argument is assumed to read from the pointed-to buffer, and may write to the
35+
// buffer as well unless the pointer points to a `const` value.
36+
i >= 0 and
37+
buffer = true and
38+
expr = call.getArgument(i).getFullyConverted() and
39+
exists(Type t | t = expr.getUnspecifiedType() |
40+
t instanceof ArrayType or
41+
t instanceof PointerType or
42+
t instanceof ReferenceType
43+
) and
44+
(
45+
isWrite = true and
46+
not call.getTarget().getParameter(i).getType().isDeeplyConstBelow()
47+
or
48+
isWrite = false
49+
)
50+
)
51+
)
52+
}
53+
54+
/**
55+
* Returns a side effect opcode for parameter index `i` of the specified call.
56+
*
57+
* This predicate will return at most two results: one read side effect, and one write side effect.
58+
*/
59+
Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
60+
exists(boolean buffer |
61+
(
62+
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(i, buffer)
63+
or
64+
not call.getTarget() instanceof SideEffectFunction and
65+
hasDefaultSideEffect(call, i, buffer, false)
66+
) and
67+
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i))
68+
then (
69+
buffer = true and
70+
result instanceof Opcode::SizedBufferReadSideEffect
71+
) else (
72+
buffer = false and result instanceof Opcode::IndirectReadSideEffect
73+
or
74+
buffer = true and result instanceof Opcode::BufferReadSideEffect
75+
)
76+
)
77+
or
78+
exists(boolean buffer, boolean mustWrite |
79+
(
80+
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(i, buffer, mustWrite)
81+
or
82+
not call.getTarget() instanceof SideEffectFunction and
83+
hasDefaultSideEffect(call, i, buffer, true) and
84+
mustWrite = false
85+
) and
86+
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i))
87+
then (
88+
buffer = true and
89+
mustWrite = false and
90+
result instanceof Opcode::SizedBufferMayWriteSideEffect
91+
or
92+
buffer = true and
93+
mustWrite = true and
94+
result instanceof Opcode::SizedBufferMustWriteSideEffect
95+
) else (
96+
buffer = false and
97+
mustWrite = false and
98+
result instanceof Opcode::IndirectMayWriteSideEffect
99+
or
100+
buffer = false and
101+
mustWrite = true and
102+
result instanceof Opcode::IndirectMustWriteSideEffect
103+
or
104+
buffer = true and mustWrite = false and result instanceof Opcode::BufferMayWriteSideEffect
105+
or
106+
buffer = true and mustWrite = true and result instanceof Opcode::BufferMustWriteSideEffect
107+
)
108+
)
109+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
44
private import semmle.code.cpp.ir.internal.CppType
55
private import semmle.code.cpp.models.interfaces.SideEffect
66
private import InstructionTag
7+
private import SideEffects
78
private import TranslatedElement
89
private import TranslatedExpr
910
private import TranslatedFunction
@@ -424,12 +425,15 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
424425
}
425426

426427
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
427-
TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
428+
TranslatedStructorCallSideEffects() {
429+
getParent().(TranslatedStructorCall).hasQualifier() and
430+
getASideEffectOpcode(expr, -1) instanceof WriteSideEffectOpcode
431+
}
428432

429433
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
430-
opcode instanceof Opcode::IndirectMayWriteSideEffect and
431434
tag instanceof OnlyInstructionTag and
432-
t = getTypeForPRValue(expr.getTarget().getDeclaringType())
435+
t = getTypeForPRValue(expr.getTarget().getDeclaringType()) and
436+
opcode = getASideEffectOpcode(expr, -1).(WriteSideEffectOpcode)
433437
}
434438

435439
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -460,9 +464,11 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
460464
Call call;
461465
Expr arg;
462466
int index;
463-
boolean write;
467+
SideEffectOpcode sideEffectOpcode;
464468

465-
TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) }
469+
TranslatedSideEffect() {
470+
this = TTranslatedArgumentSideEffect(call, arg, index, sideEffectOpcode)
471+
}
466472

467473
override Locatable getAST() { result = arg }
468474

@@ -472,13 +478,13 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
472478

473479
int getArgumentIndex() { result = index }
474480

475-
predicate isWrite() { write = true }
481+
predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode }
476482

477483
override string toString() {
478-
write = true and
484+
isWrite() and
479485
result = "(write side effect for " + arg.toString() + ")"
480486
or
481-
write = false and
487+
not isWrite() and
482488
result = "(read side effect for " + arg.toString() + ")"
483489
}
484490

@@ -489,29 +495,31 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
489495
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
490496

491497
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
492-
isWrite() and
493-
hasSpecificWriteSideEffect(opcode) and
494-
tag = OnlyInstructionTag() and
495498
(
496-
opcode instanceof BufferAccessOpcode and
497-
type = getUnknownType()
498-
or
499-
not opcode instanceof BufferAccessOpcode and
500-
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
501-
if baseType instanceof VoidType
502-
then type = getUnknownType()
503-
else type = getTypeForPRValueOrUnknown(baseType)
499+
tag = OnlyInstructionTag() and
500+
opcode = sideEffectOpcode
501+
) and
502+
(
503+
isWrite() and
504+
(
505+
opcode instanceof BufferAccessOpcode and
506+
type = getUnknownType()
507+
or
508+
not opcode instanceof BufferAccessOpcode and
509+
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
510+
if baseType instanceof VoidType
511+
then type = getUnknownType()
512+
else type = getTypeForPRValueOrUnknown(baseType)
513+
)
514+
or
515+
index = -1 and
516+
not arg.getUnspecifiedType() instanceof DerivedType and
517+
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
504518
)
505519
or
506-
index = -1 and
507-
not arg.getUnspecifiedType() instanceof DerivedType and
508-
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
520+
not isWrite() and
521+
type = getVoidType()
509522
)
510-
or
511-
not isWrite() and
512-
hasSpecificReadSideEffect(opcode) and
513-
tag = OnlyInstructionTag() and
514-
type = getVoidType()
515523
}
516524

517525
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -535,7 +543,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
535543

536544
override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
537545
not isWrite() and
538-
if hasSpecificReadSideEffect(any(BufferAccessOpcode op))
546+
if sideEffectOpcode instanceof BufferAccessOpcode
539547
then
540548
result = getUnknownType() and
541549
tag instanceof OnlyInstructionTag and
@@ -557,56 +565,6 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
557565
)
558566
}
559567

560-
predicate hasSpecificWriteSideEffect(Opcode op) {
561-
exists(boolean buffer, boolean mustWrite |
562-
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
563-
then
564-
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and
565-
buffer = true and
566-
(
567-
mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect
568-
or
569-
mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect
570-
)
571-
else (
572-
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and
573-
(
574-
buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect
575-
or
576-
buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect
577-
or
578-
buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect
579-
or
580-
buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect
581-
)
582-
)
583-
)
584-
or
585-
not call.getTarget() instanceof SideEffectFunction and
586-
getArgumentIndex() != -1 and
587-
op instanceof Opcode::BufferMayWriteSideEffect
588-
or
589-
not call.getTarget() instanceof SideEffectFunction and
590-
getArgumentIndex() = -1 and
591-
op instanceof Opcode::IndirectMayWriteSideEffect
592-
}
593-
594-
predicate hasSpecificReadSideEffect(Opcode op) {
595-
exists(boolean buffer |
596-
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and
597-
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
598-
then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect
599-
else (
600-
buffer = true and op instanceof Opcode::BufferReadSideEffect
601-
or
602-
buffer = false and op instanceof Opcode::IndirectReadSideEffect
603-
)
604-
)
605-
or
606-
not call.getTarget() instanceof SideEffectFunction and
607-
op instanceof Opcode::BufferReadSideEffect
608-
}
609-
610568
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
611569
tag = OnlyInstructionTag() and
612570
result = getTranslatedCallInstruction(call)

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import TranslatedStmt
1212
private import TranslatedExpr
1313
private import IRConstruction
1414
private import semmle.code.cpp.models.interfaces.SideEffect
15+
private import SideEffects
1516

1617
/**
1718
* Gets the "real" parent of `expr`. This predicate treats conversions as if
@@ -635,46 +636,15 @@ newtype TTranslatedElement =
635636
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
636637
TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
637638
// A precise side effect of an argument to a `Call`
638-
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
639-
(
640-
expr = call.getArgument(n).getFullyConverted()
641-
or
642-
expr = call.getQualifier().getFullyConverted() and
643-
n = -1 and
644-
// Exclude calls to static member functions. They don't modify the qualifier
645-
not exists(MemberFunction func | func = call.getTarget() and func.isStatic())
646-
) and
639+
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
640+
not ignoreExpr(expr) and
641+
not ignoreExpr(call) and
647642
(
648-
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
649-
isWrite = false
650-
or
651-
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and
652-
isWrite = true
653-
or
654-
not call.getTarget() instanceof SideEffectFunction and
655-
exists(Type t | t = expr.getUnspecifiedType() |
656-
t instanceof ArrayType or
657-
t instanceof PointerType or
658-
t instanceof ReferenceType
659-
) and
660-
(
661-
isWrite = true and
662-
not call.getTarget().getParameter(n).getType().isDeeplyConstBelow()
663-
or
664-
isWrite = false
665-
)
643+
n >= 0 and expr = call.getArgument(n).getFullyConverted()
666644
or
667-
not call.getTarget() instanceof SideEffectFunction and
668-
n = -1 and
669-
(
670-
isWrite = true and
671-
not call.getTarget() instanceof ConstMemberFunction
672-
or
673-
isWrite = false
674-
)
645+
n = -1 and expr = call.getQualifier().getFullyConverted()
675646
) and
676-
not ignoreExpr(expr) and
677-
not ignoreExpr(call)
647+
opcode = getASideEffectOpcode(call, n)
678648
}
679649

680650
/**

0 commit comments

Comments
 (0)