mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
C++: Change model API based on feedback
I've separated the model interface for memory side effects from the model for escaped addresses. It will be fairly common for a given model to extend both interfaces, but they are used for two different purposes. I've also put each model interface and the non-member predicates that query it into a named module, which seemed cleaner than having predicates named `functionModelReadsMemory()` and `getFunctionModelParameterAliasBehavior()`.
This commit is contained in:
@@ -1,7 +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 semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -308,11 +308,11 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
functionReadsMemory(funcCall.getTarget())
|
||||
SideEffectModel::functionReadsMemory(funcCall.getTarget())
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
functionWritesMemory(funcCall.getTarget())
|
||||
SideEffectModel::functionWritesMemory(funcCall.getTarget())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,4 +336,3 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffectFunction
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::identity`
|
||||
*/
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction {
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectModel::SideEffectFunction,
|
||||
AliasModel::AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
@@ -23,9 +25,16 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate parameterEscapes(int index) {
|
||||
// Note that returning the value of the parameter does not count as escaping.
|
||||
none()
|
||||
override AliasModel::ParameterEscape getParameterEscapeBehavior(int index) {
|
||||
exists(getParameter(index)) and
|
||||
if index = 0 then
|
||||
result instanceof AliasModel::EscapesOnlyViaReturn
|
||||
else
|
||||
result instanceof AliasModel::DoesNotEscape
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
index = 0
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
101
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
101
cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate alias modeling of library
|
||||
* functions when source code is not available. To use this QL library,
|
||||
* create a QL class extending `AliasFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by `AliasFunction`
|
||||
* to match the flow within that function.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
module AliasModel {
|
||||
private newtype TParameterEscape =
|
||||
TDoesNotEscape() or
|
||||
TEscapesOnlyViaReturn() or
|
||||
TEscapes()
|
||||
|
||||
class ParameterEscape extends TParameterEscape {
|
||||
string toString() {
|
||||
result = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
class DoesNotEscape extends ParameterEscape, TDoesNotEscape {
|
||||
override string toString() {
|
||||
result = "DoesNotEscape"
|
||||
}
|
||||
}
|
||||
|
||||
class EscapesOnlyViaReturn extends ParameterEscape, TEscapesOnlyViaReturn {
|
||||
override string toString() {
|
||||
result = "EscapesOnlyViaReturn"
|
||||
}
|
||||
}
|
||||
|
||||
class Escapes extends ParameterEscape, TEscapes {
|
||||
override string toString() {
|
||||
result = "Escapes"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models the aliasing behavior of a library function.
|
||||
*/
|
||||
abstract class AliasFunction extends Function {
|
||||
/**
|
||||
* Specifies whether the address passed to the parameter at the specified index is retained after
|
||||
* the function returns. The result is given as a `ParameterEscape` object. See the comments for
|
||||
* that class and its subclasses for a description of each possible result.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int* g;
|
||||
* int* func(int* p, int* q, int* r, int* s, int n) {
|
||||
* *s = 1; // `s` does not escape.
|
||||
* g = p; // Stored in global. `p` escapes.
|
||||
* if (rand()) {
|
||||
* return q; // `q` escapes via the return value.
|
||||
* }
|
||||
* else {
|
||||
* return r + n; // `r` escapes via the return value, even though an offset has been added.
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For the above function, the following terms hold:
|
||||
* - `getParameterEscapeBehavior(0) instanceof Escapes`
|
||||
* - `getParameterEscapeBehavior(1) instanceof EscapesOnlyViaReturn`
|
||||
* - `getParameterEscapeBehavior(2) instanceof EscapesOnlyViaReturn`
|
||||
* - `getParameterEscapeBehavior(3) instanceof DoesNotEscape`
|
||||
*/
|
||||
abstract ParameterEscape getParameterEscapeBehavior(int index);
|
||||
|
||||
/**
|
||||
* Holds if the function always returns the value of the parameter at the specified index.
|
||||
*/
|
||||
abstract predicate parameterIsAlwaysReturned(int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether the address passed to the parameter at the specified index is retained after
|
||||
* the function returns. The result is given as a `ParameterEscape` object. See the comments for
|
||||
* that class and its subclasses for a description of each possible result.
|
||||
*/
|
||||
ParameterEscape getParameterEscapeBehavior(Function f, int index) {
|
||||
result = f.(AliasFunction).getParameterEscapeBehavior(index) or
|
||||
(
|
||||
not f instanceof AliasFunction and
|
||||
exists(f.getParameter(index)) and
|
||||
result instanceof Escapes
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function always returns the value of the parameter at the specified index.
|
||||
*/
|
||||
predicate parameterIsAlwaysReturned(Function f, int index) {
|
||||
f.(AliasFunction).parameterIsAlwaysReturned(index)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides a set of QL clcasses for indicating dataflows through a particular
|
||||
* Provides a set of QL classes for indicating dataflows through a particular
|
||||
* parameter, return value, or qualifier, as well as flows at one level of
|
||||
* pointer indirection.
|
||||
*/
|
||||
|
||||
54
cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
Normal file
54
cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
module SideEffectModel {
|
||||
/**
|
||||
* 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 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* 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