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:
Dave Bartolomeo
2018-12-05 10:58:46 -08:00
parent e11b4b6c40
commit 65360b23f9
6 changed files with 173 additions and 78 deletions

View File

@@ -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()
}
}

View File

@@ -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) {

View 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)
}
}

View File

@@ -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.
*/

View 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()
)
}
}

View File

@@ -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)
)
}