mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
C++: Models for side-effect-free functions
This commit adds a new model interface that describes the known side effects (or lack thereof) of a library function. Does it read memory, does it write memory, and do any of its parameters escape? Initially, we have models for just two Standard Library functions: `std::move` and `std::forward`, which neither read nor write memory, and do not escape their parameter. IR construction has been updated to insert the correct side effect instruction (or no side effect instruction) based on the model.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.models.interfaces.SideEffectFunction
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -37,9 +38,18 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType and
|
||||
(
|
||||
if hasWriteSideEffect() then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType
|
||||
)
|
||||
else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType instanceof VoidType
|
||||
)
|
||||
) and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
@@ -68,9 +78,13 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
(
|
||||
(
|
||||
tag = CallTag() and
|
||||
result = getInstruction(CallSideEffectTag())
|
||||
if hasSideEffect() then
|
||||
result = getInstruction(CallSideEffectTag())
|
||||
else
|
||||
result = getParent().getChildSuccessor(this)
|
||||
) or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
@@ -183,6 +197,18 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
|
||||
predicate hasReadSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
predicate hasWriteSideEffect() {
|
||||
any()
|
||||
}
|
||||
|
||||
private predicate hasSideEffect() {
|
||||
hasReadSideEffect() or hasWriteSideEffect()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +306,14 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
functionReadsMemory(funcCall.getTarget())
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
functionWritesMemory(funcCall.getTarget())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
private import implementations.Memcpy
|
||||
private import implementations.Printf
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffectFunction
|
||||
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::identity`
|
||||
*/
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
(
|
||||
this.getName() = "move" or
|
||||
this.getName() = "forward"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate readsMemory() {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate writesMemory() {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate parameterEscapes(int index) {
|
||||
// Note that returning the value of the parameter does not count as escaping.
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isInParameter(0) and output.isOutReturnValue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate dataflow modeling of library
|
||||
* functions when source code is not available. To use this QL library,
|
||||
* create a QL class extending `SideEffectFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by `SideEffectFunction`
|
||||
* to match the flow within that function.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Models the side effects of a library function.
|
||||
*/
|
||||
abstract class SideEffectFunction extends Function {
|
||||
/**
|
||||
* Holds if the function may read from memory that was defined before entry to the function. This
|
||||
* memory could be from global variables, or from other memory that was reachable from a pointer
|
||||
* that was passed into the function.
|
||||
*/
|
||||
abstract predicate readsMemory();
|
||||
|
||||
/**
|
||||
* Holds if the function may write to memory that remains allocated after the function returns.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate writesMemory();
|
||||
|
||||
/**
|
||||
* Holds if any address passed to the parameter at the specified index is retained after the
|
||||
* function returns.
|
||||
*/
|
||||
abstract predicate parameterEscapes(int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `f` may read from memory that was defined before entry to the function.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
predicate functionReadsMemory(Function f) {
|
||||
not exists(SideEffectFunction sideEffect |
|
||||
sideEffect = f and not sideEffect.readsMemory()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `f` may write to memory that remains allocated after the function returns.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
predicate functionWritesMemory(Function f) {
|
||||
not exists(SideEffectFunction sideEffect |
|
||||
sideEffect = f and not sideEffect.writesMemory()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if any address passed to the parameter at the specified index is retained after the
|
||||
* function returns.
|
||||
*/
|
||||
predicate functionParameterEscapes(Function f, int index) {
|
||||
not exists(SideEffectFunction sideEffect |
|
||||
exists(f.getParameter(index)) and sideEffect = f and not sideEffect.parameterEscapes(index)
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user