mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
C++: Generate IR side effects for smart pointer indirections
When inserting side effect instructions for argument indirections, we now insert side effects for smart pointers as we would for raw pointers. The address operand of the side effect instruction is the smart pointer object, which is a bit odd. However, I'd like to think through the design of a more principled solution before doing additional work. A few new tests are added to the existing IR tests. In addition, the IR tests now `#include` some of the shared STL headers. I've disabled IR dumps for functions from those headers, since they only get in the way of the test cases we intended.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
@@ -39,7 +40,8 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
|
||||
exists(Type t | t = expr.getUnspecifiedType() |
|
||||
t instanceof ArrayType or
|
||||
t instanceof PointerType or
|
||||
t instanceof ReferenceType
|
||||
t instanceof ReferenceType or
|
||||
t instanceof PointerWrapper
|
||||
) and
|
||||
(
|
||||
isWrite = true and
|
||||
|
||||
@@ -11365,6 +11365,86 @@ perf-regression.cpp:
|
||||
# 12| Type = [IntType] int
|
||||
# 12| Value = [Literal] 0
|
||||
# 12| ValueCategory = prvalue
|
||||
smart_ptr.cpp:
|
||||
# 8| [TopLevelFunction] void unique_ptr_arg(std::unique_ptr<int, std::default_delete<int>>)
|
||||
# 8| <params>:
|
||||
# 8| getParameter(0): [Parameter] up
|
||||
# 8| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
|
||||
# 10| [TopLevelFunction] void call_unique_ptr_arg(int*)
|
||||
# 10| <params>:
|
||||
# 10| getParameter(0): [Parameter] p
|
||||
# 10| Type = [IntPointerType] int *
|
||||
# 10| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 11| getStmt(0): [DeclStmt] declaration
|
||||
# 11| getDeclarationEntry(0): [VariableDeclarationEntry] definition of up
|
||||
# 11| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
|
||||
# 11| getVariable().getInitializer(): [Initializer] initializer for up
|
||||
# 11| getExpr(): [ConstructorCall] call to unique_ptr
|
||||
# 11| Type = [VoidType] void
|
||||
# 11| ValueCategory = prvalue
|
||||
# 11| getArgument(0): [VariableAccess] p
|
||||
# 11| Type = [IntPointerType] int *
|
||||
# 11| ValueCategory = prvalue(load)
|
||||
# 12| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 12| getExpr(): [FunctionCall] call to unique_ptr_arg
|
||||
# 12| Type = [VoidType] void
|
||||
# 12| ValueCategory = prvalue
|
||||
# 12| getArgument(0): [FunctionCall] call to move
|
||||
# 12| Type = [RValueReferenceType] type &&
|
||||
# 12| ValueCategory = prvalue
|
||||
# 12| getArgument(0): [VariableAccess] up
|
||||
# 12| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
|
||||
# 12| ValueCategory = lvalue
|
||||
# 12| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
|
||||
# 12| Type = [LValueReferenceType] unique_ptr<int, default_delete<int>> &
|
||||
# 12| ValueCategory = prvalue
|
||||
# 12| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
|
||||
# 12| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
|
||||
# 12| ValueCategory = lvalue
|
||||
# 12| getExpr(): [ReferenceDereferenceExpr] (reference dereference)
|
||||
# 12| Type = [CTypedefType,NestedTypedefType] type
|
||||
# 12| ValueCategory = prvalue(load)
|
||||
# 13| getStmt(2): [ReturnStmt] return ...
|
||||
# 15| [TopLevelFunction] void shared_ptr_arg(std::shared_ptr<float>)
|
||||
# 15| <params>:
|
||||
# 15| getParameter(0): [Parameter] sp
|
||||
# 15| Type = [ClassTemplateInstantiation] shared_ptr<float>
|
||||
# 17| [TopLevelFunction] void call_shared_ptr_arg(float*)
|
||||
# 17| <params>:
|
||||
# 17| getParameter(0): [Parameter] p
|
||||
# 17| Type = [PointerType] float *
|
||||
# 17| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 18| getStmt(0): [DeclStmt] declaration
|
||||
# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp
|
||||
# 18| Type = [ClassTemplateInstantiation] shared_ptr<float>
|
||||
# 18| getVariable().getInitializer(): [Initializer] initializer for sp
|
||||
# 18| getExpr(): [ConstructorCall] call to shared_ptr
|
||||
# 18| Type = [VoidType] void
|
||||
# 18| ValueCategory = prvalue
|
||||
# 18| getArgument(0): [VariableAccess] p
|
||||
# 18| Type = [PointerType] float *
|
||||
# 18| ValueCategory = prvalue(load)
|
||||
# 19| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 19| getExpr(): [FunctionCall] call to shared_ptr_arg
|
||||
# 19| Type = [VoidType] void
|
||||
# 19| ValueCategory = prvalue
|
||||
# 19| getArgument(0): [ConstructorCall] call to shared_ptr
|
||||
# 19| Type = [VoidType] void
|
||||
# 19| ValueCategory = prvalue
|
||||
# 19| getArgument(0): [VariableAccess] sp
|
||||
# 19| Type = [ClassTemplateInstantiation] shared_ptr<float>
|
||||
# 19| ValueCategory = lvalue
|
||||
# 19| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
|
||||
# 19| Type = [LValueReferenceType] const shared_ptr<float> &
|
||||
# 19| ValueCategory = prvalue
|
||||
# 19| getExpr(): [CStyleCast] (const shared_ptr<float>)...
|
||||
# 19| Conversion = [GlvalueConversion] glvalue conversion
|
||||
# 19| Type = [SpecifiedType] const shared_ptr<float>
|
||||
# 19| ValueCategory = lvalue
|
||||
# 19| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
|
||||
# 19| Type = [ClassTemplateInstantiation] shared_ptr<float>
|
||||
# 19| ValueCategory = lvalue
|
||||
# 20| getStmt(2): [ReturnStmt] return ...
|
||||
struct_init.cpp:
|
||||
# 1| [TopLevelFunction] int handler1(void*)
|
||||
# 1| <params>:
|
||||
|
||||
11
cpp/ql/test/library-tests/ir/ir/PrintAST.ql
Normal file
11
cpp/ql/test/library-tests/ir/ir/PrintAST.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.PrintAST
|
||||
private import PrintConfig
|
||||
|
||||
private class PrintConfig extends PrintASTConfiguration {
|
||||
override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
semmle/code/cpp/PrintAST.ql
|
||||
10
cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
Normal file
10
cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
Normal file
@@ -0,0 +1,10 @@
|
||||
private import cpp
|
||||
|
||||
/**
|
||||
* Holds if the AST or IR for the specified function should be printed in the test output.
|
||||
*
|
||||
* This predicate excludes functions defined in standard headers.
|
||||
*/
|
||||
predicate shouldDumpFunction(Function func) {
|
||||
not func.getLocation().getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+")
|
||||
}
|
||||
@@ -6,6 +6,7 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
|
||||
@@ -7903,6 +7903,80 @@ perf-regression.cpp:
|
||||
# 9| v9_6(void) = AliasedUse : ~m?
|
||||
# 9| v9_7(void) = ExitFunction :
|
||||
|
||||
smart_ptr.cpp:
|
||||
# 10| void call_unique_ptr_arg(int*)
|
||||
# 10| Block 0
|
||||
# 10| v10_1(void) = EnterFunction :
|
||||
# 10| mu10_2(unknown) = AliasedDefinition :
|
||||
# 10| mu10_3(unknown) = InitializeNonLocal :
|
||||
# 10| r10_4(glval<int *>) = VariableAddress[p] :
|
||||
# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4
|
||||
# 10| r10_6(int *) = Load[p] : &:r10_4, ~m?
|
||||
# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6
|
||||
# 11| r11_1(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[up] :
|
||||
# 11| mu11_2(unique_ptr<int, default_delete<int>>) = Uninitialized[up] : &:r11_1
|
||||
# 11| r11_3(glval<unknown>) = FunctionAddress[unique_ptr] :
|
||||
# 11| r11_4(glval<int *>) = VariableAddress[p] :
|
||||
# 11| r11_5(int *) = Load[p] : &:r11_4, ~m?
|
||||
# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5
|
||||
# 11| mu11_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 11| mu11_8(unique_ptr<int, default_delete<int>>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1
|
||||
# 12| r12_1(glval<unknown>) = FunctionAddress[unique_ptr_arg] :
|
||||
# 12| r12_2(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[#temp12:20] :
|
||||
# 12| r12_3(glval<unknown>) = FunctionAddress[move] :
|
||||
# 12| r12_4(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[up] :
|
||||
# 12| r12_5(unique_ptr<int, default_delete<int>> &) = CopyValue : r12_4
|
||||
# 12| r12_6(unique_ptr<int, default_delete<int>> &&) = Call[move] : func:r12_3, 0:r12_5
|
||||
# 12| r12_7(unique_ptr<int, default_delete<int>>) = Load[?] : &:r12_6, ~m?
|
||||
# 12| mu12_8(unique_ptr<int, default_delete<int>>) = Store[#temp12:20] : &:r12_2, r12_7
|
||||
# 12| r12_9(unique_ptr<int, default_delete<int>>) = Load[#temp12:20] : &:r12_2, ~m?
|
||||
# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9
|
||||
# 12| mu12_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m?
|
||||
# 13| v13_1(void) = NoOp :
|
||||
# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m?
|
||||
# 10| v10_9(void) = ReturnVoid :
|
||||
# 10| v10_10(void) = AliasedUse : ~m?
|
||||
# 10| v10_11(void) = ExitFunction :
|
||||
|
||||
# 17| void call_shared_ptr_arg(float*)
|
||||
# 17| Block 0
|
||||
# 17| v17_1(void) = EnterFunction :
|
||||
# 17| mu17_2(unknown) = AliasedDefinition :
|
||||
# 17| mu17_3(unknown) = InitializeNonLocal :
|
||||
# 17| r17_4(glval<float *>) = VariableAddress[p] :
|
||||
# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4
|
||||
# 17| r17_6(float *) = Load[p] : &:r17_4, ~m?
|
||||
# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6
|
||||
# 18| r18_1(glval<shared_ptr<float>>) = VariableAddress[sp] :
|
||||
# 18| mu18_2(shared_ptr<float>) = Uninitialized[sp] : &:r18_1
|
||||
# 18| r18_3(glval<unknown>) = FunctionAddress[shared_ptr] :
|
||||
# 18| r18_4(glval<float *>) = VariableAddress[p] :
|
||||
# 18| r18_5(float *) = Load[p] : &:r18_4, ~m?
|
||||
# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5
|
||||
# 18| mu18_7(unknown) = ^CallSideEffect : ~m?
|
||||
# 18| mu18_8(shared_ptr<float>) = ^IndirectMustWriteSideEffect[-1] : &:r18_1
|
||||
# 19| r19_1(glval<unknown>) = FunctionAddress[shared_ptr_arg] :
|
||||
# 19| r19_2(glval<shared_ptr<float>>) = VariableAddress[#temp19:20] :
|
||||
# 19| mu19_3(shared_ptr<float>) = Uninitialized[#temp19:20] : &:r19_2
|
||||
# 19| r19_4(glval<unknown>) = FunctionAddress[shared_ptr] :
|
||||
# 19| r19_5(glval<shared_ptr<float>>) = VariableAddress[sp] :
|
||||
# 19| r19_6(glval<shared_ptr<float>>) = Convert : r19_5
|
||||
# 19| r19_7(shared_ptr<float> &) = CopyValue : r19_6
|
||||
# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7
|
||||
# 19| mu19_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 19| mu19_10(shared_ptr<float>) = ^IndirectMustWriteSideEffect[-1] : &:r19_2
|
||||
# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m?
|
||||
# 19| r19_12(shared_ptr<float>) = Load[#temp19:20] : &:r19_2, ~m?
|
||||
# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12
|
||||
# 19| mu19_14(unknown) = ^CallSideEffect : ~m?
|
||||
# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m?
|
||||
# 20| v20_1(void) = NoOp :
|
||||
# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m?
|
||||
# 17| v17_9(void) = ReturnVoid :
|
||||
# 17| v17_10(void) = AliasedUse : ~m?
|
||||
# 17| v17_11(void) = ExitFunction :
|
||||
|
||||
struct_init.cpp:
|
||||
# 16| void let_info_escape(Info*)
|
||||
# 16| Block 0
|
||||
|
||||
11
cpp/ql/test/library-tests/ir/ir/raw_ir.ql
Normal file
11
cpp/ql/test/library-tests/ir/ir/raw_ir.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.raw.PrintIR
|
||||
private import PrintConfig
|
||||
|
||||
private class PrintConfig extends PrintIRConfiguration {
|
||||
override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
semmle/code/cpp/ir/implementation/raw/PrintIR.ql
|
||||
20
cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp
Normal file
20
cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "../../../include/memory.h"
|
||||
#include "../../../include/utility.h"
|
||||
|
||||
using std::move;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
|
||||
void unique_ptr_arg(unique_ptr<int> up);
|
||||
|
||||
void call_unique_ptr_arg(int* p) {
|
||||
unique_ptr<int> up(p);
|
||||
unique_ptr_arg(move(up));
|
||||
}
|
||||
|
||||
void shared_ptr_arg(shared_ptr<float> sp);
|
||||
|
||||
void call_shared_ptr_arg(float* p) {
|
||||
shared_ptr<float> sp(p);
|
||||
shared_ptr_arg(sp);
|
||||
}
|
||||
Reference in New Issue
Block a user