mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #16791 from MathiasVP/collection-content-2
C++: Add support for `Element` content
This commit is contained in:
@@ -14,16 +14,22 @@
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
* 2. The `type` column selects a type within that namespace.
|
||||
* 2. The `type` column selects a type within that namespace. This column can
|
||||
* introduce template names that can be mentioned in the `signature` column.
|
||||
* For example, `vector<T,Allocator>` introduces the template names `T` and
|
||||
* `Allocator`.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
|
||||
* blank (for example, a free function).
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* Like the `type` column, this column can introduce template names that can
|
||||
* be mentioned in the `signature` column. For example, `insert<InputIt>`
|
||||
* introduces the template name `InputIt`.
|
||||
* 5. The `signature` column optionally restricts the named member. If
|
||||
* `signature` is blank then no such filtering is done. The format of the
|
||||
* signature is a comma-separated list of types enclosed in parentheses. The
|
||||
* types can be short names or fully qualified names (mixing these two options
|
||||
* is not allowed within a single signature).
|
||||
* types must be stripped of template names. That is, write `const vector &`
|
||||
* instead of `const vector<T> &`.
|
||||
* 6. The `ext` column specifies additional API-graph-like edges. Currently
|
||||
* there is only one valid value: "".
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
@@ -44,6 +50,9 @@
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* The special symbol `@` can be used to specify an arbitrary (but fixed)
|
||||
* number of indirections. For example, the `input` column `Argument[*@0]`
|
||||
* indicates one or more indirections of the 0th argument.
|
||||
*
|
||||
* An `output` can be either:
|
||||
* - "": Selects a read of a selected field.
|
||||
@@ -65,6 +74,17 @@
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* The special symbol `@` can be used to specify an arbitrary (but fixed)
|
||||
* number of indirections. For example, the `output` column
|
||||
* `ReturnValue[*@0]` indicates one or more indirections of the return
|
||||
* value.
|
||||
* Note: The symbol `@` only ever takes a single value across a row. Thus,
|
||||
* the (`input`, `output`) pair `("Argument[*@0]", "ReturnValue[@]")`
|
||||
* represents:
|
||||
* - flow from the _first_ indirection of the 0th argument to the return
|
||||
* value, and
|
||||
* - flow from the _second_ indirection of the 0th argument to the first
|
||||
* indirection of the return value, etc.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
@@ -74,6 +94,8 @@
|
||||
|
||||
import cpp
|
||||
private import new.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
@@ -166,8 +188,12 @@ predicate sinkModel(
|
||||
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, _)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*
|
||||
* This predicate does not expand `@` to `*`s.
|
||||
*/
|
||||
private predicate summaryModel0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
@@ -190,6 +216,33 @@ predicate summaryModel(
|
||||
provenance, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `input` is `input0`, but with all occurrences of `@` replaced
|
||||
* by `n` repetitions of `*` (and similarly for `output` and `output0`).
|
||||
*/
|
||||
bindingset[input0, output0, n]
|
||||
pragma[inline_late]
|
||||
private predicate expandInputAndOutput(
|
||||
string input0, string input, string output0, string output, int n
|
||||
) {
|
||||
input = input0.replaceAll("@", repeatStars(n)) and
|
||||
output = output0.replaceAll("@", repeatStars(n))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*/
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string input0, string output0 |
|
||||
summaryModel0(namespace, type, subtypes, name, signature, ext, input0, output0, kind, provenance) and
|
||||
expandInputAndOutput(input0, input, output0, output,
|
||||
[0 .. Private::getMaxElementContentIndirectionIndex() - 1])
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
@@ -367,16 +420,155 @@ private predicate elementSpec(
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | c.getParameter(n).getType().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
/** Gets the fully templated version of `f`. */
|
||||
private Function getFullyTemplatedMemberFunction(Function f) {
|
||||
not f.isFromUninstantiatedTemplate(_) and
|
||||
exists(Class c, Class templateClass, int i |
|
||||
c.isConstructedFrom(templateClass) and
|
||||
f = c.getAMember(i) and
|
||||
result = templateClass.getCanonicalMember(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type name of the `n`'th parameter of `f` without any template
|
||||
* arguments.
|
||||
*/
|
||||
bindingset[f]
|
||||
pragma[inline_late]
|
||||
string getParameterTypeWithoutTemplateArguments(Function f, int n) {
|
||||
exists(string s, string base, string specifiers |
|
||||
s = f.getParameter(n).getType().getName() and
|
||||
parseAngles(s, base, _, specifiers) and
|
||||
result = base + specifiers
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `func:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
remaining = templateFunction.getNumberOfTemplateArguments() and
|
||||
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
exists(string mid, TemplateParameter tp, Function templateFunction |
|
||||
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
|
||||
templateFunction = getFullyTemplatedMemberFunction(f) and
|
||||
tp = templateFunction.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "func:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `class:N` (where `N` is the index of the template).
|
||||
*/
|
||||
private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining) {
|
||||
exists(Class template |
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
remaining = template.getNumberOfTemplateArguments() and
|
||||
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
|
||||
)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Class template |
|
||||
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
|
||||
f.getDeclaringType().isConstructedFrom(template) and
|
||||
tp = template.getTemplateArgument(remaining) and
|
||||
result = mid.replaceAll(tp.getName(), "class:" + remaining.toString())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the string representation of the `i`'th parameter of `c`. */
|
||||
private string getParameterTypeName(Function c, int i) {
|
||||
result = getTypeNameWithoutClassTemplates(c, i, 0)
|
||||
}
|
||||
|
||||
/** Splits `s` by `,` and gets the `i`'th element. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private string getAtIndex(string s, int i) {
|
||||
result = s.splitAt(",", i) and
|
||||
// when `s` is `""` and `i` is `0` we get `result = ""` which we don't want.
|
||||
not (s = "" and i = 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
|
||||
* number of template arguments in `partiallyNormalizedSignature` with their
|
||||
* index in `typeArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutClassTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result = partiallyNormalizedSignature
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string typeArg |
|
||||
typeArg = getAtIndex(typeArgs, remaining) and
|
||||
result = mid.replaceAll(typeArg, "class:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(typeArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes `partiallyNormalizedSignature` by replacing:
|
||||
* - _All_ the template arguments in `partiallyNormalizedSignature` that refer to
|
||||
* template parameters in `typeArgs` with their index in `typeArgs`, and
|
||||
* - The `remaining` number of template arguments in `partiallyNormalizedSignature`
|
||||
* with their index in `nameArgs`.
|
||||
*/
|
||||
private string getSignatureWithoutFunctionTemplateNames(
|
||||
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
|
||||
) {
|
||||
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
|
||||
result =
|
||||
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
|
||||
or
|
||||
exists(string mid |
|
||||
mid =
|
||||
getSignatureWithoutFunctionTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs,
|
||||
remaining + 1)
|
||||
|
|
||||
exists(string nameArg |
|
||||
nameArg = getAtIndex(nameArgs, remaining) and
|
||||
result = mid.replaceAll(nameArg, "func:" + remaining.toString())
|
||||
)
|
||||
or
|
||||
// Make sure `remaining` is properly bound
|
||||
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
|
||||
not exists(getAtIndex(nameArgs, remaining)) and
|
||||
result = mid
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | getParameterTypeName(c, n) = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +588,193 @@ private predicate matchesSignature(Function func, string signature) {
|
||||
paramsString(func) = signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` holds and
|
||||
* - `typeArgs` represents the named template parameters supplied to `type`, and
|
||||
* - `nameArgs` represents the named template parameters supplied to `name`, and
|
||||
* - `normalizedSignature` is `signature`, except with
|
||||
* - template parameter names replaced by `func:i` if the template name is
|
||||
* the `i`'th entry in `nameArgs`, and
|
||||
* - template parameter names replaced by `class:i` if the template name is
|
||||
* the `i`'th entry in `typeArgs`.
|
||||
*
|
||||
* In other words, the string `normalizedSignature` represents a "normalized"
|
||||
* signature with no mention of any free template parameters.
|
||||
*
|
||||
* For example, consider a summary row such as:
|
||||
* ```
|
||||
* elementSpec(_, "MyClass<B, C>", _, myFunc<A>, "(const A &,int,C,B *)", _)
|
||||
* ```
|
||||
* In this case, `normalizedSignature` will be `"(const func:0 &,int,class:1,class:0 *)"`.
|
||||
*/
|
||||
private predicate elementSpecWithArguments(
|
||||
string signature, string type, string name, string normalizedSignature, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
exists(string signatureWithoutParens |
|
||||
elementSpecWithArguments0(signature, type, name, signatureWithoutParens, typeArgs, nameArgs) and
|
||||
normalizedSignature =
|
||||
getSignatureWithoutFunctionTemplateNames(signatureWithoutParens, typeArgs, nameArgs, 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the `n`'th normalized signature parameter for the function `name` in class `type`. */
|
||||
private string getSignatureParameterName(string signature, string type, string name, int n) {
|
||||
exists(string normalizedSignature |
|
||||
elementSpecWithArguments(signature, type, name, normalizedSignature, _, _) and
|
||||
result = getAtIndex(normalizedSignature, n)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the suffix containing the entries in `signature` starting at entry
|
||||
* `i` matches the suffix containing the parameters of `func` starting at entry `i`.
|
||||
*
|
||||
* For example, consider the signature `(int,bool,char)` and a function:
|
||||
* ```
|
||||
* void f(int a, bool b, char c);
|
||||
* ```
|
||||
* 1. The predicate holds for `i = 2` because the suffix containing all the entries
|
||||
* in `signature` starting at `2` is `char`, and suffix containing all the parameters
|
||||
* of `func` starting at `2` is `char`.
|
||||
* 2. The predicate holds for `i = 1` because the suffix containing all the entries
|
||||
* in `signature` starting at `1` is `bool,char`, and the suffix containing all the
|
||||
* parameters of `func` starting at `1` is `bool, char`.
|
||||
* 3. The predicate holds for `i = 0` because the suffix containing all the entries
|
||||
* in `signature` starting at `0` is `int,bool,char` and the suffix containing all
|
||||
* the parameters of `func` starting at `0` is `int, bool, char`.
|
||||
*
|
||||
* When `paramsString(func)[i]` is `class:n` then the signature name is
|
||||
* compared with the `n`'th name in `type`, and when `paramsString(func)[i]`
|
||||
* is `func:n` then the signature name is compared with the `n`'th name
|
||||
* in `name`.
|
||||
*/
|
||||
private predicate signatureMatches(Function func, string signature, string type, string name, int i) {
|
||||
exists(string s |
|
||||
s = getSignatureParameterName(signature, type, name, i) and
|
||||
s = getParameterTypeName(func, i)
|
||||
) and
|
||||
if exists(getParameterTypeName(func, i + 1))
|
||||
then signatureMatches(func, signature, type, name, i + 1)
|
||||
else i = count(signature.indexOf(","))
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: Do not use.
|
||||
*
|
||||
* This module only exists to expose internal predicates for testing purposes.
|
||||
*/
|
||||
module ExternalFlowDebug {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate signatureMatches_debug = signatureMatches/5;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate getSignatureParameterName_debug = getSignatureParameterName/4;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Exposed for testing purposes.
|
||||
*/
|
||||
predicate getParameterTypeName_debug = getParameterTypeName/2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` can be broken into a string of the form
|
||||
* `beforeAngles<betweenAngles>`,
|
||||
* or `s = beforeAngles` where `beforeAngles` does not have any brackets.
|
||||
*/
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseAngles(
|
||||
string s, string beforeAngles, string betweenAngles, string afterAngles
|
||||
) {
|
||||
beforeAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 1) and
|
||||
(
|
||||
betweenAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2) and
|
||||
afterAngles = s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 3)
|
||||
or
|
||||
not exists(s.regexpCapture("([^<]+)(?:<([^>]+)>(.*))?", 2)) and
|
||||
betweenAngles = "" and
|
||||
afterAngles = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `s` can be broken into a string of the form `(betweenParens)`. */
|
||||
bindingset[s]
|
||||
pragma[inline_late]
|
||||
private predicate parseParens(string s, string betweenParens) { s = "(" + betweenParens + ")" }
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(_, type, _, name, signature, _)` and:
|
||||
* - `type` introduces template parameters `typeArgs`, and
|
||||
* - `name` introduces template parameters `nameArgs`, and
|
||||
* - `signatureWithoutParens` equals `signature`, but with the surrounding
|
||||
* parentheses removed.
|
||||
*/
|
||||
private predicate elementSpecWithArguments0(
|
||||
string signature, string type, string name, string signatureWithoutParens, string typeArgs,
|
||||
string nameArgs
|
||||
) {
|
||||
elementSpec(_, type, _, name, signature, _) and
|
||||
parseAngles(name, _, nameArgs, "") and
|
||||
(
|
||||
type = "" and typeArgs = ""
|
||||
or
|
||||
parseAngles(type, _, typeArgs, "")
|
||||
) and
|
||||
parseParens(signature, signatureWithoutParens)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `elementSpec(namespace, type, subtypes, name, signature, _)` and
|
||||
* `method`'s signature matches `signature`.
|
||||
*
|
||||
* `signature` may contain template parameter names that are bound by `type` and `name`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate elementSpecMatchesSignature(
|
||||
Function method, string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, pragma[only_bind_into](type), subtypes, pragma[only_bind_into](name),
|
||||
pragma[only_bind_into](signature), _) and
|
||||
signatureMatches(method, signature, type, name, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `classWithMethod` has `method` named `name` (excluding any
|
||||
* template parameters).
|
||||
*/
|
||||
bindingset[name]
|
||||
pragma[inline_late]
|
||||
private predicate hasClassAndName(Class classWithMethod, Function method, string name) {
|
||||
exists(string nameWithoutArgs |
|
||||
parseAngles(name, nameWithoutArgs, _, "") and
|
||||
classWithMethod = method.getClassAndName(nameWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `namedClass` is in namespace `namespace` and has
|
||||
* name `type` (excluding any template parameters).
|
||||
*/
|
||||
bindingset[type, namespace]
|
||||
pragma[inline_late]
|
||||
private predicate hasQualifiedName(Class namedClass, string namespace, string type) {
|
||||
exists(string typeWithoutArgs |
|
||||
parseAngles(type, typeWithoutArgs, _, "") and
|
||||
namedClass.hasQualifiedName(namespace, typeWithoutArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element in module `namespace` that satisfies the following properties:
|
||||
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
|
||||
@@ -410,8 +789,8 @@ pragma[nomagic]
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
(
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Non-member functions
|
||||
exists(Function func |
|
||||
func.hasQualifiedName(namespace, name) and
|
||||
@@ -423,21 +802,28 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Member functions
|
||||
exists(Class namedClass, Class classWithMethod, Function method |
|
||||
classWithMethod = method.getClassAndName(name) and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
matchesSignature(method, signature) and
|
||||
result = method
|
||||
|
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
exists(Class namedClass, Class classWithMethod |
|
||||
(
|
||||
elementSpecMatchesSignature(result, namespace, type, subtypes, name, signature) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
or
|
||||
signature = "" and
|
||||
elementSpec(namespace, type, subtypes, name, "", _) and
|
||||
hasClassAndName(classWithMethod, result, name)
|
||||
) and
|
||||
hasQualifiedName(namedClass, namespace, type) and
|
||||
(
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
)
|
||||
)
|
||||
or
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
// Member variables
|
||||
signature = "" and
|
||||
exists(Class namedClass, Class classWithMember, MemberVariable member |
|
||||
@@ -456,6 +842,7 @@ private Element interpretElement0(
|
||||
)
|
||||
or
|
||||
// Global or namespace variables
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
signature = "" and
|
||||
type = "" and
|
||||
subtypes = false and
|
||||
|
||||
@@ -35,16 +35,22 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
result = "Field" and
|
||||
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
|
||||
)
|
||||
or
|
||||
exists(ElementContent ec |
|
||||
cs.isSingleton(ec) and
|
||||
result = "Element" and
|
||||
arg = repeatStars(ec.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
|
||||
string encodeWithoutContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithoutContent" + c and arg = ""
|
||||
none()
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithContent" + c and arg = ""
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,25 +85,6 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
token.getName() = "Parameter" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
|
||||
// field content (no indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
not exists(token.getArgumentList()) and
|
||||
c.getIndirectionIndex() = 1
|
||||
)
|
||||
or
|
||||
// field content (with indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
|
||||
|
||||
@@ -412,6 +412,8 @@ class ArgumentPosition = Position;
|
||||
|
||||
abstract class Position extends TPosition {
|
||||
abstract string toString();
|
||||
|
||||
abstract int getIndirectionIndex();
|
||||
}
|
||||
|
||||
class DirectPosition extends Position, TDirectPosition {
|
||||
@@ -421,13 +423,15 @@ class DirectPosition extends Position, TDirectPosition {
|
||||
|
||||
override string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
result = "this pointer"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
|
||||
final override int getIndirectionIndex() { result = 0 }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
@@ -438,16 +442,13 @@ class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
|
||||
override string toString() {
|
||||
if argumentIndex = -1
|
||||
then if indirectionIndex > 0 then result = "this indirection" else result = "this"
|
||||
else
|
||||
if indirectionIndex > 0
|
||||
then result = argumentIndex.toString() + " indirection"
|
||||
else result = argumentIndex.toString()
|
||||
then result = repeatStars(indirectionIndex - 1) + "this"
|
||||
else result = repeatStars(indirectionIndex) + argumentIndex.toString()
|
||||
}
|
||||
|
||||
int getArgumentIndex() { result = argumentIndex }
|
||||
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
final override int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
@@ -1322,7 +1323,7 @@ import IsUnreachableInCall
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
predicate forceHighPrecision(Content c) { c instanceof ElementContent }
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) {
|
||||
@@ -1393,7 +1394,8 @@ private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().c
|
||||
cached
|
||||
private newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) }
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox extends TContentApprox {
|
||||
@@ -1424,6 +1426,10 @@ private class UnionApproxContent extends ContentApprox, TUnionApproxContent {
|
||||
final override string toString() { result = s }
|
||||
}
|
||||
|
||||
private class ElementApproxContent extends ContentApprox, TElementApproxContent {
|
||||
final override string toString() { result = "ElementApprox" }
|
||||
}
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) {
|
||||
@@ -1438,6 +1444,9 @@ ContentApprox getContentApprox(Content c) {
|
||||
u = c.(UnionContent).getUnion() and
|
||||
unionHasApproxName(u, prefix)
|
||||
)
|
||||
or
|
||||
c instanceof ElementContent and
|
||||
result instanceof ElementApproxContent
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1697,6 +1706,14 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
||||
/** Gets the second-level scope containing the node `n`, if any. */
|
||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections to use for `ElementContent`.
|
||||
*
|
||||
* This should be equal to the largest number of stars (i.e., `*`s) in any
|
||||
* `Element` content across all of our MaD summaries, sources, and sinks.
|
||||
*/
|
||||
int getMaxElementContentIndirectionIndex() { result = 5 }
|
||||
|
||||
/**
|
||||
* Module that defines flow through iterators.
|
||||
* For example,
|
||||
|
||||
@@ -2083,6 +2083,9 @@ private newtype TContent =
|
||||
indirectionIndex =
|
||||
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
|
||||
)
|
||||
} or
|
||||
TElementContent(int indirectionIndex) {
|
||||
indirectionIndex = [1 .. getMaxElementContentIndirectionIndex()]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2193,6 +2196,25 @@ class UnionContent extends Content, TUnionContent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Content` that represents one of the elements of a
|
||||
* container (e.g., `std::vector`).
|
||||
*/
|
||||
class ElementContent extends Content, TElementContent {
|
||||
int indirectionIndex;
|
||||
|
||||
ElementContent() { this = TElementContent(indirectionIndex) }
|
||||
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() }
|
||||
|
||||
override string toString() { result = contentStars(this) + "element" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entity that represents a set of `Content`s.
|
||||
*
|
||||
|
||||
@@ -147,7 +147,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, st
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
node instanceof ArgumentNode and
|
||||
c.isSingleton(any(ElementContent ec))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
@@ -55,73 +55,6 @@ private class Vector extends StdSequenceContainer {
|
||||
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for standard container constructors that reference the
|
||||
* value type of the container (that is, the `T` in `std::vector<T>`). For
|
||||
* example the fill constructor:
|
||||
* ```
|
||||
* std::vector<std::string> v(100, potentially_tainted_string);
|
||||
* ```
|
||||
*/
|
||||
private class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
StdSequenceContainerConstructor() {
|
||||
this.getDeclaringType() instanceof Vector or
|
||||
this.getDeclaringType() instanceof Deque or
|
||||
this.getDeclaringType() instanceof List or
|
||||
this.getDeclaringType() instanceof ForwardList
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from any parameter of the value type to the returned object
|
||||
(
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
|
||||
or
|
||||
output.isQualifierObject()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container function `data`.
|
||||
*/
|
||||
private class StdSequenceContainerData extends TaintFunction {
|
||||
StdSequenceContainerData() {
|
||||
this.getClassAndName("data") instanceof Array or
|
||||
this.getClassAndName("data") instanceof Vector
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from container itself (qualifier) to return value
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier (for writes to
|
||||
// `data`)
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
@@ -143,35 +76,6 @@ class StdSequenceContainerPush extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerPushModel extends StdSequenceContainerPush, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `front` and `back`.
|
||||
*/
|
||||
private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.getClassAndName(["front", "back"]) instanceof Array or
|
||||
this.getClassAndName(["front", "back"]) instanceof Deque or
|
||||
this.getClassAndName("front") instanceof ForwardList or
|
||||
this.getClassAndName(["front", "back"]) instanceof List or
|
||||
this.getClassAndName(["front", "back"]) instanceof Vector
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from object to returned reference
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `insert` and `insert_after`.
|
||||
*/
|
||||
@@ -198,58 +102,6 @@ class StdSequenceContainerInsert extends MemberFunction {
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
}
|
||||
|
||||
private class StdSequenceContainerInsertModel extends StdSequenceContainerInsert, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to container itself (qualifier) and return value
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container function `assign`.
|
||||
*/
|
||||
private class StdSequenceContainerAssign extends TaintFunction {
|
||||
StdSequenceContainerAssign() {
|
||||
this.getClassAndName("assign") instanceof Deque or
|
||||
this.getClassAndName("assign") instanceof ForwardList or
|
||||
this.getClassAndName("assign") instanceof List or
|
||||
this.getClassAndName("assign") instanceof Vector
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to container itself (qualifier)
|
||||
(
|
||||
input.isParameterDeref(this.getAValueTypeParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `at` and `operator[]`.
|
||||
*/
|
||||
@@ -261,20 +113,6 @@ class StdSequenceContainerAt extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard `emplace` function.
|
||||
*/
|
||||
@@ -297,20 +135,6 @@ class StdSequenceEmplace extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceModel extends StdSequenceEmplace, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter except the position iterator to qualifier and return value
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([1 .. this.getNumberOfParameters() - 1]) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace` function.
|
||||
*/
|
||||
@@ -340,17 +164,6 @@ class StdSequenceEmplaceBack extends MemberFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceBackModel extends StdSequenceEmplaceBack, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([0 .. this.getNumberOfParameters() - 1]) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate isPartialWrite(FunctionOutput output) { output.isQualifierObject() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user