Merge branch 'main' into andersfugmann/improve_upper_bound

This commit is contained in:
Anders Fugmann
2021-09-06 10:32:44 +02:00
2463 changed files with 53265 additions and 14876 deletions

View File

@@ -0,0 +1,9 @@
/**
* @name AST Consistency Check
* @description Performs consistency checks on the Abstract Syntax Tree. This query should have no results.
* @kind table
* @id cpp/ast-consistency-check
*/
import cpp
import CastConsistency

View File

@@ -0,0 +1,133 @@
/**
* Provides a class and predicate for recognizing files that are likely to have been generated
* automatically.
*/
import semmle.code.cpp.Comments
import semmle.code.cpp.File
import semmle.code.cpp.Preprocessor
/**
* Holds if comment `c` indicates that it might be in an auto-generated file, for
* example because it contains the text "auto-generated by".
*/
bindingset[comment]
private predicate autogeneratedComment(string comment) {
// ?s = include newlines in anything (`.`)
// ?i = ignore case
exists(string cond |
cond =
// generated by (not mid-sentence)
"(^ generated by[^a-z])|" + "(! generated by[^a-z])|" +
// generated file
"(generated file)|" +
// file [is/was/has been] generated
"(file( is| was| has been)? generated)|" +
// changes made in this file will be lost
"(changes made in this file will be lost)|" +
// do not edit/modify (not mid-sentence)
"(^ do(n't|nt| not) (hand-?)?(edit|modify))|" +
"(! do(n't|nt| not) (hand-?)?(edit|modify))|" +
// do not edit/modify + generated
"(do(n't|nt| not) (hand-?)?(edit|modify).*generated)|" +
"(generated.*do(n't|nt| not) (hand-?)?(edit|modify))" and
comment
.regexpMatch("(?si).*(" +
// replace `generated` with a regexp that also catches things like
// `auto-generated`.
cond.replaceAll("generated", "(auto[\\w-]*[\\s/\\*\\r\\n]*)?generated")
// replace `!` with a regexp for end-of-sentence / separator characters.
.replaceAll("!", "[\\.\\?\\!\\-\\;\\,]")
// replace ` ` with a regexp for one or more whitespace characters
// (including newlines and `/*`).
.replaceAll(" ", "[\\s/\\*\\r\\n]+") + ").*")
)
}
/**
* Holds if the file contains `#line` pragmas that refer to a different file.
* For example, in `parser.c` a pragma `#line 1 "parser.rl"`.
* Such pragmas usually indicate that the file was automatically generated.
*/
predicate hasPragmaDifferentFile(File f) {
exists(PreprocessorLine pl, string s |
pl.getFile() = f and
pl.getHead().splitAt(" ", 1) = s and
/* Zero index is line number, one index is file reference */
not "\"" + f.getAbsolutePath() + "\"" = s and
not "\"" + f.getRelativePath() + "\"" = s and
not "\"" + f.getBaseName() + "\"" = s
)
}
/**
* The line where the first comment in file `f` begins (maximum of 5). This allows
* us to skip past any preprocessor logic or similar code before the first comment.
*/
private int fileFirstComment(File f) {
result =
min(int line |
exists(Comment c |
c.getFile() = f and
c.getLocation().getStartLine() = line and
line < 5
)
).minimum(5)
}
/**
* The line where the initial comments of file `f` end. This is just before the
* first bit of code, excluding anything skipped over by `fileFirstComment`.
*/
private int fileHeaderLimit(File f) {
exists(int fc |
fc = fileFirstComment(f) and
result =
min(int line |
exists(DeclarationEntry de, Location l |
l = de.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
exists(PreprocessorDirective pd, Location l |
l = pd.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
exists(NamespaceDeclarationEntry nde, Location l |
l = nde.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
line = f.getMetrics().getNumberOfLines()
)
)
}
/**
* Holds if the file is probably an autogenerated file.
*
* A file is probably autogenerated if either of the following heuristics
* hold:
* 1. There is a comment in the start of the file that matches
* 'autogenerated', 'generated by', or a similar phrase.
* 2. There is a `#line` directive referring to a different file.
*/
class AutogeneratedFile extends File {
cached
AutogeneratedFile() {
autogeneratedComment(strictconcat(Comment c |
c.getFile() = this and
c.getLocation().getStartLine() <= fileHeaderLimit(this)
|
c.getContents() order by c.getLocation().getStartLine()
)) or
hasPragmaDifferentFile(this)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/**
* Provides classes representing C and C++ comments.
*/
import semmle.code.cpp.Location
import semmle.code.cpp.Element
/**
* A C/C++ comment. For example the comment in the following code:
* ```
* // C++ style single-line comment
* ```
* or a C style comment (which starts with `/*`).
*/
class Comment extends Locatable, @comment {
override string toString() { result = this.getContents() }
override Location getLocation() { comments(underlyingElement(this), _, result) }
/**
* Gets the text of this comment, including the opening `//` or `/*`, and the closing `*``/` if
* present.
*/
string getContents() { comments(underlyingElement(this), result, _) }
/**
* Gets the AST element this comment is associated with. For example, the comment in the
* following code is associated with the declaration of `j`.
* ```
* int i;
* int j; // Comment on j
* ```
*/
Element getCommentedElement() {
commentbinding(underlyingElement(this), unresolveElement(result))
}
}
/**
* A C style comment (one which starts with `/*`).
*/
class CStyleComment extends Comment {
CStyleComment() { this.getContents().matches("/*%") }
}
/**
* A CPP style comment. For example the comment in the following code:
* ```
* // C++ style single-line comment
* ```
*/
class CppStyleComment extends Comment {
CppStyleComment() { this.getContents().matches("//%") }
}

View File

@@ -0,0 +1,115 @@
/**
* Provides a class representing individual compiler invocations that occurred during the build.
*/
import semmle.code.cpp.File
/*
* These two helper predicates are used to associate a unique integer with
* each `@compilation`, for use in the `toString` method of `Compilation`.
* These integers are not stable across trap imports, but stable across
* runs with the same database.
*/
private predicate id(@compilation x, @compilation y) { x = y }
private predicate idOf(@compilation x, int y) = equivalenceRelation(id/2)(x, y)
/**
* An invocation of the compiler. Note that more than one file may be
* compiled per invocation. For example, this command compiles three
* source files:
*
* gcc -c f1.c f2.c f3.c
*
* Three things happen to each file during a compilation:
*
* 1. The file is compiled by a real compiler, such as gcc or VC.
* 2. The file is parsed by the CodeQL C++ front-end.
* 3. The parsed representation is converted to database tables by
* the CodeQL extractor.
*
* This class provides CPU and elapsed time information for steps 2 and 3,
* but not for step 1.
*/
class Compilation extends @compilation {
/** Gets a textual representation of this element. */
string toString() {
exists(int i |
idOf(this, i) and
result = "<compilation #" + i.toString() + ">"
)
}
/** Gets a file compiled during this invocation. */
File getAFileCompiled() { result = getFileCompiled(_) }
/** Gets the `i`th file compiled during this invocation */
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
/**
* Gets the amount of CPU time spent processing file number `i` in the C++
* front-end.
*/
float getFrontendCpuSeconds(int i) { compilation_time(this, i, 1, result) }
/**
* Gets the amount of elapsed time while processing file number `i` in the
* C++ front-end.
*/
float getFrontendElapsedSeconds(int i) { compilation_time(this, i, 2, result) }
/**
* Gets the amount of CPU time spent processing file number `i` in the
* extractor.
*/
float getExtractorCpuSeconds(int i) { compilation_time(this, i, 3, result) }
/**
* Gets the amount of elapsed time while processing file number `i` in the
* extractor.
*/
float getExtractorElapsedSeconds(int i) { compilation_time(this, i, 4, result) }
/**
* Gets an argument passed to the extractor on this invocation.
*/
string getAnArgument() { result = getArgument(_) }
/**
* Gets the `i`th argument passed to the extractor on this invocation.
*
* If the compiler was invoked as `gcc -c f1.c f2.c f3.c` then this
* will typically hold for
*
* i | result
* - | ---
* 0 | *path to extractor*
* 1 | `--mimic`
* 2 | `/usr/bin/gcc`
* 3 | `-c`
* 4 | f1.c
* 5 | f2.c
* 6 | f3.c
*/
string getArgument(int i) { compilation_args(this, i, result) }
/**
* Gets the total amount of CPU time spent processing all the files in the
* front-end and extractor.
*/
float getTotalCpuSeconds() { compilation_finished(this, result, _) }
/**
* Gets the total amount of elapsed time while processing all the files in
* the front-end and extractor.
*/
float getTotalElapsedSeconds() { compilation_finished(this, _, result) }
/**
* Holds if the extractor terminated normally. Terminating with an exit
* code indicating that an error occurred is considered normal
* termination, but crashing due to something like a segfault is not.
*/
predicate normalTermination() { compilation_finished(this, _, _) }
}

View File

@@ -0,0 +1,721 @@
/**
* Provides classes for working with C and C++ declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Specifier
import semmle.code.cpp.Namespace
private import semmle.code.cpp.internal.QualifiedName as Q
/**
* A C/C++ declaration: for example, a variable declaration, a type
* declaration, or a function declaration.
*
* This file defines two closely related classes: `Declaration` and
* `DeclarationEntry`. Some declarations do not correspond to a unique
* location in the source code. For example, a global variable might
* be declared in multiple source files:
* ```
* extern int myglobal;
* ```
* and defined in one:
* ```
* int myglobal;
* ```
* Each of these declarations (including the definition) is given its own
* distinct `DeclarationEntry`, but they all share the same `Declaration`.
*
* Some derived class of `Declaration` do not have a corresponding
* `DeclarationEntry`, because they always have a unique source location.
* `EnumConstant` and `FriendDecl` are both examples of this.
*/
class Declaration extends Locatable, @declaration {
/**
* Gets the innermost namespace which contains this declaration.
*
* The result will either be `GlobalNamespace`, or the tightest lexically
* enclosing namespace block. In particular, note that for declarations
* within structures, the namespace of the declaration is the same as the
* namespace of the structure.
*/
Namespace getNamespace() {
result = underlyingElement(this).(Q::Declaration).getNamespace()
or
exists(Parameter p | p = this and result = p.getFunction().getNamespace())
or
exists(LocalVariable v | v = this and result = v.getFunction().getNamespace())
}
/**
* Gets the name of the declaration, fully qualified with its
* namespace and declaring type.
*
* For performance, prefer the multi-argument `hasQualifiedName` or
* `hasGlobalName` predicates since they don't construct so many intermediate
* strings. For debugging, the `semmle.code.cpp.Print` module produces more
* detailed output but are also more expensive to compute.
*
* Example: `getQualifiedName() =
* "namespace1::namespace2::TemplateClass1<int>::Class2::memberName"`.
*/
string getQualifiedName() { result = underlyingElement(this).(Q::Declaration).getQualifiedName() }
/**
* DEPRECATED: Prefer `hasGlobalName` or the 2-argument or 3-argument
* `hasQualifiedName` predicates. To get the exact same results as this
* predicate in all edge cases, use `getQualifiedName()`.
*
* Holds if this declaration has the fully-qualified name `qualifiedName`.
* See `getQualifiedName`.
*/
predicate hasQualifiedName(string qualifiedName) { this.getQualifiedName() = qualifiedName }
/**
* Holds if this declaration has a fully-qualified name with a name-space
* component of `namespaceQualifier`, a declaring type of `typeQualifier`,
* and a base name of `baseName`. Template parameters and arguments are
* stripped from all components. Missing components are `""`.
*
* Example: `hasQualifiedName("namespace1::namespace2",
* "TemplateClass1::Class2", "memberName")`.
*
* Example (the class `std::vector`): `hasQualifiedName("std", "", "vector")`
* or `hasQualifiedName("std", "vector")`.
*
* Example (the `size` member function of class `std::vector`):
* `hasQualifiedName("std", "vector", "size")`.
*/
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
underlyingElement(this)
.(Q::Declaration)
.hasQualifiedName(namespaceQualifier, typeQualifier, baseName)
}
/**
* Holds if this declaration has a fully-qualified name with a name-space
* component of `namespaceQualifier`, no declaring type, and a base name of
* `baseName`.
*
* See the 3-argument `hasQualifiedName` for examples.
*/
predicate hasQualifiedName(string namespaceQualifier, string baseName) {
this.hasQualifiedName(namespaceQualifier, "", baseName)
}
/**
* Gets a description of this `Declaration` for display purposes.
*/
string getDescription() { result = this.getName() }
final override string toString() { result = this.getDescription() }
/**
* Gets the name of this declaration.
*
* This name doesn't include a namespace or any argument types, so
* for example both functions `::open()` and `::std::ifstream::open(...)`
* have the same name. The name of a template _class_ includes a string
* representation of its parameters, and the names of its instantiations
* include string representations of their arguments. Template _functions_
* and their instantiations do not include template parameters or arguments.
*
* To get the name including the namespace, use `hasQualifiedName`.
*
* To test whether this declaration has a particular name in the global
* namespace, use `hasGlobalName`.
*/
string getName() { none() } // overridden in subclasses
/** Holds if this declaration has the given name. */
predicate hasName(string name) { name = this.getName() }
/** Holds if this declaration has the given name in the global namespace. */
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) }
/** Holds if this declaration has the given name in the global namespace or the `std` namespace. */
predicate hasGlobalOrStdName(string name) {
this.hasGlobalName(name)
or
this.hasQualifiedName("std", "", name)
}
/**
* Holds if this declaration has the given name in the global namespace,
* the `std` namespace or the `bsl` namespace.
* We treat `std` and `bsl` as the same in some of our models.
*/
predicate hasGlobalOrStdOrBslName(string name) {
this.hasGlobalName(name)
or
this.hasQualifiedName("std", "", name)
or
this.hasQualifiedName("bsl", "", name)
}
/** Gets a specifier of this declaration. */
Specifier getASpecifier() { none() } // overridden in subclasses
/** Holds if this declaration has a specifier with the given name. */
predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) }
/**
* Gets a declaration entry corresponding to this declaration. See the
* comment above this class for an explanation of the relationship
* between `Declaration` and `DeclarationEntry`.
*/
DeclarationEntry getADeclarationEntry() { none() }
/**
* Gets the location of a declaration entry corresponding to this
* declaration.
*/
Location getADeclarationLocation() { none() } // overridden in subclasses
/**
* Gets the declaration entry corresponding to this declaration that is a
* definition, if any.
*/
DeclarationEntry getDefinition() { none() }
/** Gets the location of the definition, if any. */
Location getDefinitionLocation() { none() } // overridden in subclasses
/** Holds if the declaration has a definition. */
predicate hasDefinition() { exists(this.getDefinition()) }
/** DEPRECATED: Use `hasDefinition` instead. */
predicate isDefined() { hasDefinition() }
/** Gets the preferred location of this declaration, if any. */
override Location getLocation() { none() }
/** Gets a file where this element occurs. */
File getAFile() { result = this.getADeclarationLocation().getFile() }
/** Holds if this declaration is a top-level declaration. */
predicate isTopLevel() {
not (
this.isMember() or
this instanceof EnumConstant or
this instanceof Parameter or
this instanceof ProxyClass or
this instanceof LocalVariable or
this instanceof TemplateParameter or
this.(UserType).isLocal()
)
}
/** Holds if this declaration is static. */
predicate isStatic() { this.hasSpecifier("static") }
/** Holds if this declaration is a member of a class/struct/union. */
predicate isMember() { hasDeclaringType() }
/** Holds if this declaration is a member of a class/struct/union. */
predicate hasDeclaringType() { exists(this.getDeclaringType()) }
/**
* Gets the class where this member is declared, if it is a member.
* For templates, both the template itself and all instantiations of
* the template are considered to have the same declaring class.
*/
Class getDeclaringType() { this = result.getAMember() }
/**
* Gets a template argument used to instantiate this declaration from a template.
* When called on a template, this will return a template parameter type for
* both typed and non-typed parameters.
*/
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
/**
* Gets a template argument used to instantiate this declaration from a template.
* When called on a template, this will return a non-typed template
* parameter value.
*/
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
/**
* Gets the `i`th template argument used to instantiate this declaration from a
* template.
*
* For example:
*
* `template<typename T, T X> class Foo;`
*
* Will have `getTemplateArgument(0)` return `T`, and
* `getTemplateArgument(1)` return `X`.
*
* `Foo<int, 1> bar;`
*
* Will have `getTemplateArgument())` return `int`, and
* `getTemplateArgument(1)` return `1`.
*/
final Locatable getTemplateArgument(int index) {
if exists(getTemplateArgumentValue(index))
then result = getTemplateArgumentValue(index)
else result = getTemplateArgumentType(index)
}
/**
* Gets the `i`th template argument value used to instantiate this declaration
* from a template. When called on a template, this will return the `i`th template
* parameter value if it exists.
*
* For example:
*
* `template<typename T, T X> class Foo;`
*
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
* `getTemplateArgumentKind(0)`.
*
* `Foo<int, 10> bar;
*
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
* `getTemplateArgumentKind(0)`.
*/
final Locatable getTemplateArgumentKind(int index) {
if exists(getTemplateArgumentValue(index))
then result = getTemplateArgumentType(index)
else none()
}
/** Gets the number of template arguments for this declaration. */
final int getNumberOfTemplateArguments() {
result = count(int i | exists(getTemplateArgument(i)))
}
private Type getTemplateArgumentType(int index) {
class_template_argument(underlyingElement(this), index, unresolveElement(result))
or
function_template_argument(underlyingElement(this), index, unresolveElement(result))
or
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
}
private Expr getTemplateArgumentValue(int index) {
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
or
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
or
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
}
}
private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl;
/**
* A C/C++ declaration entry. For example the following code contains five
* declaration entries:
* ```
* extern int myGlobal;
* int myVariable;
* typedef char MyChar;
* void myFunction();
* void myFunction() {
* // ...
* }
* ```
* See the comment above `Declaration` for an explanation of the relationship
* between `Declaration` and `DeclarationEntry`.
*/
class DeclarationEntry extends Locatable, TDeclarationEntry {
/** Gets a specifier associated with this declaration entry. */
string getASpecifier() { none() } // overridden in subclasses
/**
* Gets the name associated with the corresponding definition (where
* available), or the name declared by this entry otherwise.
*/
string getCanonicalName() {
if getDeclaration().hasDefinition()
then result = getDeclaration().getDefinition().getName()
else result = getName()
}
/**
* Gets the declaration for which this is a declaration entry.
*
* Note that this is *not* always the inverse of
* `Declaration.getADeclarationEntry()`, for example if `C` is a
* `TemplateClass`, `I` is an instantiation of `C`, and `D` is a
* `Declaration` of `C`, then:
* `C.getADeclarationEntry()` returns `D`
* `I.getADeclarationEntry()` returns `D`
* but `D.getDeclaration()` only returns `C`
*/
Declaration getDeclaration() { none() } // overridden in subclasses
/** Gets the name associated with this declaration entry, if any. */
string getName() { none() } // overridden in subclasses
/**
* Gets the type associated with this declaration entry.
*
* For variable declarations, get the type of the variable.
* For function declarations, get the return type of the function.
* For type declarations, get the type being declared.
*/
Type getType() { none() } // overridden in subclasses
/**
* Gets the type associated with this declaration entry after specifiers
* have been deeply stripped and typedefs have been resolved.
*
* For variable declarations, get the type of the variable.
* For function declarations, get the return type of the function.
* For type declarations, get the type being declared.
*/
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
/**
* Holds if this declaration entry has a specifier with the given name.
*/
predicate hasSpecifier(string specifier) { getASpecifier() = specifier }
/** Holds if this declaration entry is a definition. */
predicate isDefinition() { none() } // overridden in subclasses
override string toString() {
if isDefinition()
then result = "definition of " + getName()
else
if getName() = getCanonicalName()
then result = "declaration of " + getName()
else result = "declaration of " + getCanonicalName() + " as " + getName()
}
}
private class TAccessHolder = @function or @usertype;
/**
* A declaration that can potentially have more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own
* private members) along with other `UserType`s and `Function` (they can be
* the target of `friend` declarations). For example `MyClass` and
* `myFunction` in the following code:
* ```
* class MyClass
* {
* public:
* ...
* };
*
* void myFunction() {
* // ...
* }
* ```
* In the C++ standard (N4140 11.2), rules for access control revolve around
* the informal phrase "_R_ occurs in a member or friend of class C", where
* `AccessHolder` corresponds to this _R_.
*/
class AccessHolder extends Declaration, TAccessHolder {
/**
* Holds if `this` can access private members of class `c`.
*
* This predicate encodes the phrase "occurs in a member or friend" that is
* repeated many times in the C++14 standard, section 11.2.
*/
predicate inMemberOrFriendOf(Class c) {
this.getEnclosingAccessHolder*() = c
or
exists(FriendDecl fd | fd.getDeclaringClass() = c |
this.getEnclosingAccessHolder*() = fd.getFriend()
)
}
/**
* Gets the nearest enclosing `AccessHolder`.
*/
AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses
/**
* Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140
* 11.2/4). When this holds, and `derived` has only one base subobject of
* type `base`, code in `this` can implicitly convert a pointer to `derived`
* into a pointer to `base`. Conversely, if such a conversion is possible
* then this predicate holds.
*
* For the sake of generality, this predicate also holds whenever `base` =
* `derived`.
*
* This predicate is `pragma[inline]` because it is infeasible to fully
* compute it on large code bases: all classes `derived` can be converted to
* their public bases `base` from everywhere (`this`), so this predicate
* could yield a number of tuples that is quadratic in the size of the
* program. To avoid this combinatorial explosion, only use this predicate in
* a context where `this` together with `base` or `derived` are sufficiently
* restricted.
*/
pragma[inline]
predicate canAccessClass(Class base, Class derived) {
// This predicate is marked `inline` and implemented in a very particular
// way. If we allowed this predicate to be fully computed, it would relate
// all `AccessHolder`s to all classes, which would be too much.
// There are four rules in N4140 11.2/4. Only the one named (4.4) is
// recursive, and it describes a transitive closure: intuitively, if A can
// be converted to B, and B can be converted to C, then A can be converted
// to C. To limit the number of tuples in the non-inline helper predicates,
// we first separate the derivation of 11.2/4 into two cases:
// Derivations using only (4.1) and (4.4). Note that these derivations are
// independent of `this`, which is why users of this predicate must take
// care to avoid a combinatorial explosion.
isDirectPublicBaseOf*(base, derived)
or
exists(DirectAccessHolder n |
this.getEnclosingAccessHolder*() = n and
// Derivations using (4.2) or (4.3) at least once.
n.thisCanAccessClassTrans(base, derived)
)
}
/**
* Holds if a non-static member `member` _is accessible at_ `this` when named
* in a class `derived` that is derived from or equal to the declaring class
* of `member` (N4140 11.2/5 and 11.4).
*
* This predicate determines whether an expression `x.member` would be
* allowed in `this` when `x` has type `derived`. The more general syntax
* `x.N::member`, where `N` may be a base class of `derived`, is not
* supported. This should only affect very rare edge cases of 11.4. This
* predicate concerns only _access_ and thus does not determine whether
* `member` can be unambiguously named at `this`: multiple overloads may
* apply, or `member` may be declared in an ambiguous base class.
*
* This predicate is `pragma[inline]` because it is infeasible to fully
* compute it on large code bases: all public members `member` are accessible
* from everywhere (`this`), so this predicate could yield a number of tuples
* that is quadratic in the size of the program. To avoid this combinatorial
* explosion, only use this predicate in a context where `this` and `member`
* are sufficiently restricted when `member` is public.
*/
pragma[inline]
predicate canAccessMember(Declaration member, Class derived) {
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier),
derived)
}
/**
* Holds if a hypothetical non-static member of `memberClass` with access
* specifier `memberAccess` _is accessible at_ `this` when named in a class
* `derived` that is derived from or equal to `memberClass` (N4140 11.2/5 and
* 11.4).
*
* This predicate determines whether an expression `x.m` would be
* allowed in `this` when `x` has type `derived` and `m` has `memberAccess`
* in `memberClass`. The more general syntax `x.N::n`, where `N` may be a
* base class of `derived`, is not supported. This should only affect very
* rare edge cases of 11.4.
*
* This predicate is `pragma[inline]` because it is infeasible to fully
* compute it on large code bases: all classes `memberClass` have their
* public members accessible from everywhere (`this`), so this predicate
* could yield a number of tuples that is quadratic in the size of the
* program. To avoid this combinatorial explosion, only use this predicate in
* a context where `this` and `memberClass` are sufficiently restricted when
* `memberAccess` is public.
*/
pragma[inline]
predicate couldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) {
// There are four rules in N4140 11.2/5. To limit the number of tuples in
// the non-inline helper predicates, we first separate the derivation of
// 11.2/5 into two cases:
// Rule (5.1) directly: the member is public, and `derived` uses public
// inheritance all the way up to `memberClass`. Note that these derivations
// are independent of `this`, which is why users of this predicate must
// take care to avoid a combinatorial explosion.
everyoneCouldAccessMember(memberClass, memberAccess, derived)
or
exists(DirectAccessHolder n |
this.getEnclosingAccessHolder*() = n and
// Any other derivation.
n.thisCouldAccessMember(memberClass, memberAccess, derived)
)
}
}
/**
* A declaration that very likely has more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own
* private members) along with any target of a `friend` declaration. For
* example `MyClass` and `friendFunction` in the following code:
* ```
* class MyClass
* {
* public:
* friend void friendFunction();
* };
*
* void friendFunction() {
* // ...
* }
* ```
* Most access rights are computed for `DirectAccessHolder` instead of
* `AccessHolder` -- that's more efficient because there are fewer
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
* then the contained `AccessHolder` inherits its access rights.
*/
private class DirectAccessHolder extends Element {
DirectAccessHolder() {
this instanceof Class
or
exists(FriendDecl fd | fd.getFriend() = this)
}
/**
* Holds if a base class `base` of `derived` _is accessible at_ `this` when
* the derivation of that fact uses rule (4.2) and (4.3) of N4140 11.2/4 at
* least once. In other words, the `this` parameter is not ignored. This
* restriction makes it feasible to fully enumerate this predicate even on
* large code bases.
*/
predicate thisCanAccessClassTrans(Class base, Class derived) {
// This implementation relies on the following property of our predicates:
// if `this.thisCanAccessClassStep(b, d)` and
// `isDirectPublicBaseOf(b2, b)`, then
// `this.thisCanAccessClassStep(b2, d)`. In other words, if a derivation
// uses (4.2) or (4.3) somewhere and uses (4.1) directly above that in the
// transitive chain, then the use of (4.1) is redundant. This means we only
// need to consider derivations that use (4.2) or (4.3) as the "first"
// step, that is, towards `base`, so this implementation is essentially a
// transitive closure with a restricted base case.
this.thisCanAccessClassStep(base, derived)
or
exists(Class between | thisCanAccessClassTrans(base, between) |
isDirectPublicBaseOf(between, derived) or
this.thisCanAccessClassStep(between, derived)
)
// It is possible that this predicate could be computed faster for deep
// hierarchies if we can prove and utilize that all derivations of 11.2/4
// can be broken down into steps where `base` is a _direct_ base of
// `derived` in each step.
}
/**
* Holds if a base class `base` of `derived` _is accessible at_ `this` using
* only a single application of rule (4.2) and (4.3) of N4140 11.2/4.
*/
private predicate thisCanAccessClassStep(Class base, Class derived) {
exists(AccessSpecifier public | public.hasName("public") |
// Rules (4.2) and (4.3) are implemented together as one here with
// reflexive-transitive inheritance, where (4.3) is the transitive case,
// and (4.2) is the reflexive case.
exists(Class p | p = derived.getADerivedClass*() |
this.isFriendOfOrEqualTo(p) and
// Note: it's crucial that this is `!=` rather than `not =` since
// `accessOfBaseMember` does not have a result when the member would be
// inaccessible.
p.accessOfBaseMember(base, public) != public
)
) and
// This is the only case that doesn't in itself guarantee that
// `derived` < `base`, so we add the check here. The standard suggests
// computing `canAccessClass` only for derived classes, but that seems
// incompatible with the execution model of QL, so we instead construct
// every case to guarantee `derived` < `base`.
derived = base.getADerivedClass+()
}
/**
* Like `couldAccessMember` but only contains derivations in which either
* (5.2), (5.3) or (5.4) must be invoked. In other words, the `this`
* parameter is not ignored. This restriction makes it feasible to fully
* enumerate this predicate even on large code bases. We check for 11.4 as
* part of (5.3), since this further limits the number of tuples produced by
* this predicate.
*/
predicate thisCouldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) {
// Only (5.4) is recursive, and chains of invocations of (5.4) can always
// be collapsed to one invocation by the transitivity of 11.2/4.
// Derivations not using (5.4) can always be rewritten to have a (5.4) rule
// in front because our encoding of 11.2/4 in `canAccessClass` is
// reflexive. Thus, we only need to consider three cases: rule (5.4)
// followed by either (5.1), (5.2) or (5.3).
// Rule (5.4), using a non-trivial derivation of 11.2/4, followed by (5.1).
// If the derivation of 11.2/4 is trivial (only uses (4.1) and (4.4)), this
// case can be replaced with purely (5.1) and thus does not need to be in
// this predicate.
exists(Class between | this.thisCanAccessClassTrans(between, derived) |
everyoneCouldAccessMember(memberClass, memberAccess, between)
)
or
// Rule (5.4) followed by Rule (5.2)
exists(Class between | this.(AccessHolder).canAccessClass(between, derived) |
between.accessOfBaseMember(memberClass, memberAccess).hasName("private") and
this.isFriendOfOrEqualTo(between)
)
or
// Rule (5.4) followed by Rule (5.3), integrating 11.4. We integrate 11.4
// here because we would otherwise generate too many tuples. This code is
// very performance-sensitive, and any changes should be benchmarked on
// LibreOffice.
// Rule (5.4) requires that `this.canAccessClass(between, derived)`
// (implying that `derived <= between` in the class hierarchy) and that
// `p <= between`. Rule 11.4 additionally requires `derived <= p`, but
// all these rules together result in too much freedom and overlap between
// cases. Therefore, for performance, we split into three cases for how
// `between` as a base of `derived` is accessible at `this`, where `this`
// is the implementation of `p`:
// 1. `between` is an accessible base of `derived` by going through `p` as
// an intermediate step.
// 2. `this` is part of the implementation of `derived` because it's a
// member or a friend. In this case, we do not need `p` to perform this
// derivation, so we can set `p = derived` and proceed as in case 1.
// 3. `derived` has an alternative inheritance path up to `between` that
// bypasses `p`. Then that path must be public, or we are in case 2.
exists(AccessSpecifier public | public.hasName("public") |
exists(Class between, Class p |
between.accessOfBaseMember(memberClass, memberAccess).hasName("protected") and
this.isFriendOfOrEqualTo(p) and
(
// This is case 1 from above. If `p` derives privately from `between`
// then the member we're trying to access is private or inaccessible
// in `derived`, so either rule (5.2) applies instead, or the member
// is inaccessible. Therefore, in this case, `p` must derive at least
// protected from `between`. Further, since the access of `derived`
// to its base `between` must pass through `p` in this case, we know
// that `derived` must derived publicly from `p` unless we are in
// case 2; there are no other cases of 11.2/4 where the
// implementation of a base class can access itself as a base.
p.accessOfBaseMember(between, public).getName() >= "protected" and
derived.accessOfBaseMember(p, public) = public
or
// This is case 3 above.
derived.accessOfBaseMember(between, public) = public and
derived = p.getADerivedClass*() and
exists(p.accessOfBaseMember(between, memberAccess))
)
)
)
}
private predicate isFriendOfOrEqualTo(Class c) {
exists(FriendDecl fd | fd.getDeclaringClass() = c | this = fd.getFriend())
or
this = c
}
}
/**
* Holds if `base` is a direct public base of `derived`, possibly virtual and
* possibly through typedefs. The transitive closure of this predicate encodes
* derivations of N4140 11.2/4 that use only (4.1) and (4.4).
*/
private predicate isDirectPublicBaseOf(Class base, Class derived) {
exists(ClassDerivation cd |
cd.getBaseClass() = base and
cd.getDerivedClass() = derived and
cd.getASpecifier().hasName("public")
)
}
/**
* Holds if a hypothetical member of `memberClass` with access specifier
* `memberAccess` would be public when named as a member of `derived`.
* This encodes N4140 11.2/5 case (5.1).
*/
private predicate everyoneCouldAccessMember(
Class memberClass, AccessSpecifier memberAccess, Class derived
) {
derived.accessOfBaseMember(memberClass, memberAccess).hasName("public")
}

View File

@@ -0,0 +1,68 @@
/**
* Provides classes representing warnings generated during compilation.
*/
import semmle.code.cpp.Location
/** A compiler-generated error, warning or remark. */
class Diagnostic extends Locatable, @diagnostic {
/** Gets the compilation that generated this diagnostic. */
Compilation getCompilation() { diagnostic_for(underlyingElement(this), result, _, _) }
/**
* Gets the severity of the message, on a range from 1 to 5: 1=remark,
* 2=warning, 3=discretionary error, 4=error, 5=catastrophic error.
*/
int getSeverity() { diagnostics(underlyingElement(this), result, _, _, _, _) }
/** Gets the error code for this compiler message. */
string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) }
/** Holds if `s` is the error code for this compiler message. */
predicate hasTag(string s) { this.getTag() = s }
/**
* Gets the error message text associated with this compiler
* diagnostic.
*/
string getMessage() { diagnostics(underlyingElement(this), _, _, result, _, _) }
/**
* Gets the full error message text associated with this compiler
* diagnostic.
*/
string getFullMessage() { diagnostics(underlyingElement(this), _, _, _, result, _) }
/** Gets the source location corresponding to the compiler message. */
override Location getLocation() { diagnostics(underlyingElement(this), _, _, _, _, result) }
override string toString() { result = this.getMessage() }
}
/** A compiler-generated remark (milder than a warning). */
class CompilerRemark extends Diagnostic {
CompilerRemark() { this.getSeverity() = 1 }
}
/** A compiler-generated warning. */
class CompilerWarning extends Diagnostic {
CompilerWarning() { this.getSeverity() = 2 }
}
/**
* A compiler-generated discretionary error (a compile-time error that may
* be suppressed).
*/
class CompilerDiscretionaryError extends Diagnostic {
CompilerDiscretionaryError() { this.getSeverity() = 3 }
}
/** A compiler error message. */
class CompilerError extends Diagnostic {
CompilerError() { this.getSeverity() = 4 }
}
/** A compiler error that prevents compilation from continuing. */
class CompilerCatastrophe extends Diagnostic {
CompilerCatastrophe() { this.getSeverity() = 5 }
}

View File

@@ -0,0 +1,297 @@
/**
* Provides the `Element` class, which is the base class for all classes representing C or C++
* program elements.
*/
import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
/**
* Get the `Element` that represents this `@element`.
* Normally this will simply be a cast of `e`, but sometimes it is not.
* For example, for an incomplete struct `e` the result may be a
* complete struct with the same name.
*/
pragma[inline]
Element mkElement(@element e) { unresolveElement(result) = e }
/**
* INTERNAL: Do not use.
*
* Gets an `@element` that resolves to the `Element`. This should
* normally only be called from member predicates, where `e` is not
* `this` and you need the result for an argument to a database
* extensional.
* See `underlyingElement` for when `e` is `this`.
*/
pragma[inline]
@element unresolveElement(Element e) {
not result instanceof @usertype and
result = e
or
e = resolveClass(result)
}
/**
* INTERNAL: Do not use.
*
* Gets the `@element` that this `Element` extends. This should normally
* only be called from member predicates, where `e` is `this` and you
* need the result for an argument to a database extensional.
* See `unresolveElement` for when `e` is not `this`.
*/
@element underlyingElement(Element e) { result = e }
/**
* A C/C++ element with no member predicates other than `toString`. Not for
* general use. This class does not define a location, so classes wanting to
* change their location without affecting other classes can extend
* `ElementBase` instead of `Element` to create a new rootdef for `getURL`,
* `getLocation`, or `hasLocationInfo`.
*/
class ElementBase extends @element {
/** Gets a textual representation of this element. */
cached
string toString() { none() }
/** DEPRECATED: use `getAPrimaryQlClass` instead. */
deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
/**
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
*/
final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
/**
* Gets the name of a primary CodeQL class to which this element belongs.
*
* For most elements, this is simply the most precise syntactic category to
* which they belong; for example, `AddExpr` is a primary class, but
* `BinaryOperation` is not.
*
* This predicate can have multiple results if multiple primary classes match.
* For some elements, this predicate may not have a result.
*/
string getAPrimaryQlClass() { none() }
}
/**
* A C/C++ element. This class is the base class for all C/C++
* elements, such as functions, classes, expressions, and so on.
*/
class Element extends ElementBase {
/** Gets the primary file where this element occurs. */
File getFile() { result = this.getLocation().getFile() }
/**
* Holds if this element may be from source. This predicate holds for all
* elements, except for those in the dummy file, whose name is the empty string.
* The dummy file contains declarations that are built directly into the compiler.
*/
predicate fromSource() { this.getFile().fromSource() }
/**
* Holds if this element may be from a library.
*
* DEPRECATED: always true.
*/
deprecated predicate fromLibrary() { this.getFile().fromLibrary() }
/** Gets the primary location of this element. */
Location getLocation() { none() }
/**
* Gets the source of this element: either itself or a macro that expanded
* to this element.
*
* If the element is not in a macro expansion, then the "root" is just
* the element itself. Otherwise, it is the definition of the innermost
* macro whose expansion the element is in.
*
* This method is useful for filtering macro results in checks: simply
* blame `e.findRootCause` rather than `e`. This will report only bugs
* that are not in macros, and in addition report macros that (somewhere)
* expand to a bug.
*/
Element findRootCause() {
if exists(MacroInvocation mi | this = mi.getAGeneratedElement())
then
exists(MacroInvocation mi |
this = mi.getAGeneratedElement() and
not exists(MacroInvocation closer |
this = closer.getAGeneratedElement() and
mi = closer.getParentInvocation+()
) and
result = mi.getMacro()
)
else result = this
}
/**
* Gets the parent scope of this `Element`, if any.
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
* or certain kinds of `Statement`.
*/
Element getParentScope() {
// result instanceof class
exists(Declaration m |
m = this and
result = m.getDeclaringType() and
not this instanceof EnumConstant
)
or
exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc)
or
// result instanceof namespace
exists(Namespace n | result = n and n.getADeclaration() = this)
or
exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n)
or
exists(Namespace n | this = n and result = n.getParentNamespace())
or
// result instanceof stmt
exists(LocalVariable v |
this = v and
exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent())
)
or
exists(Parameter p | this = p and result = p.getFunction())
or
exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n)
or
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
or
// result instanceof block|function
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
or
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
or
// result instanceof stmt
exists(ControlStructure s | this = s and result = s.getParent())
or
using_container(unresolveElement(result), underlyingElement(this))
}
/**
* Holds if this element comes from a macro expansion. Only elements that
* are entirely generated by a macro are included - for elements that
* partially come from a macro, see `isAffectedByMacro`.
*/
predicate isInMacroExpansion() { inMacroExpansion(this) }
/**
* Holds if this element is affected in any way by a macro. All elements
* that are totally or partially generated by a macro are included, so
* this is a super-set of `isInMacroExpansion`.
*/
predicate isAffectedByMacro() { affectedByMacro(this) }
private Element getEnclosingElementPref() {
enclosingfunction(underlyingElement(this), unresolveElement(result)) or
result.(Function) = stmtEnclosingElement(this) or
this.(LocalScopeVariable).getFunction() = result or
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) or
derivations(underlyingElement(this), unresolveElement(result), _, _, _) or
stmtparents(underlyingElement(this), _, unresolveElement(result)) or
exprparents(underlyingElement(this), _, unresolveElement(result)) or
namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
exprconv(unresolveElement(result), underlyingElement(this)) or
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
using_container(unresolveElement(result), underlyingElement(this)) or
static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
}
/** Gets the closest `Element` enclosing this one. */
cached
Element getEnclosingElement() {
result = getEnclosingElementPref()
or
not exists(getEnclosingElementPref()) and
(
this = result.(Class).getAMember()
or
result = exprEnclosingElement(this)
or
var_decls(underlyingElement(this), unresolveElement(result), _, _, _)
)
}
/**
* Holds if this `Element` is a part of a template instantiation (but not
* the template itself).
*/
predicate isFromTemplateInstantiation(Element instantiation) {
exists(Element e | isFromTemplateInstantiationRec(e, instantiation) |
this = e or
this.(DeclarationEntry).getDeclaration() = e
)
}
/**
* Holds if this `Element` is part of a template `template` (not if it is
* part of an instantiation of `template`). This means it is represented in
* the database purely as syntax and without guarantees on the presence or
* correctness of type-based operations such as implicit conversions.
*
* If an element is nested within several templates, this predicate holds with
* a value of `template` for each containing template.
*/
predicate isFromUninstantiatedTemplate(Element template) {
exists(Element e | isFromUninstantiatedTemplateRec(e, template) |
this = e or
this.(DeclarationEntry).getDeclaration() = e
)
}
}
private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) {
instantiation.(Function).isConstructedFrom(_) and
e = instantiation
or
instantiation.(Class).isConstructedFrom(_) and
e = instantiation
or
instantiation.(Variable).isConstructedFrom(_) and
e = instantiation
or
isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation)
}
private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
is_class_template(unresolveElement(template)) and
e = template
or
is_function_template(unresolveElement(template)) and
e = template
or
is_variable_template(unresolveElement(template)) and
e = template
or
isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template)
}
/**
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
* line in the following example contains a static assert:
* ```
* static_assert(sizeof(MyStruct) <= 4096);
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
* ```
*/
class StaticAssert extends Locatable, @static_assert {
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
/**
* Gets the expression which this static assertion ensures is true.
*/
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
/**
* Gets the message which will be reported by the compiler if this static assertion fails.
*/
string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
}

View File

@@ -0,0 +1,63 @@
/**
* Provides predicates for finding the smallest element that encloses an expression or statement.
*/
import cpp
/**
* Gets the enclosing element of statement `s`.
*/
cached
Element stmtEnclosingElement(Stmt s) {
result.(Function).getEntryPoint() = s or
result = stmtEnclosingElement(s.getParent()) or
result = exprEnclosingElement(s.getParent())
}
/**
* Gets the enclosing element of expression `e`.
*/
// The `pragma[nomagic]` is a workaround to prevent this cached stage (and all
// subsequent stages) from being evaluated twice. See QL-888. It has the effect
// of making the `Conversion` class predicate get the same optimization in all
// queries.
pragma[nomagic]
cached
Element exprEnclosingElement(Expr e) {
result = exprEnclosingElement(e.getParent())
or
result = stmtEnclosingElement(e.getParent())
or
result.(Function) = e.getParent()
or
result = exprEnclosingElement(e.(Conversion).getExpr())
or
exists(Initializer i |
i.getExpr() = e and
if exists(i.getEnclosingStmt())
then result = stmtEnclosingElement(i.getEnclosingStmt())
else
if i.getDeclaration() instanceof Parameter
then result = i.getDeclaration().(Parameter).getFunction()
else result = i.getDeclaration()
)
or
exists(Expr anc |
expr_ancestor(unresolveElement(e), unresolveElement(anc)) and result = exprEnclosingElement(anc)
)
or
exists(Stmt anc |
expr_ancestor(unresolveElement(e), unresolveElement(anc)) and result = stmtEnclosingElement(anc)
)
or
exists(DeclarationEntry de |
expr_ancestor(unresolveElement(e), unresolveElement(de)) and
if exists(DeclStmt ds | de = ds.getADeclarationEntry())
then
exists(DeclStmt ds |
de = ds.getADeclarationEntry() and
result = stmtEnclosingElement(ds)
)
else result = de.getDeclaration()
)
}

View File

@@ -0,0 +1,204 @@
/**
* Provides classes representing C/C++ enums and enum constants.
*/
import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ enum [N4140 7.2]. For example, the types `MyEnum` and
* `MyScopedEnum` in:
* ```
* enum MyEnum {
* MyEnumConstant
* };
*
* enum class MyScopedEnum {
* MyScopedEnumConstant
* };
* ```
* This includes C++ scoped enums, see the `ScopedEnum` QL class.
*/
class Enum extends UserType, IntegralOrEnumType {
/** Gets an enumerator of this enumeration. */
EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this }
/**
* Gets the enumerator of this enumeration that was declared at the zero-based position `index`.
* For example, `zero` is at index 2 in the following declaration:
* ```
* enum ReversedOrder {
* two = 2,
* one = 1,
* zero = 0
* };
* ```
*/
EnumConstant getEnumConstant(int index) {
enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _)
}
override string getAPrimaryQlClass() { result = "Enum" }
/**
* Gets a descriptive string for the enum. This method is only intended to
* be used for debugging purposes. For more information, see the comment
* for `Type.explain`.
*/
override string explain() { result = "enum " + this.getName() }
override int getSize() {
// Workaround for extractor bug CPP-348: No size information for enums.
// If the extractor didn't provide a size, assume four bytes.
result = UserType.super.getSize()
or
not exists(UserType.super.getSize()) and result = 4
}
/** See `Type.isDeeplyConst` and `Type.isDeeplyConstBelow`. Internal. */
override predicate isDeeplyConstBelow() { any() } // No subparts
/**
* Holds if this enum has an enum-base [N4140 7.2].
* For example: `enum E : int`.
*/
predicate hasExplicitUnderlyingType() { derivations(_, underlyingElement(this), _, _, _) }
/**
* The type of the enum-base [N4140 7.2], if it is specified.
* For example: `int` in `enum E : int`.
*/
Type getExplicitUnderlyingType() {
derivations(_, underlyingElement(this), _, unresolveElement(result), _)
}
}
/**
* A C/C++ enum that is directly enclosed by a function. For example, the type
* `MyLocalEnum` in:
* ```
* void myFunction() {
* enum MyLocalEnum {
* MyLocalEnumConstant
* };
* }
* ```
*/
class LocalEnum extends Enum {
LocalEnum() { isLocal() }
override string getAPrimaryQlClass() { result = "LocalEnum" }
}
/**
* A C/C++ enum that is declared within a class/struct. For example, the type
* `MyNestedEnum` in:
* ```
* class MyClass {
* public:
* enum MyNestedEnum {
* MyNestedEnumConstant
* };
* };
* ```
*/
class NestedEnum extends Enum {
NestedEnum() { this.isMember() }
override string getAPrimaryQlClass() { result = "NestedEnum" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
}
/**
* A C++ scoped enum, that is, an enum whose constants must be qualified with
* the name of the enum. For example, the type `Color` in:
* ```
* enum class Color {
* red,
* blue
* }
* ```
*/
class ScopedEnum extends Enum {
ScopedEnum() { usertypes(underlyingElement(this), _, 13) }
override string getAPrimaryQlClass() { result = "ScopedEnum" }
}
/**
* A C/C++ enumerator [N4140 7.2], also known as an enumeration constant.
*
* For example the enumeration constant `green` in:
* ```
* enum {
* red,
* green,
* blue
* }
* ```
*/
class EnumConstant extends Declaration, @enumconstant {
/**
* Gets the enumeration of which this enumerator is a member.
*/
Enum getDeclaringEnum() {
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _)
}
override string getAPrimaryQlClass() { result = "EnumConstant" }
override Class getDeclaringType() { result = this.getDeclaringEnum().getDeclaringType() }
/**
* Gets the name of this enumerator.
*/
override string getName() { enumconstants(underlyingElement(this), _, _, _, result, _) }
/**
* Gets the value that this enumerator is initialized to, as a
* string. This can be a value explicitly given to the enumerator, or an
* automatically assigned value.
*/
string getValue() { result = this.getInitializer().getExpr().getValue() }
/** Gets the type of this enumerator. */
Type getType() { enumconstants(underlyingElement(this), _, _, unresolveElement(result), _, _) }
/** Gets the location of a declaration of this enumerator. */
override Location getADeclarationLocation() { result = this.getDefinitionLocation() }
/** Gets the location of the definition of this enumerator. */
override Location getDefinitionLocation() {
enumconstants(underlyingElement(this), _, _, _, _, result)
}
/** Gets the location of the definition of this enumerator. */
override Location getLocation() { result = this.getDefinitionLocation() }
/** Gets the initializer of this enumerator, if any. */
Initializer getInitializer() { result.getDeclaration() = this }
/** Gets an access of this enumerator. */
EnumConstantAccess getAnAccess() { result.getTarget() = this }
/** Gets a specifier of this enumerator. */
override Specifier getASpecifier() {
varspecifiers(underlyingElement(this), unresolveElement(result))
}
/**
* An attribute of this enumerator.
*
* Note that allowing attributes on enumerators is a language extension
* which is only supported by Clang.
*/
Attribute getAnAttribute() { varattributes(underlyingElement(this), unresolveElement(result)) }
}

View File

@@ -0,0 +1,126 @@
/**
* Provides classes representing C structure members and C++ non-static member variables.
*/
import semmle.code.cpp.Variable
import semmle.code.cpp.Enum
import semmle.code.cpp.exprs.Access
/**
* A C structure member or C++ non-static member variable. For example the
* member variable `m` in the following code (but not `s`):
* ```
* class MyClass {
* public:
* int m;
* static int s;
* };
* ```
*
* This does not include static member variables in C++. To include static member
* variables, use `MemberVariable` instead of `Field`.
*/
class Field extends MemberVariable {
Field() { fieldoffsets(underlyingElement(this), _, _) }
override string getAPrimaryQlClass() { result = "Field" }
/**
* Gets the offset of this field in bytes from the start of its declaring
* type (on the machine where facts were extracted).
*/
int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) }
/**
* Gets the byte offset within `mostDerivedClass` of each occurence of this
* field within `mostDerivedClass` itself or a base class subobject of
* `mostDerivedClass`.
* Note that for fields of virtual base classes, and non-virtual base classes
* thereof, this predicate assumes that `mostDerivedClass` is the type of the
* complete most-derived object.
*/
int getAByteOffsetIn(Class mostDerivedClass) {
result = mostDerivedClass.getABaseClassByteOffset(getDeclaringType()) + getByteOffset()
}
/**
* Holds if the field can be initialized as part of an initializer list. For
* example, in:
* ```
* struct S {
* unsigned int a : 5;
* unsigned int : 5;
* unsigned int b : 5;
* };
* ```
*
* Fields `a` and `b` are initializable, but the unnamed bitfield is not.
*/
predicate isInitializable() {
// All non-bitfield fields are initializable. This predicate is overridden
// in `BitField` to handle the anonymous bitfield case.
any()
}
/**
* Gets the zero-based index of the specified field within its enclosing
* class, counting only fields that can be initialized. This is the order in
* which the field will be initialized, whether by an initializer list or in a
* constructor.
*/
pragma[nomagic]
final int getInitializationOrder() {
exists(Class cls, int memberIndex |
this = cls.getCanonicalMember(memberIndex) and
memberIndex =
rank[result + 1](int index | cls.getCanonicalMember(index).(Field).isInitializable())
)
}
}
/**
* A C structure member or C++ member variable declared with an explicit size
* in bits. For example the member variable `x` in the following code:
* ```
* struct MyStruct {
* int x : 3;
* };
* ```
*/
class BitField extends Field {
BitField() { bitfield(underlyingElement(this), _, _) }
override string getAPrimaryQlClass() { result = "BitField" }
/**
* Gets the size of this bitfield in bits (on the machine where facts
* were extracted).
*/
int getNumBits() { bitfield(underlyingElement(this), result, _) }
/**
* Gets the value which appeared after the colon in the bitfield
* declaration.
*
* In most cases, this will give the same value as `getNumBits`. It will
* only differ when the value after the colon is larger than the size of
* the variable's type. For example, given `int32_t x : 1234`,
* `getNumBits` will give 32, whereas `getDeclaredNumBits` will give
* 1234.
*/
int getDeclaredNumBits() { bitfield(underlyingElement(this), _, result) }
/**
* Gets the offset of this bitfield in bits from the byte identified by
* getByteOffset (on the machine where facts were extracted).
*/
int getBitOffset() { fieldoffsets(underlyingElement(this), _, result) }
/** Holds if this bitfield is anonymous. */
predicate isAnonymous() { hasName("(unnamed bitfield)") }
override predicate isInitializable() {
// Anonymous bitfields are not initializable.
not isAnonymous()
}
}

View File

@@ -0,0 +1,443 @@
/**
* Provides classes representing files and folders.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.metrics.MetricFile
/** A file or folder. */
class Container extends Locatable, @container {
/**
* Gets the absolute, canonical path of this container, using forward slashes
* as path separator.
*
* The path starts with a _root prefix_ followed by zero or more _path
* segments_ separated by forward slashes.
*
* The root prefix is of one of the following forms:
*
* 1. A single forward slash `/` (Unix-style)
* 2. An upper-case drive letter followed by a colon and a forward slash,
* such as `C:/` (Windows-style)
* 3. Two forward slashes, a computer name, and then another forward slash,
* such as `//FileServer/` (UNC-style)
*
* Path segments are never empty (that is, absolute paths never contain two
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
* segments never contain forward slashes, and no path segment is of the
* form `.` (one dot) or `..` (two dots).
*
* Note that an absolute path never ends with a forward slash, except if it is
* a bare root prefix, that is, the path has no path segments. A container
* whose absolute path has no segments is always a `Folder`, not a `File`.
*/
string getAbsolutePath() { none() } // overridden by subclasses
/**
* DEPRECATED: Use `getLocation` instead.
* Gets a URL representing the location of this container.
*
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
*/
deprecated string getURL() { none() } // overridden by subclasses
/**
* Gets the relative path of this file or folder from the root folder of the
* analyzed source location. The relative path of the root folder itself is
* the empty string.
*
* This has no result if the container is outside the source root, that is,
* if the root folder is not a reflexive, transitive parent of this container.
*/
string getRelativePath() {
exists(string absPath, string pref |
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
|
absPath = pref and result = ""
or
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
not result.matches("/%")
)
}
/**
* Gets the base name of this container including extension, that is, the last
* segment of its absolute path, or the empty string if it has no segments.
*
* Here are some examples of absolute paths and the corresponding base names
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Base name</th></tr>
* <tr><td>"/tmp/tst.js"</td><td>"tst.js"</td></tr>
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
* <tr><td>"/"</td><td>""</td></tr>
* <tr><td>"C:/"</td><td>""</td></tr>
* <tr><td>"D:/"</td><td>""</td></tr>
* <tr><td>"//FileServer/"</td><td>""</td></tr>
* </table>
*/
string getBaseName() {
result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
}
/**
* Gets the extension of this container, that is, the suffix of its base name
* after the last dot character, if any.
*
* In particular,
*
* - if the name does not include a dot, there is no extension, so this
* predicate has no result;
* - if the name ends in a dot, the extension is the empty string;
* - if the name contains multiple dots, the extension follows the last dot.
*
* Here are some examples of absolute paths and the corresponding extensions
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Extension</th></tr>
* <tr><td>"/tmp/tst.js"</td><td>"js"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
* </table>
*/
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
/**
* Gets the stem of this container, that is, the prefix of its base name up to
* (but not including) the last dot character if there is one, or the entire
* base name if there is not.
*
* Here are some examples of absolute paths and the corresponding stems
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Stem</th></tr>
* <tr><td>"/tmp/tst.js"</td><td>"tst"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
* </table>
*/
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
/** Gets the parent container of this file or folder, if any. */
Container getParentContainer() {
containerparent(unresolveElement(result), underlyingElement(this))
}
/** Gets a file or sub-folder in this container. */
Container getAChildContainer() { this = result.getParentContainer() }
/** Gets a file in this container. */
File getAFile() { result = getAChildContainer() }
/** Gets the file in this container that has the given `baseName`, if any. */
File getFile(string baseName) {
result = getAFile() and
result.getBaseName() = baseName
}
/** Gets a sub-folder in this container. */
Folder getAFolder() { result = getAChildContainer() }
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
Folder getFolder(string baseName) {
result = getAFolder() and
result.getBaseName() = baseName
}
/**
* Gets a textual representation of the path of this container.
*
* This is the absolute path of the container.
*/
override string toString() { result = getAbsolutePath() }
}
/**
* A folder that was observed on disk during the build process.
*
* For the example folder name of "/usr/home/me", the path decomposes to:
*
* 1. "/usr/home" - see `getParentContainer`.
* 2. "me" - see `getBaseName`.
*
* To get the full path, use `getAbsolutePath`.
*/
class Folder extends Container, @folder {
override string getAbsolutePath() { folders(underlyingElement(this), result, _) }
override Location getLocation() {
result.getContainer() = this and
result.hasLocationInfo(_, 0, 0, 0, 0)
}
override string getAPrimaryQlClass() { result = "Folder" }
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this folder.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the name of this folder.
*/
deprecated string getName() { folders(underlyingElement(this), result, _) }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Holds if this element is named `name`.
*/
deprecated predicate hasName(string name) { name = this.getName() }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the full name of this folder.
*/
deprecated string getFullName() { result = this.getName() }
/**
* DEPRECATED: use `getBaseName` instead.
* Gets the last part of the folder name.
*/
deprecated string getShortName() {
exists(string longnameRaw, string longname |
folders(underlyingElement(this), _, longnameRaw) and
longname = longnameRaw.replaceAll("\\", "/")
|
exists(int index |
result = longname.splitAt("/", index) and
not exists(longname.splitAt("/", index + 1))
)
)
}
/**
* DEPRECATED: use `getParentContainer` instead.
* Gets the parent folder.
*/
deprecated Folder getParent() {
containerparent(unresolveElement(result), underlyingElement(this))
}
}
/**
* A file that was observed on disk during the build process.
*
* For the example filename of "/usr/home/me/myprogram.c", the filename
* decomposes to:
*
* 1. "/usr/home/me" - see `getParentContainer`.
* 2. "myprogram.c" - see `getBaseName`.
*
* The base name further decomposes into the _stem_ and _extension_ -- see
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
*/
class File extends Container, @file {
override string getAbsolutePath() { files(underlyingElement(this), result, _, _, _) }
override string toString() { result = Container.super.toString() }
override string getAPrimaryQlClass() { result = "File" }
override Location getLocation() {
result.getContainer() = this and
result.hasLocationInfo(_, 0, 0, 0, 0)
}
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this file.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/** Holds if this file was compiled as C (at any point). */
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }
/** Holds if this file was compiled as C++ (at any point). */
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
/**
* Holds if this file was compiled by a Microsoft compiler (at any point).
*
* Note: currently unreliable - on some projects only some of the files that
* are compiled by a Microsoft compiler are detected by this predicate.
*/
predicate compiledAsMicrosoft() {
exists(File f, Compilation c |
c.getAFileCompiled() = f and
(
c.getAnArgument() = "--microsoft" or
c.getAnArgument()
.toLowerCase()
.replaceAll("\\", "/")
.matches(["%/cl.exe", "%/clang-cl.exe"])
) and
f.getAnIncludedFile*() = this
)
}
/** Gets a top-level element declared in this file. */
Declaration getATopLevelDeclaration() { result.getAFile() = this and result.isTopLevel() }
/** Gets a declaration in this file. */
Declaration getADeclaration() { result.getAFile() = this }
/** Holds if this file uses the given macro. */
predicate usesMacro(Macro m) {
exists(MacroInvocation mi |
mi.getFile() = this and
mi.getMacro() = m
)
}
/**
* Gets a file that is directly included from this file (using a
* pre-processor directive like `#include`).
*/
File getAnIncludedFile() {
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
}
/**
* Holds if this file may be from source. This predicate holds for all files
* except the dummy file, whose name is the empty string, which contains
* declarations that are built into the compiler.
*/
override predicate fromSource() { numlines(underlyingElement(this), _, _, _) }
/**
* Holds if this file may be from a library.
*
* DEPRECATED: For historical reasons this is true for any file.
*/
deprecated override predicate fromLibrary() { any() }
/** Gets the metric file. */
MetricFile getMetrics() { result = this }
/**
* Gets the remainder of the base name after the first dot character. Note
* that the name of this predicate is in plural form, unlike `getExtension`,
* which gets the remainder of the base name after the _last_ dot character.
*
* Predicates `getStem` and `getExtension` should be preferred over
* `getShortName` and `getExtensions` since the former pair is compatible
* with the file libraries of other languages.
* Note the slight difference between this predicate and `getStem`:
* for example, for "file.tar.gz", this predicate will have the result
* "tar.gz", while `getExtension` will have the result "gz".
*/
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
/**
* Gets the short name of this file, that is, the prefix of its base name up
* to (but not including) the first dot character if there is one, or the
* entire base name if there is not. For example, if the full name is
* "/path/to/filename.a.bcd" then the short name is "filename".
*
* Predicates `getStem` and `getExtension` should be preferred over
* `getShortName` and `getExtensions` since the former pair is compatible
* with the file libraries of other languages.
* Note the slight difference between this predicate and `getStem`:
* for example, for "file.tar.gz", this predicate will have the result
* "file", while `getStem` will have the result "file.tar".
*/
string getShortName() { files(underlyingElement(this), _, result, _, _) }
}
/**
* Holds if any file was compiled by a Microsoft compiler.
*/
predicate anyFileCompiledAsMicrosoft() { any(File f).compiledAsMicrosoft() }
/**
* A C/C++ header file, as determined (mainly) by file extension.
*
* For the related notion of whether a file is included anywhere (using a
* pre-processor directive like `#include`), use `Include.getIncludedFile`.
*/
class HeaderFile extends File {
HeaderFile() {
this.getExtension().toLowerCase() =
["h", "r", "hpp", "hxx", "h++", "hh", "hp", "tcc", "tpp", "txx", "t++"]
or
not exists(this.getExtension()) and
exists(Include i | i.getIncludedFile() = this)
}
override string getAPrimaryQlClass() { result = "HeaderFile" }
/**
* Holds if this header file does not contain any declaration entries or top level
* declarations. For example it might be:
* - a file containing only preprocessor directives and/or comments
* - an empty file
* - a file that contains non-top level code or data that's included in an
* unusual way
*/
predicate noTopLevelCode() {
not exists(DeclarationEntry de | de.getFile() = this) and
not exists(Declaration d | d.getFile() = this and d.isTopLevel()) and
not exists(UsingEntry ue | ue.getFile() = this)
}
}
/**
* A C source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as C code, use
* `File.compiledAsC`.
*/
class CFile extends File {
CFile() { this.getExtension().toLowerCase() = ["c", "i"] }
override string getAPrimaryQlClass() { result = "CFile" }
}
/**
* A C++ source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as C++ code, use
* `File.compiledAsCpp`.
*/
class CppFile extends File {
CppFile() {
this.getExtension().toLowerCase() =
["cpp", "cxx", "c++", "cc", "cp", "icc", "ipp", "ixx", "i++", "ii"]
// Note: .C files are indistinguishable from .c files on some
// file systems, so we just treat them as CFile's.
}
override string getAPrimaryQlClass() { result = "CppFile" }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as Objective C
* code, use `File.compiledAsObjC`.
*/
deprecated class ObjCFile extends File {
ObjCFile() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C++ source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as Objective C++
* code, use `File.compiledAsObjCpp`.
*/
deprecated class ObjCppFile extends File {
ObjCppFile() { none() }
}

View File

@@ -0,0 +1,69 @@
/**
* Provides a class representing C++ `friend` declarations.
*/
import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass
/**
* A C++ friend declaration [N4140 11.3]. For example the two friend
* declarations in class `A` of the following code:
* ```
* class A {
* friend void f(int);
* friend class X;
* };
*
* void f(int x) { ... }
* class X { ... };
* ```
*/
class FriendDecl extends Declaration, @frienddecl {
/**
* Gets the location of this friend declaration. The result is the
* location of the friend declaration itself, not the class or function
* that it refers to. Note: to get the target of the friend declaration,
* use `getFriend`.
*/
override Location getADeclarationLocation() { result = this.getLocation() }
override string getAPrimaryQlClass() { result = "FriendDecl" }
/**
* Implements the abstract method `Declaration.getDefinitionLocation`. A
* friend declaration cannot be a definition because it is only a link to
* another class or function. But we have to provide an implementation of
* this method, so we use the location of the declaration as the location
* of the definition. Note: to get the target of the friend declaration,
* use `getFriend`.
*/
override Location getDefinitionLocation() { result = this.getLocation() }
/** Gets the location of this friend declaration. */
override Location getLocation() { frienddecls(underlyingElement(this), _, _, result) }
/** Gets a descriptive string for this friend declaration. */
override string getName() { result = this.getDeclaringClass().getName() + "'s friend" }
/**
* Friend declarations do not have specifiers. It makes no difference
* whether they are declared in a public, protected or private section of
* the class.
*/
override Specifier getASpecifier() { none() }
/**
* Gets the target of this friend declaration.
* For example: `X` in `class A { friend class X }`.
*/
AccessHolder getFriend() { frienddecls(underlyingElement(this), _, unresolveElement(result), _) }
/**
* Gets the declaring class (also known as the befriending class).
* For example: `A` in `class A { friend class X }`.
*/
Class getDeclaringClass() { frienddecls(underlyingElement(this), unresolveElement(result), _, _) }
/* Holds if this declaration is a top-level declaration. */
override predicate isTopLevel() { none() }
}

View File

@@ -0,0 +1,876 @@
/**
* Provides classes for working with functions, including template functions.
*/
import semmle.code.cpp.Location
import semmle.code.cpp.Class
import semmle.code.cpp.Parameter
import semmle.code.cpp.exprs.Call
import semmle.code.cpp.metrics.MetricFunction
import semmle.code.cpp.Linkage
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ function [N4140 8.3.5]. Both member functions and non-member
* functions are included. For example the function `MyFunction` in:
* ```
* void MyFunction() {
* DoSomething();
* }
* ```
*
* Function has a one-to-many relationship with FunctionDeclarationEntry,
* because the same function can be declared in multiple locations. This
* relationship between `Declaration` and `DeclarationEntry` is explained
* in more detail in `Declaration.qll`.
*/
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
override string getName() { functions(underlyingElement(this), result, _) }
/**
* DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead.
* Gets the full signature of this function, including return type, parameter
* types, and template arguments.
*
* For example, in the following code:
* ```
* template<typename T> T min(T x, T y);
* int z = min(5, 7);
* ```
* The full signature of the function called on the last line would be
* "min<int>(int, int) -> int", and the full signature of the uninstantiated
* template on the first line would be "min<T>(T, T) -> T".
*/
string getFullSignature() {
exists(string name, string templateArgs, string args |
result = name + templateArgs + args + " -> " + getType().toString() and
name = getQualifiedName() and
(
if exists(getATemplateArgument())
then
templateArgs =
"<" +
concat(int i |
exists(getTemplateArgument(i))
|
getTemplateArgument(i).toString(), ", " order by i
) + ">"
else templateArgs = ""
) and
args =
"(" +
concat(int i |
exists(getParameter(i))
|
getParameter(i).getType().toString(), ", " order by i
) + ")"
)
}
/** Gets a specifier of this function. */
override Specifier getASpecifier() {
funspecifiers(underlyingElement(this), unresolveElement(result)) or
result.hasName(getADeclarationEntry().getASpecifier())
}
/** Gets an attribute of this function. */
Attribute getAnAttribute() { funcattributes(underlyingElement(this), unresolveElement(result)) }
/** Holds if this function is generated by the compiler. */
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
/** Holds if this function is inline. */
predicate isInline() { this.hasSpecifier("inline") }
/**
* Holds if this function is virtual.
*
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
* is not explicitly declared with the `virtual` specifier.
*/
predicate isVirtual() { this.hasSpecifier("virtual") }
/** Holds if this function is declared with the `virtual` specifier. */
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
/** Holds if this function is declared with the `override` specifier. */
predicate isOverride() { this.hasSpecifier("override") }
/** Holds if this function is declared with the `final` specifier. */
predicate isFinal() { this.hasSpecifier("final") }
/**
* Holds if this function is deleted.
* This may be because it was explicitly deleted with an `= delete`
* definition, or because the compiler was unable to auto-generate a
* definition for it.
*
* Most implicitly deleted functions are omitted from the database.
* `Class.implicitCopyConstructorDeleted` and
* `Class.implicitCopyAssignmentOperatorDeleted` can be used to find
* whether a class would have had those members implicitly deleted.
*/
predicate isDeleted() { function_deleted(underlyingElement(this)) }
/**
* Holds if this function is explicitly defaulted with the `= default`
* specifier.
*/
predicate isDefaulted() { function_defaulted(underlyingElement(this)) }
/**
* Holds if this function is declared to be `constexpr`.
*
* Note that this does not hold if the function has been declared
* `consteval`.
*/
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
/**
* Holds if this function is `constexpr`. Normally, this holds if and
* only if `isDeclaredConstexpr()` holds, but in some circumstances
* they differ. For example, with
* ```
* int f(int i) { return 6; }
* template <typename T> constexpr int g(T x) { return f(x); }
* ```
* `g<int>` is declared constexpr, but is not constexpr.
*
* Will also hold if this function is `consteval`.
*/
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
/**
* Holds if this function is declared to be `consteval`.
*/
predicate isConsteval() { this.hasSpecifier("is_consteval") }
/**
* Holds if this function is declared with `__attribute__((naked))` or
* `__declspec(naked)`.
*/
predicate isNaked() { getAnAttribute().hasName("naked") }
/**
* Holds if this function has a trailing return type.
*
* Note that this is true whether or not deduction took place. For example,
* this holds for both `e` and `f`, but not `g` or `h`:
* ```
* auto e() -> int { return 0; }
* auto f() -> auto { return 0; }
* auto g() { return 0; }
* int h() { return 0; }
* ```
*/
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
/** Gets the return type of this function. */
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the return type of this function after specifiers have been deeply
* stripped and typedefs have been resolved.
*/
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
/**
* Gets the nth parameter of this function. There is no result for the
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
/**
* Gets a parameter of this function. There is no result for the implicit
* `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
/**
* Gets an access of this function.
*
* To get calls to this function, use `getACallToThisFunction` instead.
*/
FunctionAccess getAnAccess() { result.getTarget() = this }
/**
* Gets the number of parameters of this function, _not_ including any
* implicit `this` parameter or any `...` varargs pseudo-parameter.
*/
int getNumberOfParameters() { result = count(this.getAParameter()) }
/**
* Gets the number of parameters of this function, _including_ any implicit
* `this` parameter but _not_ including any `...` varargs pseudo-parameter.
*/
int getEffectiveNumberOfParameters() {
// This method is overridden in `MemberFunction`, where the result is
// adjusted to account for the implicit `this` parameter.
result = getNumberOfParameters()
}
/**
* Gets a string representing the parameters of this function.
*
* For example: for a function `int Foo(int p1, int p2)` this would
* return `int p1, int p2`.
*/
string getParameterString() {
result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
}
/** Gets a call to this function. */
FunctionCall getACallToThisFunction() { result.getTarget() = this }
/**
* Gets a declaration entry corresponding to this declaration. The
* relationship between `Declaration` and `DeclarationEntry` is explained
* in `Declaration.qll`.
*/
override FunctionDeclarationEntry getADeclarationEntry() {
if fun_decls(_, underlyingElement(this), _, _, _)
then declEntry(result)
else
exists(Function f |
this.isConstructedFrom(f) and
fun_decls(unresolveElement(result), unresolveElement(f), _, _, _)
)
}
private predicate declEntry(FunctionDeclarationEntry fde) {
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
// If one .cpp file specializes a function, and another calls the
// specialized function, then when extracting the second we only see an
// instantiation, not the specialization. We Therefore need to ignore
// any non-specialized declarations if there are any specialized ones.
(this.isSpecialization() implies fde.isSpecialization())
}
/**
* Gets the location of a `FunctionDeclarationEntry` corresponding to this
* declaration.
*/
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
/** Holds if this Function is a Template specialization. */
predicate isSpecialization() {
exists(FunctionDeclarationEntry fde |
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
fde.isSpecialization()
)
}
/**
* Gets the declaration entry corresponding to this declaration that is a
* definition, if any.
*/
override FunctionDeclarationEntry getDefinition() {
result = getADeclarationEntry() and
result.isDefinition()
}
/** Gets the location of the definition, if any. */
override Location getDefinitionLocation() {
if exists(getDefinition())
then result = getDefinition().getLocation()
else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation())
}
/**
* Gets the preferred location of this declaration. (The location of the
* definition, if possible.)
*/
override Location getLocation() {
if exists(getDefinition())
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
/** Gets a child declaration of this function. */
Declaration getADeclaration() { result = this.getAParameter() }
/**
* Gets the block that is the function body.
*
* For C++ functions whose body is a function try statement rather than a
* block, this gives the block guarded by the try statement. See
* `FunctionTryStmt` for further information.
*/
BlockStmt getBlock() { result.getParentScope() = this }
/** Holds if this function has an entry point. */
predicate hasEntryPoint() { exists(getEntryPoint()) }
/**
* Gets the first node in this function's control flow graph.
*
* For most functions, this first node will be the `BlockStmt` returned by
* `getBlock`. However in C++, the first node can also be a
* `FunctionTryStmt`.
*/
Stmt getEntryPoint() { function_entry_point(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the metric class. `MetricFunction` has methods for computing
* various metrics, such as "number of lines of code" and "number of
* function calls".
*/
MetricFunction getMetrics() { result = this }
/** Holds if this function calls the function `f`. */
predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) }
/**
* Holds if this function calls the function `f` in the `FunctionCall`
* expression `l`.
*/
predicate calls(Function f, Locatable l) {
exists(FunctionCall call |
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
)
or
exists(DestructorCall call |
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
)
}
/** Holds if this function accesses a function or variable or enumerator `a`. */
predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) }
/**
* Holds if this function accesses a function or variable or enumerator `a`
* in the `Access` expression `l`.
*/
predicate accesses(Declaration a, Locatable l) {
exists(Access access |
access.getEnclosingFunction() = this and
a = access.getTarget() and
access = l
)
}
/** Gets a variable that is written-to in this function. */
Variable getAWrittenVariable() {
exists(ConstructorFieldInit cfi |
cfi.getEnclosingFunction() = this and result = cfi.getTarget()
)
or
exists(VariableAccess va |
va = result.getAnAccess() and
va.isUsedAsLValue() and
va.getEnclosingFunction() = this
)
}
/**
* Gets the class of which this function, called `memberName`, is a member.
*
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
* need to reason about both.
*/
pragma[nomagic]
Class getClassAndName(string memberName) {
this.hasName(memberName) and
this.getDeclaringType() = result
}
/**
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
* used to represent the exit node of the control flow graph, so it is
* its own scope.
*/
override Function getControlFlowScope() { result = this }
/**
* Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is
* used to represent the exit node of the control flow graph, so it
* has no enclosing statement.
*/
override Stmt getEnclosingStmt() { none() }
/**
* Holds if this function has C linkage, as specified by one of its
* declaration entries. For example: `extern "C" void foo();`.
*/
predicate hasCLinkage() { getADeclarationEntry().hasCLinkage() }
/**
* Holds if this function is constructed from `f` as a result
* of template instantiation. If so, it originates either from a template
* function or from a function nested in a template class.
*/
predicate isConstructedFrom(Function f) {
function_instantiation(underlyingElement(this), unresolveElement(f))
}
/**
* Holds if this function is defined in several files. This is illegal in
* C (though possible in some C++ compilers), and likely indicates that
* several functions that are not linked together have been compiled. An
* example would be a project with many 'main' functions.
*/
predicate isMultiplyDefined() { strictcount(getFile()) > 1 }
/** Holds if this function is a varargs function. */
predicate isVarargs() { hasSpecifier("varargs") }
/** Gets a type that is specified to be thrown by the function. */
Type getAThrownType() { result = getADeclarationEntry().getAThrownType() }
/**
* Gets the `i`th type specified to be thrown by the function.
*/
Type getThrownType(int i) { result = getADeclarationEntry().getThrownType(i) }
/** Holds if the function has an exception specification. */
predicate hasExceptionSpecification() { getADeclarationEntry().hasExceptionSpecification() }
/** Holds if this function has a `throw()` exception specification. */
predicate isNoThrow() { getADeclarationEntry().isNoThrow() }
/** Holds if this function has a `noexcept` exception specification. */
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
/**
* Gets a function that overloads this one.
*
* Note: if _overrides_ are wanted rather than _overloads_ then
* `MemberFunction::getAnOverridingFunction` should be used instead.
*/
Function getAnOverload() {
(
// If this function is declared in a class, only consider other
// functions from the same class.
exists(string name, Class declaringType |
candGetAnOverloadMember(name, declaringType, this) and
candGetAnOverloadMember(name, declaringType, result)
)
or
// Conversely, if this function is not
// declared in a class, only consider other functions not declared in a
// class.
exists(string name, Namespace namespace |
candGetAnOverloadNonMember(name, namespace, this) and
candGetAnOverloadNonMember(name, namespace, result)
)
) and
result != this and
// Instantiations and specializations don't participate in overload
// resolution.
not (
this instanceof FunctionTemplateInstantiation or
result instanceof FunctionTemplateInstantiation
) and
not (
this instanceof FunctionTemplateSpecialization or
result instanceof FunctionTemplateSpecialization
)
}
/** Gets a link target which compiled or referenced this function. */
LinkTarget getALinkTarget() { this = result.getAFunction() }
/**
* Holds if this function is side-effect free (conservative
* approximation).
*/
predicate isSideEffectFree() { not this.mayHaveSideEffects() }
/**
* Holds if this function may have side-effects; if in doubt, we assume it
* may.
*/
predicate mayHaveSideEffects() {
// If we cannot see the definition then we assume that it may have
// side-effects.
if exists(this.getEntryPoint())
then
// If it might be globally impure (we don't care about it modifying
// temporaries) then it may have side-effects.
this.getEntryPoint().mayBeGloballyImpure()
or
// Constructor initializers are separate from the entry point ...
this.(Constructor).getAnInitializer().mayBeGloballyImpure()
or
// ... and likewise for destructors.
this.(Destructor).getADestruction().mayBeGloballyImpure()
else
// Unless it's a function that we know is side-effect free, it may
// have side-effects.
not this.hasGlobalOrStdName([
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
"_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l",
"_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr",
"memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit",
"isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof"
])
}
/**
* Gets the nearest enclosing AccessHolder.
*/
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
}
pragma[noinline]
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
f.getName() = name and
f.getDeclaringType() = declaringType
}
pragma[noinline]
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
f.getName() = name and
f.getNamespace() = namespace and
not exists(f.getDeclaringType())
}
/**
* A particular declaration or definition of a C/C++ function. For example the
* declaration and definition of `MyFunction` in the following code are each a
* `FunctionDeclarationEntry`:
* ```
* void MyFunction();
*
* void MyFunction() {
* DoSomething();
* }
* ```
*/
class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
/** Gets the function which is being declared or defined. */
override Function getDeclaration() { result = getFunction() }
override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
/** Gets the function which is being declared or defined. */
Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
/** Gets the name of the function. */
override string getName() { fun_decls(underlyingElement(this), _, _, result, _) }
/**
* Gets the return type of the function which is being declared or
* defined.
*/
override Type getType() { fun_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
/** Gets the location of this declaration entry. */
override Location getLocation() { fun_decls(underlyingElement(this), _, _, _, result) }
/** Gets a specifier associated with this declaration entry. */
override string getASpecifier() { fun_decl_specifiers(underlyingElement(this), result) }
/**
* Implements `Element.getEnclosingElement`. A function declaration does
* not have an enclosing element.
*/
override Element getEnclosingElement() { none() }
/**
* Gets the typedef type (if any) used for this function declaration. As
* an example, the typedef type in the declaration of function foo in the
* following is Foo:
*
* typedef int Foo();
* static Foo foo;
*/
TypedefType getTypedefType() {
fun_decl_typedef_type(underlyingElement(this), unresolveElement(result))
}
/**
* Gets the cyclomatic complexity of this function:
*
* The number of branching statements (if, while, do, for, switch,
* case, catch) plus the number of branching expressions (`?`, `&&`,
* `||`) plus one.
*/
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(getBlock()) }
/**
* If this is a function definition, get the block containing the
* function body.
*/
BlockStmt getBlock() {
this.isDefinition() and
result = getFunction().getBlock() and
result.getFile() = this.getFile()
}
/**
* If this is a function definition, get the number of lines of code
* associated with it.
*/
pragma[noopt]
int getNumberOfLines() {
exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() |
l = b.getLocation() and
start = l.getStartLine() and
end = l.getEndLine() and
diff = end - start and
result = diff + 1
)
}
/**
* Gets the declaration entry for a parameter of this function
* declaration.
*/
ParameterDeclarationEntry getAParameterDeclarationEntry() {
result = getParameterDeclarationEntry(_)
}
/**
* Gets the declaration entry for the nth parameter of this function
* declaration.
*/
ParameterDeclarationEntry getParameterDeclarationEntry(int n) {
param_decl_bind(unresolveElement(result), n, underlyingElement(this))
}
/** Gets the number of parameters of this function declaration. */
int getNumberOfParameters() { result = count(this.getAParameterDeclarationEntry()) }
/**
* Gets a string representing the parameters of this function declaration.
*
* For example: for a function 'int Foo(int p1, int p2)' this would
* return 'int p1, int p2'.
*/
string getParameterString() {
result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
}
/**
* Holds if this declaration entry specifies C linkage:
*
* `extern "C" void foo();`
*/
predicate hasCLinkage() { getASpecifier() = "c_linkage" }
/** Holds if this declaration entry has a void parameter list. */
predicate hasVoidParamList() { getASpecifier() = "void_param_list" }
/** Holds if this declaration is also a definition of its function. */
override predicate isDefinition() { fun_def(underlyingElement(this)) }
/** Holds if this declaration is a Template specialization. */
predicate isSpecialization() { fun_specialized(underlyingElement(this)) }
/**
* Holds if this declaration is an implicit function declaration, that is,
* where a function is used before it is declared (under older C standards).
*/
predicate isImplicit() { fun_implicit(underlyingElement(this)) }
/** Gets a type that is specified to be thrown by the declared function. */
Type getAThrownType() { result = getThrownType(_) }
/**
* Gets the `i`th type specified to be thrown by the declared function
* (where `i` is indexed from 0). For example, if a function is declared
* to `throw(int,float)`, then the thrown type with index 0 would be
* `int`, and that with index 1 would be `float`.
*/
Type getThrownType(int i) {
fun_decl_throws(underlyingElement(this), i, unresolveElement(result))
}
/**
* If this declaration has a noexcept-specification [N4140 15.4], then
* this predicate returns the argument to `noexcept` if one was given.
*/
Expr getNoExceptExpr() { fun_decl_noexcept(underlyingElement(this), unresolveElement(result)) }
/**
* Holds if the declared function has an exception specification [N4140
* 15.4].
*/
predicate hasExceptionSpecification() {
fun_decl_throws(underlyingElement(this), _, _) or
fun_decl_noexcept(underlyingElement(this), _) or
isNoThrow() or
isNoExcept()
}
/**
* Holds if the declared function has a `throw()` exception specification.
*/
predicate isNoThrow() { fun_decl_empty_throws(underlyingElement(this)) }
/**
* Holds if the declared function has an empty `noexcept` exception
* specification.
*/
predicate isNoExcept() { fun_decl_empty_noexcept(underlyingElement(this)) }
}
/**
* A C/C++ non-member function (a function that is not a member of any
* class). For example, in the following code, `MyFunction` is a
* `TopLevelFunction` but `MyMemberFunction` is not:
* ```
* void MyFunction() {
* DoSomething();
* }
*
* class MyClass {
* public:
* void MyMemberFunction() {
* DoSomething();
* }
* };
* ```
*/
class TopLevelFunction extends Function {
TopLevelFunction() { not this.isMember() }
override string getAPrimaryQlClass() { result = "TopLevelFunction" }
}
/**
* A C++ user-defined operator [N4140 13.5].
*/
class Operator extends Function {
Operator() { functions(underlyingElement(this), _, 5) }
override string getAPrimaryQlClass() {
not this instanceof MemberFunction and result = "Operator"
}
}
/**
* A C++ function which has a non-empty template argument list. For example
* the function `myTemplateFunction` in the following code:
* ```
* template<class T>
* void myTemplateFunction(T t) {
* ...
* }
* ```
*
* This comprises function declarations which are immediately preceded by
* `template <...>`, where the "..." part is not empty, and therefore it does
* not include:
*
* 1. Full specializations of template functions, as they have an empty
* template argument list.
* 2. Instantiations of template functions, as they don't have an
* explicit template argument list.
* 3. Member functions of template classes - unless they have their own
* (non-empty) template argument list.
*/
class TemplateFunction extends Function {
TemplateFunction() {
is_function_template(underlyingElement(this)) and exists(getATemplateArgument())
}
override string getAPrimaryQlClass() { result = "TemplateFunction" }
/**
* Gets a compiler-generated instantiation of this function template.
*/
Function getAnInstantiation() {
result.isConstructedFrom(this) and
not result.isSpecialization()
}
/**
* Gets a full specialization of this function template.
*
* Note that unlike classes, functions overload rather than specialize
* partially. Therefore this does not include things which "look like"
* partial specializations, nor does it include full specializations of
* such things -- see FunctionTemplateSpecialization for further details.
*/
FunctionTemplateSpecialization getASpecialization() { result.getPrimaryTemplate() = this }
}
/**
* A function that is an instantiation of a template. For example
* the instantiation `myTemplateFunction<int>` in the following code:
* ```
* template<class T>
* void myTemplateFunction(T t) {
* ...
* }
*
* void caller(int i) {
* myTemplateFunction<int>(i);
* }
* ```
*/
class FunctionTemplateInstantiation extends Function {
TemplateFunction tf;
FunctionTemplateInstantiation() { tf.getAnInstantiation() = this }
override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" }
/**
* Gets the function template from which this instantiation was instantiated.
*
* Example: For `int const& std::min<int>(int const&, int const&)`, returns `T const& min<T>(T const&, T const&)`.
*/
TemplateFunction getTemplate() { result = tf }
}
/**
* An explicit specialization of a C++ function template. For example the
* function `myTemplateFunction<int>` in the following code:
* ```
* template<class T>
* void myTemplateFunction(T t) {
* ...
* }
*
* template<>
* void myTemplateFunction<int>(int i) {
* ...
* }
* ```
*
* Note that unlike classes, functions overload rather than specialize
* partially. Therefore this only includes the last two of the following
* four definitions, and in particular does not include the second one:
*
* ```
* template <typename T> void f(T) {...}
* template <typename T> void f(T*) {...}
* template <> void f<int>(int *) {...}
* template <> void f<int*>(int *) {...}
* ```
*
* Furthermore, this does not include compiler-generated instantiations of
* function templates.
*
* For further reference on function template specializations, see:
* http://www.gotw.ca/publications/mill17.htm
*/
class FunctionTemplateSpecialization extends Function {
FunctionTemplateSpecialization() { this.isSpecialization() }
override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" }
/**
* Gets the primary template for the specialization (the function template
* this specializes).
*/
TemplateFunction getPrimaryTemplate() { this.isConstructedFrom(result) }
}
/**
* A GCC built-in function. For example: `__builtin___memcpy_chk`.
*/
class BuiltInFunction extends Function {
BuiltInFunction() { functions(underlyingElement(this), _, 6) }
/** Gets a dummy location for the built-in function. */
override Location getLocation() {
suppressUnusedThis(this) and
result instanceof UnknownDefaultLocation
}
}
private predicate suppressUnusedThis(Function f) { any() }

View File

@@ -0,0 +1,69 @@
/**
* Provides classes representing C/C++ `#include`, `#include_next`, and `#import` preprocessor
* directives.
*/
import semmle.code.cpp.Preprocessor
/**
* A C/C++ `#include`, `#include_next`, or `#import` preprocessor
* directive. The following example contains four different `Include`
* directives:
* ```
* #include "header.h"
* #include <string>
* #include_next <header2.h>
* #import <header3.h>
* ```
*/
class Include extends PreprocessorDirective, @ppd_include {
override string toString() { result = "#include " + this.getIncludeText() }
/**
* Gets the token which occurs after `#include`, for example `"filename"`
* or `<filename>`.
*/
string getIncludeText() { result = getHead() }
/** Gets the file directly included by this `#include`. */
File getIncludedFile() { includes(underlyingElement(this), unresolveElement(result)) }
/**
* Gets a file which might be transitively included by this `#include`.
*
* Note that as this isn't computed within the context of a particular
* translation unit, it is often a slight over-approximation.
*/
predicate provides(File l) {
exists(Include i | this.getAnInclude*() = i and i.getIncludedFile() = l)
}
/**
* A `#include` which appears in the file directly included by this
* `#include`.
*/
Include getAnInclude() { this.getIncludedFile() = result.getFile() }
}
/**
* A `#include_next` preprocessor directive (a non-standard extension to
* C/C++). For example the following code contains one `IncludeNext` directive:
* ```
* #include_next <header2.h>
* ```
*/
class IncludeNext extends Include, @ppd_include_next {
override string toString() { result = "#include_next " + getIncludeText() }
}
/**
* A `#import` preprocessor directive (used heavily in Objective C, and
* supported by GCC as an extension in C). For example the following code
* contains one `Import` directive:
* ```
* #import <header3.h>
* ```
*/
class Import extends Include, @ppd_objc_import {
override string toString() { result = "#import " + getIncludeText() }
}

View File

@@ -0,0 +1,54 @@
/**
* Provides the `Initializer` class, representing C/C++ declaration initializers.
*/
import semmle.code.cpp.controlflow.ControlFlowGraph
/**
* A C/C++ declaration initializer. For example the initializers `1`, `2` and
* `3` in the following code:
* ```
* int myVariable = 1;
*
* enum myEnum {
* MYENUMCONST = 2
* };
*
* void myFunction(int param = 3) {
* ...
* }
* ```
* But _not_ `4` in the following code:
* ```
* int myUninitializedVariable;
* myUninitializedVariable = 4;
* ```
* Instead, this is an `Assignment`.
*/
class Initializer extends ControlFlowNode, @initialiser {
override Location getLocation() { initialisers(underlyingElement(this), _, _, result) }
override string getAPrimaryQlClass() { result = "Initializer" }
/** Holds if this initializer is explicit in the source. */
override predicate fromSource() { not this.getLocation() instanceof UnknownLocation }
override string toString() {
if exists(getDeclaration())
then result = "initializer for " + max(getDeclaration().getName())
else result = "initializer"
}
/** Gets the variable or enum constant being initialized. */
Declaration getDeclaration() {
initialisers(underlyingElement(this), unresolveElement(result), _, _)
}
/** Gets the initializing expression. */
Expr getExpr() { initialisers(underlyingElement(this), _, unresolveElement(result), _) }
/** Gets the function containing this control-flow node. */
override Function getControlFlowScope() { result = this.getExpr().getEnclosingFunction() }
override Stmt getEnclosingStmt() { result = this.getExpr().getEnclosingStmt() }
}

View File

@@ -0,0 +1,60 @@
/**
* Provides classes for loop iteration variables.
*/
import semmle.code.cpp.Variable
/**
* A C/C++ variable which is used within the condition of a 'for' loop, and
* mutated within the update expression of the same 'for' loop.
*/
class LoopCounter extends Variable {
LoopCounter() { exists(ForStmt f | f.getAnIterationVariable() = this) }
/**
* Gets an access of this variable within loop `f`.
*/
VariableAccess getVariableAccessInLoop(ForStmt f) {
this.getALoop() = f and
result.getEnclosingStmt().getParent*() = f and
this = result.getTarget()
}
/**
* Gets a loop which uses this variable as its counter.
*/
ForStmt getALoop() { result.getAnIterationVariable() = this }
}
/**
* A C/C++ variable which is used within the initialization, condition, or
* update expression of a 'for' loop.
*/
class LoopControlVariable extends Variable {
LoopControlVariable() { this = loopControlVariable(_) }
/**
* Gets an access of this variable within loop `f`.
*/
VariableAccess getVariableAccessInLoop(ForStmt f) {
this.getALoop() = f and
result.getEnclosingStmt().getParent*() = f and
this = result.getTarget()
}
/**
* Gets a loop which uses this variable as its control variable.
*/
ForStmt getALoop() { this = loopControlVariable(result) }
}
/**
* Gets a control variable of loop `f`.
*/
private Variable loopControlVariable(ForStmt f) {
exists(Expr e | result.getAnAccess().getParent*() = e |
e = f.getControllingExpr() or
e = f.getInitialization().(ExprStmt).getExpr() or
e = f.getUpdate()
)
}

View File

@@ -0,0 +1,51 @@
/**
* Proivdes the `LinkTarget` class representing linker invocations during the build process.
*/
import semmle.code.cpp.Class
import semmle.code.cpp.File
import semmle.code.cpp.Function
/**
* A linker call during the build process, typically resulting in an
* executable or a shared library.
*
* Note that if linkage information isn't captured as part of the snapshot,
* then everything is grouped together into a single dummy link target.
*/
class LinkTarget extends @link_target {
/**
* Gets the file which was built.
*/
File getBinary() { link_targets(this, unresolveElement(result)) }
/**
* Holds if this is the dummy link target: if linkage information isn't
* captured as part of the snapshot, then everything is grouped together
* into a single dummy link target.
*/
predicate isDummy() { getBinary().getAbsolutePath() = "" }
/** Gets a textual representation of this element. */
string toString() { result = getBinary().getAbsolutePath() }
/**
* Gets a function which was compiled into this link target, or had its
* declaration included by one of the translation units which contributed
* to this link target.
*/
Function getAFunction() { link_parent(unresolveElement(result), this) }
/**
* Gets a class which had its declaration included by one of the
* translation units which contributed to this link target.
*/
Class getAClass() { link_parent(unresolveElement(result), this) }
}
/**
* Holds if this database was created with the linker awareness feature
* switched on.
*/
cached
predicate isLinkerAwareExtracted() { exists(LinkTarget lt | not lt.isDummy()) }

View File

@@ -0,0 +1,173 @@
/**
* Provides classes and predicates for locations in the source code.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.File
/**
* A location of a C/C++ artifact.
*/
class Location extends @location {
/** Gets the container corresponding to this location. */
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
/** Gets the file corresponding to this location, if any. */
File getFile() { result = this.getContainer() }
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { this.fullLocationInfo(_, _, _, result, _) }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) }
/**
* Gets a textual representation of this element.
*
* The format is "file://filePath:startLine:startColumn:endLine:endColumn".
*/
string toString() {
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
toUrl(filepath, startline, startcolumn, endline, endcolumn, result)
)
}
/**
* Holds if this element is in the specified container.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline`.
*
* This predicate is similar to `hasLocationInfo`, but exposes the `Container`
* entity, rather than merely its path.
*/
predicate fullLocationInfo(
Container container, int startline, int startcolumn, int endline, int endcolumn
) {
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_stmt(this, unresolveElement(container), startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Container f | this.fullLocationInfo(f, startline, startcolumn, endline, endcolumn) |
filepath = f.getAbsolutePath()
)
}
/** Holds if `this` comes on a line strictly before `l`. */
pragma[inline]
predicate isBefore(Location l) {
this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine()
}
/** Holds if location `l` is completely contained within this one. */
predicate subsumes(Location l) {
exists(File f | f = getFile() |
exists(int thisStart, int thisEnd | charLoc(f, thisStart, thisEnd) |
exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) |
thisStart <= lStart and lEnd <= thisEnd
)
)
)
}
/**
* Holds if this location corresponds to file `f` and character "offsets"
* `start..end`. Note that these are not real character offsets, because
* we use `maxCols` to find the length of the longest line and then pretend
* that all the lines are the same length. However, these offsets are
* convenient for comparing or sorting locations in a file. For an example,
* see `subsumes`.
*/
predicate charLoc(File f, int start, int end) {
f = getFile() and
exists(int maxCols | maxCols = maxCols(f) |
start = getStartLine() * maxCols + getStartColumn() and
end = getEndLine() * maxCols + getEndColumn()
)
}
}
/**
* DEPRECATED: Use `Location` instead.
* A location of an element. Not used for expressions or statements, which
* instead use LocationExpr and LocationStmt respectively.
*/
deprecated library class LocationDefault extends Location, @location_default { }
/**
* DEPRECATED: Use `Location` instead.
* A location of a statement.
*/
deprecated library class LocationStmt extends Location, @location_stmt { }
/**
* DEPRECATED: Use `Location` instead.
* A location of an expression.
*/
deprecated library class LocationExpr extends Location, @location_expr { }
/**
* Gets the length of the longest line in file `f`.
*/
pragma[nomagic]
private int maxCols(File f) {
result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn()))
}
/**
* A C/C++ element that has a location in a file
*/
class Locatable extends Element { }
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it. There
* may be several distinct kinds of unknown locations. For example: one for
* expressions, one for statements and one for other program elements.
*/
class UnknownLocation extends Location {
UnknownLocation() { getFile().getAbsolutePath() = "" }
}
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it.
*/
class UnknownDefaultLocation extends UnknownLocation {
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
}
/**
* A dummy location which is used when an expression doesn't have a
* location in the source code but needs to have a `Location` associated
* with it.
*/
class UnknownExprLocation extends UnknownLocation {
UnknownExprLocation() { locations_expr(this, _, 0, 0, 0, 0) }
}
/**
* A dummy location which is used when a statement doesn't have a location
* in the source code but needs to have a `Location` associated with it.
*/
class UnknownStmtLocation extends UnknownLocation {
UnknownStmtLocation() { locations_stmt(this, _, 0, 0, 0, 0) }
}

View File

@@ -0,0 +1,351 @@
import cpp
/**
* A macro. For example, the macro `MYMACRO` in the following code:
* ```
* #define MYMACRO 1
* ```
*/
class Macro extends PreprocessorDirective, @ppd_define {
/**
* Gets the head of this macro. For example, `MAX(x,y)` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
override string getHead() { preproctext(underlyingElement(this), result, _) }
override string getAPrimaryQlClass() { result = "Macro" }
/**
* Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getBody() { preproctext(underlyingElement(this), _, result) }
/** Gets an invocation of this macro. */
MacroInvocation getAnInvocation() { result.getMacro() = this }
override string toString() {
if this.getBody() = ""
then result = "#define " + this.getHead()
else result = "#define " + this.getHead() + " " + this.getBody()
}
/**
* Holds if the body of the macro starts with an unmatched closing
* parenthesis. For example:
*
* #define RPAREN() )
*
* DEPRECATED: This predicate has a misleading name.
*/
deprecated predicate isFunctionLike() { this.getBody().regexpMatch("[^(]*\\).*") }
/**
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getName() { result = getHead().splitAt("(", 0) }
/** Holds if the macro has name `name`. */
predicate hasName(string name) { getName() = name }
}
/**
* A macro access. For example:
* ```
* #ifdef MACRO1 // this line contains a MacroAccess
* int x = MACRO2; // this line contains a MacroAccess
* #endif
* ```
*
* See also `MacroInvocation`, which represents only macro accesses
* that are expanded (such as in the second line of the example above).
*/
class MacroAccess extends Locatable, @macroinvocation {
/** Gets the macro that is being accessed. */
Macro getMacro() { macroinvocations(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the location of the outermost macro access that triggered this macro
* access. This is equivalent to calling
* `this.getOutermostMacroAccess().getActualLocation()`. For example, the
* location of the invocation of `C` in `P(C)` will be the whole source range
* starting with `P` and ending with `)`.
*/
override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() }
override string getAPrimaryQlClass() { result = "MacroAccess" }
/**
* Gets the location of this macro access. For a nested access, where
* `exists(this.getParentInvocation())`, this yields a location either inside
* a `#define` directive or inside an argument to another macro.
*/
Location getActualLocation() { macroinvocations(underlyingElement(this), _, result, _) }
/**
* Gets the parent macro invocation, if any. For example:
*
* ```
* 1: #define C 0
* 2: #define P C
* 3: static int x = P;
* ```
*
* The invocation of `P` on line 3 also invokes `C`. The invocation of
* `P` is the parent of the invocation of `C`.
*
* A macro invocation occurring in a macro argument often also establishes a
* parent relationship. This is due to the "call-by-name" evaluation order of
* C macros, where macro arguments are first substituted textually into the
* macro body before macro expansion is again performed on the body, invoking
* the macros present in the original macro argument. For example:
*
* ```
* 1: #define C 0
* 2: #define P(c) c + c
* 3: static int x = P(C);
* ```
*
* In this case, `P(C)` first expands to `C + C`, which triggers an
* invocation of `C` whose parent is the invocation of `P`. Had `c` not
* occurred in the body of `P`, there would have been no invocation of `C`.
* There is only a single invocation even though `c` occurs twice; this is an
* optimization for efficiency.
*/
MacroInvocation getParentInvocation() {
macroparent(underlyingElement(this), unresolveElement(result))
}
/**
* Gets the outermost `MacroAccess` along the chain of `getParentInvocation`.
* If `this` has no parent, the result will be `this` itself.
*/
MacroAccess getOutermostMacroAccess() {
if not exists(this.getParentInvocation())
then result = this
else result = this.getParentInvocation().getOutermostMacroAccess()
}
override string toString() { result = this.getMacro().getHead() }
/** Gets the name of the accessed macro. */
string getMacroName() { result = getMacro().getName() }
}
/**
* A macro invocation (macro access that is expanded). For example:
* ```
* #ifdef MACRO1
* int x = MACRO2; // this line contains a MacroInvocation
* #endif
* ```
*
* See also `MacroAccess`, which also represents macro accesses where the macro
* is checked but not expanded (such as in the first line of the example above).
*/
class MacroInvocation extends MacroAccess {
MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) }
override string getAPrimaryQlClass() { result = "MacroInvocation" }
/**
* Gets an element that occurs in this macro invocation or a nested macro
* invocation.
*/
Locatable getAnExpandedElement() {
inmacroexpansion(unresolveElement(result), underlyingElement(this))
}
/**
* Gets an element that is (partially) affected by a macro
* invocation. This is a superset of the set of expanded elements and
* includes elements that are not completely enclosed by the expansion as
* well.
*/
Locatable getAnAffectedElement() {
inmacroexpansion(unresolveElement(result), underlyingElement(this)) or
macrolocationbind(underlyingElement(this), result.getLocation())
}
/**
* Gets an element that is either completely in the macro expansion, or
* (if it is a statement) 'almost' in the macro expansion (for instance
* up to a trailing semicolon). Useful for common patterns in which
* macros are almost syntactically complete elements but not quite.
*/
Locatable getAGeneratedElement() {
result = this.getAnExpandedElement() or
result.(Stmt).getGeneratingMacro() = this
}
/**
* Gets a function that includes an expression that is affected by this macro
* invocation. If the macro expansion includes the end of one function and
* the beginning of another, this predicate will get both.
*/
Function getEnclosingFunction() {
result = this.getAnAffectedElement().(Expr).getEnclosingFunction()
}
/**
* Gets a top-level expression associated with this macro invocation,
* if any. Note that this predicate will fail if the top-level expanded
* element is not an expression (for example if it is a statement).
*
* This macro is intended to be used with macros that expand to a complete
* expression. In other cases, it may have multiple results or no results.
*/
Expr getExpr() {
result = getAnExpandedElement() and
not result.getParent() = getAnExpandedElement() and
not result instanceof Conversion
}
/**
* Gets the top-level statement associated with this macro invocation, if
* any. Note that this predicate will fail if the top-level expanded
* element is not a statement (for example if it is an expression).
*/
Stmt getStmt() {
result = getAnExpandedElement() and
not result.getParent() = getAnExpandedElement()
}
/**
* Gets the `i`th _unexpanded_ argument of this macro invocation, where the
* first argument has `i = 0`. The result has been expanded for macro
* parameters but _not_ for macro invocations. This means that for macro
* invocations not inside a `#define`, which can have no macro parameters in
* their arguments, the result is equivalent to what is in the source text,
* modulo whitespace.
*
* In the following code example, the argument of the outermost invocation is
* `ID(1)` in unexpanded form and `1` in expanded form.
*
* ```
* #define ID(x) x
* ID(ID(1))
* ```
*
* In the following example code, the last line contains an invocation of
* macro `A` and a child invocation of macro `ID`. The argument to `ID` is
* `1` in both unexpanded and expanded form because macro parameters (here,
* `x`) are expanded in both cases.
*
* ```
* #define ID(x) x
* #define A(x) ID(x)
* A(1)
* ```
*
* The `...` parameter in variadic macros counts as one parameter that always
* receives one argument, which may contain commas.
*
* Use `getExpandedArgument` to get the expanded form.
*/
string getUnexpandedArgument(int i) {
macro_argument_unexpanded(underlyingElement(this), i, result)
}
/**
* Gets the `i`th _expanded_ argument of this macro invocation, where the
* first argument has `i = 0`. The result has been expanded for macros _and_
* macro parameters. If the macro definition does not use this argument, the
* extractor will avoid computing the expanded form for efficiency, and the
* result will be "".
*
* See the documentation of `getUnexpandedArgument` for examples of the
* differences between expanded and unexpanded arguments.
*/
string getExpandedArgument(int i) { macro_argument_expanded(underlyingElement(this), i, result) }
}
/**
* A top-level expression generated by a macro invocation.
*
* DEPRECATED: Use `MacroInvocation.getExpr()` directly to get an
* expression generated at the top-level of a macro invocation. Use
* `MacroInvocation.getAnAffectedElement()` to get any element generated
* by a macro invocation.
*/
deprecated class MacroInvocationExpr extends Expr {
MacroInvocationExpr() { exists(MacroInvocation i | this = i.getExpr()) }
/**
* Gets the macro invocation of which this is the top-level expression.
*/
MacroInvocation getInvocation() { result.getExpr() = this }
/** Gets the name of the invoked macro. */
string getMacroName() { result = getInvocation().getMacroName() }
}
/**
* A top-level statement generated by a macro invocation.
*
* DEPRECATED: Use `MacroInvocation.getStmt()` directly to get a
* statement generated at the top-level of a macro invocation. Use
* `MacroInvocation.getAnAffectedElement()` to get any element generated
* by a macro invocation.
*/
deprecated class MacroInvocationStmt extends Stmt {
MacroInvocationStmt() { exists(MacroInvocation i | this = i.getStmt()) }
/**
* Gets the macro invocation of which this is the top-level statement.
*/
MacroInvocation getInvocation() { result.getStmt() = this }
/** Gets the name of the invoked macro. */
string getMacroName() { result = getInvocation().getMacroName() }
}
/** Holds if `l` is the location of a macro. */
predicate macroLocation(Location l) { macrolocationbind(_, l) }
/** Holds if `element` is in the expansion of a macro. */
predicate inMacroExpansion(Locatable element) {
inmacroexpansion(unresolveElement(element), _)
or
macroLocation(element.getLocation()) and
not topLevelMacroAccess(element)
}
/**
* Holds if `ma` is a `MacroAccess` that is not nested inside another
* macro invocation.
*/
private predicate topLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
/**
* Holds if `element` is in the expansion of a macro from
* a system header.
*/
predicate inSystemMacroExpansion(Locatable element) {
exists(MacroInvocation m |
element = m.getAnExpandedElement() and
not exists(m.getMacro().getLocation().getFile().getRelativePath())
)
}
/** Holds if `element` is affected by a macro. */
predicate affectedByMacro(Locatable element) {
inMacroExpansion(element) or
affectedbymacroexpansion(unresolveElement(element), _)
}
/** Holds if there is a macro invocation on line `line` of file `f`. */
predicate macroLine(File f, int line) {
exists(MacroInvocation mi, Location l |
l = mi.getLocation() and
l.getFile() = f and
(l.getStartLine() = line or l.getEndLine() = line)
)
}
/** Holds if there might be a macro invocation at location `l`. */
predicate possibleMacroLocation(Location l) {
macroLine(l.getFile(), l.getStartLine()) or
macroLine(l.getFile(), l.getEndLine())
}

View File

@@ -0,0 +1,6 @@
/**
* DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Type

View File

@@ -0,0 +1,517 @@
/**
* Provides classes for working with C++ member functions, constructors, destructors,
* and user-defined operators.
*/
import cpp
/**
* A C++ function declared as a member of a class [N4140 9.3]. This includes
* static member functions. For example the functions `MyStaticMemberFunction`
* and `MyMemberFunction` in:
* ```
* class MyClass {
* public:
* void MyMemberFunction() {
* DoSomething();
* }
*
* static void MyStaticMemberFunction() {
* DoSomething();
* }
* };
* ```
*/
class MemberFunction extends Function {
MemberFunction() { this.isMember() }
override string getAPrimaryQlClass() {
not this instanceof CopyAssignmentOperator and
not this instanceof MoveAssignmentOperator and
result = "MemberFunction"
}
/**
* Gets the number of parameters of this function, including any implicit
* `this` parameter.
*/
override int getEffectiveNumberOfParameters() {
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
}
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
/** Holds if this declaration has the lvalue ref-qualifier */
predicate isLValueRefQualified() { hasSpecifier("&") }
/** Holds if this declaration has the rvalue ref-qualifier */
predicate isRValueRefQualified() { hasSpecifier("&&") }
/** Holds if this declaration has a ref-qualifier */
predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() }
/** Holds if this function overrides that function. */
predicate overrides(MemberFunction that) {
overrides(underlyingElement(this), unresolveElement(that))
}
/** Gets a directly overridden function. */
MemberFunction getAnOverriddenFunction() { this.overrides(result) }
/** Gets a directly overriding function. */
MemberFunction getAnOverridingFunction() { result.overrides(this) }
/**
* Gets the declaration entry for this member function that is within the
* class body.
*/
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
if strictcount(getADeclarationEntry()) = 1
then result = getDefinition()
else (
result = getADeclarationEntry() and result != getDefinition()
)
}
/**
* Gets the type of the `this` parameter associated with this member function, if any. The type
* may have `const` and/or `volatile` qualifiers, matching the function declaration.
*/
PointerType getTypeOfThis() {
member_function_this_type(underlyingElement(this), unresolveElement(result))
}
}
/**
* A C++ virtual function. For example the two functions called
* `myVirtualFunction` in the following code are each a
* `VirtualFunction`:
* ```
* class A {
* public:
* virtual void myVirtualFunction() = 0;
* };
*
* class B: public A {
* public:
* virtual void myVirtualFunction() {
* doSomething();
* }
* };
* ```
*/
class VirtualFunction extends MemberFunction {
VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
override string getAPrimaryQlClass() { result = "VirtualFunction" }
/** Holds if this virtual function is pure. */
predicate isPure() { this instanceof PureVirtualFunction }
/**
* Holds if this function was declared with the `override` specifier
* [N4140 10.3].
*/
predicate isOverrideExplicit() { this.hasSpecifier("override") }
}
/**
* A C++ pure virtual function [N4140 10.4]. For example the first function
* called `myVirtualFunction` in the following code:
* ```
* class A {
* public:
* virtual void myVirtualFunction() = 0;
* };
*
* class B: public A {
* public:
* virtual void myVirtualFunction() {
* doSomething();
* }
* };
* ```
*/
class PureVirtualFunction extends VirtualFunction {
PureVirtualFunction() { purefunctions(underlyingElement(this)) }
override string getAPrimaryQlClass() { result = "PureVirtualFunction" }
}
/**
* A const C++ member function [N4140 9.3.1/4]. A const function has the
* `const` specifier and does not modify the state of its class. For example
* the member function `day` in the following code:
* ```
* class MyClass {
* ...
*
* int day() const {
* return d;
* }
*
* ...
* };
* ```
*/
class ConstMemberFunction extends MemberFunction {
ConstMemberFunction() { this.hasSpecifier("const") }
override string getAPrimaryQlClass() { result = "ConstMemberFunction" }
}
/**
* A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
* following code is a constructor:
* ```
* class MyClass {
* public:
* MyClass() {
* ...
* }
* };
* ```
*/
class Constructor extends MemberFunction {
Constructor() { functions(underlyingElement(this), _, 2) }
override string getAPrimaryQlClass() { result = "Constructor" }
/**
* Holds if this constructor serves as a default constructor.
*
* This holds for constructors with zero formal parameters. It also holds
* for constructors which have a non-zero number of formal parameters,
* provided that every parameter has a default value.
*/
predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
/**
* Gets an entry in the constructor's initializer list, or a
* compiler-generated action which initializes a base class or member
* variable.
*/
ConstructorInit getAnInitializer() { result = getInitializer(_) }
/**
* Gets an entry in the constructor's initializer list, or a
* compiler-generated action which initializes a base class or member
* variable. The index specifies the order in which the initializer is
* to be evaluated.
*/
ConstructorInit getInitializer(int i) {
exprparents(unresolveElement(result), i, underlyingElement(this))
}
}
/**
* A function that defines an implicit conversion.
*/
class ImplicitConversionFunction extends MemberFunction {
ImplicitConversionFunction() {
// ConversionOperator
functions(underlyingElement(this), _, 4)
or
// ConversionConstructor (deprecated)
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
}
/** Gets the type this `ImplicitConversionFunction` takes as input. */
Type getSourceType() { none() } // overridden in subclasses
/** Gets the type this `ImplicitConversionFunction` converts to. */
Type getDestType() { none() } // overridden in subclasses
}
/**
* DEPRECATED: as of C++11 this class does not correspond perfectly with the
* language definition of a converting constructor.
*
* A C++ constructor that also defines an implicit conversion. For example the
* function `MyClass` in the following code is a `ConversionConstructor`:
* ```
* class MyClass {
* public:
* MyClass(const MyOtherClass &from) {
* ...
* }
* };
* ```
*/
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
ConversionConstructor() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
}
override string getAPrimaryQlClass() {
not this instanceof CopyConstructor and
not this instanceof MoveConstructor and
result = "ConversionConstructor"
}
/** Gets the type this `ConversionConstructor` takes as input. */
override Type getSourceType() { result = this.getParameter(0).getType() }
/** Gets the type this `ConversionConstructor` is a constructor of. */
override Type getDestType() { result = this.getDeclaringType() }
}
private predicate hasCopySignature(MemberFunction f) {
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
}
private predicate hasMoveSignature(MemberFunction f) {
f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
}
/**
* A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
* the following code is a `CopyConstructor`:
* ```
* class MyClass {
* public:
* MyClass(const MyClass &from) {
* ...
* }
* };
* ```
*
* As per the standard, a copy constructor of class `T` is a non-template
* constructor whose first parameter has type `T&`, `const T&`, `volatile
* T&`, or `const volatile T&`, and either there are no other parameters,
* or the rest of the parameters all have default values.
*
* For template classes, it can generally not be determined until instantiation
* whether a constructor is a copy constructor. For such classes, `CopyConstructor`
* over-approximates the set of copy constructors; if an under-approximation is
* desired instead, see the member predicate
* `mayNotBeCopyConstructorInInstantiation`.
*/
class CopyConstructor extends Constructor {
CopyConstructor() {
hasCopySignature(this) and
(
// The rest of the parameters all have default values
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
or
// or this is a template class, in which case the default values have
// not been extracted even if they exist. In that case, we assume that
// there are default values present since that is the most common case
// in real-world code.
getDeclaringType() instanceof TemplateClass
) and
not exists(getATemplateArgument())
}
override string getAPrimaryQlClass() { result = "CopyConstructor" }
/**
* Holds if we cannot determine that this constructor will become a copy
* constructor in all instantiations. Depending on template parameters of the
* enclosing class, this may become an ordinary constructor or a copy
* constructor.
*/
predicate mayNotBeCopyConstructorInInstantiation() {
// In general, default arguments of template classes can only be
// type-checked for each template instantiation; if an argument in an
// instantiation fails to type-check then the corresponding parameter has
// no default argument in the instantiation.
getDeclaringType() instanceof TemplateClass and
getNumberOfParameters() > 1
}
}
/**
* A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
* the following code is a `MoveConstructor`:
* ```
* class MyClass {
* public:
* MyClass(MyClass &&from) {
* ...
* }
* };
* ```
*
* As per the standard, a move constructor of class `T` is a non-template
* constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
* or `const volatile T&&`, and either there are no other parameters, or
* the rest of the parameters all have default values.
*
* For template classes, it can generally not be determined until instantiation
* whether a constructor is a move constructor. For such classes, `MoveConstructor`
* over-approximates the set of move constructors; if an under-approximation is
* desired instead, see the member predicate
* `mayNotBeMoveConstructorInInstantiation`.
*/
class MoveConstructor extends Constructor {
MoveConstructor() {
hasMoveSignature(this) and
(
// The rest of the parameters all have default values
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
or
// or this is a template class, in which case the default values have
// not been extracted even if they exist. In that case, we assume that
// there are default values present since that is the most common case
// in real-world code.
getDeclaringType() instanceof TemplateClass
) and
not exists(getATemplateArgument())
}
override string getAPrimaryQlClass() { result = "MoveConstructor" }
/**
* Holds if we cannot determine that this constructor will become a move
* constructor in all instantiations. Depending on template parameters of the
* enclosing class, this may become an ordinary constructor or a move
* constructor.
*/
predicate mayNotBeMoveConstructorInInstantiation() {
// In general, default arguments of template classes can only be
// type-checked for each template instantiation; if an argument in an
// instantiation fails to type-check then the corresponding parameter has
// no default argument in the instantiation.
getDeclaringType() instanceof TemplateClass and
getNumberOfParameters() > 1
}
}
/**
* A C++ constructor that takes no arguments ('default' constructor). This
* is the constructor that is invoked when no initializer is given. For
* example the function `MyClass` in the following code is a
* `NoArgConstructor`:
* ```
* class MyClass {
* public:
* MyClass() {
* ...
* }
* };
* ```
*/
class NoArgConstructor extends Constructor {
NoArgConstructor() { this.getNumberOfParameters() = 0 }
}
/**
* A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
* following code is a destructor:
* ```
* class MyClass {
* public:
* ~MyClass() {
* ...
* }
* };
* ```
*/
class Destructor extends MemberFunction {
Destructor() { functions(underlyingElement(this), _, 3) }
override string getAPrimaryQlClass() { result = "Destructor" }
/**
* Gets a compiler-generated action which destructs a base class or member
* variable.
*/
DestructorDestruction getADestruction() { result = getDestruction(_) }
/**
* Gets a compiler-generated action which destructs a base class or member
* variable. The index specifies the order in which the destruction should
* be evaluated.
*/
DestructorDestruction getDestruction(int i) {
exprparents(unresolveElement(result), i, underlyingElement(this))
}
}
/**
* A C++ conversion operator [N4140 12.3.2]. For example the function
* `operator int` in the following code is a `ConversionOperator`:
* ```
* class MyClass {
* public:
* operator int();
* };
* ```
*/
class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
ConversionOperator() { functions(underlyingElement(this), _, 4) }
override string getAPrimaryQlClass() { result = "ConversionOperator" }
override Type getSourceType() { result = this.getDeclaringType() }
override Type getDestType() { result = this.getType() }
}
/**
* A C++ copy assignment operator [N4140 12.8]. For example the function
* `operator=` in the following code is a `CopyAssignmentOperator`:
* ```
* class MyClass {
* public:
* MyClass &operator=(const MyClass &other);
* };
* ```
*
* As per the standard, a copy assignment operator of class `T` is a
* non-template non-static member function with the name `operator=` that
* takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
* T&`, or `const volatile T&`.
*/
class CopyAssignmentOperator extends Operator {
CopyAssignmentOperator() {
hasName("operator=") and
(
hasCopySignature(this)
or
// Unlike CopyConstructor, this member allows a non-reference
// parameter.
getParameter(0).getUnspecifiedType() = getDeclaringType()
) and
not exists(this.getParameter(1)) and
not exists(getATemplateArgument())
}
override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" }
}
/**
* A C++ move assignment operator [N4140 12.8]. For example the function
* `operator=` in the following code is a `MoveAssignmentOperator`:
* ```
* class MyClass {
* public:
* MyClass &operator=(MyClass &&other);
* };
* ```
*
* As per the standard, a move assignment operator of class `T` is a
* non-template non-static member function with the name `operator=` that
* takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
* or `const volatile T&&`.
*/
class MoveAssignmentOperator extends Operator {
MoveAssignmentOperator() {
hasName("operator=") and
hasMoveSignature(this) and
not exists(this.getParameter(1)) and
not exists(getATemplateArgument())
}
override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" }
}

View File

@@ -0,0 +1,167 @@
/**
* Provides classes for working with name qualifiers such as the `N::` in
* `N::f()`.
*/
import cpp
/**
* A C++ name qualifier, for example `N::` in the following code:
* ```
* namespace N {
* int f() {
* ...
* }
* }
*
* int g() {
* return N::f();
* }
* ```
*/
class NameQualifier extends NameQualifiableElement, @namequalifier {
/**
* Gets the expression ultimately qualified by the chain of name
* qualifiers. For example, `f()` in `N1::N2::f()`.
*/
Expr getExpr() { result = getQualifiedElement+() }
/** Gets a location for this name qualifier. */
override Location getLocation() { namequalifiers(underlyingElement(this), _, _, result) }
/**
* Gets the name qualifier that qualifies this name qualifier, if any.
* This is used for name qualifier chains, for example the name qualifier
* `N2::` has a name qualifier `N1::` in the chain `N1::N2::f()`.
*/
override NameQualifier getNameQualifier() {
namequalifiers(unresolveElement(result), underlyingElement(this), _, _)
}
/**
* Gets the element qualified by this name qualifier. For example, `f()`
* in `N::f()`.
*/
NameQualifiableElement getQualifiedElement() {
namequalifiers(underlyingElement(this), unresolveElement(result), _, _)
}
/**
* Gets the qualifying element of this name qualifier. For example, `N`
* in `N::f()`.
*/
NameQualifyingElement getQualifyingElement() {
exists(NameQualifyingElement nqe |
namequalifiers(underlyingElement(this), _, unresolveElement(nqe), _) and
if nqe instanceof SpecialNameQualifyingElement
then
exists(Access a |
a = getQualifiedElement() and
result = a.getTarget().getDeclaringType()
)
or
exists(FunctionCall c |
c = getQualifiedElement() and
result = c.getTarget().getDeclaringType()
)
else result = nqe
)
}
override string toString() {
exists(NameQualifyingElement nqe |
namequalifiers(underlyingElement(this), _, unresolveElement(nqe), _) and
result = nqe.getName() + "::"
)
}
}
/**
* A C++ element that can be qualified with a name. This is in practice
* either an expression or a name qualifier. For example, there are two
* name-qualifiable elements in the following code, the expression `f()`
* (which is qualified by `N::`), and the qualifier `N::` (which is not
* itself qualified in this example):
* ```
* namespace N {
* int f() {
* ...
* }
* }
*
* int g() {
* return N::f();
* }
* ```
*/
class NameQualifiableElement extends Element, @namequalifiableelement {
/**
* Gets the name qualifier associated with this element. For example, the
* name qualifier of `N::f()` is `N`.
*/
NameQualifier getNameQualifier() {
namequalifiers(unresolveElement(result), underlyingElement(this), _, _)
}
/**
* Holds if this element has a globally qualified name. For example,
* `::x` is globally qualified. It is used to refer to `x` in the global
* namespace.
*/
predicate hasGlobalQualifiedName() {
getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace
}
/**
* Holds if this element has a `__super`-qualified name. For example:
* `__super::get()`. Note: `__super` is non-standard C++ extension, only
* supported by some C++ compilers.
*/
predicate hasSuperQualifiedName() {
exists(NameQualifier nq, SpecialNameQualifyingElement snqe |
nq = getNameQualifier*() and
namequalifiers(unresolveElement(nq), _, unresolveElement(snqe), _) and
snqe.getName() = "__super"
)
}
}
/**
* A C++ element that can qualify a name. For example, the namespaces `A` and
* `A::B` and the class `A::C` in the following code:
* ```
* namespace A {
* namespace B {
* ...
* }
*
* class C {
* ...
* };
* }
* ```
*/
class NameQualifyingElement extends Element, @namequalifyingelement {
/**
* Gets a name qualifier for which this is the qualifying namespace or
* user-defined type. For example: class `X` is the
* `NameQualifyingElement` and `X::` is the `NameQualifier`.
*/
NameQualifier getANameQualifier() {
namequalifiers(unresolveElement(result), _, underlyingElement(this), _)
}
/** Gets the name of this namespace or user-defined type. */
string getName() { none() }
}
/**
* A special name-qualifying element. For example: `__super`.
*/
library class SpecialNameQualifyingElement extends NameQualifyingElement,
@specialnamequalifyingelement {
/** Gets the name of this special qualifying element. */
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
override string toString() { result = getName() }
}

View File

@@ -0,0 +1,249 @@
/**
* Provides classes for modeling namespaces, `using` directives and `using` declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Type
import semmle.code.cpp.metrics.MetricNamespace
/**
* A C++ namespace. For example the (single) namespace `A` in the following
* code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
* Note that namespaces are somewhat nebulous entities, as they do not in
* general have a single well-defined location in the source code. The
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
* and should be used when a location is required. For example, the `std::`
* namespace is particularly nebulous, as parts of it are defined across a
* wide range of headers. As a more extreme example, the global namespace
* is never explicitly declared, but might correspond to a large proportion
* of the source code.
*/
class Namespace extends NameQualifyingElement, @namespace {
/**
* Gets the location of the namespace. Most namespaces do not have a
* single well-defined source location, so a dummy location is returned,
* unless the namespace has exactly one declaration entry.
*/
override Location getLocation() {
if strictcount(getADeclarationEntry()) = 1
then result = getADeclarationEntry().getLocation()
else result instanceof UnknownDefaultLocation
}
/** Gets the simple name of this namespace. */
override string getName() { namespaces(underlyingElement(this), result) }
/** Holds if this element is named `name`. */
predicate hasName(string name) { name = this.getName() }
/** Holds if this namespace is anonymous. */
predicate isAnonymous() { hasName("(unnamed namespace)") }
/** Gets the name of the parent namespace, if it exists. */
private string getParentName() {
result = this.getParentNamespace().getName() and
result != ""
}
/** Gets the qualified name of this namespace. For example: `a::b`. */
string getQualifiedName() {
if exists(getParentName())
then result = getParentNamespace().getQualifiedName() + "::" + getName()
else result = getName()
}
/** Gets the parent namespace, if any. */
Namespace getParentNamespace() {
namespacembrs(unresolveElement(result), underlyingElement(this))
or
not namespacembrs(_, underlyingElement(this)) and result instanceof GlobalNamespace
}
/** Gets a child declaration of this namespace. */
Declaration getADeclaration() { namespacembrs(underlyingElement(this), unresolveElement(result)) }
/** Gets a child namespace of this namespace. */
Namespace getAChildNamespace() {
namespacembrs(underlyingElement(this), unresolveElement(result))
}
/** Holds if the namespace is inline. */
predicate isInline() { namespace_inline(underlyingElement(this)) }
/** Holds if this namespace may be from source. */
override predicate fromSource() { this.getADeclaration().fromSource() }
/**
* Holds if this namespace is in a library.
*
* DEPRECATED: never holds.
*/
deprecated override predicate fromLibrary() { not this.fromSource() }
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
string getFriendlyName() { result = this.getQualifiedName() }
final override string toString() { result = getFriendlyName() }
/** Gets a declaration of (part of) this namespace. */
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
/** Gets a file which declares (part of) this namespace. */
File getAFile() { result = this.getADeclarationEntry().getLocation().getFile() }
}
/**
* A declaration of (part of) a C++ namespace. This corresponds to a single
* `namespace N { ... }` occurrence in the source code. For example the two
* mentions of `A` in the following code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
*/
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/**
* Get the namespace that this declaration entry corresponds to. There
* is a one-to-many relationship between `Namespace` and
* `NamespaceDeclarationEntry`.
*/
Namespace getNamespace() {
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
}
override string toString() { result = this.getNamespace().getFriendlyName() }
/**
* Gets the location of the token preceding the namespace declaration
* entry's body.
*
* For named declarations, such as "namespace MyStuff { ... }", this will
* give the "MyStuff" token.
*
* For anonymous declarations, such as "namespace { ... }", this will
* give the "namespace" token.
*/
override Location getLocation() { namespace_decls(underlyingElement(this), _, result, _) }
/**
* Gets the location of the namespace declaration entry's body. For
* example: the "{ ... }" in "namespace N { ... }".
*/
Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) }
override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" }
}
/**
* A C++ `using` directive or `using` declaration.
*/
class UsingEntry extends Locatable, @using {
override Location getLocation() { usings(underlyingElement(this), _, result) }
}
/**
* A C++ `using` declaration. For example:
* ```
* using std::string;
* ```
*/
class UsingDeclarationEntry extends UsingEntry {
UsingDeclarationEntry() {
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the declaration that is referenced by this using declaration. For
* example, `std::string` in `using std::string`.
*/
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using " + this.getDeclaration().getDescription() }
}
/**
* A C++ `using` directive. For example:
* ```
* using namespace std;
* ```
*/
class UsingDirectiveEntry extends UsingEntry {
UsingDirectiveEntry() {
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the namespace that is referenced by this using directive. For
* example, `std` in `using namespace std`.
*/
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
}
/**
* Holds if `g` is an instance of `GlobalNamespace`. This predicate
* is used suppress a warning in `GlobalNamespace.getADeclaration()`
* by providing a fake use of `this`.
*/
private predicate suppressWarningForUnused(GlobalNamespace g) { any() }
/**
* The C/C++ global namespace.
*/
class GlobalNamespace extends Namespace {
GlobalNamespace() { this.hasName("") }
override Declaration getADeclaration() {
suppressWarningForUnused(this) and
result.isTopLevel() and
not namespacembrs(_, unresolveElement(result))
}
/** Gets a child namespace of the global namespace. */
override Namespace getAChildNamespace() {
suppressWarningForUnused(this) and
not namespacembrs(unresolveElement(result), _)
}
override Namespace getParentNamespace() { none() }
/**
* DEPRECATED: use `getName()`.
*/
deprecated string getFullName() { result = this.getName() }
override string getFriendlyName() { result = "(global namespace)" }
}
/**
* The C++ `std::` namespace.
*/
class StdNamespace extends Namespace {
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
}

View File

@@ -0,0 +1,69 @@
/**
* Provides a class for reasoning about nested field accesses, for example
* the access `myLine.start.x`.
*/
import cpp
/**
* Gets a `Field` that is within the given `Struct`, either directly or nested
* inside one or more levels of member structs.
*/
private Field getANestedField(Struct s) {
result = s.getAField()
or
exists(NestedStruct ns |
s = ns.getDeclaringType() and
result = getANestedField(ns)
)
}
/**
* Unwraps a series of field accesses to determine the outer-most qualifier.
*/
private Expr getUltimateQualifier(FieldAccess fa) {
exists(Expr qualifier | qualifier = fa.getQualifier() |
result = getUltimateQualifier(qualifier)
or
not qualifier instanceof FieldAccess and result = qualifier
)
}
/**
* A nested field access, for example the access `myLine.start.x`.
*/
class NestedFieldAccess extends FieldAccess {
Expr ultimateQualifier;
NestedFieldAccess() {
ultimateQualifier = getUltimateQualifier(this) and
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
}
/**
* Gets the outermost qualifier of this nested field access. In the
* following example, the access to `myLine.start.x` has outermost qualifier
* `myLine`:
* ```
* struct Point
* {
* float x, y;
* };
*
* struct Line
* {
* Point start, end;
* };
*
* void init()
* {
* Line myLine;
*
* myLine.start.x = 0.0f;
*
* // ...
* }
* ```
*/
Expr getUltimateQualifier() { result = ultimateQualifier }
}

View File

@@ -0,0 +1,196 @@
/**
* DEPRECATED: Objective-C is no longer supported.
*/
import semmle.code.cpp.Class
private import semmle.code.cpp.internal.ResolveClass
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C class.
*/
deprecated class ObjectiveClass extends Class {
ObjectiveClass() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C protocol.
*/
deprecated class Protocol extends Class {
Protocol() { none() }
/**
* Holds if the type implements the protocol, either because the type
* itself does, or because it is a type conforming to the protocol.
*/
predicate isImplementedBy(Type t) { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* A type which conforms to a protocol. Use `getAProtocol` to get a
* protocol that this type conforms to.
*/
deprecated class TypeConformingToProtocol extends DerivedType {
TypeConformingToProtocol() { none() }
/** Gets a protocol that this type conforms to. */
Protocol getAProtocol() { none() }
/** Gets the size of this type. */
override int getSize() { none() }
override int getAlignment() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@autoreleasepool` statement, for example
* `@autoreleasepool { int x; int y; }`.
*/
deprecated class AutoReleasePoolStmt extends Stmt {
AutoReleasePoolStmt() { none() }
override string toString() { none() }
/** Gets the body statement of this `@autoreleasepool` statement. */
Stmt getStmt() { none() }
override predicate mayBeImpure() { none() }
override predicate mayBeGloballyImpure() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@synchronized statement`, for example
* `@synchronized (x) { [x complicationOperation]; }`.
*/
deprecated class SynchronizedStmt extends Stmt {
SynchronizedStmt() { none() }
override string toString() { none() }
/** Gets the expression which gives the object to be locked. */
Expr getLockedObject() { none() }
/** Gets the body statement of this `@synchronized` statement. */
Stmt getStmt() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C for-in statement.
*/
deprecated class ForInStmt extends Loop {
ForInStmt() { none() }
/**
* Gets the condition expression of the `while` statement that the
* `for...in` statement desugars into.
*/
override Expr getCondition() { none() }
override Expr getControllingExpr() { none() }
/** Gets the collection that the loop iterates over. */
Expr getCollection() { none() }
/** Gets the body of the loop. */
override Stmt getStmt() { none() }
override string toString() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C category or class extension.
*/
deprecated class Category extends Class {
Category() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C class extension.
*/
deprecated class ClassExtension extends Category {
ClassExtension() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C try statement.
*/
deprecated class ObjcTryStmt extends TryStmt {
ObjcTryStmt() { none() }
override string toString() { none() }
/** Gets the finally clause of this try statement, if any. */
FinallyBlock getFinallyClause() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@finally` block.
*/
deprecated class FinallyBlock extends BlockStmt {
FinallyBlock() { none() }
/** Gets the try statement corresponding to this finally block. */
ObjcTryStmt getTryStmt() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@property`.
*/
deprecated class Property extends Declaration {
Property() { none() }
/** Gets the name of this property. */
override string getName() { none() }
/**
* Gets nothing (provided for compatibility with Declaration).
*
* For the attribute list following the `@property` keyword, use
* `getAnAttribute()`.
*/
override Specifier getASpecifier() { none() }
/**
* Gets an attribute of this property (such as `readonly`, `nonatomic`,
* or `getter=isEnabled`).
*/
Attribute getAnAttribute() { none() }
override Location getADeclarationLocation() { result = getLocation() }
override Location getDefinitionLocation() { result = getLocation() }
override Location getLocation() { none() }
/** Gets the type of this property. */
Type getType() { none() }
/**
* Gets the instance method which is called to get the value of this
* property.
*/
MemberFunction getGetter() { none() }
/**
* Gets the instance method which is called to set the value of this
* property (if it is a writable property).
*/
MemberFunction getSetter() { none() }
/**
* Gets the instance variable which stores the property value (if this
* property was explicitly or automatically `@synthesize`d).
*/
MemberVariable getInstanceVariable() { none() }
}

View File

@@ -0,0 +1,125 @@
/**
* Provides predicates to determine whether a type is an aggregate or POD
* (Plain Old Data), as defined by C++03.
*/
import cpp
/**
* Holds if `t` is a scalar type, according to the rules specified in
* C++03 3.9(10):
*
* Arithmetic types (3.9.1), enumeration types, pointer types, and
* pointer to member types (3.9.2), and cv-qualified versions of these
* types (3.9.3) are collectively called scalar types.
*/
predicate isScalarType03(Type t) {
exists(Type ut | ut = t.getUnderlyingType() |
ut instanceof ArithmeticType or
ut instanceof Enum or
ut instanceof FunctionPointerType or
ut instanceof PointerToMemberType or
ut instanceof PointerType or
isScalarType03(ut.(SpecifiedType).getUnspecifiedType())
)
}
/**
* Holds if `c` is an aggregate class, according to the rules specified in
* C++03 8.5.1(1):
*
* An aggregate [class] is ... a class (clause 9) with no user-declared
* constructors (12.1), no private or protected non-static data members
* (clause 11), no base classes (clause 10), and no virtual functions
* (10.3).
*/
predicate isAggregateClass03(Class c) {
not c instanceof TemplateClass and
not exists(Constructor cons |
cons.getDeclaringType() = c and
not cons.isCompilerGenerated()
) and
not exists(Variable v |
v.getDeclaringType() = c and
not v.isStatic()
|
v.hasSpecifier("private") or
v.hasSpecifier("protected")
) and
not exists(c.getABaseClass()) and
not exists(VirtualFunction f | f.getDeclaringType() = c)
}
/**
* Holds if `t` is an aggregate type, according to the rules specified in
* C++03 8.5.1(1):
*
* An aggregate is an array or a class (clause 9) with no user-declared
* constructors (12.1), no private or protected non-static data members
* (clause 11), no base classes (clause 10), and no virtual functions
* (10.3).
*/
predicate isAggregateType03(Type t) {
exists(Type ut | ut = t.getUnderlyingType() |
ut instanceof ArrayType or
isAggregateClass03(ut)
)
}
/**
* Holds if `c` is a POD class, according to the rules specified in
* C++03 9(4):
*
* A POD-struct is an aggregate class that has no non-static data members
* of type non-POD-struct, non-POD-union (or array of such types) or
* reference, and has no user-defined copy assignment operator and no
* user-defined destructor. Similarly, a POD-union is an aggregate union
* that has no non-static data members of type non-POD-struct,
* non-POD-union (or array of such types) or reference, and has no
* user-defined copy assignment operator and no user-defined destructor.
* A POD class is a class that is either a POD-struct or a POD-union.
*/
predicate isPODClass03(Class c) {
isAggregateClass03(c) and
not exists(Variable v |
v.getDeclaringType() = c and
not v.isStatic()
|
not isPODType03(v.getType())
or
exists(ArrayType at |
at = v.getType() and
not isPODType03(at.getBaseType())
)
or
v.getType() instanceof ReferenceType
) and
not exists(CopyAssignmentOperator o |
o.getDeclaringType() = c and
not o.isCompilerGenerated()
) and
not exists(Destructor dest |
dest.getDeclaringType() = c and
not dest.isCompilerGenerated()
)
}
/**
* Holds if `t` is a POD type, according to the rules specified in
* C++03 3.9(10):
*
* Scalar types, POD-struct types, POD-union types (clause 9), arrays of
* such types and cv-qualified versions of these types (3.9.3) are
* collectively called POD types.
*/
predicate isPODType03(Type t) {
exists(Type ut | ut = t.getUnderlyingType() |
isScalarType03(ut)
or
isPODClass03(ut)
or
exists(ArrayType at | at = ut and isPODType03(at.getBaseType()))
or
isPODType03(ut.(SpecifiedType).getUnspecifiedType())
)
}

View File

@@ -0,0 +1,176 @@
/**
* Provides a class that models parameters to functions.
*/
import semmle.code.cpp.Location
import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ function parameter or catch block parameter. For example the
* function parameter `p` and the catch block parameter `e` in the following
* code:
* ```
* void myFunction(int p) {
* try {
* ...
* } catch (const std::exception &e) {
* ...
* }
* }
* ```
*
* For catch block parameters, there is a one-to-one correspondence between
* the `Parameter` and its `ParameterDeclarationEntry`.
*
* For function parameters, there is a one-to-many relationship between
* `Parameter` and `ParameterDeclarationEntry`, because one function can
* have multiple declarations.
*/
class Parameter extends LocalScopeVariable, @parameter {
/**
* Gets the canonical name, or names, of this parameter.
*
* The canonical names are the first non-empty category from the
* following list:
* 1. The name given to the parameter at the function's definition or
* (for catch block parameters) at the catch block.
* 2. A name given to the parameter at a function declaration.
* 3. The name "(unnamed parameter i)" where i is the index of the parameter.
*/
override string getName() {
exists(VariableDeclarationEntry vde |
vde = getANamedDeclarationEntry() and result = vde.getName()
|
vde.isDefinition() or not getANamedDeclarationEntry().isDefinition()
)
or
not exists(getANamedDeclarationEntry()) and
result = "(unnamed parameter " + this.getIndex().toString() + ")"
}
override string getAPrimaryQlClass() { result = "Parameter" }
/**
* Gets the name of this parameter, including it's type.
*
* For example: `int p`.
*/
string getTypedName() {
exists(string typeString, string nameString |
(if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and
(if exists(getName()) then nameString = getName() else nameString = "") and
(
if typeString != "" and nameString != ""
then result = typeString + " " + nameString
else result = typeString + nameString
)
)
}
private VariableDeclarationEntry getANamedDeclarationEntry() {
result = getAnEffectiveDeclarationEntry() and result.getName() != ""
}
/**
* Gets a declaration entry corresponding to this declaration.
*
* This predicate is the same as getADeclarationEntry(), except that for
* parameters of instantiated function templates, gives the declaration
* entry of the prototype instantiation of the parameter (as
* non-prototype instantiations don't have declaration entries of their
* own).
*/
private VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
if getFunction().isConstructedFrom(_)
then
exists(Function prototypeInstantiation |
prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and
getFunction().isConstructedFrom(prototypeInstantiation)
)
else result = getADeclarationEntry()
}
/**
* Gets the name of this parameter in the given block (which should be
* the body of a function with which the parameter is associated).
*
* DEPRECATED: this method was used in a previous implementation of
* getName, but is no longer in use.
*/
deprecated string getNameInBlock(BlockStmt b) {
exists(ParameterDeclarationEntry pde |
pde.getFunctionDeclarationEntry().getBlock() = b and
this.getFunction().getBlock() = b and
pde.getVariable() = this and
result = pde.getName()
)
}
/**
* Holds if this parameter has a name.
*
* In other words, this predicate holds precisely when the result of
* `getName()` is not "(unnamed parameter i)" (where `i` is the index
* of the parameter).
*/
predicate isNamed() { exists(getANamedDeclarationEntry()) }
/**
* Gets the function to which this parameter belongs, if it is a function
* parameter.
*/
override Function getFunction() {
params(underlyingElement(this), unresolveElement(result), _, _)
}
/**
* Gets the catch block to which this parameter belongs, if it is a catch
* block parameter.
*/
BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the zero-based index of this parameter.
*
* For catch block parameters, this is always zero.
*/
int getIndex() { params(underlyingElement(this), _, result, _) }
/**
* Gets the type of this parameter.
*
* Function parameters of array type are a special case in C/C++,
* as they are syntactic sugar for parameters of pointer type. The
* result is an array type for such parameters.
*/
override Type getType() { params(underlyingElement(this), _, _, unresolveElement(result)) }
/**
* Gets the canonical location, or locations, of this parameter.
*
* 1. For catch block parameters, gets the obvious location.
* 2. For parameters of functions which have a definition, gets the
* location within the function definition.
* 3. For parameters of functions which don't have a definition, gets all
* of the declaration locations.
*/
override Location getLocation() {
exists(VariableDeclarationEntry vde |
vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation()
|
vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition()
)
}
}
/**
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
*/
class ParameterIndex extends int {
ParameterIndex() {
exists(Parameter p | this = p.getIndex()) or
exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
this = -1 // used for `this`
}
}

View File

@@ -0,0 +1,294 @@
import semmle.code.cpp.Location
import semmle.code.cpp.Element
/**
* A C/C++ preprocessor directive. For example each of the following lines of
* code contains a `PreprocessorDirective`:
* ```
* #pragma once
* #ifdef MYDEFINE
* #include "myfile.h"
* #line 1 "source.c"
* ```
*/
class PreprocessorDirective extends Locatable, @preprocdirect {
override string toString() { result = "Preprocessor directive" }
override Location getLocation() { preprocdirects(underlyingElement(this), _, result) }
string getHead() { preproctext(underlyingElement(this), result, _) }
/**
* Gets a preprocessor branching directive whose condition affects
* whether this directive is performed.
*
* From a lexical point of view, this returns all `#if`, `#ifdef`,
* `#ifndef`, or `#elif` directives which occur before this directive and
* have a matching `#endif` which occurs after this directive.
*/
PreprocessorBranch getAGuard() {
exists(PreprocessorEndif e, int line |
result.getEndIf() = e and
e.getFile() = getFile() and
result.getFile() = getFile() and
line = this.getLocation().getStartLine() and
result.getLocation().getStartLine() < line and
line < e.getLocation().getEndLine()
)
}
}
private class TPreprocessorBranchDirective = @ppd_branch or @ppd_else or @ppd_endif;
/**
* A C/C++ preprocessor branch related directive: `#if`, `#ifdef`,
* `#ifndef`, `#elif`, `#else` or `#endif`.
*/
class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective {
/**
* Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this
* branching directive.
*
* If this branch directive was unbalanced, then there will be no
* result. Conversely, if the branch matches different `#if` directives
* in different translation units, then there can be more than one
* result.
*/
PreprocessorBranch getIf() {
result = this.(PreprocessorIf) or
result = this.(PreprocessorIfdef) or
result = this.(PreprocessorIfndef) or
preprocpair(unresolveElement(result), underlyingElement(this))
}
/**
* Gets the `#endif` directive which matches this branching directive.
*
* If this branch directive was unbalanced, then there will be no
* result. Conversely, if the branch matched different `#endif`
* directives in different translation units, then there can be more than
* one result.
*/
PreprocessorEndif getEndIf() { preprocpair(unresolveElement(getIf()), unresolveElement(result)) }
/**
* Gets the next `#elif`, `#else` or `#endif` matching this branching
* directive.
*
* For example `somePreprocessorBranchDirective.getIf().getNext()` gets
* the second directive in the same construct as
* `somePreprocessorBranchDirective`.
*/
PreprocessorBranchDirective getNext() {
exists(PreprocessorBranch branch |
this.getIndexInBranch(branch) + 1 = result.getIndexInBranch(branch)
)
}
/**
* Gets the index of this branching directive within the matching #if,
* #ifdef or #ifndef.
*/
private int getIndexInBranch(PreprocessorBranch branch) {
this =
rank[result](PreprocessorBranchDirective other |
other.getIf() = branch
|
other order by other.getLocation().getStartLine()
)
}
}
/**
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or
* `#elif`.
*
* A branching directive has a condition and that condition may be evaluated
* at compile-time. As a result, the preprocessor will either take the
* branch, or not take the branch.
*
* However, there are also situations in which a branch's condition isn't
* evaluated. The obvious case of this is when the directive is contained
* within a branch which wasn't taken. There is also a much more subtle
* case involving header guard branches: suitably clever compilers can
* notice that a branch is a header guard, and can then subsequently ignore
* a `#include` for the file being guarded. It is for this reason that
* `wasTaken()` always holds on header guard branches, but `wasNotToken()`
* rarely holds on header guard branches.
*/
class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
/**
* Holds if at least one translation unit evaluated this directive's
* condition and subsequently took the branch.
*/
predicate wasTaken() { preproctrue(underlyingElement(this)) }
/**
* Holds if at least one translation unit evaluated this directive's
* condition but then didn't take the branch.
*
* If `#else` is the next matching directive, then this means that the
* `#else` was taken instead.
*/
predicate wasNotTaken() { preprocfalse(underlyingElement(this)) }
/**
* Holds if this directive was either taken by all translation units
* which evaluated it, or was not taken by any translation unit which
* evaluated it.
*/
predicate wasPredictable() { not (wasTaken() and wasNotTaken()) }
}
/**
* A C/C++ preprocessor `#if` directive. For example there is a
* `PreprocessorIf` on the first line of the following code:
* ```
* #if defined(MYDEFINE)
* // ...
* #endif
* ```
* For the related notion of a directive which causes branching (which
* includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see
* `PreprocessorBranch`.
*/
class PreprocessorIf extends PreprocessorBranch, @ppd_if {
override string toString() { result = "#if " + this.getHead() }
}
/**
* A C/C++ preprocessor `#ifdef` directive. For example there is a
* `PreprocessorIfdef` on the first line of the following code:
* ```
* #ifdef MYDEFINE
* // ...
* #endif
* ```
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
*/
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
override string toString() { result = "#ifdef " + this.getHead() }
override string getAPrimaryQlClass() { result = "PreprocessorIfdef" }
}
/**
* A C/C++ preprocessor `#ifndef` directive. For example there is a
* `PreprocessorIfndef` on the first line of the following code:
* ```
* #ifndef MYDEFINE
* // ...
* #endif
* ```
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
*/
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
override string toString() { result = "#ifndef " + this.getHead() }
}
/**
* A C/C++ preprocessor `#else` directive. For example there is a
* `PreprocessorElse` on the fifth line of the following code:
* ```
* #ifdef MYDEFINE1
* // ...
* #elif MYDEFINE2
* // ...
* #else
* // ...
* #endif
* ```
*/
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
override string toString() { result = "#else" }
}
/**
* A C/C++ preprocessor `#elif` directive. For example there is a
* `PreprocessorElif` on the third line of the following code:
* ```
* #ifdef MYDEFINE1
* // ...
* #elif MYDEFINE2
* // ...
* #else
* // ...
* #endif
* ```
*/
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
override string toString() { result = "#elif " + this.getHead() }
}
/**
* A C/C++ preprocessor `#endif` directive. For example there is a
* `PreprocessorEndif` on the third line of the following code:
* ```
* #ifdef MYDEFINE
* // ...
* #endif
* ```
*/
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
override string toString() { result = "#endif" }
}
/**
* A C/C++ preprocessor `#warning` directive. For example:
* ```
* #warning "This configuration is not supported."
* ```
*/
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
override string toString() { result = "#warning " + this.getHead() }
}
/**
* A C/C++ preprocessor `#error` directive. For example:
* ```
* #error "This configuration is not implemented."
* ```
*/
class PreprocessorError extends PreprocessorDirective, @ppd_error {
override string toString() { result = "#error " + this.getHead() }
}
/**
* A C/C++ preprocessor `#undef` directive. For example there is a
* `PreprocessorUndef` on the second line of the following code:
* ```
* #ifdef MYMACRO
* #undef MYMACRO
* #endif
* ```
*/
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
override string toString() { result = "#undef " + this.getHead() }
/**
* Gets the name of the macro that is undefined.
*/
string getName() { result = getHead() }
}
/**
* A C/C++ preprocessor `#pragma` directive. For example:
* ```
* #pragma once
* ```
*/
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
override string toString() {
if exists(this.getHead()) then result = "#pragma " + this.getHead() else result = "#pragma"
}
}
/**
* A C/C++ preprocessor `#line` directive. For example:
* ```
* #line 1 "source.c"
* ```
*/
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
override string toString() { result = "#line " + this.getHead() }
}

View File

@@ -0,0 +1,413 @@
import cpp
private import PrintAST
/**
* Print function declarations only if there is a `PrintASTConfiguration`
* that requests that function, or no `PrintASTConfiguration` exists.
*/
private predicate shouldPrintDeclaration(Declaration decl) {
not decl instanceof Function
or
not exists(PrintASTConfiguration c)
or
exists(PrintASTConfiguration config | config.shouldPrintFunction(decl))
}
/**
* Gets a string containing the scope in which this declaration is declared.
*/
private string getScopePrefix(Declaration decl) {
decl.isMember() and result = decl.getDeclaringType().(UserDumpType).getIdentityString() + "::"
or
decl.isTopLevel() and
exists(string parentName |
parentName = decl.getNamespace().getQualifiedName() and
(
parentName != "" and result = parentName + "::"
or
parentName = "" and result = ""
)
)
or
exists(UserType type |
type = decl and
type.isLocal() and
result = "(" + type.getEnclosingFunction().(DumpFunction).getIdentityString() + ")::"
)
or
decl instanceof TemplateParameter and result = ""
}
/**
* Gets the identity string of a type used as a parameter. Identical to `Type.getTypeIdentityString()`, except that
* it returns `...` for `UnknownType`, which is used to represent variable arguments.
*/
private string getParameterTypeString(Type parameterType) {
if parameterType instanceof UnknownType
then result = "..."
else result = parameterType.(DumpType).getTypeIdentityString()
}
private string getTemplateArgumentString(Declaration d, int i) {
if exists(d.getTemplateArgumentKind(i))
then
result =
d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
d.getTemplateArgument(i)
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
}
/**
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
*/
private class DumpDeclaration extends Declaration {
DumpDeclaration() { shouldPrintDeclaration(this) }
/**
* Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for
* functions, user-defined types, global and namespace-scope variables, and member variables.
*
* This operation is very expensive, and should not be used in production queries. Consider using
* `hasQualifiedName()` for identifying known declarations in production queries.
*/
string getIdentityString() { none() }
language[monotonicAggregates]
final string getTemplateArgumentsString() {
if exists(this.getATemplateArgument())
then
result =
"<" +
strictconcat(int i |
exists(this.getTemplateArgument(i))
|
getTemplateArgumentString(this, i), ", " order by i
) + ">"
else result = ""
}
}
/**
* A `Type` extended to add methods for generating strings useful only for dumps and debugging.
*/
private class DumpType extends Type {
/**
* Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and
* decltypes are expanded, and all symbol names are fully qualified.
*
* This operation is very expensive, and should not be used in production queries.
*/
final string getTypeIdentityString() {
// The identity string of a type is just the concatenation of the four
// components below. To create the type identity for a derived type, insert
// the declarator of the derived type between the `getDeclaratorPrefix()`
// and `getDeclaratorSuffixBeforeQualifiers()`. To create the type identity
// for a `SpecifiedType`, insert the qualifiers after
// `getDeclaratorSuffixBeforeQualifiers()`.
result =
getTypeSpecifier() + getDeclaratorPrefix() + getDeclaratorSuffixBeforeQualifiers() +
getDeclaratorSuffix()
}
/**
* Gets the "type specifier" part of this type's name. This is generally the "leaf" type from which the type was
* constructed.
*
* Examples:
* - `int` -> `int`
* - `int*` -> `int`
* - `int (*&)(float, double) const` -> `int`
*
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
*/
string getTypeSpecifier() { result = "" }
/**
* Gets the portion of this type's declarator that comes before the declarator for any derived type.
*
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
*/
string getDeclaratorPrefix() { result = "" }
/**
* Gets the portion of this type's declarator that comes after the declarator for any derived type, but before any
* qualifiers on the current type.
*
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
*/
string getDeclaratorSuffixBeforeQualifiers() { result = "" }
/**
* Gets the portion of this type's declarator that comes after the declarator for any derived type and after any
* qualifiers on the current type.
*
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
*/
string getDeclaratorSuffix() { result = "" }
}
private class BuiltInDumpType extends DumpType, BuiltInType {
override string getTypeSpecifier() { result = toString() }
}
private class IntegralDumpType extends BuiltInDumpType, IntegralType {
override string getTypeSpecifier() { result = getCanonicalArithmeticType().toString() }
}
private class DerivedDumpType extends DumpType, DerivedType {
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
override string getDeclaratorSuffixBeforeQualifiers() {
result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
}
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
}
private class DecltypeDumpType extends DumpType, Decltype {
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() }
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
}
private class PointerIshDumpType extends DerivedDumpType {
PointerIshDumpType() {
this instanceof PointerType or
this instanceof ReferenceType
}
override string getDeclaratorPrefix() {
exists(string declarator |
result = getBaseType().(DumpType).getDeclaratorPrefix() + declarator and
if getBaseType().getUnspecifiedType() instanceof ArrayType
then declarator = "(" + getDeclaratorToken() + ")"
else declarator = getDeclaratorToken()
)
}
/**
* Gets the token used when declaring this kind of type (e.g. `*`, `&`, `&&`)/
*/
string getDeclaratorToken() { result = "" }
}
private class PointerDumpType extends PointerIshDumpType, PointerType {
override string getDeclaratorToken() { result = "*" }
}
private class LValueReferenceDumpType extends PointerIshDumpType, LValueReferenceType {
override string getDeclaratorToken() { result = "&" }
}
private class RValueReferenceDumpType extends PointerIshDumpType, RValueReferenceType {
override string getDeclaratorToken() { result = "&&" }
}
private class PointerToMemberDumpType extends DumpType, PointerToMemberType {
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
override string getDeclaratorPrefix() {
exists(string declarator, string parenDeclarator, Type baseType |
declarator = getClass().(DumpType).getTypeIdentityString() + "::*" and
result = getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and
baseType = getBaseType().getUnspecifiedType() and
if baseType instanceof ArrayType or baseType instanceof RoutineType
then parenDeclarator = "(" + declarator
else parenDeclarator = declarator
)
}
override string getDeclaratorSuffixBeforeQualifiers() {
exists(Type baseType |
baseType = getBaseType().getUnspecifiedType() and
if baseType instanceof ArrayType or baseType instanceof RoutineType
then result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
else result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
)
}
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
}
private class ArrayDumpType extends DerivedDumpType, ArrayType {
override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() }
override string getDeclaratorSuffixBeforeQualifiers() {
if exists(getArraySize())
then
result =
"[" + getArraySize().toString() + "]" +
getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
else result = "[]" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
}
}
private class FunctionPointerIshDumpType extends DerivedDumpType, FunctionPointerIshType {
override string getDeclaratorSuffixBeforeQualifiers() {
result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
}
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
override string getDeclaratorPrefix() {
result = getBaseType().(DumpType).getDeclaratorPrefix() + "(" + getDeclaratorToken()
}
/**
* Gets the token used when declaring this kind of type (e.g. `*`, `&`, `^`)/
*/
string getDeclaratorToken() { result = "" }
}
private class FunctionPointerDumpType extends FunctionPointerIshDumpType, FunctionPointerType {
override string getDeclaratorToken() { result = "*" }
}
private class FunctionReferenceDumpType extends FunctionPointerIshDumpType, FunctionReferenceType {
override string getDeclaratorToken() { result = "&" }
}
private class BlockDumpType extends FunctionPointerIshDumpType, BlockType {
override string getDeclaratorToken() { result = "^" }
}
private class RoutineDumpType extends DumpType, RoutineType {
override string getTypeSpecifier() { result = getReturnType().(DumpType).getTypeSpecifier() }
override string getDeclaratorPrefix() {
result = getReturnType().(DumpType).getDeclaratorPrefix()
}
language[monotonicAggregates]
override string getDeclaratorSuffixBeforeQualifiers() {
result =
"(" +
concat(int i |
exists(getParameterType(i))
|
getParameterTypeString(getParameterType(i)), ", " order by i
) + ")"
}
override string getDeclaratorSuffix() {
result =
getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
getReturnType().(DumpType).getDeclaratorSuffix()
}
}
private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
override string getDeclaratorPrefix() {
exists(string basePrefix |
basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and
if getBaseType().getUnspecifiedType() instanceof RoutineType
then result = basePrefix
else result = basePrefix + " " + getSpecifierString()
)
}
override string getDeclaratorSuffixBeforeQualifiers() {
exists(string baseSuffix |
baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
if getBaseType().getUnspecifiedType() instanceof RoutineType
then result = baseSuffix + " " + getSpecifierString()
else result = baseSuffix
)
}
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
}
private class UserDumpType extends DumpType, DumpDeclaration, UserType {
override string getIdentityString() {
exists(string simpleName |
(
if this instanceof Closure
then
// Parenthesize the name of the lambda because it's freeform text similar to
// "lambda [] type at line 12, col. 40"
// Use `min(getSimpleName())` to work around an extractor bug where a lambda can have different names
// from different compilation units.
simpleName = "(" + min(getSimpleName()) + ")"
else simpleName = getSimpleName()
) and
result = getScopePrefix(this) + simpleName + getTemplateArgumentsString()
)
}
override string getTypeSpecifier() { result = getIdentityString() }
}
private class DumpProxyClass extends UserDumpType, ProxyClass {
override string getIdentityString() { result = getName() }
}
private class DumpVariable extends DumpDeclaration, Variable {
override string getIdentityString() {
exists(DumpType type |
(this instanceof MemberVariable or this instanceof GlobalOrNamespaceVariable) and
type = this.getType() and
result =
type.getTypeSpecifier() + type.getDeclaratorPrefix() + " " + getScopePrefix(this) +
this.getName() + this.getTemplateArgumentsString() +
type.getDeclaratorSuffixBeforeQualifiers() + type.getDeclaratorSuffix()
)
}
}
private class DumpFunction extends DumpDeclaration, Function {
override string getIdentityString() {
result =
getType().(DumpType).getTypeSpecifier() + getType().(DumpType).getDeclaratorPrefix() + " " +
getScopePrefix(this) + getName() + getTemplateArgumentsString() +
getDeclaratorSuffixBeforeQualifiers() + getDeclaratorSuffix()
}
language[monotonicAggregates]
private string getDeclaratorSuffixBeforeQualifiers() {
result =
"(" +
concat(int i |
exists(getParameter(i).getType())
|
getParameterTypeString(getParameter(i).getType()), ", " order by i
) + ")" + getQualifierString()
}
private string getQualifierString() {
if exists(getACVQualifier())
then
result = " " + strictconcat(string qualifier | qualifier = getACVQualifier() | qualifier, " ")
else result = ""
}
private string getACVQualifier() {
result = getASpecifier().getName() and
result = ["const", "volatile"]
}
private string getDeclaratorSuffix() {
result =
getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
getType().(DumpType).getDeclaratorSuffix()
}
}
/**
* Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for
* functions, user-defined types, global and namespace-scope variables, and member variables.
*
* This operation is very expensive, and should not be used in production queries. Consider using `hasName()` or
* `hasQualifiedName()` for identifying known declarations in production queries.
*/
string getIdentityString(Declaration decl) { result = decl.(DumpDeclaration).getIdentityString() }
/**
* Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and
* decltypes are expanded, and all symbol names are fully qualified.
*
* This operation is very expensive, and should not be used in production queries.
*/
string getTypeIdentityString(Type type) { result = type.(DumpType).getTypeIdentityString() }

View File

@@ -0,0 +1,21 @@
/**
* @name Print AST
* @description Outputs a representation of the Abstract Syntax Tree.
* @id cpp/print-ast
* @kind graph
*/
import cpp
import PrintAST
/**
* Temporarily tweak this class or make a copy to control which functions are
* printed.
*/
class Cfg extends PrintASTConfiguration {
/**
* TWEAK THIS PREDICATE AS NEEDED.
* Holds if the AST for `func` should be printed.
*/
override predicate shouldPrintFunction(Function func) { any() }
}

View File

@@ -0,0 +1,884 @@
/**
* Provides queries to pretty-print a C++ AST as a graph.
*
* By default, this will print the AST for all functions in the database. To change this behavior,
* extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions
* you wish to view the AST for.
*/
import cpp
private import semmle.code.cpp.Print
private newtype TPrintASTConfiguration = MkPrintASTConfiguration()
/**
* The query can extend this class to control which functions are printed.
*/
class PrintASTConfiguration extends TPrintASTConfiguration {
/**
* Gets a textual representation of this `PrintASTConfiguration`.
*/
string toString() { result = "PrintASTConfiguration" }
/**
* Holds if the AST for `func` should be printed. By default, holds for all
* functions.
*/
predicate shouldPrintFunction(Function func) { any() }
}
private predicate shouldPrintFunction(Function func) {
exists(PrintASTConfiguration config | config.shouldPrintFunction(func))
}
bindingset[s]
private string escapeString(string s) {
result =
s.replaceAll("\\", "\\\\")
.replaceAll("\n", "\\n")
.replaceAll("\r", "\\r")
.replaceAll("\t", "\\t")
}
/**
* Due to extractor issues with ODR violations, a given AST may wind up with
* multiple locations. This predicate returns a single location - the one whose
* string representation comes first in lexicographical order.
*/
private Location getRepresentativeLocation(Locatable ast) {
result = min(Location loc | loc = ast.getLocation() | loc order by loc.toString())
}
/**
* Computes the sort keys to sort the given AST node by location. An AST without
* a location gets an empty file name and a zero line and column number.
*/
private predicate locationSortKeys(Locatable ast, string file, int line, int column) {
if exists(getRepresentativeLocation(ast))
then
exists(Location loc |
loc = getRepresentativeLocation(ast) and
file = loc.getFile().toString() and
line = loc.getStartLine() and
column = loc.getStartColumn()
)
else (
file = "" and
line = 0 and
column = 0
)
}
private Function getEnclosingFunction(Locatable ast) {
result = ast.(Expr).getEnclosingFunction()
or
result = ast.(Stmt).getEnclosingFunction()
or
result = ast.(Initializer).getExpr().getEnclosingFunction()
or
result = ast.(Parameter).getFunction()
or
result = ast
}
/**
* Most nodes are just a wrapper around `Locatable`, but we do synthesize new
* nodes for things like parameter lists and constructor init lists.
*/
private newtype TPrintASTNode =
TASTNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
// multiple parents due to extractor bug CPP-413.
stmt.getADeclarationEntry() = entry and
shouldPrintFunction(stmt.getEnclosingFunction())
} or
TParametersNode(Function func) { shouldPrintFunction(func) } or
TConstructorInitializersNode(Constructor ctor) {
ctor.hasEntryPoint() and
shouldPrintFunction(ctor)
} or
TDestructorDestructionsNode(Destructor dtor) {
dtor.hasEntryPoint() and
shouldPrintFunction(dtor)
}
/**
* A node in the output tree.
*/
class PrintASTNode extends TPrintASTNode {
/**
* Gets a textual representation of this node in the PrintAST output tree.
*/
abstract string toString();
/**
* Gets the child node at index `childIndex`. Child indices must be unique,
* but need not be contiguous.
*/
abstract PrintASTNode getChildInternal(int childIndex);
/**
* Gets the child node at index `childIndex`.
* Adds edges to fully converted expressions, that are not part of the
* regular parent/child relation traversal.
*/
final PrintASTNode getChild(int childIndex) {
// The exact value of `childIndex` doesn't matter, as long as we preserve the correct order.
result =
rank[childIndex](PrintASTNode child, int nonConvertedIndex, boolean isConverted |
childAndAccessorPredicate(child, _, nonConvertedIndex, isConverted)
|
// Unconverted children come first, then sort by original child index within each group.
child order by isConverted, nonConvertedIndex
)
}
/**
* Gets the node for the `.getFullyConverted()` version of the child originally at index
* `childIndex`, if that node has any conversions.
*/
private PrintASTNode getConvertedChild(int childIndex) {
exists(Expr expr |
expr = getChildInternal(childIndex).(ASTNode).getAST() and
expr.getFullyConverted() instanceof Conversion and
result.(ASTNode).getAST() = expr.getFullyConverted() and
not expr instanceof Conversion
)
}
/**
* Gets the child access predicate for the `.getFullyConverted()` version of the child originally
* at index `childIndex`, if that node has any conversions.
*/
private string getConvertedChildAccessorPredicate(int childIndex) {
exists(getConvertedChild(childIndex)) and
result = getChildAccessorPredicateInternal(childIndex) + ".getFullyConverted()"
}
/**
* Holds if this node should be printed in the output. By default, all nodes
* within a function are printed, but the query can override
* `PrintASTConfiguration.shouldPrintFunction` to filter the output.
*/
final predicate shouldPrint() { shouldPrintFunction(getEnclosingFunction()) }
/**
* Gets the children of this node.
*/
final PrintASTNode getAChild() { result = getChild(_) }
/**
* Gets the parent of this node, if any.
*/
final PrintASTNode getParent() { result.getAChild() = this }
/**
* Gets the location of this node in the source code.
*/
abstract Location getLocation();
/**
* Gets the value of the property of this node, where the name of the property
* is `key`.
*/
string getProperty(string key) {
key = "semmle.label" and
result = toString()
}
/**
* Holds if there is a child node `child` for original child index `nonConvertedIndex` with
* predicate name `childPredicate`. If the original child at that index has any conversions, there
* will be two result tuples for this predicate: one with the original child and predicate, with
* `isConverted = false`, and the other with the `.getFullyConverted()` version of the child and
* predicate, with `isConverted = true`. For a child without any conversions, there will be only
* one result tuple, with `isConverted = false`.
*/
private predicate childAndAccessorPredicate(
PrintASTNode child, string childPredicate, int nonConvertedIndex, boolean isConverted
) {
child = getChildInternal(nonConvertedIndex) and
childPredicate = getChildAccessorPredicateInternal(nonConvertedIndex) and
isConverted = false
or
child = getConvertedChild(nonConvertedIndex) and
childPredicate = getConvertedChildAccessorPredicate(nonConvertedIndex) and
isConverted = true
}
/**
* Gets the QL predicate that can be used to access the child at `childIndex`.
* May not always return a QL predicate, see for example `FunctionNode`.
*/
final string getChildAccessorPredicate(int childIndex) {
// The exact value of `childIndex` doesn't matter, as long as we preserve the correct order.
result =
rank[childIndex](string childPredicate, int nonConvertedIndex, boolean isConverted |
childAndAccessorPredicate(_, childPredicate, nonConvertedIndex, isConverted)
|
// Unconverted children come first, then sort by original child index within each group.
childPredicate order by isConverted, nonConvertedIndex
)
}
/**
* Gets the QL predicate that can be used to access the child at `childIndex`.
* INTERNAL DO NOT USE: Does not contain accessors for the synthesized nodes for conversions.
*/
abstract string getChildAccessorPredicateInternal(int childIndex);
/**
* Gets the `Function` that contains this node.
*/
private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() }
}
/**
* Class that restricts the elements that we compute `qlClass` for.
*/
private class PrintableElement extends Element {
PrintableElement() {
exists(TASTNode(this))
or
exists(TDeclarationEntryNode(_, this))
or
this instanceof Type
}
pragma[noinline]
string getAPrimaryQlClass0() { result = getAPrimaryQlClass() }
}
/**
* Retrieves the canonical QL class(es) for entity `el`
*/
private string qlClass(PrintableElement el) {
result = "[" + concat(el.getAPrimaryQlClass0(), ",") + "] "
// Alternative implementation -- do not delete. It is useful for QL class discovery.
//result = "["+ concat(el.getAQlClass(), ",") + "] "
}
/**
* A node representing an AST node.
*/
abstract class BaseASTNode extends PrintASTNode {
Locatable ast;
override string toString() { result = qlClass(ast) + ast.toString() }
final override Location getLocation() { result = getRepresentativeLocation(ast) }
/**
* Gets the AST represented by this node.
*/
final Locatable getAST() { result = ast }
}
/**
* A node representing an AST node other than a `DeclarationEntry`.
*/
abstract class ASTNode extends BaseASTNode, TASTNode {
ASTNode() { this = TASTNode(ast) }
}
/**
* A node representing an `Expr`.
*/
class ExprNode extends ASTNode {
Expr expr;
ExprNode() { expr = ast }
override ASTNode getChildInternal(int childIndex) { result.getAST() = expr.getChild(childIndex) }
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "Value" and
result = qlClass(expr) + getValue()
or
key = "Type" and
result = qlClass(expr.getType()) + expr.getType().toString()
or
key = "ValueCategory" and
result = expr.getValueCategoryString()
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST())
}
/**
* Gets the value of this expression, if it is a constant.
*/
string getValue() { result = expr.getValue() }
}
/**
* A node representing a `StringLiteral`.
*/
class StringLiteralNode extends ExprNode {
StringLiteralNode() { expr instanceof StringLiteral }
override string toString() { result = escapeString(expr.getValue()) }
override string getValue() { result = "\"" + escapeString(expr.getValue()) + "\"" }
}
/**
* A node representing a `Conversion`.
*/
class ConversionNode extends ExprNode {
Conversion conv;
ConversionNode() { conv = expr }
override ASTNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = conv.getExpr() and
conv.getExpr() instanceof Conversion
}
}
/**
* A node representing a `Cast`.
*/
class CastNode extends ConversionNode {
Cast cast;
CastNode() { cast = conv }
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "Conversion" and
result = "[" + qlConversion(cast) + "] " + cast.getSemanticConversionString()
}
}
/**
* A node representing a `StmtExpr`.
*/
class StmtExprNode extends ExprNode {
override StmtExpr expr;
override ASTNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = expr.getStmt()
}
}
/**
* A node representing a `DeclarationEntry`.
*/
class DeclarationEntryNode extends BaseASTNode, TDeclarationEntryNode {
override DeclarationEntry ast;
DeclStmt declStmt;
DeclarationEntryNode() { this = TDeclarationEntryNode(declStmt, ast) }
override PrintASTNode getChildInternal(int childIndex) { none() }
override string getChildAccessorPredicateInternal(int childIndex) { none() }
override string getProperty(string key) {
result = BaseASTNode.super.getProperty(key)
or
key = "Type" and
result = qlClass(ast.getType()) + ast.getType().toString()
}
}
/**
* A node representing a `VariableDeclarationEntry`.
*/
class VariableDeclarationEntryNode extends DeclarationEntryNode {
override VariableDeclarationEntry ast;
override ASTNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = ast.getVariable().getInitializer()
}
override string getChildAccessorPredicateInternal(int childIndex) {
childIndex = 0 and
result = "getVariable().getInitializer()"
}
}
/**
* A node representing a `Stmt`.
*/
class StmtNode extends ASTNode {
Stmt stmt;
StmtNode() { stmt = ast }
override BaseASTNode getChildInternal(int childIndex) {
exists(Locatable child |
child = stmt.getChild(childIndex) and
(
result.getAST() = child.(Expr) or
result.getAST() = child.(Stmt)
)
)
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST())
}
}
/**
* A node representing a `DeclStmt`.
*/
class DeclStmtNode extends StmtNode {
DeclStmt declStmt;
DeclStmtNode() { declStmt = stmt }
override DeclarationEntryNode getChildInternal(int childIndex) {
exists(DeclarationEntry entry |
declStmt.getDeclarationEntry(childIndex) = entry and
result = TDeclarationEntryNode(declStmt, entry)
)
}
}
/**
* A node representing a `Parameter`.
*/
class ParameterNode extends ASTNode {
Parameter param;
ParameterNode() { param = ast }
final override PrintASTNode getChildInternal(int childIndex) { none() }
final override string getChildAccessorPredicateInternal(int childIndex) { none() }
final override string getProperty(string key) {
result = super.getProperty(key)
or
key = "Type" and
result = qlClass(param.getType()) + param.getType().toString()
}
}
/**
* A node representing an `Initializer`.
*/
class InitializerNode extends ASTNode {
Initializer init;
InitializerNode() { init = ast }
override ASTNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = init.getExpr()
}
override string getChildAccessorPredicateInternal(int childIndex) {
childIndex = 0 and
result = "getExpr()"
}
}
/**
* A node representing the parameters of a `Function`.
*/
class ParametersNode extends PrintASTNode, TParametersNode {
Function func;
ParametersNode() { this = TParametersNode(func) }
final override string toString() { result = "" }
final override Location getLocation() { result = getRepresentativeLocation(func) }
override ASTNode getChildInternal(int childIndex) {
result.getAST() = func.getParameter(childIndex)
}
override string getChildAccessorPredicateInternal(int childIndex) {
exists(getChildInternal(childIndex)) and
result = "getParameter(" + childIndex.toString() + ")"
}
/**
* Gets the `Function` for which this node represents the parameters.
*/
final Function getFunction() { result = func }
}
/**
* A node representing the initializer list of a `Constructor`.
*/
class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializersNode {
Constructor ctor;
ConstructorInitializersNode() { this = TConstructorInitializersNode(ctor) }
final override string toString() { result = "" }
final override Location getLocation() { result = getRepresentativeLocation(ctor) }
final override ASTNode getChildInternal(int childIndex) {
result.getAST() = ctor.getInitializer(childIndex)
}
final override string getChildAccessorPredicateInternal(int childIndex) {
exists(getChildInternal(childIndex)) and
result = "getInitializer(" + childIndex.toString() + ")"
}
/**
* Gets the `Constructor` for which this node represents the initializer list.
*/
final Constructor getConstructor() { result = ctor }
}
/**
* A node representing the destruction list of a `Destructor`.
*/
class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNode {
Destructor dtor;
DestructorDestructionsNode() { this = TDestructorDestructionsNode(dtor) }
final override string toString() { result = "" }
final override Location getLocation() { result = getRepresentativeLocation(dtor) }
final override ASTNode getChildInternal(int childIndex) {
result.getAST() = dtor.getDestruction(childIndex)
}
final override string getChildAccessorPredicateInternal(int childIndex) {
exists(getChildInternal(childIndex)) and
result = "getDestruction(" + childIndex.toString() + ")"
}
/**
* Gets the `Destructor` for which this node represents the destruction list.
*/
final Destructor getDestructor() { result = dtor }
}
/**
* A node representing a `Function`.
*/
class FunctionNode extends ASTNode {
Function func;
FunctionNode() { func = ast }
override string toString() { result = qlClass(func) + getIdentityString(func) }
override PrintASTNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(ParametersNode).getFunction() = func
or
childIndex = 1 and
result.(ConstructorInitializersNode).getConstructor() = func
or
childIndex = 2 and
result.(ASTNode).getAST() = func.getEntryPoint()
or
childIndex = 3 and
result.(DestructorDestructionsNode).getDestructor() = func
}
override string getChildAccessorPredicateInternal(int childIndex) {
childIndex = 0 and result = "<params>"
or
childIndex = 1 and result = "<initializations>"
or
childIndex = 2 and result = "getEntryPoint()"
or
childIndex = 3 and result = "<destructions>"
}
private int getOrder() {
this =
rank[result](FunctionNode node, Function function, string file, int line, int column |
node.getAST() = function and
locationSortKeys(function, file, line, column)
|
node order by file, line, column, getIdentityString(function)
)
}
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = getOrder().toString()
}
/**
* Gets the `Function` this node represents.
*/
final Function getFunction() { result = func }
}
private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
shouldPrintFunction(getEnclosingFunction(parent)) and
(
exists(Stmt s | s = parent |
namedStmtChildPredicates(s, child, result)
or
not namedStmtChildPredicates(s, child, _) and
exists(int n | s.getChild(n) = child and result = "getChild(" + n + ")")
)
or
exists(Expr expr | expr = parent |
namedExprChildPredicates(expr, child, result)
or
not namedExprChildPredicates(expr, child, _) and
exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")")
)
)
}
private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) {
shouldPrintFunction(getEnclosingFunction(s)) and
(
exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")")
or
s.(ComputedGotoStmt).getExpr() = e and pred = "getExpr()"
or
s.(ConstexprIfStmt).getCondition() = e and pred = "getCondition()"
or
s.(ConstexprIfStmt).getThen() = e and pred = "getThen()"
or
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
or
s.(IfStmt).getCondition() = e and pred = "getCondition()"
or
s.(IfStmt).getThen() = e and pred = "getThen()"
or
s.(IfStmt).getElse() = e and pred = "getElse()"
or
s.(SwitchStmt).getExpr() = e and pred = "getExpr()"
or
s.(SwitchStmt).getStmt() = e and pred = "getStmt()"
or
s.(DoStmt).getCondition() = e and pred = "getCondition()"
or
s.(DoStmt).getStmt() = e and pred = "getStmt()"
or
s.(ForStmt).getInitialization() = e and pred = "getInitialization()"
or
s.(ForStmt).getCondition() = e and pred = "getCondition()"
or
s.(ForStmt).getUpdate() = e and pred = "getUpdate()"
or
s.(ForStmt).getStmt() = e and pred = "getStmt()"
or
s.(RangeBasedForStmt).getChild(0) = e and pred = "getChild(0)"
or
s.(RangeBasedForStmt).getBeginEndDeclaration() = e and pred = "getBeginEndDeclaration()"
or
s.(RangeBasedForStmt).getCondition() = e and pred = "getCondition()"
or
s.(RangeBasedForStmt).getUpdate() = e and pred = "getUpdate()"
or
s.(RangeBasedForStmt).getChild(4) = e and pred = "getChild(4)"
or
s.(RangeBasedForStmt).getStmt() = e and pred = "getStmt()"
or
s.(WhileStmt).getCondition() = e and pred = "getCondition()"
or
s.(WhileStmt).getStmt() = e and pred = "getStmt()"
or
exists(int n |
s.(DeclStmt).getDeclarationEntry(n) = e and pred = "getDeclarationEntry(" + n.toString() + ")"
)
or
// EmptyStmt does not have children
s.(ExprStmt).getExpr() = e and pred = "getExpr()"
or
s.(Handler).getBlock() = e and pred = "getBlock()"
or
s.(JumpStmt).getTarget() = e and pred = "getTarget()"
or
s.(MicrosoftTryStmt).getStmt() = e and pred = "getStmt()"
or
s.(MicrosoftTryExceptStmt).getCondition() = e and pred = "getCondition()"
or
s.(MicrosoftTryExceptStmt).getExcept() = e and pred = "getExcept()"
or
s.(MicrosoftTryFinallyStmt).getFinally() = e and pred = "getFinally()"
or
s.(ReturnStmt).getExpr() = e and pred = "getExpr()"
or
s.(SwitchCase).getExpr() = e and pred = "getExpr()"
or
s.(SwitchCase).getEndExpr() = e and pred = "getEndExpr()"
or
s.(TryStmt).getStmt() = e and pred = "getStmt()"
or
s.(VlaDimensionStmt).getDimensionExpr() = e and pred = "getDimensionExpr()"
)
}
private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) {
shouldPrintFunction(expr.getEnclosingFunction()) and
(
expr.(Access).getTarget() = ele and pred = "getTarget()"
or
expr.(VariableAccess).getQualifier() = ele and pred = "getQualifier()"
or
exists(Field f |
expr.(ClassAggregateLiteral).getFieldExpr(f) = ele and
pred = "getFieldExpr(" + f.toString() + ")"
)
or
exists(int n |
expr.(ArrayOrVectorAggregateLiteral).getElementExpr(n) = ele and
pred = "getElementExpr(" + n.toString() + ")"
)
or
expr.(AlignofExprOperator).getExprOperand() = ele and pred = "getExprOperand()"
or
expr.(ArrayExpr).getArrayBase() = ele and pred = "getArrayBase()"
or
expr.(ArrayExpr).getArrayOffset() = ele and pred = "getArrayOffset()"
or
expr.(AssumeExpr).getOperand() = ele and pred = "getOperand()"
or
expr.(BuiltInComplexOperation).getRealOperand() = ele and pred = "getRealOperand()"
or
expr.(BuiltInComplexOperation).getImaginaryOperand() = ele and pred = "getImaginaryOperand()"
or
expr.(BuiltInVarArg).getVAList() = ele and pred = "getVAList()"
or
expr.(BuiltInVarArgCopy).getDestinationVAList() = ele and pred = "getDestinationVAList()"
or
expr.(BuiltInVarArgCopy).getSourceVAList() = ele and pred = "getSourceVAList()"
or
expr.(BuiltInVarArgsEnd).getVAList() = ele and pred = "getVAList()"
or
expr.(BuiltInVarArgsStart).getVAList() = ele and pred = "getVAList()"
or
expr.(BuiltInVarArgsStart).getLastNamedParameter() = ele and pred = "getLastNamedParameter()"
or
expr.(Call).getQualifier() = ele and pred = "getQualifier()"
or
exists(int n | expr.(Call).getArgument(n) = ele and pred = "getArgument(" + n.toString() + ")")
or
expr.(ExprCall).getExpr() = ele and pred = "getExpr()"
or
expr.(OverloadedArrayExpr).getArrayBase() = ele and pred = "getArrayBase()"
or
expr.(OverloadedArrayExpr).getArrayOffset() = ele and pred = "getArrayOffset()"
or
expr.(OverloadedPointerDereferenceExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(CommaExpr).getLeftOperand() = ele and pred = "getLeftOperand()"
or
expr.(CommaExpr).getRightOperand() = ele and pred = "getRightOperand()"
or
expr.(ConditionDeclExpr).getVariableAccess() = ele and pred = "getVariableAccess()"
or
expr.(ConstructorFieldInit).getExpr() = ele and pred = "getExpr()"
or
expr.(Conversion).getExpr() = ele and pred = "getExpr()"
or
expr.(DeleteArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or
expr.(DeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
or
expr.(DeleteArrayExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(DeleteExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or
expr.(DeleteExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
or
expr.(DeleteExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()"
or
expr.(FoldExpr).getInitExpr() = ele and pred = "getInitExpr()"
or
expr.(FoldExpr).getPackExpr() = ele and pred = "getPackExpr()"
or
expr.(LambdaExpression).getInitializer() = ele and pred = "getInitializer()"
or
expr.(NewOrNewArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or
expr.(NewOrNewArrayExpr).getAlignmentArgument() = ele and pred = "getAlignmentArgument()"
or
expr.(NewArrayExpr).getInitializer() = ele and pred = "getInitializer()"
or
expr.(NewArrayExpr).getExtent() = ele and pred = "getExtent()"
or
expr.(NewExpr).getInitializer() = ele and pred = "getInitializer()"
or
expr.(NoExceptExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(Assignment).getLValue() = ele and pred = "getLValue()"
or
expr.(Assignment).getRValue() = ele and pred = "getRValue()"
or
not expr instanceof RelationalOperation and
expr.(BinaryOperation).getLeftOperand() = ele and
pred = "getLeftOperand()"
or
not expr instanceof RelationalOperation and
expr.(BinaryOperation).getRightOperand() = ele and
pred = "getRightOperand()"
or
expr.(RelationalOperation).getGreaterOperand() = ele and pred = "getGreaterOperand()"
or
expr.(RelationalOperation).getLesserOperand() = ele and pred = "getLesserOperand()"
or
expr.(ConditionalExpr).getCondition() = ele and pred = "getCondition()"
or
// If ConditionalExpr is in two-operand form, getThen() = getCondition() holds
not expr.(ConditionalExpr).isTwoOperand() and
expr.(ConditionalExpr).getThen() = ele and
pred = "getThen()"
or
expr.(ConditionalExpr).getElse() = ele and pred = "getElse()"
or
expr.(UnaryOperation).getOperand() = ele and pred = "getOperand()"
or
expr.(SizeofExprOperator).getExprOperand() = ele and pred = "getExprOperand()"
or
expr.(StmtExpr).getStmt() = ele and pred = "getStmt()"
or
expr.(ThrowExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(TypeidOperator).getExpr() = ele and pred = "getExpr()"
)
}
/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
query predicate nodes(PrintASTNode node, string key, string value) {
node.shouldPrint() and
value = node.getProperty(key)
}
/**
* Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the
* given `value`.
*/
query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) {
exists(int childIndex |
source.shouldPrint() and
target.shouldPrint() and
target = source.getChild(childIndex) and
(
key = "semmle.label" and value = source.getChildAccessorPredicate(childIndex)
or
key = "semmle.order" and value = childIndex.toString()
)
)
}
/** Holds if property `key` of the graph has the given `value`. */
query predicate graphProperties(string key, string value) {
key = "semmle.graphKind" and value = "tree"
}

View File

@@ -0,0 +1,318 @@
/**
* Provides classes for modeling specifiers and attributes.
*/
import semmle.code.cpp.Element
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ specifier: `friend`, `auto`, `register`, `static`, `extern`,
* `mutable`, `inline`, `virtual`, or `explicit`.
*/
class Specifier extends Element, @specifier {
/** Gets a dummy location for the specifier. */
override Location getLocation() {
suppressUnusedThis(this) and
result instanceof UnknownDefaultLocation
}
override string getAPrimaryQlClass() { result = "Specifier" }
/** Gets the name of this specifier. */
string getName() { specifiers(underlyingElement(this), result) }
/** Holds if the name of this specifier is `name`. */
predicate hasName(string name) { name = this.getName() }
override string toString() { result = this.getName() }
}
/**
* A C/C++ function specifier: `inline`, `virtual`, or `explicit`.
*/
class FunctionSpecifier extends Specifier {
FunctionSpecifier() {
this.hasName("inline") or
this.hasName("virtual") or
this.hasName("explicit")
}
override string getAPrimaryQlClass() { result = "FunctionSpecifier" }
}
/**
* A C/C++ storage class specifier: `auto`, `register`, `static`, `extern`,
* or `mutable".
*/
class StorageClassSpecifier extends Specifier {
StorageClassSpecifier() {
this.hasName("auto") or
this.hasName("register") or
this.hasName("static") or
this.hasName("extern") or
this.hasName("mutable")
}
override string getAPrimaryQlClass() { result = "StorageClassSpecifier" }
}
/**
* A C++ access specifier: `public`, `protected`, or `private`.
*/
class AccessSpecifier extends Specifier {
AccessSpecifier() {
this.hasName("public") or
this.hasName("protected") or
this.hasName("private")
}
/**
* Gets the visibility of a field with access specifier `this` if it is
* directly inherited with access specifier `baseAccess`. For example:
*
* ```
* class A { protected int f; };
* class B : private A {};
* ```
*
* In this example, `this` is `protected`, `baseAccess` is `private`, and
* `result` is `private` because the visibility of field `f` in class `B`
* is `private`.
*
* This method encodes the rules of N4140 11.2/1, tabulated here:
*
* ```
* `this` | `baseAccess` | `result`
* (access in base) | (base class specifier) | (access in derived)
* ----------------------------------------------------------
* private | private | N/A
* private | protected | N/A
* private | public | N/A
* protected | private | private
* protected | protected | protected
* protected | public | protected
* public | private | private
* public | protected | protected
* public | public | public
* ```
*/
AccessSpecifier accessInDirectDerived(AccessSpecifier baseAccess) {
this.getName() != "private" and
(
// Alphabetically, "private" < "protected" < "public". This disjunction
// encodes that `result` is the minimum access of `this` and
// `baseAccess`.
baseAccess.getName() < this.getName() and result = baseAccess
or
baseAccess.getName() >= this.getName() and result = this
)
}
override string getAPrimaryQlClass() { result = "AccessSpecifier" }
}
/**
* An attribute introduced by GNU's `__attribute__((name))` syntax,
* Microsoft's `__declspec(name)` syntax, Microsoft's `[name]` syntax, the
* C++11 standard `[[name]]` syntax, or the C++11 `alignas` syntax.
*/
class Attribute extends Element, @attribute {
/**
* Gets the name of this attribute.
*
* As examples, this is "noreturn" for `__attribute__((__noreturn__))`,
* "fallthrough" for `[[clang::fallthrough]]`, and "dllimport" for
* `__declspec(dllimport)`.
*
* Note that the name does not include the namespace. For example, the
* name of `[[clang::fallthrough]]` is "fallthrough".
*/
string getName() { attributes(underlyingElement(this), _, result, _, _) }
override Location getLocation() { attributes(underlyingElement(this), _, _, _, result) }
/** Holds if the name of this attribute is `name`. */
predicate hasName(string name) { name = this.getName() }
override string toString() { result = this.getName() }
/** Gets the `i`th argument of the attribute. */
AttributeArgument getArgument(int i) { result.getAttribute() = this and result.getIndex() = i }
/** Gets an argument of the attribute. */
AttributeArgument getAnArgument() { result = getArgument(_) }
}
/**
* An attribute introduced by GNU's `__attribute__((name))` syntax, for
* example: `__attribute__((__noreturn__))`.
*/
class GnuAttribute extends Attribute, @gnuattribute { }
/**
* An attribute introduced by the C++11 standard `[[name]]` syntax, for
* example: `[[clang::fallthrough]]`.
*/
class StdAttribute extends Attribute, @stdattribute {
/**
* Gets the namespace of this attribute.
*
* As examples, this is "" for `[[carries_dependency]]`, and "clang" for
* `[[clang::fallthrough]]`.
*/
string getNamespace() { attributes(underlyingElement(this), _, _, result, _) }
/**
* Holds if this attribute has the given namespace and name.
*/
predicate hasQualifiedName(string namespace, string name) {
namespace = getNamespace() and hasName(name)
}
}
/**
* An attribute introduced by Microsoft's `__declspec(name)` syntax. For
* example the attribute on the following declaration:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class Declspec extends Attribute, @declspec { }
/**
* An attribute introduced by Microsoft's "[name]" syntax, for example "[SA_Pre(Deref=1,Access=SA_Read)]".
*/
class MicrosoftAttribute extends Attribute, @msattribute {
AttributeArgument getNamedArgument(string name) {
result = getAnArgument() and result.getName() = name
}
}
/**
* A C++11 `alignas` construct. For example the attribute in the following
* code:
* ```
* struct alignas(16) MyStruct {
* int x;
* };
* ```
* Though it doesn't use the attribute syntax, `alignas(...)` is presented
* as an `Attribute` for consistency with the `[[align(...)]]` attribute.
*/
class AlignAs extends Attribute, @alignas {
override string toString() { result = "alignas(...)" }
}
/**
* A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))`
* that declares a function to accept a `printf` style format string. For example the attribute
* on the following declaration:
* ```
* int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
* ```
*/
class FormatAttribute extends GnuAttribute {
FormatAttribute() { getName() = "format" }
/**
* Gets the archetype of this format attribute, for example
* `"printf"`.
*/
string getArchetype() { result = getArgument(0).getValueText() }
/**
* Gets the index in (1-based) format attribute notation associated
* with the first argument of the function.
*/
private int firstArgumentNumber() {
if exists(MemberFunction f | f.getAnAttribute() = this and not f.isStatic())
then
// 1 is `this`, so the first parameter is 2
result = 2
else result = 1
}
/**
* Gets the (0-based) index of the format string,
* according to this attribute.
*/
int getFormatIndex() { result = getArgument(1).getValueInt() - firstArgumentNumber() }
/**
* Gets the (0-based) index of the first format argument (if any),
* according to this attribute.
*/
int getFirstFormatArgIndex() {
exists(int val |
val = getArgument(2).getValueInt() and
result = val - firstArgumentNumber() and
not val = 0 // indicates a `vprintf` style format function with arguments not directly available.
)
}
override string getAPrimaryQlClass() { result = "FormatAttribute" }
}
/**
* An argument to an `Attribute`. For example the argument "dllimport" on the
* attribute in the following code:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class AttributeArgument extends Element, @attribute_arg {
/**
* Gets the name of this argument, if it is a named argument. Named
* arguments are a Microsoft feature, so only a `MicrosoftAttribute` can
* have a named argument.
*/
string getName() { attribute_arg_name(underlyingElement(this), result) }
/**
* Gets the text for the value of this argument, if its value is
* a string or a number.
*/
string getValueText() { attribute_arg_value(underlyingElement(this), result) }
/**
* Gets the value of this argument, if its value is integral.
*/
int getValueInt() { result = getValueText().toInt() }
/**
* Gets the value of this argument, if its value is a type.
*/
Type getValueType() { attribute_arg_type(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the attribute to which this is an argument.
*/
Attribute getAttribute() {
attribute_args(underlyingElement(this), _, unresolveElement(result), _, _)
}
/**
* Gets the zero-based index of this argument in the containing
* attribute's argument list.
*/
int getIndex() { attribute_args(underlyingElement(this), _, _, result, _) }
override Location getLocation() { attribute_args(underlyingElement(this), _, _, _, result) }
override string toString() {
if exists(@attribute_arg_empty self | self = underlyingElement(this))
then result = "empty argument"
else
exists(string prefix, string tail |
(if exists(getName()) then prefix = getName() + "=" else prefix = "") and
(
if exists(@attribute_arg_type self | self = underlyingElement(this))
then tail = getValueType().getName()
else tail = getValueText()
) and
result = prefix + tail
)
}
}
private predicate suppressUnusedThis(Specifier s) { any() }

View File

@@ -0,0 +1,75 @@
/**
* Provides classes for modeling `struct`s.
*/
import semmle.code.cpp.Type
import semmle.code.cpp.Class
/**
* A C/C++ structure or union. For example, the types `MyStruct` and `MyUnion`
* in:
* ```
* struct MyStruct {
* int x, y, z;
* };
*
* union MyUnion {
* int i;
* float f;
* };
* ```
*/
class Struct extends Class {
Struct() { usertypes(underlyingElement(this), _, 1) or usertypes(underlyingElement(this), _, 3) }
override string getAPrimaryQlClass() { result = "Struct" }
override string explain() { result = "struct " + this.getName() }
override predicate isDeeplyConstBelow() { any() } // No subparts
}
/**
* A C/C++ struct that is directly enclosed by a function. For example, the type
* `MyLocalStruct` in:
* ```
* void myFunction() {
* struct MyLocalStruct {
* int x, y, z;
* };
* }
* ```
*/
class LocalStruct extends Struct {
LocalStruct() { isLocal() }
override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" }
}
/**
* A C/C++ nested struct. See 11.12. For example, the type `MyNestedStruct` in:
* ```
* class MyClass {
* public:
* struct MyNestedStruct {
* int x, y, z;
* };
* };
* ```
*/
class NestedStruct extends Struct {
NestedStruct() { this.isMember() }
override string getAPrimaryQlClass() {
not this instanceof NestedUnion and result = "NestedStruct"
}
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
}

View File

@@ -0,0 +1,89 @@
/**
* Provides classes for identifying files that contain test cases. It is often
* desirable to exclude these files from analysis.
*/
import semmle.code.cpp.File
/**
* The `gtest/gtest.h` file.
*/
private class GoogleTestHeader extends File {
GoogleTestHeader() {
getBaseName() = "gtest.h" and
getParentContainer().getBaseName() = "gtest"
}
}
/**
* A test using the Google Test library.
*/
private class GoogleTest extends MacroInvocation {
GoogleTest() {
// invocation of a macro from Google Test.
this.getMacro().getFile() instanceof GoogleTestHeader
}
}
/**
* The `boost/test` directory.
*/
private class BoostTestFolder extends Folder {
BoostTestFolder() {
getBaseName() = "test" and
getParentContainer().getBaseName() = "boost"
}
}
/**
* A test using the Boost Test library.
*/
private class BoostTest extends MacroInvocation {
BoostTest() {
// invocation of a macro from Boost Test.
this.getMacro().getFile().getParentContainer+() instanceof BoostTestFolder
}
}
/**
* The `cppunit` directory.
*/
private class CppUnitFolder extends Folder {
CppUnitFolder() { getBaseName() = "cppunit" }
}
/**
* A class from the `cppunit` directory.
*/
private class CppUnitClass extends Class {
CppUnitClass() {
getFile().getParentContainer+() instanceof CppUnitFolder and
getNamespace().getParentNamespace*().getName() = "CppUnit"
}
}
/**
* A test using the CppUnit library.
*/
private class CppUnitTest extends Element {
CppUnitTest() {
// class with a base class from cppunit.
this.(Class).getABaseClass*() instanceof CppUnitClass and
// class itself is not a part of cppunit.
not this instanceof CppUnitClass
or
// any member function of a test is also test code
this.(Function).getDeclaringType() instanceof CppUnitTest
}
}
/**
* A file that contains one or more test cases.
*/
class TestFile extends File {
TestFile() {
exists(GoogleTest test | test.getFile() = this) or
exists(BoostTest test | test.getFile() = this) or
exists(CppUnitTest test | test.getFile() = this)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
/**
* Provides classes for modeling typedefs and type aliases.
*/
import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ typedef type. See 4.9.1. For example the types declared on each line of the following code:
* ```
* typedef int my_int;
* using my_int2 = int;
* ```
*/
class TypedefType extends UserType {
TypedefType() {
usertypes(underlyingElement(this), _, 5) or
usertypes(underlyingElement(this), _, 14)
}
/**
* Gets the base type of this typedef type.
*/
Type getBaseType() { typedefbase(underlyingElement(this), unresolveElement(result)) }
override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() }
override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() }
override int getSize() { result = this.getBaseType().getSize() }
override int getAlignment() { result = this.getBaseType().getAlignment() }
override int getPointerIndirectionLevel() {
result = this.getBaseType().getPointerIndirectionLevel()
}
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
override Specifier internal_getAnAdditionalSpecifier() {
result = this.getBaseType().getASpecifier()
}
override predicate involvesReference() { getBaseType().involvesReference() }
override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() }
override Type stripType() { result = getBaseType().stripType() }
}
/**
* A traditional C/C++ typedef type. See 4.9.1. For example the type declared in the following code:
* ```
* typedef int my_int;
* ```
*/
class CTypedefType extends TypedefType {
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
override string getAPrimaryQlClass() { result = "CTypedefType" }
override string explain() {
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
}
}
/**
* A using alias C++ typedef type. For example the type declared in the following code:
* ```
* using my_int2 = int;
* ```
*/
class UsingAliasTypedefType extends TypedefType {
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
override string getAPrimaryQlClass() { result = "UsingAliasTypedefType" }
override string explain() {
result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
}
}
/**
* A C++ `typedef` type that is directly enclosed by a function. For example the type declared inside the function `foo` in
* the following code:
* ```
* int foo(void) { typedef int local; }
* ```
*/
class LocalTypedefType extends TypedefType {
LocalTypedefType() { isLocal() }
override string getAPrimaryQlClass() { result = "LocalTypedefType" }
}
/**
* A C++ `typedef` type that is directly enclosed by a `class`, `struct` or `union`. For example the type declared inside
* the class `C` in the following code:
* ```
* class C { typedef int nested; };
* ```
*/
class NestedTypedefType extends TypedefType {
NestedTypedefType() { this.isMember() }
override string getAPrimaryQlClass() { result = "NestedTypedefType" }
/**
* DEPRECATED: use `.hasSpecifier("private")` instead.
*
* Holds if this member is private.
*/
deprecated predicate isPrivate() { this.hasSpecifier("private") }
/**
* DEPRECATED: `.hasSpecifier("protected")` instead.
*
* Holds if this member is protected.
*/
deprecated predicate isProtected() { this.hasSpecifier("protected") }
/**
* DEPRECATED: use `.hasSpecifier("public")` instead.
*
* Holds if this member is public.
*/
deprecated predicate isPublic() { this.hasSpecifier("public") }
}

View File

@@ -0,0 +1,70 @@
/**
* Provides classes for modeling `union`s.
*/
import semmle.code.cpp.Type
import semmle.code.cpp.Struct
/**
* A C/C++ union. See C.8.2. For example, the type `MyUnion` in:
* ```
* union MyUnion {
* int i;
* float f;
* };
* ```
*/
class Union extends Struct {
Union() { usertypes(underlyingElement(this), _, 3) }
override string getAPrimaryQlClass() { result = "Union" }
override string explain() { result = "union " + this.getName() }
override predicate isDeeplyConstBelow() { any() } // No subparts
}
/**
* A C/C++ union that is directly enclosed by a function. For example, the type
* `MyLocalUnion` in:
* ```
* void myFunction() {
* union MyLocalUnion {
* int i;
* float f;
* };
* }
* ```
*/
class LocalUnion extends Union {
LocalUnion() { isLocal() }
override string getAPrimaryQlClass() { result = "LocalUnion" }
}
/**
* A C/C++ nested union. For example, the type `MyNestedUnion` in:
* ```
* class MyClass {
* public:
* union MyNestedUnion {
* int i;
* float f;
* };
* };
* ```
*/
class NestedUnion extends Union {
NestedUnion() { this.isMember() }
override string getAPrimaryQlClass() { result = "NestedUnion" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
}

View File

@@ -0,0 +1,132 @@
/**
* Provides classes for modeling user-defined types such as classes, typedefs
* and enums.
*/
import semmle.code.cpp.Declaration
import semmle.code.cpp.Type
import semmle.code.cpp.Function
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ user-defined type. Examples include `class`, `struct`, `union`,
* `enum` and `typedef` types.
* ```
* enum e1 { val1, val2 } b;
* enum class e2: short { val3, val4 } c;
* typedef int my_int;
* class C { int a, b; };
* ```
*/
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
/**
* Gets the name of this type.
*/
override string getName() { usertypes(underlyingElement(this), result, _) }
override string getAPrimaryQlClass() { result = "UserType" }
/**
* Gets the simple name of this type, without any template parameters. For example
* if the name of the type is `"myType<int>"`, the simple name is just `"myType"`.
*/
string getSimpleName() { result = getName().regexpReplaceAll("<.*", "") }
override predicate hasName(string name) { usertypes(underlyingElement(this), name, _) }
/** Holds if this type is anonymous. */
predicate isAnonymous() { getName().matches("(unnamed%") }
override predicate hasSpecifier(string s) { Type.super.hasSpecifier(s) }
override Specifier getASpecifier() { result = Type.super.getASpecifier() }
override Location getLocation() {
if hasDefinition()
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
override TypeDeclarationEntry getADeclarationEntry() {
if type_decls(_, underlyingElement(this), _)
then type_decls(unresolveElement(result), underlyingElement(this), _)
else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry())
}
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
override TypeDeclarationEntry getDefinition() {
result = getADeclarationEntry() and
result.isDefinition()
}
override Location getDefinitionLocation() {
if exists(getDefinition())
then result = getDefinition().getLocation()
else
exists(Class t |
this.(Class).isConstructedFrom(t) and result = t.getDefinition().getLocation()
)
}
/**
* Gets the function that directly encloses this type (if any).
*/
Function getEnclosingFunction() {
enclosingfunction(underlyingElement(this), unresolveElement(result))
}
/**
* Holds if this is a local type (that is, a type that has a directly-enclosing
* function).
*/
predicate isLocal() { exists(getEnclosingFunction()) }
/*
* Dummy implementations of inherited methods. This class must not be
* made abstract, because it is important that it captures the @usertype
* type exactly - but this is not apparent from its subclasses
*/
/**
* Gets a child declaration within this user-defined type.
*/
Declaration getADeclaration() { none() }
override string explain() { result = this.getName() }
// further overridden in LocalClass
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
}
/**
* A particular definition or forward declaration of a C/C++ user-defined type.
* ```
* class C;
* typedef int ti;
* ```
*/
class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
override UserType getDeclaration() { result = getType() }
override string getName() { result = getType().getName() }
override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" }
/**
* The type which is being declared or defined.
*/
override Type getType() { type_decls(underlyingElement(this), unresolveElement(result), _) }
override Location getLocation() { type_decls(underlyingElement(this), _, result) }
override predicate isDefinition() { type_def(underlyingElement(this)) }
override string getASpecifier() { none() }
/**
* A top level type declaration entry is not declared within a function, function declaration,
* class or typedef.
*/
predicate isTopLevel() { type_decl_top(underlyingElement(this)) }
}

View File

@@ -0,0 +1,608 @@
/**
* Provides classes for modeling variables and their declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.exprs.Access
import semmle.code.cpp.Initializer
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ variable. For example, in the following code there are four
* variables, `a`, `b`, `c` and `d`:
* ```
* extern int a;
* int a;
*
* void myFunction(int b) {
* int c;
* }
*
* namespace N {
* extern int d;
* int d = 1;
* }
* ```
*
* For local variables, there is a one-to-one correspondence between
* `Variable` and `VariableDeclarationEntry`.
*
* For other types of variable, there is a one-to-many relationship between
* `Variable` and `VariableDeclarationEntry`. For example, a `Parameter`
* can have multiple declarations.
*/
class Variable extends Declaration, @variable {
override string getAPrimaryQlClass() { result = "Variable" }
/** Gets the initializer of this variable, if any. */
Initializer getInitializer() { result.getDeclaration() = this }
/** Holds if this variable has an initializer. */
predicate hasInitializer() { exists(this.getInitializer()) }
/** Gets an access to this variable. */
VariableAccess getAnAccess() { result.getTarget() = this }
/**
* Gets a specifier of this variable. This includes `extern`, `static`,
* `auto`, `private`, `protected`, `public`. Specifiers of the *type* of
* this variable, such as `const` and `volatile`, are instead accessed
* through `this.getType().getASpecifier()`.
*/
override Specifier getASpecifier() {
varspecifiers(underlyingElement(this), unresolveElement(result))
}
/** Gets an attribute of this variable. */
Attribute getAnAttribute() { varattributes(underlyingElement(this), unresolveElement(result)) }
/** Holds if this variable is `const`. */
predicate isConst() { this.getType().isConst() }
/** Holds if this variable is `volatile`. */
predicate isVolatile() { this.getType().isVolatile() }
/** Gets the name of this variable. */
override string getName() { none() }
/** Gets the type of this variable. */
Type getType() { none() }
/** Gets the type of this variable, after typedefs have been resolved. */
Type getUnderlyingType() { result = this.getType().getUnderlyingType() }
/**
* Gets the type of this variable, after specifiers have been deeply
* stripped and typedefs have been resolved.
*/
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
/**
* Gets the type of this variable prior to deduction caused by the C++11
* `auto` keyword.
*
* If the type of this variable was not declared with the C++11 `auto`
* keyword, then this predicate does not hold.
*
* If the type of this variable is completely `auto`, then `result` is an
* instance of `AutoType`. For example:
*
* `auto four = 4;`
*
* If the type of this variable is partially `auto`, then a descendant of
* `result` is an instance of `AutoType`. For example:
*
* `const auto& c = container;`
*/
Type getTypeWithAuto() { autoderivation(underlyingElement(this), unresolveElement(result)) }
/**
* Holds if the type of this variable is declared using the C++ `auto`
* keyword.
*/
predicate declaredUsingAutoType() { autoderivation(underlyingElement(this), _) }
override VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
override VariableDeclarationEntry getDefinition() {
result = getADeclarationEntry() and
result.isDefinition()
}
override Location getDefinitionLocation() { result = getDefinition().getLocation() }
override Location getLocation() {
if exists(getDefinition())
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
/**
* Gets an expression that is assigned to this variable somewhere in the
* program.
*/
Expr getAnAssignedValue() {
result = this.getInitializer().getExpr()
or
exists(ConstructorFieldInit cfi | cfi.getTarget() = this and result = cfi.getExpr())
or
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
or
exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
}
/**
* Gets an assignment expression that assigns to this variable.
* For example: `x=...` or `x+=...`.
*
* This does _not_ include the initialization of the variable. Use
* `Variable.getInitializer()` to get the variable's initializer,
* or use `Variable.getAnAssignedValue()` to get an expression that
* is the right-hand side of an assignment or an initialization of
* the varible.
*/
Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() }
/**
* Holds if this variable is `constexpr`.
*/
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
/**
* Holds if this variable is declared `constinit`.
*/
predicate isConstinit() { this.hasSpecifier("declared_constinit") }
/**
* Holds if this variable is `thread_local`.
*/
predicate isThreadLocal() { this.hasSpecifier("is_thread_local") }
/**
* Holds if this variable is constructed from `v` as a result
* of template instantiation. If so, it originates either from a template
* variable or from a variable nested in a template class.
*/
predicate isConstructedFrom(Variable v) {
variable_instantiation(underlyingElement(this), unresolveElement(v))
}
/**
* Holds if this is a compiler-generated variable. For example, a
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
* typically has three compiler-generated variables, named `__range`,
* `__begin`, and `__end`:
*
* `for (char c : str) { ... }`
*/
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
}
/**
* A particular declaration or definition of a C/C++ variable. For example, in
* the following code there are six variable declaration entries - two each for
* `a` and `d`, and one each for `b` and `c`:
* ```
* extern int a;
* int a;
*
* void myFunction(int b) {
* int c;
* }
*
* namespace N {
* extern int d;
* int d = 1;
* }
* ```
*/
class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
override Variable getDeclaration() { result = getVariable() }
override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" }
/**
* Gets the variable which is being declared or defined.
*/
Variable getVariable() { var_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
/**
* Gets the name, if any, used for the variable at this declaration or
* definition.
*
* In most cases, this will be the name of the variable itself. The only
* case in which it can differ is in a parameter declaration entry,
* because the parameter may have a different name in the declaration
* than in the definition. For example:
*
* ```
* // Declaration. Parameter is named "x".
* int f(int x);
*
* // Definition. Parameter is named "y".
* int f(int y) { return y; }
* ```
*/
override string getName() { var_decls(underlyingElement(this), _, _, result, _) and result != "" }
/**
* Gets the type of the variable which is being declared or defined.
*/
override Type getType() { var_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
override Location getLocation() { var_decls(underlyingElement(this), _, _, _, result) }
/**
* Holds if this is a definition of a variable.
*
* This always holds for local variables and member variables, but need
* not hold for global variables. In the case of function parameters,
* this holds precisely when the enclosing `FunctionDeclarationEntry` is
* a definition.
*/
override predicate isDefinition() { var_def(underlyingElement(this)) }
override string getASpecifier() { var_decl_specifiers(underlyingElement(this), result) }
}
/**
* A parameter as described within a particular declaration or definition
* of a C/C++ function. For example the declaration of `a` in the following
* code:
* ```
* void myFunction(int a) {
* int b;
* }
* ```
*/
class ParameterDeclarationEntry extends VariableDeclarationEntry {
ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) }
override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" }
/**
* Gets the function declaration or definition which this parameter
* description is part of.
*/
FunctionDeclarationEntry getFunctionDeclarationEntry() {
param_decl_bind(underlyingElement(this), _, unresolveElement(result))
}
/**
* Gets the zero-based index of this parameter.
*/
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
private string getAnonymousParameterDescription() {
not exists(getName()) and
exists(string idx |
idx =
((getIndex() + 1).toString() + "th")
.replaceAll("1th", "1st")
.replaceAll("2th", "2nd")
.replaceAll("3th", "3rd")
.replaceAll("11st", "11th")
.replaceAll("12nd", "12th")
.replaceAll("13rd", "13th") and
if exists(getCanonicalName())
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
else result = "declaration of " + idx + " parameter"
)
}
override string toString() {
isDefinition() and
result = "definition of " + getName()
or
not isDefinition() and
if getName() = getCanonicalName()
then result = "declaration of " + getName()
else result = "declaration of " + getCanonicalName() + " as " + getName()
or
result = getAnonymousParameterDescription()
}
/**
* Gets the name of this `ParameterDeclarationEntry` including it's type.
*
* For example: "int p".
*/
string getTypedName() {
exists(string typeString, string nameString |
(if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and
(if exists(getName()) then nameString = getName() else nameString = "") and
if typeString != "" and nameString != ""
then result = typeString + " " + nameString
else result = typeString + nameString
)
}
}
/**
* A C/C++ variable with block scope [N4140 3.3.3]. In other words, a local
* variable or a function parameter. For example, the variables `a`, `b` and
* `c` in the following code:
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*
* See also `StackVariable`, which is the class of local-scope variables
* without statics and thread-locals.
*/
class LocalScopeVariable extends Variable, @localscopevariable {
/** Gets the function to which this variable belongs. */
Function getFunction() { none() } // overridden in subclasses
}
/**
* A C/C++ variable with _automatic storage duration_. In other words, a
* function parameter or a local variable that is not static or thread-local.
* For example, the variables `a` and `b` in the following code.
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*/
class StackVariable extends LocalScopeVariable {
StackVariable() {
not this.isStatic() and
not this.isThreadLocal()
}
}
/**
* A C/C++ local variable. In other words, any variable that has block
* scope [N4140 3.3.3], but is not a parameter of a `Function` or `CatchBlock`.
* For example the variables `b` and `c` in the following code:
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*
* Local variables can be static; use the `isStatic` member predicate to detect
* those.
*
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
*/
class LocalVariable extends LocalScopeVariable, @localvariable {
override string getAPrimaryQlClass() { result = "LocalVariable" }
override string getName() { localvariables(underlyingElement(this), _, result) }
override Type getType() { localvariables(underlyingElement(this), unresolveElement(result), _) }
override Function getFunction() {
exists(DeclStmt s | s.getADeclaration() = this and s.getEnclosingFunction() = result)
or
exists(ConditionDeclExpr e | e.getVariable() = this and e.getEnclosingFunction() = result)
}
}
/**
* A variable whose contents always have static storage duration. This can be a
* global variable, a namespace variable, a static local variable, or a static
* member variable.
*/
class StaticStorageDurationVariable extends Variable {
StaticStorageDurationVariable() {
this instanceof GlobalOrNamespaceVariable
or
this.(LocalVariable).isStatic()
or
this.(MemberVariable).isStatic()
}
/**
* Holds if the initializer for this variable is evaluated at runtime.
*/
predicate hasDynamicInitialization() {
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
}
}
/**
* Holds if `e` is an expression in a static initializer that must be evaluated
* at run time. This predicate computes "is non-const" instead of "is const"
* since computing "is const" for an aggregate literal with many children would
* either involve recursion through `forall` on those children or an iteration
* through the rank numbers of the children, both of which can be slow.
*/
private predicate runtimeExprInStaticInitializer(Expr e) {
inStaticInitializer(e) and
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
then runtimeExprInStaticInitializer(e.getAChild())
else not e.getFullyConverted().isConstant()
}
/**
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
* directly or below some top-level `AggregateLiteral`s.
*/
private predicate inStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
// The cast to `AggregateLiteral` ensures we only compute what'll later be
// needed by `runtimeExprInStaticInitializer`.
inStaticInitializer(e.getParent().(AggregateLiteral))
}
/**
* A C++ local variable declared as `static`.
*/
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
/**
* A C/C++ variable which has global scope or namespace scope. For example the
* variables `a` and `b` in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*/
class GlobalOrNamespaceVariable extends Variable, @globalvariable {
override string getName() { globalvariables(underlyingElement(this), _, result) }
override Type getType() { globalvariables(underlyingElement(this), unresolveElement(result), _) }
override Element getEnclosingElement() { none() }
}
/**
* A C/C++ variable which has namespace scope. For example the variable `b`
* in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*/
class NamespaceVariable extends GlobalOrNamespaceVariable {
NamespaceVariable() {
exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this)))
}
override string getAPrimaryQlClass() { result = "NamespaceVariable" }
}
/**
* A C/C++ variable which has global scope. For example the variable `a`
* in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*
* Note that variables declared in anonymous namespaces have namespace scope,
* even though they are accessed in the same manner as variables declared in
* the enclosing scope of said namespace (which may be the global scope).
*/
class GlobalVariable extends GlobalOrNamespaceVariable {
GlobalVariable() { not this instanceof NamespaceVariable }
override string getAPrimaryQlClass() { result = "GlobalVariable" }
}
/**
* A C structure member or C++ member variable. For example the member
* variables `m` and `s` in the following code:
* ```
* class MyClass {
* public:
* int m;
* static int s;
* };
* ```
*
* This includes static member variables in C++. To exclude static member
* variables, use `Field` instead of `MemberVariable`.
*/
class MemberVariable extends Variable, @membervariable {
MemberVariable() { this.isMember() }
override string getAPrimaryQlClass() { result = "MemberVariable" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
override string getName() { membervariables(underlyingElement(this), _, result) }
override Type getType() {
if strictcount(this.getAType()) = 1
then result = this.getAType()
else
// In rare situations a member variable may have multiple types in
// different translation units. In that case, we return the unspecified
// type.
result = this.getAType().getUnspecifiedType()
}
/** Holds if this member is mutable. */
predicate isMutable() { getADeclarationEntry().hasSpecifier("mutable") }
private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) }
}
/**
* A C/C++ function pointer variable.
*
* DEPRECATED: use `Variable.getType() instanceof FunctionPointerType` instead.
*/
deprecated class FunctionPointerVariable extends Variable {
FunctionPointerVariable() { this.getType() instanceof FunctionPointerType }
}
/**
* A C/C++ function pointer member variable.
*
* DEPRECATED: use `MemberVariable.getType() instanceof FunctionPointerType` instead.
*/
deprecated class FunctionPointerMemberVariable extends MemberVariable {
FunctionPointerMemberVariable() { this instanceof FunctionPointerVariable }
}
/**
* A C++14 variable template. For example, in the following code the variable
* template `v` defines a family of variables:
* ```
* template<class T>
* T v;
* ```
*/
class TemplateVariable extends Variable {
TemplateVariable() { is_variable_template(underlyingElement(this)) }
/**
* Gets an instantiation of this variable template.
*/
Variable getAnInstantiation() { result.isConstructedFrom(this) }
}
/**
* A non-static local variable or parameter that is not part of an
* uninstantiated template. Uninstantiated templates are purely syntax, and
* only on instantiation will they be complete with information about types,
* conversions, call targets, etc. For example in the following code, the
* variables `a` in `myFunction` and `b` in the instantiation
* `myTemplateFunction<int>`, but not `b` in the template
* `myTemplateFunction<T>`:
* ```
* void myFunction() {
* float a;
* }
*
* template<typename T>
* void myTemplateFunction() {
* T b;
* }
*
* ...
*
* myTemplateFunction<int>();
* ```
*/
class SemanticStackVariable extends StackVariable {
SemanticStackVariable() { not this.isFromUninstantiatedTemplate(_) }
}

View File

@@ -0,0 +1,343 @@
/**
* Provides classes and predicates for working with XML files and their content.
*/
import semmle.files.FileSystem
private class TXMLLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
/** An XML element that has a location. */
class XMLLocatable extends @xmllocatable, TXMLLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Gets the source location for this element.
*/
deprecated Location getALocation() { result = this.getLocation() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(File f, Location l | l = this.getLocation() |
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
filepath = f.getAbsolutePath()
)
}
/** Gets a textual representation of this element. */
string toString() { none() } // overridden in subclasses
}
/**
* An `XMLParent` is either an `XMLElement` or an `XMLFile`,
* both of which can contain other elements.
*/
class XMLParent extends @xmlparent {
XMLParent() {
// explicitly restrict `this` to be either an `XMLElement` or an `XMLFile`;
// the type `@xmlparent` currently also includes non-XML files
this instanceof @xmlelement or xmlEncoding(this, _)
}
/**
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)
*/
string getName() { none() } // overridden in subclasses
/** Gets the file to which this XML parent belongs. */
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
/** Gets the child element at a specified index of this XML parent. */
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
/** Gets a child element of this XML parent. */
XMLElement getAChild() { xmlElements(result, _, this, _, _) }
/** Gets a child element of this XML parent with the given `name`. */
XMLElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
/** Gets a comment that is a child of this XML parent. */
XMLComment getAComment() { xmlComments(result, _, this, _) }
/** Gets a character sequence that is a child of this XML parent. */
XMLCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
/** Gets the depth in the tree. (Overridden in XMLElement.) */
int getDepth() { result = 0 }
/** Gets the number of child XML elements of this XML parent. */
int getNumberOfChildren() { result = count(XMLElement e | xmlElements(e, _, this, _, _)) }
/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
/**
* DEPRECATED: Internal.
*
* Append the character sequences of this XML parent from left to right, separated by a space,
* up to a specified (zero-based) index.
*/
deprecated string charsSetUpTo(int n) {
n = 0 and xmlChars(_, result, this, 0, _, _)
or
n > 0 and
exists(string chars | xmlChars(_, chars, this, n, _, _) |
result = this.charsSetUpTo(n - 1) + " " + chars
)
}
/**
* Gets the result of appending all the character sequences of this XML parent from
* left to right, separated by a space.
*/
string allCharactersString() {
result =
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
}
/** Gets the text value contained in this XML parent. */
string getTextValue() { result = allCharactersString() }
/** Gets a printable representation of this XML parent. */
string toString() { result = this.getName() }
}
/** An XML file. */
class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
override string toString() { result = getName() }
/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }
/**
* DEPRECATED: Use `getAbsolutePath()` instead.
*
* Gets the path of this XML file.
*/
deprecated string getPath() { result = getAbsolutePath() }
/**
* DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead.
*
* Gets the path of the folder that contains this XML file.
*/
deprecated string getFolder() { result = getParentContainer().getAbsolutePath() }
/** Gets the encoding of this XML file. */
string getEncoding() { xmlEncoding(this, result) }
/** Gets the XML file itself. */
override XMLFile getFile() { result = this }
/** Gets a top-most element in an XML file. */
XMLElement getARootElement() { result = this.getAChild() }
/** Gets a DTD associated with this XML file. */
XMLDTD getADTD() { xmlDTDs(result, _, _, _, this) }
}
/**
* An XML document type definition (DTD).
*
* Example:
*
* ```
* <!ELEMENT person (firstName, lastName?)>
* <!ELEMENT firstName (#PCDATA)>
* <!ELEMENT lastName (#PCDATA)>
* ```
*/
class XMLDTD extends XMLLocatable, @xmldtd {
/** Gets the name of the root element of this DTD. */
string getRoot() { xmlDTDs(this, result, _, _, _) }
/** Gets the public ID of this DTD. */
string getPublicId() { xmlDTDs(this, _, result, _, _) }
/** Gets the system ID of this DTD. */
string getSystemId() { xmlDTDs(this, _, _, result, _) }
/** Holds if this DTD is public. */
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
/** Gets the parent of this DTD. */
XMLParent getParent() { xmlDTDs(this, _, _, _, result) }
override string toString() {
this.isPublic() and
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
or
not this.isPublic() and
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
}
}
/**
* An XML element in an XML file.
*
* Example:
*
* ```
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
* package="com.example.exampleapp" android:versionCode="1">
* </manifest>
* ```
*/
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = getName() }
/** Gets the name of this XML element. */
override string getName() { xmlElements(this, result, _, _, _) }
/** Gets the XML file in which this XML element occurs. */
override XMLFile getFile() { xmlElements(this, _, _, _, result) }
/** Gets the parent of this XML element. */
XMLParent getParent() { xmlElements(this, _, result, _, _) }
/** Gets the index of this XML element among its parent's children. */
int getIndex() { xmlElements(this, _, _, result, _) }
/** Holds if this XML element has a namespace. */
predicate hasNamespace() { xmlHasNs(this, _, _) }
/** Gets the namespace of this XML element, if any. */
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
/** Gets the index of this XML element among its parent's children. */
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
/** Gets the depth of this element within the XML file tree structure. */
override int getDepth() { result = this.getParent().getDepth() + 1 }
/** Gets an XML attribute of this XML element. */
XMLAttribute getAnAttribute() { result.getElement() = this }
/** Gets the attribute with the specified `name`, if any. */
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
/** Gets the value of the attribute with the specified `name`, if any. */
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
/** Gets a printable representation of this XML element. */
override string toString() { result = getName() }
}
/**
* An attribute that occurs inside an XML element.
*
* Examples:
*
* ```
* package="com.example.exampleapp"
* android:versionCode="1"
* ```
*/
class XMLAttribute extends @xmlattribute, XMLLocatable {
/** Gets the name of this attribute. */
string getName() { xmlAttrs(this, _, result, _, _, _) }
/** Gets the XML element to which this attribute belongs. */
XMLElement getElement() { xmlAttrs(this, result, _, _, _, _) }
/** Holds if this attribute has a namespace. */
predicate hasNamespace() { xmlHasNs(this, _, _) }
/** Gets the namespace of this attribute, if any. */
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
/** Gets the value of this attribute. */
string getValue() { xmlAttrs(this, _, _, result, _, _) }
/** Gets a printable representation of this XML attribute. */
override string toString() { result = this.getName() + "=" + this.getValue() }
}
/**
* A namespace used in an XML file.
*
* Example:
*
* ```
* xmlns:android="http://schemas.android.com/apk/res/android"
* ```
*/
class XMLNamespace extends XMLLocatable, @xmlnamespace {
/** Gets the prefix of this namespace. */
string getPrefix() { xmlNs(this, result, _, _) }
/** Gets the URI of this namespace. */
string getURI() { xmlNs(this, _, result, _) }
/** Holds if this namespace has no prefix. */
predicate isDefault() { this.getPrefix() = "" }
override string toString() {
this.isDefault() and result = this.getURI()
or
not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()
}
}
/**
* A comment in an XML file.
*
* Example:
*
* ```
* <!-- This is a comment. -->
* ```
*/
class XMLComment extends @xmlcomment, XMLLocatable {
/** Gets the text content of this XML comment. */
string getText() { xmlComments(this, result, _, _) }
/** Gets the parent of this XML comment. */
XMLParent getParent() { xmlComments(this, _, result, _) }
/** Gets a printable representation of this XML comment. */
override string toString() { result = this.getText() }
}
/**
* A sequence of characters that occurs between opening and
* closing tags of an XML element, excluding other elements.
*
* Example:
*
* ```
* <content>This is a sequence of characters.</content>
* ```
*/
class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets the content of this character sequence. */
string getCharacters() { xmlChars(this, result, _, _, _, _) }
/** Gets the parent of this character sequence. */
XMLParent getParent() { xmlChars(this, _, result, _, _, _) }
/** Holds if this character sequence is CDATA. */
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}

View File

@@ -0,0 +1,66 @@
import cpp
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.Deallocation
/**
* A library routine that allocates memory.
*
* DEPRECATED: Use the `AllocationFunction` class instead of this predicate.
*/
deprecated predicate allocationFunction(Function f) { f instanceof AllocationFunction }
/**
* A call to a library routine that allocates memory.
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
deprecated predicate allocationCall(FunctionCall fc) { fc instanceof AllocationExpr }
/**
* A library routine that frees memory.
*/
predicate freeFunction(Function f, int argNum) { argNum = f.(DeallocationFunction).getFreedArg() }
/**
* A call to a library routine that frees memory.
*
* DEPRECATED: Use `DeallocationExpr` instead (this also includes `delete` expressions).
*/
predicate freeCall(FunctionCall fc, Expr arg) { arg = fc.(DeallocationExpr).getFreedExpr() }
/**
* Is e some kind of allocation or deallocation (`new`, `alloc`, `realloc`, `delete`, `free` etc)?
*/
predicate isMemoryManagementExpr(Expr e) { isAllocationExpr(e) or e instanceof DeallocationExpr }
/**
* Is e an allocation from stdlib.h (`malloc`, `realloc` etc)?
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
deprecated predicate isStdLibAllocationExpr(Expr e) { allocationCall(e) }
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc)?
*/
predicate isAllocationExpr(Expr e) {
e.(FunctionCall) instanceof AllocationExpr
or
e = any(NewOrNewArrayExpr new | not exists(new.getPlacementPointer()))
}
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc) with a fixed size?
*
* DEPRECATED: Use `AllocationExpr.getSizeBytes()` instead.
*/
deprecated predicate isFixedSizeAllocationExpr(Expr allocExpr, int size) {
size = allocExpr.(AllocationExpr).getSizeBytes()
}
/**
* Is e some kind of deallocation (`delete`, `free`, `realloc` etc)?
*
* DEPRECATED: Use `DeallocationExpr` instead.
*/
deprecated predicate isDeallocationExpr(Expr e) { e instanceof DeallocationExpr }

View File

@@ -0,0 +1,60 @@
import cpp
/**
* An assertion, that is, a condition that is only false if there is a bug in
* the program. To add support for more, define additional subclasses. A
* typical subclass will extend extend either `MacroInvocation` (for assertion
* macros) or `FunctionCall` (for assertion functions).
*/
abstract class Assertion extends Locatable {
/** Gets the expression whose truth is being asserted. */
abstract Expr getAsserted();
}
/**
* A libc assert, as defined in assert.h. A macro with a head
* that matches the prefix "assert(", and expands to a conditional
* expression which may terminate the program.
*/
class LibcAssert extends MacroInvocation, Assertion {
LibcAssert() { this.getMacro().getHead().matches("assert(%") }
override Expr getAsserted() {
exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition())
}
}
/**
* A macro assert that expands to an if statement; the head is taken
* to be "Assert(x, y)", but further alternatives could be added.
*/
class MacroAssert extends MacroInvocation, Assertion {
MacroAssert() { this.getMacro().getHead() = "Assert(x, y)" }
override Expr getAsserted() {
exists(IfStmt i | this.getAGeneratedElement() = i | result = i.getCondition())
}
}
/**
* An assertion that is not completely disabled in release builds but returns a
* Boolean value to enable recovery from unexpected anomalous behavior. This
* style of assert is advocated by the _Power of 10_ rules and the _NASA JPL
* Coding Standard_.
*/
class RecoverableAssert extends MacroInvocation, Assertion {
RecoverableAssert() { this.getMacro().getHead().matches("c\\_assert(%") }
private Expr getAnAssertedExpr() {
result = this.getAGeneratedElement() and
not result.getLocation().getStartColumn() = this.getLocation().getStartColumn()
}
override Expr getAsserted() {
result = this.getAnAssertedExpr() and
not result.getParent() = this.getAnAssertedExpr() and
// Remove spurious "string literals" that arise when the macro
// uses #stringification
not result.(Literal).getUnspecifiedType().(ArrayType).getBaseType() instanceof CharType
}
}

View File

@@ -0,0 +1,124 @@
import cpp
import semmle.code.cpp.dataflow.DataFlow
/**
* Holds if `v` is a member variable of `c` that looks like it might be variable sized in practice. For
* example:
* ```
* struct myStruct { // c
* int amount;
* char data[1]; // v
* };
* ```
* This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. In addition,
* there must be at least one instance where a `c` pointer is allocated with additional space. For
* example, holds for `c` if it occurs as
* ```
* malloc(sizeof(c) + 100 * sizeof(char))
* ```
* but not if it only ever occurs as
* ```
* malloc(sizeof(c))
* ```
*/
predicate memberMayBeVarSize(Class c, MemberVariable v) {
exists(int i |
// `v` is the last field in `c`
i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and
v = c.getCanonicalMember(i) and
// v is an array of size at most 1
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
) and
(
exists(SizeofOperator so |
// `sizeof(c)` is taken
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
|
// arithmetic is performed on the result
so.getParent*() instanceof AddExpr
)
or
exists(AddressOfExpr aoe |
// `&(c.v)` is taken
aoe.getAddressable() = v
)
or
exists(BuiltInOperationBuiltInOffsetOf oo |
// `offsetof(c, v)` using a builtin
oo.getAChild().(VariableAccess).getTarget() = v
)
)
}
/**
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
*/
language[monotonicAggregates]
int getBufferSize(Expr bufferExpr, Element why) {
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
// buffer is a fixed size array
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
why = bufferVar and
not memberMayBeVarSize(_, bufferVar) and
not result = 0 // zero sized arrays are likely to have special usage, for example
or
// behaving a bit like a 'union' overlapping other fields.
// buffer is an initialized array
// e.g. int buffer[] = {1, 2, 3};
why = bufferVar.getInitializer().getExpr() and
(
why instanceof AggregateLiteral or
why instanceof StringLiteral
) and
result = why.(Expr).getType().(ArrayType).getSize() and
not exists(bufferVar.getUnspecifiedType().(ArrayType).getSize())
or
exists(Class parentClass, VariableAccess parentPtr |
// buffer is the parentPtr->bufferVar of a 'variable size struct'
memberMayBeVarSize(parentClass, bufferVar) and
why = bufferVar and
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize()
)
)
or
// buffer is a fixed size dynamic allocation
result = bufferExpr.(AllocationExpr).getSizeBytes() and
why = bufferExpr
or
exists(DataFlow::ExprNode bufferExprNode |
// dataflow (all sources must be the same size)
bufferExprNode = DataFlow::exprNode(bufferExpr) and
result =
unique(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
getBufferSize(def, _)
) and
// find reason
exists(Expr def | DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode) |
why = def or
exists(getBufferSize(def, why))
)
)
or
exists(Type bufferType |
// buffer is the address of a variable
why = bufferExpr.(AddressOfExpr).getAddressable() and
bufferType = why.(Variable).getType() and
result = bufferType.getSize() and
not bufferType instanceof ReferenceType and
not any(Union u).getAMemberVariable() = why
)
or
exists(Union bufferType |
// buffer is the address of a union member; in this case, we
// take the size of the union itself rather the union member, since
// it's usually OK to access that amount (e.g. clearing with memset).
why = bufferExpr.(AddressOfExpr).getAddressable() and
bufferType.getAMemberVariable() = why and
result = bufferType.getSize()
)
}

View File

@@ -0,0 +1,441 @@
import semmle.code.cpp.Type
/**
* The C/C++ `char*` type.
*/
class CharPointerType extends PointerType {
CharPointerType() { this.getBaseType() instanceof CharType }
override string getAPrimaryQlClass() { result = "CharPointerType" }
}
/**
* The C/C++ `int*` type.
*/
class IntPointerType extends PointerType {
IntPointerType() { this.getBaseType() instanceof IntType }
override string getAPrimaryQlClass() { result = "IntPointerType" }
}
/**
* The C/C++ `void*` type.
*/
class VoidPointerType extends PointerType {
VoidPointerType() { this.getBaseType() instanceof VoidType }
override string getAPrimaryQlClass() { result = "VoidPointerType" }
}
/**
* The C/C++ `size_t` type.
*/
class Size_t extends Type {
Size_t() {
this.getUnderlyingType() instanceof IntegralType and
this.hasName("size_t")
}
override string getAPrimaryQlClass() { result = "Size_t" }
}
/**
* The C/C++ `ssize_t` type.
*/
class Ssize_t extends Type {
Ssize_t() {
this.getUnderlyingType() instanceof IntegralType and
this.hasName("ssize_t")
}
override string getAPrimaryQlClass() { result = "Ssize_t" }
}
/**
* The C/C++ `ptrdiff_t` type.
*/
class Ptrdiff_t extends Type {
Ptrdiff_t() {
this.getUnderlyingType() instanceof IntegralType and
this.hasName("ptrdiff_t")
}
override string getAPrimaryQlClass() { result = "Ptrdiff_t" }
}
/**
* A parent class representing C/C++ a typedef'd `UserType` such as `int8_t`.
*/
abstract private class IntegralUnderlyingUserType extends UserType {
IntegralUnderlyingUserType() { this.getUnderlyingType() instanceof IntegralType }
}
abstract private class TFixedWidthIntegralType extends IntegralUnderlyingUserType { }
/**
* A C/C++ fixed-width numeric type, such as `int8_t`.
*/
class FixedWidthIntegralType extends TFixedWidthIntegralType {
FixedWidthIntegralType() { this instanceof TFixedWidthIntegralType }
}
abstract private class TMinimumWidthIntegralType extends IntegralUnderlyingUserType { }
/**
* A C/C++ minimum-width numeric type, such as `int_least8_t`.
*/
class MinimumWidthIntegralType extends TMinimumWidthIntegralType {
MinimumWidthIntegralType() { this instanceof TMinimumWidthIntegralType }
}
abstract private class TFastestMinimumWidthIntegralType extends IntegralUnderlyingUserType { }
/**
* A C/C++ minimum-width numeric type, representing the fastest integer type with a
* width of at least `N` such as `int_fast8_t`.
*/
class FastestMinimumWidthIntegralType extends TFastestMinimumWidthIntegralType {
FastestMinimumWidthIntegralType() { this instanceof TFastestMinimumWidthIntegralType }
}
abstract private class TMaximumWidthIntegralType extends IntegralUnderlyingUserType { }
/**
* A C/C++ maximum-width numeric type, either `intmax_t` or `uintmax_t`.
*/
class MaximumWidthIntegralType extends TMaximumWidthIntegralType {
MaximumWidthIntegralType() { this instanceof TMaximumWidthIntegralType }
}
/**
* An enum type based on a fixed-width integer type. For instance, `enum e: uint8_t = { a, b };`
*/
class FixedWidthEnumType extends UserType {
FixedWidthEnumType() { this.(Enum).getExplicitUnderlyingType() instanceof FixedWidthIntegralType }
}
/**
* The C/C++ `int8_t` type.
*/
class Int8_t extends TFixedWidthIntegralType {
Int8_t() { this.hasGlobalOrStdName("int8_t") }
override string getAPrimaryQlClass() { result = "Int8_t" }
}
/**
* The C/C++ `int16_t` type.
*/
class Int16_t extends TFixedWidthIntegralType {
Int16_t() { this.hasGlobalOrStdName("int16_t") }
override string getAPrimaryQlClass() { result = "Int16_t" }
}
/**
* The C/C++ `int32_t` type.
*/
class Int32_t extends TFixedWidthIntegralType {
Int32_t() { this.hasGlobalOrStdName("int32_t") }
override string getAPrimaryQlClass() { result = "Int32_t" }
}
/**
* The C/C++ `int64_t` type.
*/
class Int64_t extends TFixedWidthIntegralType {
Int64_t() { this.hasGlobalOrStdName("int64_t") }
override string getAPrimaryQlClass() { result = "Int64_t" }
}
/**
* The C/C++ `uint8_t` type.
*/
class UInt8_t extends TFixedWidthIntegralType {
UInt8_t() { this.hasGlobalOrStdName("uint8_t") }
override string getAPrimaryQlClass() { result = "UInt8_t" }
}
/**
* The C/C++ `uint16_t` type.
*/
class UInt16_t extends TFixedWidthIntegralType {
UInt16_t() { this.hasGlobalOrStdName("uint16_t") }
override string getAPrimaryQlClass() { result = "UInt16_t" }
}
/**
* The C/C++ `uint32_t` type.
*/
class UInt32_t extends TFixedWidthIntegralType {
UInt32_t() { this.hasGlobalOrStdName("uint32_t") }
override string getAPrimaryQlClass() { result = "UInt32_t" }
}
/**
* The C/C++ `uint64_t` type.
*/
class UInt64_t extends TFixedWidthIntegralType {
UInt64_t() { this.hasGlobalOrStdName("uint64_t") }
override string getAPrimaryQlClass() { result = "UInt64_t" }
}
/**
* The C/C++ `int_least8_t` type.
*/
class Int_least8_t extends TMinimumWidthIntegralType {
Int_least8_t() { this.hasGlobalOrStdName("int_least8_t") }
override string getAPrimaryQlClass() { result = "Int_least8_t" }
}
/**
* The C/C++ `int_least16_t` type.
*/
class Int_least16_t extends TMinimumWidthIntegralType {
Int_least16_t() { this.hasGlobalOrStdName("int_least16_t") }
override string getAPrimaryQlClass() { result = "Int_least16_t" }
}
/**
* The C/C++ `int_least32_t` type.
*/
class Int_least32_t extends TMinimumWidthIntegralType {
Int_least32_t() { this.hasGlobalOrStdName("int_least32_t") }
override string getAPrimaryQlClass() { result = "Int_least32_t" }
}
/**
* The C/C++ `int_least64_t` type.
*/
class Int_least64_t extends TMinimumWidthIntegralType {
Int_least64_t() { this.hasGlobalOrStdName("int_least64_t") }
override string getAPrimaryQlClass() { result = "Int_least64_t" }
}
/**
* The C/C++ `uint_least8_t` type.
*/
class UInt_least8_t extends TMinimumWidthIntegralType {
UInt_least8_t() { this.hasGlobalOrStdName("uint_least8_t") }
override string getAPrimaryQlClass() { result = "UInt_least8_t" }
}
/**
* The C/C++ `uint_least16_t` type.
*/
class UInt_least16_t extends TMinimumWidthIntegralType {
UInt_least16_t() { this.hasGlobalOrStdName("uint_least16_t") }
override string getAPrimaryQlClass() { result = "UInt_least16_t" }
}
/**
* The C/C++ `uint_least32_t` type.
*/
class UInt_least32_t extends TMinimumWidthIntegralType {
UInt_least32_t() { this.hasGlobalOrStdName("uint_least32_t") }
override string getAPrimaryQlClass() { result = "UInt_least32_t" }
}
/**
* The C/C++ `uint_least64_t` type.
*/
class UInt_least64_t extends TMinimumWidthIntegralType {
UInt_least64_t() { this.hasGlobalOrStdName("uint_least64_t") }
override string getAPrimaryQlClass() { result = "UInt_least64_t" }
}
/**
* The C/C++ `int_fast8_t` type.
*/
class Int_fast8_t extends TFastestMinimumWidthIntegralType {
Int_fast8_t() { this.hasGlobalOrStdName("int_fast8_t") }
override string getAPrimaryQlClass() { result = "Int_fast8_t" }
}
/**
* The C/C++ `int_fast16_t` type.
*/
class Int_fast16_t extends TFastestMinimumWidthIntegralType {
Int_fast16_t() { this.hasGlobalOrStdName("int_fast16_t") }
override string getAPrimaryQlClass() { result = "Int_fast16_t" }
}
/**
* The C/C++ `int_fast32_t` type.
*/
class Int_fast32_t extends TFastestMinimumWidthIntegralType {
Int_fast32_t() { this.hasGlobalOrStdName("int_fast32_t") }
override string getAPrimaryQlClass() { result = "Int_fast32_t" }
}
/**
* The C/C++ `int_fast64_t` type.
*/
class Int_fast64_t extends TFastestMinimumWidthIntegralType {
Int_fast64_t() { this.hasGlobalOrStdName("int_fast64_t") }
override string getAPrimaryQlClass() { result = "Int_fast64_t" }
}
/**
* The C/C++ `uint_fast8_t` type.
*/
class UInt_fast8_t extends TFastestMinimumWidthIntegralType {
UInt_fast8_t() { this.hasGlobalOrStdName("uint_fast8_t") }
override string getAPrimaryQlClass() { result = "UInt_fast8_t" }
}
/**
* The C/C++ `uint_fast16_t` type.
*/
class UInt_fast16_t extends TFastestMinimumWidthIntegralType {
UInt_fast16_t() { this.hasGlobalOrStdName("uint_fast16_t") }
override string getAPrimaryQlClass() { result = "UInt_fast16_t" }
}
/**
* The C/C++ `uint_fast32_t` type.
*/
class UInt_fast32_t extends TFastestMinimumWidthIntegralType {
UInt_fast32_t() { this.hasGlobalOrStdName("uint_fast32_t") }
override string getAPrimaryQlClass() { result = "UInt_fast32_t" }
}
/**
* The C/C++ `uint_fast64_t` type.
*/
class UInt_fast64_t extends TFastestMinimumWidthIntegralType {
UInt_fast64_t() { this.hasGlobalOrStdName("uint_fast64_t") }
override string getAPrimaryQlClass() { result = "UInt_fast64_t" }
}
/**
* The C/C++ `intmax_t` type.
*/
class Intmax_t extends TMaximumWidthIntegralType {
Intmax_t() { this.hasGlobalOrStdName("intmax_t") }
override string getAPrimaryQlClass() { result = "Intmax_t" }
}
/**
* The C/C++ `uintmax_t` type.
*/
class Uintmax_t extends TMaximumWidthIntegralType {
Uintmax_t() { this.hasGlobalOrStdName("uintmax_t") }
override string getAPrimaryQlClass() { result = "Uintmax_t" }
}
/**
* The C/C++ `wchar_t` type.
*
* Note that on some platforms `wchar_t` doesn't exist as a built-in
* type but a typedef is provided. This QL class includes both cases
* (see also `WideCharType`).
*/
class Wchar_t extends Type {
Wchar_t() {
this.getUnderlyingType() instanceof IntegralType and
this.hasName("wchar_t")
}
override string getAPrimaryQlClass() { result = "Wchar_t" }
}
/**
* The type that the Microsoft C/C++ `__int8` type specifier is a
* synonym for. Note that since `__int8` is not a distinct type,
* `MicrosoftInt8Type` corresponds to an existing `IntegralType` as
* well.
*
* This class is meaningless if a Microsoft compiler was not used.
*/
class MicrosoftInt8Type extends IntegralType {
MicrosoftInt8Type() {
this instanceof CharType and
not isExplicitlyUnsigned() and
not isExplicitlySigned()
}
}
/**
* The type that the Microsoft C/C++ `__int16` type specifier is a
* synonym for. Note that since `__int16` is not a distinct type,
* `MicrosoftInt16Type` corresponds to an existing `IntegralType` as
* well.
*
* This class is meaningless if a Microsoft compiler was not used.
*/
class MicrosoftInt16Type extends IntegralType {
MicrosoftInt16Type() {
this instanceof ShortType and
not isExplicitlyUnsigned() and
not isExplicitlySigned()
}
}
/**
* The type that the Microsoft C/C++ `__int32` type specifier is a
* synonym for. Note that since `__int32` is not a distinct type,
* `MicrosoftInt32Type` corresponds to an existing `IntegralType` as
* well.
*
* This class is meaningless if a Microsoft compiler was not used.
*/
class MicrosoftInt32Type extends IntegralType {
MicrosoftInt32Type() {
this instanceof IntType and
not isExplicitlyUnsigned() and
not isExplicitlySigned()
}
}
/**
* The type that the Microsoft C/C++ `__int64` type specifier is a
* synonym for. Note that since `__int64` is not a distinct type,
* `MicrosoftInt64Type` corresponds to an existing `IntegralType` as
* well.
*
* This class is meaningless if a Microsoft compiler was not used.
*/
class MicrosoftInt64Type extends IntegralType {
MicrosoftInt64Type() {
this instanceof LongLongType and
not isExplicitlyUnsigned() and
not isExplicitlySigned()
}
}
/**
* The `__builtin_va_list` type, used to provide variadic functionality.
*
* This is a complement to the `__builtin_va_start`, `__builtin_va_end`,
* `__builtin_va_copy` and `__builtin_va_arg` expressions.
*/
class BuiltInVarArgsList extends Type {
BuiltInVarArgsList() { this.hasName("__builtin_va_list") }
override string getAPrimaryQlClass() { result = "BuiltInVarArgsList" }
}

View File

@@ -0,0 +1,97 @@
/**
* Provides a library for helping working with a set of known data structures representing dates in C++.
*/
import cpp
/**
* A type that is used to represent time in a 'packed' form, such as an integer.
*/
class PackedTimeType extends Type {
PackedTimeType() {
this.getName() = "_FILETIME" or
this.(DerivedType).getBaseType*().getName() = "_FILETIME"
}
}
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
/**
* A type that is used to represent times and dates in an 'unpacked' form, that is,
* with separate fields for day, month, year etc.
*/
class UnpackedTimeType extends Type {
UnpackedTimeType() {
timeType(this.getName()) or
timeType(this.(DerivedType).getBaseType*().getName())
}
}
/**
* A `FieldAccess` that would represent an access to a field on a `struct`.
*/
abstract private class DateStructFieldAccess extends FieldAccess {
DateStructFieldAccess() {
exists(Field f, StructLikeClass struct |
f.getAnAccess() = this and
struct.getAField() = f
)
}
}
/**
* A `FieldAccess` where access is to a day of the month field of the `struct`.
*/
abstract class DayFieldAccess extends DateStructFieldAccess { }
/**
* A `FieldAccess` where access is to a month field of the `struct`.
*/
abstract class MonthFieldAccess extends DateStructFieldAccess { }
/**
* A `FieldAccess` where access is to a year field of the `struct`.
*/
abstract class YearFieldAccess extends DateStructFieldAccess { }
/**
* A `DayFieldAccess` for the `SYSTEMTIME` struct.
*/
class SystemTimeDayFieldAccess extends DayFieldAccess {
SystemTimeDayFieldAccess() { this.getTarget().getName() = "wDay" }
}
/**
* A `MonthFieldAccess` for the `SYSTEMTIME` struct.
*/
class SystemTimeMonthFieldAccess extends MonthFieldAccess {
SystemTimeMonthFieldAccess() { this.getTarget().getName() = "wMonth" }
}
/**
* A `YearFieldAccess` for the `SYSTEMTIME` struct.
*/
class StructSystemTimeYearFieldAccess extends YearFieldAccess {
StructSystemTimeYearFieldAccess() { this.getTarget().getName() = "wYear" }
}
/**
* A `DayFieldAccess` for `struct tm`.
*/
class StructTmDayFieldAccess extends DayFieldAccess {
StructTmDayFieldAccess() { this.getTarget().getName() = "tm_mday" }
}
/**
* A `MonthFieldAccess` for `struct tm`.
*/
class StructTmMonthFieldAccess extends MonthFieldAccess {
StructTmMonthFieldAccess() { this.getTarget().getName() = "tm_mon" }
}
/**
* A `YearFieldAccess` for `struct tm`.
*/
class StructTmYearFieldAccess extends YearFieldAccess {
StructTmYearFieldAccess() { this.getTarget().getName() = "tm_year" }
}

View File

@@ -0,0 +1,502 @@
import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.Function
import semmle.code.cpp.Variable
/**
* Options that control the dependencies generated by
* this library.
*/
class DependencyOptions extends string {
DependencyOptions() { this = "DependencyOptions" }
/**
* Holds if dependencies should only be generated in templates rather than
* in both templates and instantiations, where possible. This is expensive
* to compute, but tends to produce dependencies that are easier to read.
*/
cached
predicate preferTemplateDeps() { any() }
}
/**
* Gets the `DependencyOptions`.
*/
DependencyOptions getDependencyOptions() { any() }
/**
* An Element that can be the source of a transitive dependency. This is any
* Element that is not in a template instantiation, plus declarations of template
* specializations (even though they are technically in an instantiation) because
* we need to generate (at least) a dependency from them to the general declaration.
*/
class DependsSource extends Element {
DependsSource() {
// not inside a template instantiation
not exists(Element other | isFromTemplateInstantiation(other)) or
// allow DeclarationEntrys of template specializations
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
}
}
/**
* A program element which can be the target of inter-function or inter-file dependencies.
*
* This is the union of Declaration, DeclarationEntry and Macro, minus various kinds of declaration:
* * FriendDecl is not included, as a friend declaration cannot be the target of a dependency (nor, as it happens, can they be a source).
* * TemplateParameter and related UserTypes are not included, as they are intrinsic sub-components of their associated template.
* * Template instantiations are excluded, as the template itself is more useful as a dependency target.
* * Stack variables and local types are excluded, as they are lexically tied to their enclosing function, and intra-function dependencies
* can only be inter-file dependencies in pathological cases.
* * Builtin functions and macros are excluded, as dependencies on them do not translate to inter-file dependencies (note that static functions
* and declarations within anonymous namespaces cannot be excluded for this reason, as the declaration can be in a header).
* * DeclarationEntrys are only needed if they're not definitions, for the definition to declaration dependency.
*/
class Symbol extends DependsSource {
Symbol() {
(
exists(EnumConstant ec | this = ec and not ec.getDeclaringEnum() instanceof LocalEnum)
or
this instanceof Macro and this.getFile().getAbsolutePath() != ""
or
this instanceof DeclarationEntry and
not this.(VariableDeclarationEntry).getVariable() instanceof LocalScopeVariable and
not this.(FunctionDeclarationEntry).getFunction() instanceof BuiltInFunction and
not this.(TypeDeclarationEntry).getType() instanceof LocalEnum and
not this.(TypeDeclarationEntry).getType() instanceof LocalClass and
not this.(TypeDeclarationEntry).getType() instanceof LocalTypedefType and
not this.(TypeDeclarationEntry).getType() instanceof TemplateParameter
or
this instanceof NamespaceDeclarationEntry
)
}
/**
* Gets an element which depends upon this symbol.
*
* To a first approximation, dependent elements can be thought of as occurrences of the symbol's name: instances of `VariableAccess`
* for `Variable` symbols, instances of `MacroInvocation` for `Macro` symbols, and so on.
*
* category:
* 1 - C/C++ compile-time dependency
* 2 - C/C++ link-time dependency (or transitive dependency with a link-time component)
*/
cached
Element getADependentElement(int category) { dependsOnFull(result, this, category) }
}
/**
* Associates a Declaration with it's DeclarationEntries, or (for a template
* instantiation) with the DeclarationEntries of its template.
*/
cached
predicate getDeclarationEntries(Declaration decl, DeclarationEntry de) {
(
decl = de.getDeclaration() or
decl.(Function).isConstructedFrom(de.getDeclaration()) or
decl.(Class).isConstructedFrom(de.getDeclaration())
) and
// ParameterDeclarationEntries are special, as (a) they can only be accessed
// from within the definition, and (b) non-definition PDEs may be commonly
// included. Thus, for PDEs, we point only to the definition.
(de instanceof ParameterDeclarationEntry implies de.isDefinition())
}
/**
* A 'simple' dependency from src to dest. This type of dependency
* does not make any special account of templates.
*
* Consider using Symbol.getADependentElement() rather than directly
* accessing this predicate.
*/
predicate dependsOnSimple(Element src, Element dest) {
dependsOnSimpleInline(src, dest) or
dependency_macroUse(src, dest)
}
/**
* A 'simple' dependency that might be inlined.
*/
private predicate dependsOnSimpleInline(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_typeUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_usingDeclaration(src, dest) or
dependency_usingNamespace(src, dest) or
dependency_enumConstantUse(src, dest) or
dependency_outOfLineDeclaration(src, dest) or
dependency_outOfLineInitializer(src, dest) or
dependency_functionSpecialization(src, dest) or
dependency_classSpecialization(src, dest)
}
/**
* Holds if a simple, non-template dependency exists between two Locations
* specified by the parameters.
*/
private predicate dependsLocation(
File f1, int sl1, int sc1, int el1, int ec1, File f2, int sl2, int sc2, int el2, int ec2
) {
exists(Element src, Element dest, Location loc1, Location loc2 |
dependsOnSimpleInline(src, dest) and
src instanceof DependsSource and
loc1 = src.getLocation() and
f1 = loc1.getFile() and
sl1 = loc1.getStartLine() and
sc1 = loc1.getStartColumn() and
el1 = loc1.getEndLine() and
ec1 = loc1.getEndColumn() and
loc2 = dest.getLocation() and
f2 = loc2.getFile() and
sl2 = loc2.getStartLine() and
sc2 = loc2.getStartColumn() and
el2 = loc2.getEndLine() and
ec2 = loc2.getEndColumn()
)
}
/**
* Holds if a simple dependency from `loc` to `loc2` in a template has a
* non-template alternative?
* (if `DependencyOptions.preferTemplateDeps()` is enabled)
*/
private predicate dependsNonTemplateAlternative(Location loc1, Location loc2) {
getDependencyOptions().preferTemplateDeps() and
exists(Element src, Element dest |
dependsOnSimpleInline(src, dest) and
src.isFromTemplateInstantiation(_) and
src.getLocation() = loc1 and
dest.getLocation() = loc2
) and
dependsLocation(loc1.getFile(), loc1.getStartLine(), loc1.getStartColumn(), loc1.getEndLine(),
loc1.getEndColumn(), loc2.getFile(), loc2.getStartLine(), loc2.getStartColumn(),
loc2.getEndLine(), loc2.getEndColumn())
}
/**
* A simple dependency from src to a declaration dest, where the definition is not
* needed at compile time.
*/
predicate dependsOnDeclOnly(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_pointerTypeUse(src, dest)
}
/**
* A dependency from src to dest. This predicate inlines
* template dependencies.
*/
private predicate dependsOnViaTemplate(Declaration src, Element dest) {
// A template instantiation depends on everything that anything
// inside it depends upon. This effectively inlines the things
// inside at the point where the template is called or
// referenced.
exists(Element internal, Location internalLocation, Location destLocation |
// internal is an element in the template {function or class} instantiation that cannot
// itself be a transitive dependency source
internal.isFromTemplateInstantiation(src) and
// don't generate template dependencies through a member function of a template class;
// these dependencies are also generated through the class, which has to be referenced
// somewhere anyway.
not exists(Class c |
internal.isFromTemplateInstantiation(c) and
src.getDeclaringType() = c
) and
// dest is anything that the internal element depends upon
dependsOnSimpleInline(internal, dest) and
// is there something in the template (not the instantiation) that's generating
// (better) dependencies from internal anyway?
internalLocation = internal.getLocation() and
destLocation = dest.getLocation() and
not dependsNonTemplateAlternative(internalLocation, destLocation)
)
}
/**
* Holds if `src` is related to `dest` by one `dependsOnSimple` and any
* number of `dependsOnViaTemplate` steps.
*
* Consider using `Symbol.getADependentElement()` rather than directly
* accessing this predicate.
*/
predicate dependsOnTransitive(DependsSource src, Element dest) {
exists(Element mid1 |
// begin with a simple step
dependsOnSimpleInline(src, mid1) and
// any number of recursive steps
(
mid1 = dest or // mid1 is not necessarily a Declaration
dependsOnViaTemplate+(mid1, dest)
)
)
or
dependency_macroUse(src, dest)
}
/**
* A dependency that targets a TypeDeclarationEntry.
*/
private predicate dependsOnTDE(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTransitive(src, t) and
getDeclarationEntries(t, dest)
}
/**
* A dependency that targets a visible TypeDeclarationEntry.
*/
pragma[noopt]
private predicate dependsOnVisibleTDE(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTDE(src, t, dest) and
exists(File g | g = dest.getFile() |
exists(File f | f = src.getFile() | f.getAnIncludedFile*() = g)
)
}
/**
* A dependency that targets a DeclarationEntry
*/
private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) {
exists(Type t |
// dependency from a Type use -> unique visible TDE
dependsOnVisibleTDE(src, t, dest) and
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTDE(src, t, alt)) = 1
)
or
exists(TypedefType mid |
// dependency from a TypedefType use -> any (visible) TDE
dependsOnTransitive(src, mid) and
getDeclarationEntries(mid, dest.(TypeDeclarationEntry))
)
or
exists(Declaration mid |
// dependency from a Variable / Function use -> any (visible) declaration entry
dependsOnTransitive(src, mid) and
not mid instanceof Type and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
not dest instanceof TypeDeclarationEntry
)
or
exists(Declaration mid |
// dependency from a Type / Variable / Function use -> any (visible) definition
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
// must be definition
dest.(DeclarationEntry).isDefinition()
)
}
/**
* The full dependsOn relation, made up of dependsOnTransitive plus some logic
* to fix up the results for Declarations to most reasonable DeclarationEntrys.
*/
private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
// direct result
dependsOnTransitive(src, dest) and
category = 1
or
// result to a visible DeclarationEntry
dependsOnDeclarationEntry(src, dest) and
src.getFile().getAnIncludedFile*() = dest.getFile() and
category = 1
or
exists(Declaration mid |
// dependency from a Variable / Function use -> non-visible definition (link time)
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
not dest instanceof TypeDeclarationEntry and
// must be definition
dest.(DeclarationEntry).isDefinition() and
// must not be visible (else covered above)
not src.getFile().getAnIncludedFile*() = dest.getFile() and
// filter out FDEs that are only defined in the dummy link target
(
(
dest instanceof FunctionDeclarationEntry and
isLinkerAwareExtracted()
)
implies
exists(LinkTarget lt | not lt.isDummy() |
lt.getAFunction() = dest.(FunctionDeclarationEntry).getFunction()
)
) and
category = 2
)
}
/**
* A dependency caused by a function call / use.
*/
private predicate dependency_functionUse(Element src, Function dest) {
funbind(unresolveElement(src), unresolveElement(dest))
}
/**
* A Type which refers to a UserType.
*/
cached
private predicate refersToUserType(Type a, UserType b) { a.refersTo(b) }
/**
* A Type which refers to a type directly, without using a pointer or reference.
*/
private predicate refersToDirectlyNonPointer(Type a, Type b) {
a.refersToDirectly(b) and
not a instanceof PointerType and
not a instanceof ReferenceType
}
/**
* A Type which refers to a UserType, but only through a pointer or reference.
*/
cached
private predicate refersToUserTypePointer(Type a, UserType b) {
refersToUserType(a, b) and
not refersToDirectlyNonPointer*(a, b)
}
/**
* A dependency caused by a type use.
*/
private predicate dependency_typeUse(Element src, UserType dest) {
refersToUserType(typeUsedBy(src), dest)
}
/**
* A dependency caused by a pointer/reference type use only.
*/
predicate dependency_pointerTypeUse(Element src, UserType dest) {
refersToUserTypePointer(typeUsedBy(src), dest)
}
/**
* The Types that must be defined for a particular Element.
*/
private Type typeUsedBy(Element src) {
result = src.(VariableDeclarationEntry).getType() and
not src.(VariableDeclarationEntry).getVariable().declaredUsingAutoType()
or
result = src.(FunctionDeclarationEntry).getType()
or
result = src.(Cast).getType() and not src.(Cast).isImplicit()
or
result = src.(ClassDerivation).getBaseClass()
or
result = src.(TypeDeclarationEntry).getType().(TypedefType).getBaseType()
or
result = src.(TypeDeclarationEntry).getDeclaration().(Enum).getExplicitUnderlyingType()
or
result = src.(SizeofTypeOperator).getTypeOperand()
or
exists(Function f |
funbind(unresolveElement(src), unresolveElement(f)) and result = f.getATemplateArgument()
)
or
result = src.(NewExpr).getType() and not result.(Class).hasConstructor()
or
result = src.(NewArrayExpr).getType() and
not result.(ArrayType).getBaseType().(Class).hasConstructor()
or
result = src.(DeleteExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
or
result = src.(DeleteArrayExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
}
/**
* A dependency caused by a variable use.
*/
private predicate dependency_variableUse(VariableAccess src, Variable dest) {
src.getTarget() = dest and
not dest instanceof LocalScopeVariable
}
/**
* A dependency caused by an enum constant use.
*/
private predicate dependency_enumConstantUse(EnumConstantAccess src, EnumConstant dest) {
src.getTarget() = dest
}
/**
* A dependency caused by a macro access.
*/
private predicate dependency_macroUse(MacroAccess src, Macro dest) { src.getMacro() = dest }
/**
* A dependency caused by a 'using' declaration 'using X::Y'.
*/
private predicate dependency_usingDeclaration(UsingDeclarationEntry src, Declaration dest) {
src.getDeclaration() = dest
}
/**
* A dependency caused by a 'using' directive 'using namespace X'.
*/
private predicate dependency_usingNamespace(UsingDirectiveEntry src, NamespaceDeclarationEntry dest) {
exists(Namespace nsdecl |
nsdecl = src.getNamespace() and
dest.getNamespace() = nsdecl and
dest.getFile().getAnIncludedFile*() = src.getFile() and
(
dest.getFile() = src.getFile()
implies
dest.getLocation().getStartLine() < src.getLocation().getStartLine()
) and
none() // temporarily disabled until we have suitable UI in Architect
)
}
/**
* A dependency from the definition of a class member to a corresponding declaration. This
* ensures that an externally defined class member has a dependency on (something in) the
* class definition.
*/
private predicate dependency_outOfLineDeclaration(DeclarationEntry src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
src.isDefinition() and
(
dest.getDeclaration() = src.getDeclaration()
or
// also permit out of line declarations to jump from the declaration of a specialized
// function to it's definition in the primary template. Note that the specialization
// in this case may be on a template class parameter.
src.getDeclaration().(Function).isConstructedFrom(dest.getDeclaration())
) and
not dest.isDefinition()
}
/**
* A dependency from an initialization of a (static) class member to a corresponding
* declaration.
*/
private predicate dependency_outOfLineInitializer(Initializer src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
dest.getDeclaration() = src.getDeclaration() and
not dest.isDefinition()
}
/**
* A dependency from a template function specialization to the general one.
*/
private predicate dependency_functionSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(FunctionTemplateSpecialization fts |
src.getDeclaration() = fts and
dest.getDeclaration() = fts.getPrimaryTemplate()
)
}
/**
* A dependency from a template class specialization to the most general one.
*/
private predicate dependency_classSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(ClassTemplateSpecialization cts |
src.getDeclaration() = cts and
dest.getDeclaration() = cts.getPrimaryTemplate()
)
}

View File

@@ -0,0 +1,35 @@
/**
* Reading from the environment, for example with 'getenv'.
*/
import cpp
/**
* An expression that reads from an environment variable.
*/
class EnvironmentRead extends Expr {
EnvironmentRead() { readsEnvironment(this, _) }
/**
* The name of the environment variable.
*/
string getEnvironmentVariable() {
// Conveniently, it's always the first argument to the call
this.(Call).getArgument(0).(TextLiteral).getValue() = result
}
/**
* A very short description of the source, suitable for use in
* an error message.
*/
string getSourceDescription() { readsEnvironment(this, result) }
}
private predicate readsEnvironment(Expr read, string sourceDescription) {
exists(FunctionCall call, string name |
read = call and
call.getTarget().hasGlobalOrStdName(name) and
name = ["getenv", "secure_getenv", "_wgetenv"] and
sourceDescription = name
)
}

View File

@@ -0,0 +1,111 @@
/**
* Common predicates used to exclude results from a query based on heuristics.
*/
import cpp
/**
* Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`.
*/
private predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) {
pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _)
}
/**
* Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`.
*/
private predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) {
f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _)
}
/**
* Holds if the function `f` is inside a preprocessor branch that may have code in another arm.
*/
predicate functionDefinedInIfDef(Function f) {
exists(
PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine,
int fBlockStartLine, int fBlockEndLine
|
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdLocation(pbd.getNext(), file, pbdEndLine) and
pbdStartLine <= fBlockStartLine and
pbdEndLine >= fBlockEndLine and
// pbd is a preprocessor branch where multiple branches exist
(
pbd.getNext() instanceof PreprocessorElse or
pbd instanceof PreprocessorElse or
pbd.getNext() instanceof PreprocessorElif or
pbd instanceof PreprocessorElif
)
)
}
/**
* Holds if the function `f` contains code excluded by the preprocessor.
*/
predicate functionContainsDisabledCode(Function f) {
// `f` contains a preprocessor branch that was not taken
exists(
PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine,
int fBlockEndLine
|
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdStartLine <= fBlockEndLine and
pbdStartLine >= fBlockStartLine and
(
pbd.(PreprocessorBranch).wasNotTaken()
or
// an else either was not taken, or it's corresponding branch
// was not taken.
pbd instanceof PreprocessorElse
)
)
}
/**
* Holds if the function `f` contains code that could be excluded by the preprocessor.
*/
predicate functionContainsPreprocCode(Function f) {
// `f` contains a preprocessor branch
exists(
PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine,
int fBlockEndLine
|
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdStartLine <= fBlockEndLine and
pbdStartLine >= fBlockStartLine
)
}
/**
* Holds if `e` is completely or partially from a macro definition, as opposed
* to being passed in as an argument.
*
* In the following example, the call to `f` is from a macro definition,
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
* from `M` refers to a macro.
* ```
* #define M(x) f(x)
* ...
* M(y + 1);
* ```
*/
predicate isFromMacroDefinition(Element e) {
exists(MacroInvocation mi, Location eLocation, Location miLocation |
mi.getAnExpandedElement() = e and
eLocation = e.getLocation() and
miLocation = mi.getLocation() and
// If the location of `e` coincides with the macro invocation, then `e` did
// not come from a macro argument. The inequalities here could also be
// equalities, but that confuses the join orderer into joining on the source
// locations too early.
// There are cases where the start location of a non-argument element comes
// right after the invocation's open parenthesis, so it appears to be more
// robust to match on the end location instead.
eLocation.getEndLine() >= miLocation.getEndLine() and
eLocation.getEndColumn() >= miLocation.getEndColumn()
)
}

View File

@@ -0,0 +1,42 @@
/**
* Provides predicates for identifying function calls that open or close a file.
*/
import cpp
/**
* A call to a library function that opens a file.
*/
predicate fopenCall(FunctionCall fc) {
exists(Function f | f = fc.getTarget() |
f.hasGlobalOrStdName("fopen") or
f.hasGlobalName("open") or
f.hasGlobalName("_open") or
f.hasGlobalName("_wopen") or
f.hasGlobalName("CreateFile") or
f.hasGlobalName("CreateFileA") or
f.hasGlobalName("CreateFileW") or
f.hasGlobalName("CreateFileTransacted") or
f.hasGlobalName("CreateFileTransactedA") or
f.hasGlobalName("CreateFileTransactedW")
)
}
/**
* A call to a library function that closes a file.
*/
predicate fcloseCall(FunctionCall fc, Expr closed) {
exists(Function f | f = fc.getTarget() |
f.hasGlobalOrStdName("fclose") and
closed = fc.getArgument(0)
or
f.hasGlobalName("close") and
closed = fc.getArgument(0)
or
f.hasGlobalName("_close") and
closed = fc.getArgument(0)
or
f.hasGlobalOrStdName("CloseHandle") and
closed = fc.getArgument(0)
)
}

View File

@@ -0,0 +1,11 @@
import semmle.code.cpp.Macro
/** A macro defining NULL. */
class NULLMacro extends Macro {
NULLMacro() { this.getHead() = "NULL" }
}
/** A use of the NULL macro. */
class NULL extends Literal {
NULL() { exists(NULLMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
}

View File

@@ -0,0 +1,117 @@
import cpp
private import semmle.code.cpp.models.interfaces.ArrayFunction
private import semmle.code.cpp.models.implementations.Strcat
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
exists(StackVariable v0, Expr val |
exprDefinition(v0, e, val) and
val.getAChild*() = va and
mayAddNullTerminator(e0, v0.getAnAccess())
)
}
/**
* Holds if the expression `e` may add a null terminator to the string in
* variable `v`.
*/
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
// Assignment: dereferencing or array access
exists(AssignExpr ae | e = ae |
(
// *v = x, *v++ = x, etc.
ae.getLValue().(PointerDereferenceExpr).getOperand().getAChild*() = va
or
// v[x] = y
ae.getLValue().(ArrayExpr).getArrayBase() = va
) and
// Rule out assignments where the assigned value is a non-zero constant
not ae.getRValue().getFullyConverted().getValue().toInt() != 0
)
or
// Assignment to another stack variable
exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
mayAddNullTerminatorHelper(e, va, e0) and
bb.getNode(pos) = e and
bb0.getNode(pos0) = e0
|
bb = bb0 and pos < pos0
or
bb.getASuccessor+() = bb0
)
or
// Assignment to non-stack variable
exists(AssignExpr ae | e = ae |
not ae.getLValue().(VariableAccess).getTarget() instanceof StackVariable and
ae.getRValue().getAChild*() = va
)
or
// Function call: library function, varargs function, function
// containing assembler code, or function where the relevant
// parameter is potentially added a null terminator.
exists(Call c, Function f, int i |
e = c and
f = c.getTarget() and
not functionArgumentMustBeNullTerminated(f, i) and
c.getAnArgumentSubExpr(i) = va
|
not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i)
or
mayAddNullTerminator(_, f.getParameter(i).getAnAccess())
or
f.isVarargs() and i >= f.getNumberOfParameters()
or
exists(AsmStmt s | s.getEnclosingFunction() = f)
)
or
// Call without target (e.g., function pointer call)
exists(Call c |
e = c and
not exists(c.getTarget()) and
c.getAnArgumentSubExpr(_) = va
)
}
/**
* Holds if `f` is a (library) function whose `i`th argument must be null
* terminated.
*/
predicate functionArgumentMustBeNullTerminated(Function f, int i) {
f.(ArrayFunction).hasArrayWithNullTerminator(i) and
f.(ArrayFunction).hasArrayInput(i)
or
f instanceof StrcatFunction and i = 0
}
/**
* Holds if `va` is a variable access where the contents must be null terminated.
*/
predicate variableMustBeNullTerminated(VariableAccess va) {
exists(FunctionCall fc |
// Call to a function that requires null termination
exists(int i |
functionArgumentMustBeNullTerminated(fc.getTarget(), i) and
fc.getArgument(i) = va
)
or
// Call to a wrapper function that requires null termination
// (not itself adding a null terminator)
exists(Function wrapper, int i, Parameter p, VariableAccess use |
fc.getTarget() = wrapper and
fc.getArgument(i) = va and
p = wrapper.getParameter(i) and
parameterUsePair(p, use) and
variableMustBeNullTerminated(use) and
// Simplified: check that `p` may not be null terminated on *any*
// path to `use` (including the one found via `parameterUsePair`)
not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
mayAddNullTerminator(e, p.getAnAccess()) and
bb1.getNode(pos1) = e and
bb2.getNode(pos2) = use
|
bb1 = bb2 and pos1 < pos2
or
bb1.getASuccessor+() = bb2
)
)
)
}

View File

@@ -0,0 +1,10 @@
import cpp
/**
* A C++ class or structure which (possibly by inheritance) has at least one virtual method.
*/
class PolymorphicClass extends Class {
PolymorphicClass() {
exists(MemberFunction f | this.getABaseClass*() = f.getDeclaringType() and f.isVirtual())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
/**
* A library for dealing with scanf-like formatting strings. This is similar to
* printf.qll but the format specification for scanf is quite different.
*/
import semmle.code.cpp.Type
/**
* A `scanf`-like standard library function.
*/
abstract class ScanfFunction extends Function {
/**
* Gets the position at which the input string or stream parameter occurs,
* if this function does not read from standard input.
*/
abstract int getInputParameterIndex();
/**
* Gets the position at which the format parameter occurs.
*/
abstract int getFormatParameterIndex();
/**
* Holds if the default meaning of `%s` is a `wchar_t*` string
* (rather than a `char*`).
*/
predicate isWideCharDefault() { exists(getName().indexOf("wscanf")) }
}
/**
* The standard function `scanf` (and variations).
*/
class Scanf extends ScanfFunction {
Scanf() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
)
}
override int getInputParameterIndex() { none() }
override int getFormatParameterIndex() { result = 0 }
}
/**
* The standard function `fscanf` (and variations).
*/
class Fscanf extends ScanfFunction {
Fscanf() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
)
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 1 }
}
/**
* The standard function `sscanf` (and variations).
*/
class Sscanf extends ScanfFunction {
Sscanf() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
)
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 1 }
}
/**
* The standard(ish) function `snscanf` (and variations).
*/
class Snscanf extends ScanfFunction {
Snscanf() {
this instanceof TopLevelFunction and
(
hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
// note that the max_amount is not a limit on the output length, it's an input length
// limit used with non null-terminated strings.
)
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 2 }
/**
* Gets the position at which the maximum number of characters in the
* input string is specified.
*/
int getInputLengthParameterIndex() { result = 1 }
}
/**
* A call to one of the `scanf` functions.
*/
class ScanfFunctionCall extends FunctionCall {
ScanfFunctionCall() { this.getTarget() instanceof ScanfFunction }
/**
* Gets the `scanf`-like function that is called.
*/
ScanfFunction getScanfFunction() { result = getTarget() }
/**
* Gets the position at which the input string or stream parameter occurs,
* if this function call does not read from standard input.
*/
int getInputParameterIndex() { result = getScanfFunction().getInputParameterIndex() }
/**
* Gets the position at which the format parameter occurs.
*/
int getFormatParameterIndex() { result = getScanfFunction().getFormatParameterIndex() }
/**
* Gets the format expression used in this call.
*/
Expr getFormat() { result = this.getArgument(this.getFormatParameterIndex()) }
/**
* Holds if the default meaning of `%s` is a `wchar_t*` string
* (rather than a `char*`).
*/
predicate isWideCharDefault() { getScanfFunction().isWideCharDefault() }
}
/**
* A class to represent format strings that occur as arguments to invocations of `scanf` functions.
*/
class ScanfFormatLiteral extends Expr {
ScanfFormatLiteral() {
exists(ScanfFunctionCall sfc | sfc.getFormat() = this) and
this.isConstant()
}
/** the function call where this format string is used */
ScanfFunctionCall getUse() { result.getFormat() = this }
/** Holds if the default meaning of `%s` is a `wchar_t*` (rather than a `char*`). */
predicate isWideCharDefault() { getUse().getTarget().(ScanfFunction).isWideCharDefault() }
/**
* Gets the format string itself, transformed as follows:
* - '%%' is replaced with '_'
* (this avoids accidentally processing them as format specifiers)
* - '%*' is replaced with '_'
* (%*any is matched but not assigned to an argument)
*/
string getFormat() { result = this.getValue().replaceAll("%%", "_").replaceAll("%*", "_") }
/**
* Gets the number of conversion specifiers (not counting `%%` and `%*`...).
*/
int getNumConvSpec() { result = count(this.getFormat().indexOf("%")) }
/**
* Gets the position in the string at which the nth conversion specifier starts.
*/
int getConvSpecOffset(int n) {
n = 0 and result = this.getFormat().indexOf("%", 0, 0)
or
n > 0 and
exists(int p |
n = p + 1 and result = this.getFormat().indexOf("%", 0, this.getConvSpecOffset(p) + 2)
)
}
/**
* Gets the regular expression to match each individual part of a conversion specifier.
*/
private string getMaxWidthRegexp() { result = "(?:[1-9][0-9]*)?" }
private string getLengthRegexp() { result = "(?:hh?|ll?|L|q|j|z|t)?" }
private string getConvCharRegexp() { result = "[aAcCdeEfFgGimnopsSuxX]" }
/**
* Gets the regular expression used for matching a whole conversion specifier.
*/
string getConvSpecRegexp() {
// capture groups: 1 - entire conversion spec, including "%"
// 2 - maximum width
// 3 - length modifier
// 4 - conversion character
result =
"(\\%(" + this.getMaxWidthRegexp() + ")(" + this.getLengthRegexp() + ")(" +
this.getConvCharRegexp() + ")).*"
}
/**
* Holds if the arguments are a parsing of a conversion specifier to this
* format string, where `n` is which conversion specifier to parse, `spec` is
* the whole conversion specifier, `width` is the maximum width option of
* this input, `len` is the length flag of this input, and `conv` is the
* conversion character of this input.
*
* Each parameter is the empty string if no value is given by the conversion
* specifier.
*/
predicate parseConvSpec(int n, string spec, string width, string len, string conv) {
exists(int offset, string fmt, string rst, string regexp |
offset = this.getConvSpecOffset(n) and
fmt = this.getFormat() and
rst = fmt.substring(offset, fmt.length()) and
regexp = this.getConvSpecRegexp() and
(
spec = rst.regexpCapture(regexp, 1) and
width = rst.regexpCapture(regexp, 2) and
len = rst.regexpCapture(regexp, 3) and
conv = rst.regexpCapture(regexp, 4)
)
)
}
/**
* Gets the maximum width option of the nth input (empty string if none is given).
*/
string getMaxWidthOpt(int n) {
exists(string spec, string len, string conv | this.parseConvSpec(n, spec, result, len, conv))
}
/**
* Gets the maximum width of the nth input.
*/
int getMaxWidth(int n) { result = this.getMaxWidthOpt(n).toInt() }
/**
* Gets the length flag of the nth conversion specifier.
*/
string getLength(int n) {
exists(string spec, string width, string conv |
this.parseConvSpec(n, spec, width, result, conv)
)
}
/**
* Gets the conversion character of the nth conversion specifier.
*/
string getConversionChar(int n) {
exists(string spec, string width, string len | this.parseConvSpec(n, spec, width, len, result))
}
/**
* Gets the maximum length of the string that can be produced by the nth
* conversion specifier of this format string; fails if no estimate is
* possible (or implemented).
*/
int getMaxConvertedLength(int n) {
this.getConversionChar(n).toLowerCase() = "s" and
result = this.getMaxWidth(n)
}
}

View File

@@ -0,0 +1,22 @@
import cpp
/**
* DEPRECATED: use `semmle.code.cpp.models.implementations.Strcat.qll` instead.
*
* A function that concatenates the string from its second argument
* to the string from its first argument, for example `strcat`.
*/
class StrcatFunction extends Function {
StrcatFunction() {
getName() =
[
"strcat", // strcat(dst, src)
"strncat", // strncat(dst, src, max_amount)
"wcscat", // wcscat(dst, src)
"_mbscat", // _mbscat(dst, src)
"wcsncat", // wcsncat(dst, src, max_amount)
"_mbsncat", // _mbsncat(dst, src, max_amount)
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
]
}
}

View File

@@ -0,0 +1,73 @@
/**
* Provides a class for calculating the possible length of string expressions.
*/
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.controlflow.SSA
/**
* Holds if a value can flow directly from one expr to another.
*/
predicate canValueFlow(Expr fromExpr, Expr toExpr) {
// value propagated via a definition use pair
exists(Variable v, SsaDefinition def | fromExpr = def.getAnUltimateDefiningValue(v) |
toExpr = def.getAUse(v)
)
or
// expr -> containing parenthesized expression
fromExpr = toExpr.(ParenthesisExpr).getExpr()
or
// R value -> containing assignment expression ('=' assignment)
fromExpr = toExpr.(AssignExpr).getRValue()
or
// then -> containing ternary (? :) operator
fromExpr = toExpr.(ConditionalExpr).getThen()
or
// else -> containing ternary (? :) operator
fromExpr = toExpr.(ConditionalExpr).getElse()
}
/**
* An analysed null terminated string.
*/
class AnalysedString extends Expr {
AnalysedString() {
this.getUnspecifiedType() instanceof ArrayType or
this.getUnspecifiedType() instanceof PointerType
}
/**
* Gets the maximum length (including null) this string can be, when this
* can be calculated.
*/
int getMaxLength() {
// take the longest AnalysedString it's value could 'flow' from; however if even one doesn't
// return a value (this essentially means 'infinity') we can't return a value either.
result =
max(AnalysedString expr, int toMax |
canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength()
|
toMax
) and // maximum length
forall(AnalysedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive)
}
}
/**
* A call to a strlen like function.
*/
class StrlenCall extends FunctionCall {
StrlenCall() {
this.getTarget().hasGlobalOrStdName("strlen") or
this.getTarget().hasGlobalOrStdName("wcslen") or
this.getTarget().hasGlobalName("_mbslen") or
this.getTarget().hasGlobalName("_mbslen_l") or
this.getTarget().hasGlobalName("_mbstrlen") or
this.getTarget().hasGlobalName("_mbstrlen_l")
}
/**
* The string argument passed into this strlen-like call.
*/
Expr getStringExpr() { result = this.getArgument(0) }
}

View File

@@ -0,0 +1,69 @@
import semmle.code.cpp.Class
/**
* A class that is either a `struct` or just has getters and setters
* for its members. In either case it just stores data and has no
* real encapsulation.
*/
class StructLikeClass extends Class {
StructLikeClass() {
this instanceof Struct
or
forall(MemberFunction f | f = this.getAMemberFunction() |
exists(MemberVariable v | setter(v, f, this) or getter(v, f, this))
or
f instanceof Constructor
or
f instanceof Destructor
or
// Allow the copy and move assignment ops - still struct-like
f instanceof CopyAssignmentOperator
or
f instanceof MoveAssignmentOperator
)
}
/**
* Gets a setter function in this class, setting the given variable.
* This is a function whose name begins "set"... that assigns to this variable and no other
* member variable of the class. In addition, it takes a single parameter of
* type the type of the corresponding member variable.
*/
MemberFunction getASetter(MemberVariable v) { setter(v, result, this) }
/**
* Gets a getter function in this class, getting the given variable.
* This is a function whose name begins "get"... that reads this variable and no other
* member variable of the class. In addition, its return type is the type
* of the corresponding member variable.
*/
MemberFunction getAGetter(MemberVariable v) { getter(v, result, this) }
}
/**
* Holds if `f` is a setter member function for `v`, in class `c`.
* See `StructLikeClass.getASetter`.
*/
predicate setter(MemberVariable v, MemberFunction f, Class c) {
f.getDeclaringType() = c and
v.getDeclaringType() = c and
f.getName().matches("set%") and
v.getAnAssignedValue().getEnclosingFunction() = f and
forall(MemberVariable v2 | v2.getAnAssignedValue().getEnclosingFunction() = f | v2 = v) and
f.getNumberOfParameters() = 1 and
f.getParameter(0).getType().stripType() = v.getType().stripType()
}
/**
* Holds if `f` is a getter member function for `v`, in class `c`.
* See `StructLikeClass.getAGetter`.
*/
predicate getter(MemberVariable v, MemberFunction f, Class c) {
f.getDeclaringType() = c and
v.getDeclaringType() = c and
f.getName().matches("get%") and
v.getAnAccess().getEnclosingFunction() = f and
forall(MemberVariable v2 | v2.getAnAccess().getEnclosingFunction() = f | v2 = v) and
f.getNumberOfParameters() = 0 and
f.getType().stripType() = v.getType().stripType()
}

View File

@@ -0,0 +1,215 @@
/**
* Utilities for analyzing synchronization primitives, such
* as mutexes and semaphores.
*/
import cpp
/**
* A type that acts as a mutex. This class is extended below and and may
* be extended in `Options.qll`.
*/
abstract class MutexType extends Type {
/**
* Holds if `fc` is a call that always locks mutex `arg`
* of this type.
*/
abstract predicate mustlockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is a call that tries to lock mutex `arg`
* of this type, but may return without success.
*/
abstract predicate trylockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is a call that unlocks mutex `arg` of this type.
*/
abstract predicate unlockAccess(FunctionCall fc, Expr arg);
/**
* Holds if `fc` is n call that locks or tries to lock mutex
* `arg` of this type.
*/
predicate lockAccess(FunctionCall fc, Expr arg) {
this.mustlockAccess(fc, arg) or
this.trylockAccess(fc, arg)
}
/**
* Gets a call that locks or tries to lock any mutex of this type.
*/
FunctionCall getLockAccess() {
result = getMustlockAccess() or
result = getTrylockAccess()
}
/**
* Gets a call that always locks any mutex of this type.
*/
FunctionCall getMustlockAccess() { this.mustlockAccess(result, _) }
/**
* Gets a call that tries to lock any mutex of this type,
* by may return without success.
*/
FunctionCall getTrylockAccess() { this.trylockAccess(result, _) }
/**
* Gets a call that unlocks any mutex of this type.
*/
FunctionCall getUnlockAccess() { this.unlockAccess(result, _) }
/**
* DEPRECATED: use mustlockAccess(fc, arg) instead.
*/
deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() }
/**
* DEPRECATED: use trylockAccess(fc, arg) instead.
*/
deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() }
/**
* DEPRECATED: use lockAccess(fc, arg) instead.
*/
deprecated Function getLockFunction() { result = getLockAccess().getTarget() }
/**
* DEPRECATED: use unlockAccess(fc, arg) instead.
*/
deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() }
}
/**
* Gets a function that looks like a lock function.
*/
private Function mustlockCandidate() {
exists(string name | name = result.getName() |
name = "lock" or
name.matches("%mutex\\_lock")
)
}
/**
* Gets a function that looks like a try-lock function.
*/
private Function trylockCandidate() {
exists(string name | name = result.getName() |
name = "try_lock" or
name.matches("%mutex\\_trylock")
)
}
/**
* Gets a function that looks like an unlock function.
*/
private Function unlockCandidate() {
exists(string name | name = result.getName() |
name = "unlock" or
name.matches("%mutex\\_unlock")
)
}
/**
* Gets a type that is a parameter to a function, or it's declaring type
* (i.e. it's `this`). If the function is a locking related function,
* these can be thought of as candidates for the mutex is it locking or
* unlocking. It is narrowed down in `DefaultMutexType` by requiring that
* it must be a class type with both a lock and an unlock function.
*/
private Class lockArgTypeCandidate(Function fcn) {
result = fcn.getDeclaringType() or
result = fcn.getAParameter().getType().stripType()
}
/**
* A class or struct type that has both a lock and an unlock function
* candidate, and is therefore a mutex.
*
* This excludes types like `std::weak_ptr` which has a lock
* method, but not an unlock method, and is not a mutex.)
*/
class DefaultMutexType extends MutexType {
DefaultMutexType() {
this = lockArgTypeCandidate(mustlockCandidate()) and
this = lockArgTypeCandidate(unlockCandidate())
}
private predicate lockArgType(FunctionCall fc, Expr arg) {
exists(int n |
arg = fc.getArgument(n) and
fc.getTarget().getParameter(n).getType().stripType() = this
)
or
fc.getTarget().getDeclaringType() = this and
arg = fc.getQualifier()
or
// if we're calling our own method with an implicit `this`,
// let `arg` be the function call, since we don't really have
// anything else to use.
fc.getTarget().getDeclaringType() = this and
not exists(fc.getQualifier()) and
arg = fc
}
override predicate mustlockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = mustlockCandidate() and
lockArgType(fc, arg)
}
override predicate trylockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = trylockCandidate() and
lockArgType(fc, arg)
}
override predicate unlockAccess(FunctionCall fc, Expr arg) {
fc.getTarget() = unlockCandidate() and
lockArgType(fc, arg)
}
}
/**
* Holds if `arg` is the mutex argument of a call to lock or unlock and
* `argType` is the type of the mutex.
*/
private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) {
argType = arg.getUnderlyingType().stripType() and
(
arg = call.getQualifier() or
arg = call.getAnArgument()
)
// note: this seems to arbitrarily care about argument types, rather
// than parameter types as elsewhere. As a result `mustlockCall`,
// for example, has slightly different results from
// `MutexType.mustlockAccess`.
}
/**
* Holds if `call` is a call that locks or tries to lock its argument `arg`.
*/
predicate lockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess())
}
/**
* Holds if `call` is a call that always locks its argument `arg`.
*/
predicate mustlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess())
}
/**
* Holds if `call` is a call that tries to lock its argument `arg`, but may
* return without success.
*/
predicate trylockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess())
}
/**
* Holds if `call` is a call that unlocks its argument `arg`.
*/
predicate unlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getUnlockAccess())
}

View File

@@ -0,0 +1,42 @@
import semmle.code.cpp.exprs.Expr
/**
* An expression that is used to qualify some other expression.
*/
class Qualifier extends Expr {
Qualifier() {
exists(VariableAccess a | a.getQualifier() = this) or
exists(Call c | c.getQualifier() = this) or
exists(VacuousDestructorCall v | v.getQualifier() = this)
}
}
/**
* An expression that occurs in a void context, i.e. either as the toplevel expression of
* an expression statement or on the left hand side of the comma operator.
*
* Expressions that are explicitly cast to void are not considered to be in void context.
*/
class ExprInVoidContext extends Expr {
ExprInVoidContext() { exprInVoidContext(this) }
}
private predicate exprInVoidContext(Expr e) {
(
exists(ExprStmt s |
s = e.getParent() and
not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt())
)
or
exists(ConditionalExpr c | c.getThen() = e and c instanceof ExprInVoidContext)
or
exists(ConditionalExpr c | c.getElse() = e and c instanceof ExprInVoidContext)
or
exists(CommaExpr c | c.getLeftOperand() = e)
or
exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)
or
exists(ForStmt for | for.getUpdate() = e)
) and
not e.getActualType() instanceof VoidType
}

View File

@@ -0,0 +1,96 @@
/**
* Standard Unix constants.
*/
import cpp
/**
* Gets the number corresponding to the contents of `input` in base-8.
* Note: the first character of `input` must be `0`. For example:
* `parseOctal("012345") = 5349`.
*/
bindingset[input]
int parseOctal(string input) {
input.charAt(0) = "0" and
result =
strictsum(int ix |
ix in [0 .. input.length()]
|
8.pow(input.length() - (ix + 1)) * input.charAt(ix).toInt()
)
}
/** Gets the number corresponding to the "set-user-ID on execute bit" in Unix. */
int s_isuid() { result = parseOctal("04000") }
/** Gets the number corresponding to the "set-group-ID on execute bit" in Unix. */
int s_isgid() { result = parseOctal("02000") }
/** Gets the number corresponding to the sticky bit in Unix. */
int s_isvtx() { result = parseOctal("01000") }
/** Gets the number corresponding to the read permission bit for owner of the file in Unix. */
int s_irusr() { result = parseOctal("0400") }
/** Gets the number corresponding to the write permission bit for owner of the file in Unix. */
int s_iwusr() { result = parseOctal("0200") }
/** Gets the number corresponding to the execute permission bit for owner of the file in Unix. */
int s_ixusr() { result = parseOctal("0100") }
/** Gets the number corresponding to the permissions `S_IRUSR | S_IWUSR | S_IXUSR` in Unix. */
int s_irwxu() { result = s_irusr().bitOr(s_iwusr()).bitOr(s_ixusr()) }
/**
* Gets the number corresponding to the read permission bit for the group
* owner of the file in Unix.
*/
int s_irgrp() { result = s_irusr().bitShiftRight(3) }
/**
* Gets the number corresponding to the write permission bit for the group
* owner of the file in Unix.
*/
int s_iwgrp() { result = s_iwusr().bitShiftRight(3) }
/**
* Gets the number corresponding to the execute permission bit for the group
* owner of the file in Unix.
*/
int s_ixgrp() { result = s_ixusr().bitShiftRight(3) }
/** Gets the number corresponding to the permissions `S_IRGRP | S_IWGRP | S_IXGRP` in Unix. */
int s_irwxg() { result = s_irwxu().bitShiftRight(3) }
/** Gets the number corresponding to the read permission bit for other users in Unix. */
int s_iroth() { result = s_irgrp().bitShiftRight(3) }
/** Gets the number corresponding to the write permission bit for other users in Unix. */
int s_iwoth() { result = s_iwgrp().bitShiftRight(3) }
/** Gets the number corresponding to the execute-or-search permission bit for other users in Unix. */
int s_ixoth() { result = s_ixgrp().bitShiftRight(3) }
/** Gets the number corresponding to the permissions `S_IROTH | S_IWOTH | S_IXOTH` in Unix. */
int s_irwxo() { result = s_irwxg().bitShiftRight(3) }
/**
* Gets the number that can be used in a bitwise and with the file status flag
* to produce a number representing the file access mode.
*/
int o_accmode() { result = parseOctal("0003") }
/** Gets the number corresponding to the read-only file access mode. */
int o_rdonly() { result = parseOctal("00") }
/** Gets the number corresponding to the write-only file access mode. */
int o_wronly() { result = parseOctal("01") }
/** Gets the number corresponding to the read-and-write file access mode. */
int o_rdwr() { result = parseOctal("02") }
/** Gets the number corresponding to the file creation flag O_CREAT on Linux. */
int o_creat() { result = parseOctal("0100") }
/** Gets the number corresponding to the file creation flag O_EXCL on Linux. */
int o_excl() { result = parseOctal("0200") }

View File

@@ -0,0 +1,282 @@
/**
* Provides a library for reasoning about control flow at the granularity of basic blocks.
* This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s.
*/
import cpp
private import internal.PrimitiveBasicBlocks
private import internal.ConstantExprs
/*
* `BasicBlock`s are refinements of `PrimitiveBasicBlock`s, taking
* impossible CFG edges into account (using the `successors_adapted`
* relation). The refinement manifests itself in two changes:
*
* - The successor relation on `BasicBlock`s uses `successors_adapted`
* (instead of `successors_extended` used by `PrimtiveBasicBlock`s). Consequently,
* some edges between `BasicBlock`s may be removed. Example:
* ```
* x = 1; // s1
* if (true) { // s2
* x = 2; // s3
* } else {
* x = 3; // s4
* }
* ```
* The `BasicBlock` successor edge from the basic block containing `s1`
* and `s2` to the basic block containing `s4` is removed.
*
* - `PrimitiveBasicBlock`s may be split up into two or more
* `BasicBlock`s: Internal nodes of `PrimitiveBasicBlock`s whose
* predecessor edges have been removed (unreachable code) will be entry
* points of new `BasicBlock`s. Consequently, each entry point of a
* `PrimitiveBasicBlock` will also be an entry point of a `BasicBlock`,
* but the converse does not necessarily hold. Example:
* ```
* x = 1; // s5
* abort(); // s6
* x = 2; // s7
* ```
* `s5`-`s7` belong to the same `PrimitiveBasicBlock`, but the CFG edge
* from `s6` to `s7` is impossible, so `s7` will be the entry point of
* its own (unreachable) `BasicBlock`.
*
* Note that, although possible, two or more `PrimitiveBasicBlock`s are
* never merged to one `BasicBlock`: Consider the first example above;
* it would be possible to define a single `BasicBlock` consisting of
* `s1`-`s3`, however, the result would be counter-intuitive.
*/
private import Cached
cached
private module Cached {
/**
* Any node that is the entry point of a primitive basic block is
* also the entry point of a basic block. In addition, all nodes
* with a primitive successor, where the predecessor has been pruned
* (that is, `getAPredecessor()` does not exist while a predecessor
* using the primitive `successors_extended` relation does exist), is also
* considered a basic block entry node.
*/
cached
predicate basic_block_entry_node(ControlFlowNode node) {
primitive_basic_block_entry_node(node) or
non_primitive_basic_block_entry_node(node)
}
private predicate non_primitive_basic_block_entry_node(ControlFlowNode node) {
not primitive_basic_block_entry_node(node) and
not exists(node.getAPredecessor()) and
successors_extended(node, _)
}
/**
* Holds if basic block `bb` equals a primitive basic block.
*
* There are two situations in which this is *not* the case:
*
* - Either the entry node of `bb` does not correspond to an
* entry node of a primitive basic block, or
* - The primitive basic block with the same entry node contains
* a (non-entry) node which is the entry node of a non-primitive
* basic block (that is, the primitive basic block has been split
* up).
*
* This predicate is used for performance optimization only:
* Whenever a `BasicBlock` equals a `PrimitiveBasicBlock`, we can
* reuse predicates already computed for `PrimitiveBasicBlocks`.
*/
private predicate equalsPrimitiveBasicBlock(BasicBlock bb) {
primitive_basic_block_entry_node(bb) and
not exists(int i |
i > 0 and
non_primitive_basic_block_entry_node(bb.(PrimitiveBasicBlock).getNode(i))
)
}
/** Holds if `node` is the `pos`th control-flow node in basic block `bb`. */
cached
predicate basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) {
equalsPrimitiveBasicBlock(bb) and primitive_basic_block_member(node, bb, pos) // reuse already computed relation
or
non_primitive_basic_block_member(node, bb, pos)
}
private predicate non_primitive_basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) {
not equalsPrimitiveBasicBlock(bb) and node = bb and pos = 0
or
not node instanceof BasicBlock and
exists(ControlFlowNode pred | successors_extended(pred, node) |
non_primitive_basic_block_member(pred, bb, pos - 1)
)
}
/** Gets the number of control-flow nodes in the basic block `bb`. */
cached
int bb_length(BasicBlock bb) {
if equalsPrimitiveBasicBlock(bb)
then result = bb.(PrimitiveBasicBlock).length() // reuse already computed relation
else result = strictcount(ControlFlowNode node | basic_block_member(node, bb, _))
}
/** Successor relation for basic blocks. */
cached
predicate bb_successor_cached(BasicBlock pred, BasicBlock succ) {
exists(ControlFlowNode last |
basic_block_member(last, pred, bb_length(pred) - 1) and
last.getASuccessor() = succ
)
}
}
predicate bb_successor = bb_successor_cached/2;
/**
* A basic block in the C/C++ control-flow graph.
*
* A basic block is a simple sequence of control-flow nodes,
* connected to each other and nothing else:
*
* ```
* A - B - C - D ABCD is a basic block
* ```
*
* Any incoming or outgoing edges break the block into two:
*
* ```
* A - B > C - D AB is a basic block and CD is a basic block (C has two incoming edges)
*
*
* A - B < C - D AB is a basic block and CD is a basic block (B has two outgoing edges)
* ```
*/
class BasicBlock extends ControlFlowNodeBase {
BasicBlock() { basic_block_entry_node(this) }
/** Holds if this basic block contains `node`. */
predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) }
/** Gets the `ControlFlowNode` at position `pos` in this basic block. */
ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) }
/** Gets a `ControlFlowNode` in this basic block. */
ControlFlowNode getANode() { basic_block_member(result, this, _) }
/** Gets a `BasicBlock` that is a direct successor of this basic block. */
BasicBlock getASuccessor() { bb_successor(this, result) }
/** Gets a `BasicBlock` that is a direct predecessor of this basic block. */
BasicBlock getAPredecessor() { bb_successor(result, this) }
/**
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
* when the outgoing edge of this basic block is an expression that is true.
*/
BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() }
/**
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
* when the outgoing edge of this basic block is an expression that is false.
*/
BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() }
/** Gets the final `ControlFlowNode` of this basic block. */
ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) }
/** Gets the first `ControlFlowNode` of this basic block. */
ControlFlowNode getStart() { result = this }
/** Gets the number of `ControlFlowNode`s in this basic block. */
int length() { result = bb_length(this) }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*
* Yields no result if this basic block spans multiple source files.
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn)
}
pragma[noinline]
private predicate hasLocationInfoInternal(
string file, int line, int col, string endf, int endl, int endc
) {
this.getStart().getLocation().hasLocationInfo(file, line, col, _, _) and
this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc)
}
/** Gets the function containing this basic block. */
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
/**
* Holds if this basic block is in a loop of the control-flow graph. This
* includes loops created by `goto` statements. This predicate may not hold
* even if this basic block is syntactically inside a `while` loop if the
* necessary back edges are unreachable.
*/
predicate inLoop() { this.getASuccessor+() = this }
/**
* DEPRECATED since version 1.11: this predicate does not match the standard
* definition of _loop header_.
*
* Holds if this basic block is in a loop of the control-flow graph and
* additionally has an incoming edge that is not part of any loop containing
* this basic block. A typical example would be the basic block that computes
* `x > 0` in an outermost loop `while (x > 0) { ... }`.
*/
deprecated predicate isLoopHeader() {
this.inLoop() and
exists(BasicBlock pred | pred = this.getAPredecessor() and not pred = this.getASuccessor+())
}
/**
* Holds if control flow may reach this basic block from a function entry
* point or any handler of a reachable `try` statement.
*/
predicate isReachable() {
exists(Function f | f.getBlock() = this)
or
exists(TryStmt t, BasicBlock tryblock |
// a `Handler` preceeds the `CatchBlock`, and is always the beginning
// of a new `BasicBlock` (see `primitive_basic_block_entry_node`).
this.(Handler).getTryStmt() = t and
tryblock.isReachable() and
tryblock.contains(t)
)
or
exists(BasicBlock pred | pred.getASuccessor() = this and pred.isReachable())
}
/** Means `not isReachable()`. */
predicate isUnreachable() { not this.isReachable() }
}
/** Correct relation for reachability of ControlFlowNodes. */
predicate unreachable(ControlFlowNode n) {
exists(BasicBlock bb | bb.contains(n) and bb.isUnreachable())
}
/**
* An entry point of a function.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { exists(Function f | this = f.getEntryPoint()) }
}
/**
* A basic block whose last node is the exit point of a function.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() {
getEnd() instanceof Function or
aborting(getEnd())
}
}

View File

@@ -0,0 +1,145 @@
/**
* Provides a library for reasoning about control flow at the granularity of
* individual nodes in the control-flow graph.
*/
import cpp
import BasicBlocks
private import semmle.code.cpp.controlflow.internal.ConstantExprs
private import semmle.code.cpp.controlflow.internal.CFG
/**
* A control-flow node is either a statement or an expression; in addition,
* functions are control-flow nodes representing the exit point of the
* function. The graph represents one possible evaluation order out of all the
* ones the compiler might have picked.
*
* Control-flow nodes have successors and predecessors at the expression level,
* so control flow is accurately represented in expressions as well as between
* statements. Statements and initializers precede their contained expressions,
* and expressions deeper in the tree precede those higher up; for example, the
* statement `x = y + 1` gets a control-flow graph that looks like
*
* ```
* ExprStmt -> y -> 1 -> (+) -> x -> (=)
* ```
*
* The first control-flow node in a function is the body of the function (a
* block), and the last is the function itself, which is used to represent the
* exit point.
*
* Each `throw` expression or `Handler` has a path (along any necessary
* destructor calls) to its nearest enclosing `Handler` within the same
* function, or to the exit point of the function if there is no such
* `Handler`. There are no edges from function calls to `Handler`s.
*/
class ControlFlowNode extends Locatable, ControlFlowNodeBase {
/** Gets a direct successor of this control-flow node, if any. */
ControlFlowNode getASuccessor() { successors_adapted(this, result) }
/** Gets a direct predecessor of this control-flow node, if any. */
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
/** Gets the function containing this control-flow node. */
Function getControlFlowScope() {
none() // overridden in subclasses
}
/** Gets the smallest statement containing this control-flow node. */
Stmt getEnclosingStmt() {
none() // overridden in subclasses
}
/**
* Holds if this node is the top-level expression of a conditional statement,
* meaning that `this.getATrueSuccessor()` or `this.getAFalseSuccessor()`
* will have a result.
*/
predicate isCondition() {
exists(this.getATrueSuccessor()) or
exists(this.getAFalseSuccessor())
}
/**
* Gets a node such that the control-flow edge `(this, result)` may be
* taken when this expression is true.
*/
ControlFlowNode getATrueSuccessor() {
qlCFGTrueSuccessor(this, result) and
result = getASuccessor()
}
/**
* Gets a node such that the control-flow edge `(this, result)` may be
* taken when this expression is false.
*/
ControlFlowNode getAFalseSuccessor() {
qlCFGFalseSuccessor(this, result) and
result = getASuccessor()
}
/** Gets the `BasicBlock` containing this control-flow node. */
BasicBlock getBasicBlock() { result.getANode() = this }
}
import ControlFlowGraphPublic
/**
* An element that is convertible to `ControlFlowNode`. This class is similar
* to `ControlFlowNode` except that is has no member predicates apart from
* `toString`.
*
* This class can be used as base class for classes that want to inherit the
* extent of `ControlFlowNode` without inheriting its public member predicates.
*/
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
/**
* DEPRECATED: Use `ControlFlowNode.getATrueSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
deprecated predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
/**
* DEPRECATED: Use `ControlFlowNode.getAFalseSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
deprecated predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}
/**
* An abstract class that can be extended to add additional edges to the
* control-flow graph. Instances of this class correspond to the source nodes
* of such edges, and the predicate `getAnEdgeTarget` should be overridden to
* produce the target nodes of each source.
*
* Changing the control-flow graph in some queries and not others can be
* expensive in execution time and disk space. Most cached predicates in the
* library depend on the control-flow graph, so these predicates will be
* computed and cached for each variation of the control-flow graph
* that is used.
*
* Edges added by this class will still be removed by the library if they
* appear to be unreachable. See the documentation on `ControlFlowNode` for
* more information about the control-flow graph.
*/
abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
/** Gets a target node of this edge, where the source node is `this`. */
abstract ControlFlowNodeBase getAnEdgeTarget();
}
/**
* Holds if there is a control-flow edge from `source` to `target` in either
* the extractor-generated control-flow graph or in a subclass of
* `AdditionalControlFlowEdge`. Use this relation instead of `qlCFGSuccessor`.
*/
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
qlCFGSuccessor(source, target)
or
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
}

View File

@@ -0,0 +1,166 @@
/**
* Provides a simple data flow analysis to find expressions that are definitely
* null or that may be null.
*/
import cpp
import Nullness
import Dereferenced
/**
* INTERNAL: Do not use.
* A string that identifies a data flow analysis along with a set of member
* predicates that implement this analysis.
*/
abstract class DataflowAnnotation extends string {
DataflowAnnotation() { this = ["pointer-null", "pointer-valid"] }
/** Holds if this annotation is the default annotation. */
abstract predicate isDefault();
/** Holds if this annotation is generated when analyzing expression `e`. */
abstract predicate generatedOn(Expr e);
/**
* Holds if this annotation is generated for the variable `v` when
* the control-flow edge `(src, dest)` is taken.
*/
abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
/**
* Holds if this annotation is removed for the variable `v` when
* the control-flow edge `(src, dest)` is taken.
*/
abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
/** Holds if expression `e` is given this annotation. */
predicate marks(Expr e) {
this.generatedOn(e) and reachable(e)
or
this.marks(e.(AssignExpr).getRValue())
or
exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess())
}
/** Holds if the variable `v` accessed in control-flow node `n` is given this annotation. */
predicate marks(LocalScopeVariable v, ControlFlowNode n) {
v.getAnAccess().getEnclosingFunction().getBlock() = n and
this.isDefault()
or
this.marks(n.(Initializer).getExpr()) and
v.getInitializer() = n
or
exists(ControlFlowNode pred |
this.generatedBy(v, pred, n) and
not this.killedBy(v, pred, n) and
reachable(pred)
)
or
exists(ControlFlowNode pred |
this.assignedBy(v, pred, n) and
not this.killedBy(v, pred, n) and
reachable(pred)
)
or
exists(ControlFlowNode pred |
this.preservedBy(v, pred, n) and
not this.killedBy(v, pred, n) and
reachable(pred)
)
}
/**
* Holds if the variable `v` preserves this annotation when the control-flow
* edge `(src, dest)` is taken.
*/
predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(v, src) and
src.getASuccessor() = dest and
not v.getInitializer() = dest and
not v.getAnAssignment() = src
}
/**
* Holds if the variable `v` is assigned this annotation when `src` is an assignment
* expression that assigns to `v` and the control-flow edge `(src, dest)` is taken.
*/
predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(src.(AssignExpr).getRValue()) and
src = v.getAnAssignment() and
src.getASuccessor() = dest
}
}
/**
* INTERNAL: Do not use.
* Two analyses relating to nullness: `"pointer-null"` and `"pointer-valid"`.
* These analyses mark expressions that are possibly null or possibly non-null,
* respectively.
*/
class NullnessAnnotation extends DataflowAnnotation {
NullnessAnnotation() { this = ["pointer-null", "pointer-valid"] }
override predicate isDefault() { this = "pointer-valid" }
override predicate generatedOn(Expr e) {
exists(Variable v |
v.getAnAccess() = e and
(v instanceof GlobalVariable or v instanceof Field) and
this.isDefault()
)
or
e instanceof Call and this = "pointer-valid"
or
nullValue(e) and this = "pointer-null"
}
override predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
src.(AnalysedExpr).getNullSuccessor(v) = dest and this = "pointer-valid"
or
src.(AnalysedExpr).getNonNullSuccessor(v) = dest and this = "pointer-null"
or
dest = src.getASuccessor() and callByReference(src, v) and not this.isDefault()
or
dest = src.getASuccessor() and deref(v, src) and this = "pointer-null"
}
override predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
dest = src.getASuccessor() and
callByReference(src, v) and
this.isDefault()
}
}
/**
* Holds if evaluation of `op` dereferences `v`.
*/
predicate deref(Variable v, Expr op) { dereferencedByOperation(op, v.getAnAccess()) }
/**
* Holds if `call` passes `v` by reference, either with an explicit address-of
* operator or implicitly as a C++ reference. Both const and non-const
* references are included.
*/
predicate callByReference(Call call, Variable v) {
exists(Expr arg |
call.getAnArgument() = arg and
(
arg.(AddressOfExpr).getAChild() = v.getAnAccess()
or
v.getAnAccess() = arg and arg.getConversion*() instanceof ReferenceToExpr
)
)
}
/**
* Holds if a simple data-flow analysis determines that `e` is definitely null.
*/
predicate definitelyNull(Expr e) {
"pointer-null".(NullnessAnnotation).marks(e) and
not "pointer-valid".(NullnessAnnotation).marks(e)
}
/**
* Holds if a simple data-flow analysis determines that `e` may be null.
*/
predicate maybeNull(Expr e) { "pointer-null".(NullnessAnnotation).marks(e) }

View File

@@ -0,0 +1,383 @@
/**
* Provides classes and predicates for reasoning about definitions and uses of variables.
*/
import cpp
private import semmle.code.cpp.controlflow.StackVariableReachability
private import semmle.code.cpp.dataflow.EscapesTree
/**
* Computed relation: A "definition-use-pair" for a particular variable.
* Intuitively, this means that `def` is an assignment to `var`, and
* `use` is a read of `var` at which the value assigned by `def` may
* be read. (There can be more than one definition reaching a single
* use, and a single definition can reach many uses.)
*/
predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
exists(Use u |
u = use and
def.(Def).reaches(true, var, u) and
u.getVariable(false) = var
)
}
/**
* Holds if the definition `def` of some stack variable can reach `node`, which
* is a definition or use, without crossing definitions of the same variable.
*/
predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node.(DefOrUse)) }
private predicate hasAddressOfAccess(SemanticStackVariable var) {
var.getAnAccess().isAddressOfAccessNonConst()
}
/**
* A use/use pair is a pair of uses of a particular variable `var`
* where the same value might be read (meaning that there is a
* control-flow path from `first` to `second` without crossing
* a definition of `var`).
*/
predicate useUsePair(SemanticStackVariable var, Expr first, Expr second) {
(
/*
* If the address of `var` is used anywhere, we require that
* a definition of `var` can reach the first use. This is to
* rule out examples such as this:
* ```
* int x = 0;
* int& y = x;
* use(x);
* y = 1;
* use(x); // not a use-use pair with the use above
* ```
*/
hasAddressOfAccess(var) implies definitionUsePair(var, _, first)
) and
// If `first` is both a def and a use, like `x` in `f(x)` when `f` takes a
// reference parameter, it'll play the role of a use first and a def second.
// We are not interested in uses that follow its role as a def.
not definition(var, first) and
exists(Use u |
u = second and
first.(Use).reaches(false, var, u) and
u.getVariable(false) = var
)
}
/**
* Holds if `va` is a use of the parameter `p` that could
* observe the passed-in value.
*/
predicate parameterUsePair(Parameter p, VariableAccess va) {
not parameterIsOverwritten(_, p) and va.getTarget() = p
or
exists(ParameterDef pd | pd.reaches(true, p, va.(Use)))
}
/**
* Utility class: A definition or use of a stack variable.
*/
library class DefOrUse extends ControlFlowNodeBase {
DefOrUse() {
// Uninstantiated templates are purely syntax, and only on instantiation
// will they be complete with information about types, conversions, call
// targets, etc.
not this.(ControlFlowNode).isFromUninstantiatedTemplate(_)
}
/**
* Gets the target variable of this definition or use.
*
* The `isDef` parameter is needed in order to ensure disjointness
* of definitions and uses; in a variable initialization such as
* `int x = y`, `y` is both a definition of `x`, as well as a use of
* `y`, and `isDef` is used to distinguish the two situations.
*/
abstract SemanticStackVariable getVariable(boolean isDef);
pragma[noinline]
private predicate reaches_helper(boolean isDef, SemanticStackVariable v, BasicBlock bb, int i) {
getVariable(isDef) = v and
bb.getNode(i) = this
}
/**
* Holds if the value of `v` in this control-flow node reaches
* `defOrUse` along some control-flow path without crossing a
* definition of `v`.
*/
cached
predicate reaches(boolean isDef, SemanticStackVariable v, DefOrUse defOrUse) {
/*
* Implementation detail: this predicate and its private auxiliary
* predicates are instances of the more general predicates in
* StackVariableReachability.qll, and should be kept in sync.
*
* Unfortunately, caching of abstract predicates does not work well, so the
* predicates are duplicated for now.
*/
exists(BasicBlock bb, int i | reaches_helper(isDef, v, bb, i) |
exists(int j |
j > i and
(bbDefAt(bb, j, v, defOrUse) or bbUseAt(bb, j, v, defOrUse)) and
not exists(int k | firstBarrierAfterThis(isDef, k, v) and k < j)
)
or
not firstBarrierAfterThis(isDef, _, v) and
bbSuccessorEntryReachesDefOrUse(bb, v, defOrUse, _)
)
}
private predicate firstBarrierAfterThis(boolean isDef, int j, SemanticStackVariable v) {
exists(BasicBlock bb, int i |
getVariable(isDef) = v and
bb.getNode(i) = this and
j = min(int k | bbBarrierAt(bb, k, v, _) and k > i)
)
}
}
/** A definition of a stack variable. */
library class Def extends DefOrUse {
Def() { definition(_, this) }
override SemanticStackVariable getVariable(boolean isDef) {
definition(result, this) and isDef = true
}
}
/** Holds if parameter `p` is potentially overwritten in the body of `f`. */
private predicate parameterIsOverwritten(Function f, Parameter p) {
f.getAParameter() = p and
definitionBarrier(p, _)
}
/** A definition of a parameter. */
library class ParameterDef extends DefOrUse {
ParameterDef() {
// Optimization: parameters that are not overwritten do not require
// reachability analysis
exists(Function f | parameterIsOverwritten(f, _) | this = f.getEntryPoint())
}
override SemanticStackVariable getVariable(boolean isDef) {
exists(Function f | parameterIsOverwritten(f, result) | this = f.getEntryPoint()) and
isDef = true
}
}
/** A use of a stack variable. */
library class Use extends DefOrUse {
Use() { useOfVar(_, this) }
override SemanticStackVariable getVariable(boolean isDef) {
useOfVar(result, this) and isDef = false
}
}
private predicate bbUseAt(BasicBlock bb, int i, SemanticStackVariable v, Use use) {
bb.getNode(i) = use and
use.getVariable(false) = v
}
private predicate bbDefAt(BasicBlock bb, int i, SemanticStackVariable v, Def def) {
bb.getNode(i) = def and
def.getVariable(true) = v
}
private predicate bbBarrierAt(BasicBlock bb, int i, SemanticStackVariable v, ControlFlowNode node) {
bb.getNode(i) = node and
definitionBarrier(v, node)
}
/**
* Holds if the entry node of a _successor_ of basic block `bb` can
* reach `defOrUse` without going through a barrier for `v`.
* `defOrUse` may either be in the successor block, or in another
* basic block reachable from the successor.
*
* `skipsFirstLoopAlwaysTrueUponEntry` indicates whether the first loop
* on the path to `defOrUse` (if any), where the condition is provably
* true upon entry, is skipped (including the step from `bb` to the
* successor).
*/
private predicate bbSuccessorEntryReachesDefOrUse(
BasicBlock bb, SemanticStackVariable v, DefOrUse defOrUse,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry)
|
bbEntryReachesDefOrUseLocally(succ, v, defOrUse) and
succSkipsFirstLoopAlwaysTrueUponEntry = false and
not excludeReachesFunction(bb.getEnclosingFunction())
or
not bbBarrierAt(succ, _, v, _) and
bbSuccessorEntryReachesDefOrUse(succ, v, defOrUse, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesDefOrUseLocally(
BasicBlock bb, SemanticStackVariable v, DefOrUse defOrUse
) {
exists(int n | bbDefAt(bb, n, v, defOrUse) or bbUseAt(bb, n, v, defOrUse) |
not exists(int m | m < n | bbBarrierAt(bb, m, v, _))
)
}
/**
* Holds if `barrier` is either a (potential) definition of `v` or follows an
* access that gets the address of `v`. In both cases, the value of
* `v` after `barrier` cannot be assumed to be the same as before.
*/
predicate definitionBarrier(SemanticStackVariable v, ControlFlowNode barrier) {
definition(v, barrier)
or
exists(VariableAccess va |
// `va = barrier` does not work, as that could generate an
// incorrect use-use pair (a barrier must exist _between_
// uses):
//
// x = 0;
// int& y = x; // use1
// y = 1;
// use(x); // use2
va.getASuccessor() = barrier and
va.getTarget() = v and
va.isAddressOfAccessNonConst() and
not exists(Call c | c.passesByReferenceNonConst(_, va)) // handled in definitionByReference
)
}
/**
* Holds if `def` is a (potential) assignment to stack variable `v`. That is,
* the variable may hold another value in the control-flow node(s)
* following `def` than before.
*/
predicate definition(SemanticStackVariable v, Expr def) {
def = v.getInitializer().getExpr()
or
variableAccessedAsValue(v.getAnAccess(), def.(Assignment).getLValue().getFullyConverted())
or
variableAccessedAsValue(v.getAnAccess(), def.(CrementOperation).getOperand().getFullyConverted())
or
exists(AsmStmt asmStmt |
def = asmStmt.getAChild() and
def = v.getAnAccess().getParent*()
)
or
definitionByReference(v.getAnAccess(), def)
}
/**
* Holds if `def` is a (definite) assignment to the stack variable `v`. `e` is
* the assigned expression.
*/
predicate exprDefinition(SemanticStackVariable v, ControlFlowNode def, Expr e) {
def = v.getInitializer().getExpr() and
def = e and
not v.getType() instanceof ReferenceType
or
exists(AssignExpr assign |
def = assign and
assign.getLValue() = v.getAnAccess() and
e = assign.getRValue()
)
}
pragma[noinline]
private predicate containsAssembly(Function f) { f = any(AsmStmt s).getEnclosingFunction() }
/**
* Holds if `va` is a variable passed by reference as argument `def`, where the
* callee potentially assigns the corresponding parameter. The
* definitions-and-uses library models assignment by reference as if it happens
* on evaluation of the argument, `def`.
*
* All library functions except `std::move` are assumed to assign
* call-by-reference parameters, and source code functions are assumed to
* assign call-by-reference parameters that are accessed somewhere within the
* function. The latter is an over-approximation, but avoids having to take
* aliasing of the parameter into account.
*/
predicate definitionByReference(VariableAccess va, Expr def) {
exists(Call c, int i |
c.passesByReferenceNonConst(i, va) and
def = c.getArgument(i) and
forall(Function f | f = c.getTarget() and f.hasEntryPoint() |
exists(f.getParameter(i).getAnAccess())
or
f.isVarargs() and i >= f.getNumberOfParameters()
or
// If the callee contains an AsmStmt, then it is better to
// be conservative and assume that the parameter can be
// modified.
containsAssembly(f)
)
)
or
// Extractor artifact when using templates; an expression call where the
// target expression is an unknown literal. Since we cannot know what
// these calls represent, assume they assign all their arguments
exists(ExprCall c, Literal l |
l = c.getExpr() and
c.getAnArgument() = va and
not exists(l.getValue()) and
def = va
)
}
private predicate accessInSizeof(VariableAccess use) { use.getParent+() instanceof SizeofOperator }
/**
* Holds if `use` is a non-definition use of stack variable `v`. This will not
* include accesses on the LHS of an assignment (which don't retrieve the
* variable value), but _will_ include accesses in increment/decrement
* operations.
*/
predicate useOfVar(SemanticStackVariable v, VariableAccess use) {
use = v.getAnAccess() and
not exists(AssignExpr e | e.getLValue() = use) and
// sizeof accesses are resolved at compile-time
not accessInSizeof(use)
}
/**
* Same as `useOfVar(v, use)`, but with the extra condition that the
* access `use` actually reads the value of the stack variable `v` at
* run-time. (Non-examples include `&x` and function calls where the
* callee does not use the relevant parameter.)
*/
predicate useOfVarActual(SemanticStackVariable v, VariableAccess use) {
useOfVar(v, use) and
exists(Expr e |
variableAccessedAsValue(use, e) and
not exists(AssignExpr assign | e = assign.getLValue().getFullyConverted())
) and
// A call to a function that does not use the relevant parameter
not exists(Call c, int i |
c.getArgument(i) = use and
c.getTarget().hasEntryPoint() and
not exists(c.getTarget().getParameter(i).getAnAccess())
)
}
/**
* A function that should be excluded from 'reaches' analysis.
*
* The current implementation performs badly in some cases where a
* function has both a huge number of def/uses and a huge number of
* basic blocks, typically in generated code. We exclude these
* functions based on the former because it is cheaper to calculate.
*/
private predicate excludeReachesFunction(Function f) {
exists(int defOrUses |
defOrUses =
count(Def def | def.(ControlFlowNode).getControlFlowScope() = f) +
count(Use use | use.(ControlFlowNode).getControlFlowScope() = f) and
defOrUses >= 13000
)
}

View File

@@ -0,0 +1,110 @@
/**
* Provides predicates for detecting whether an expression dereferences a pointer.
*/
import cpp
import Nullness
import semmle.code.cpp.models.interfaces.ArrayFunction
/**
* Holds if the call `fc` will dereference argument `i`.
*/
predicate callDereferences(FunctionCall fc, int i) {
exists(ArrayFunction af |
fc.getTarget() = af and
(
af.hasArrayInput(i) or
af.hasArrayOutput(i)
)
)
or
exists(FormattingFunction ff |
fc.getTarget() = ff and
i >= ff.getFirstFormatArgumentIndex() and
fc.getArgument(i).getType() instanceof PointerType
)
}
/**
* Holds if evaluation of `op` dereferences `e`.
*/
predicate dereferencedByOperation(Expr op, Expr e) {
exists(PointerDereferenceExpr deref |
deref.getAChild() = e and
deref = op and
not deref.getParent*() instanceof SizeofOperator
)
or
exists(CrementOperation crement | dereferencedByOperation(e, op) and crement.getOperand() = e)
or
exists(ArrayExpr ae |
(
not ae.getParent() instanceof AddressOfExpr and
not ae.getParent*() instanceof SizeofOperator
) and
ae = op and
(
e = ae.getArrayBase() and e.getType() instanceof PointerType
or
e = ae.getArrayOffset() and e.getType() instanceof PointerType
)
)
or
exists(AddressOfExpr addof, ArrayExpr ae |
dereferencedByOperation(addof, op) and
addof.getOperand() = ae and
(e = ae.getArrayBase() or e = ae.getArrayOffset()) and
e.getType() instanceof PointerType
)
or
exists(UnaryArithmeticOperation arithop |
dereferencedByOperation(arithop, op) and
e = arithop.getAnOperand() and
e.getType() instanceof PointerType
)
or
exists(BinaryArithmeticOperation arithop |
dereferencedByOperation(arithop, op) and
e = arithop.getAnOperand() and
e.getType() instanceof PointerType
)
or
exists(FunctionCall fc, int i |
(callDereferences(fc, i) or functionCallDereferences(fc, i)) and
e = fc.getArgument(i) and
op = fc
)
or
// ptr->Field
e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType())
or
// ptr->method()
e = op.(Call).getQualifier() and isClassPointerType(e.getType())
}
private predicate isClassPointerType(Type t) {
t.getUnderlyingType().(PointerType).getBaseType().getUnderlyingType() instanceof Class
}
/**
* Holds if `e` will be dereferenced after being evaluated.
*/
predicate dereferenced(Expr e) { dereferencedByOperation(_, e) }
pragma[noinline]
private predicate functionCallDereferences(FunctionCall fc, int i) {
functionDereferences(fc.getTarget(), i)
}
/**
* Holds if the body of a function `f` is likely to dereference its `i`th
* parameter unconditionally. This analysis does not account for reassignment.
*/
predicate functionDereferences(Function f, int i) {
exists(VariableAccess access, Parameter p |
p = f.getParameter(i) and
dereferenced(access) and
access = p.getAnAccess() and
not checkedValid(p, access)
)
}

View File

@@ -0,0 +1,160 @@
/**
* Provides dominance predicates for control-flow nodes.
*
* These variations of the _dominance relation_ are used for computing SSA
* form. Formally, a node `d` _dominates_ a node `n` if all paths from the
* function entry point to `n` go through `d`; this applies within a function
* and only for nodes reachable from the entry point. Unreachable nodes are not
* part the dominance relation.
*/
import cpp
/**
* In rare cases, the same node is used in multiple control-flow scopes. This
* confuses the dominance analysis, so this predicate is used to exclude them.
*/
pragma[noinline]
private predicate hasMultiScopeNode(Function f) {
exists(ControlFlowNode node |
node.getControlFlowScope() = f and
node.getControlFlowScope() != f
)
}
/** Holds if `entry` is the entry point of a function. */
predicate functionEntry(ControlFlowNode entry) {
exists(Function function |
function.getEntryPoint() = entry and
not hasMultiScopeNode(function)
)
}
/** Holds if `exit` is the exit node of a function. */
predicate functionExit(ControlFlowNode exit) {
exists(Function function |
function = exit and
not hasMultiScopeNode(function)
)
}
/**
* Holds if `dest` is an immediate successor of `src` in the control-flow graph.
*/
private predicate nodeSucc(ControlFlowNode src, ControlFlowNode dest) { src.getASuccessor() = dest }
/**
* Holds if `pred` is an immediate predecessor of `src` in the control-flow graph.
*/
private predicate nodePred(ControlFlowNode src, ControlFlowNode pred) {
src.getAPredecessor() = pred
}
/**
* Holds if `dominator` is an immediate dominator of `node` in the control-flow
* graph.
*/
predicate iDominates(ControlFlowNode dominator, ControlFlowNode node) =
idominance(functionEntry/1, nodeSucc/2)(_, dominator, node)
/**
* Holds if `postDominator` is an immediate post-dominator of `node` in the control-flow
* graph.
*/
predicate iPostDominates(ControlFlowNode postDominator, ControlFlowNode node) =
idominance(functionExit/1, nodePred/2)(_, postDominator, node)
/**
* Holds if `dominator` is a strict dominator of `node` in the control-flow
* graph. Being strict means that `dominator != node`.
*/
predicate strictlyDominates(ControlFlowNode dominator, ControlFlowNode node) {
iDominates+(dominator, node)
}
/**
* Holds if `postDominator` is a strict post-dominator of `node` in the control-flow
* graph. Being strict means that `postDominator != node`.
*/
predicate strictlyPostDominates(ControlFlowNode postDominator, ControlFlowNode node) {
iPostDominates+(postDominator, node)
}
/**
* Holds if `dominator` is a dominator of `node` in the control-flow graph. This
* is reflexive.
*/
predicate dominates(ControlFlowNode dominator, ControlFlowNode node) {
strictlyDominates(dominator, node) or dominator = node
}
/**
* Holds if `postDominator` is a post-dominator of `node` in the control-flow graph. This
* is reflexive.
*/
predicate postDominates(ControlFlowNode postDominator, ControlFlowNode node) {
strictlyPostDominates(postDominator, node) or postDominator = node
}
/*
* Dominance predicates for basic blocks.
*/
/**
* Holds if `dominator` is an immediate dominator of `node` in the control-flow
* graph of basic blocks.
*/
predicate bbIDominates(BasicBlock dom, BasicBlock node) =
idominance(functionEntry/1, bb_successor/2)(_, dom, node)
/**
* Holds if `pred` is a predecessor of `succ` in the control-flow graph of
* basic blocks.
*/
private predicate bb_predecessor(BasicBlock succ, BasicBlock pred) { bb_successor(pred, succ) }
/** Holds if `exit` is an `ExitBasicBlock`. */
private predicate bb_exit(ExitBasicBlock exit) { any() }
/**
* Holds if `postDominator` is an immediate post-dominator of `node` in the control-flow
* graph of basic blocks.
*/
predicate bbIPostDominates(BasicBlock pDom, BasicBlock node) =
idominance(bb_exit/1, bb_predecessor/2)(_, pDom, node)
/**
* Holds if `dominator` is a strict dominator of `node` in the control-flow
* graph of basic blocks. Being strict means that `dominator != node`.
*/
// magic prevents fastTC
pragma[nomagic]
predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) {
bbIDominates+(dominator, node)
}
/**
* Holds if `postDominator` is a strict post-dominator of `node` in the control-flow
* graph of basic blocks. Being strict means that `postDominator != node`.
*/
// magic prevents fastTC
pragma[nomagic]
predicate bbStrictlyPostDominates(BasicBlock postDominator, BasicBlock node) {
bbIPostDominates+(postDominator, node)
}
/**
* Holds if `dominator` is a dominator of `node` in the control-flow graph of
* basic blocks. This is reflexive.
*/
predicate bbDominates(BasicBlock dominator, BasicBlock node) {
bbStrictlyDominates(dominator, node) or dominator = node
}
/**
* Holds if `postDominator` is a post-dominator of `node` in the control-flow graph of
* basic blocks. This is reflexive.
*/
predicate bbPostDominates(BasicBlock postDominator, BasicBlock node) {
bbStrictlyPostDominates(postDominator, node) or postDominator = node
}

View File

@@ -0,0 +1,384 @@
/**
* Provides classes and predicates for reasoning about guards and the control
* flow elements controlled by those guards.
*/
import cpp
import semmle.code.cpp.controlflow.BasicBlocks
import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.controlflow.Dominance
/**
* A Boolean condition that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements.
*/
class GuardCondition extends Expr {
GuardCondition() { is_condition(this) }
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached
predicate controls(BasicBlock controlled, boolean testIsTrue) {
// This condition must determine the flow of control; that is, this
// node must be a top-level condition.
this.controlsBlock(controlled, testIsTrue)
or
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
this = binop and
lhs = binop.getLeftOperand() and
rhs = binop.getRightOperand() and
lhs.controls(controlled, testIsTrue) and
rhs.controls(controlled, testIsTrue)
)
or
exists(GuardCondition ne, GuardCondition operand |
this = operand and
operand = ne.(NotExpr).getOperand() and
ne.controls(controlled, testIsTrue.booleanNot())
)
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
compares_lt(this, left, right, k, isLessThan, testIsTrue)
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
* If `isLessThan = false` then this implies `left >= right + k`.
*/
cached
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
compares_eq(this, left, right, k, areEqual, testIsTrue)
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
* If `areEqual = false` then this implies `left != right + k`.
*/
cached
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(boolean testIsTrue |
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(BasicBlock thisblock | thisblock.contains(this) |
exists(BasicBlock succ |
testIsTrue = true and succ = this.getATrueSuccessor()
or
testIsTrue = false and succ = this.getAFalseSuccessor()
|
bbDominates(succ, controlled) and
forall(BasicBlock pred | pred.getASuccessor() = succ |
pred = thisblock or bbDominates(succ, pred) or not reachable(pred)
)
)
)
}
}
private predicate is_condition(Expr guard) {
guard.isCondition()
or
is_condition(guard.(BinaryLogicalOperation).getAnOperand())
or
exists(NotExpr cond | is_condition(cond) and cond.getOperand() = guard)
}
/*
* Simplification of equality expressions:
* Simplify conditions in the source to the canonical form l op r + k.
*/
/**
* Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`.
*
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
*/
private predicate compares_eq(
Expr test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
areEqual = true and testIsTrue = eq
or
areEqual = false and testIsTrue = eq.booleanNot()
)
or
logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
or
/* a == b + k => b == a - k */
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
or
complex_eq(test, left, right, k, areEqual, testIsTrue)
or
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_eq(test.(NotExpr).getOperand(), left, right, k, areEqual, isFalse)
)
}
/**
* If `test => part` and `part => left == right + k` then `test => left == right + k`.
* Similarly for the case where `test` is false.
*/
private predicate logical_comparison_eq(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(boolean partIsTrue, Expr part | test.impliesValue(part, partIsTrue, testIsTrue) |
compares_eq(part, left, right, k, areEqual, partIsTrue)
)
}
/** Rearrange various simple comparisons into `left == right + k` form. */
private predicate simple_comparison_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual
) {
left = cmp.getLeftOperand() and
cmp.getOperator() = "==" and
right = cmp.getRightOperand() and
k = 0 and
areEqual = true
or
left = cmp.getLeftOperand() and
cmp.getOperator() = "!=" and
right = cmp.getRightOperand() and
k = 0 and
areEqual = false
}
private predicate complex_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
or
add_eq(cmp, left, right, k, areEqual, testIsTrue)
}
// left - x == right + c => left == right + (c+x)
// left == (right - x) + c => left == right + (c-x)
private predicate sub_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(SubExpr lhs, int c, int x |
compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRightOperand()) and
k = c + x
)
or
exists(SubExpr rhs, int c, int x |
compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRightOperand()) and
k = c - x
)
}
// left + x == right + c => left == right + (c-x)
// left == (right + x) + c => left == right + (c+x)
private predicate add_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(AddExpr lhs, int c, int x |
compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
) and
k = c - x
)
or
exists(AddExpr rhs, int c, int x |
compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
) and
k = c + x
)
}
/*
* Simplification of inequality expressions:
* Simplify conditions in the source to the canonical form l < r + k.
*/
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
private predicate compares_lt(
Expr test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
or
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
or
logical_comparison_lt(test, left, right, k, isLt, testIsTrue)
or
complex_lt(test, left, right, k, isLt, testIsTrue)
or
/* (not (left < right + k)) => (left >= right + k) */
exists(boolean isGe | isLt = isGe.booleanNot() |
compares_ge(test, left, right, k, isGe, testIsTrue)
)
or
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_lt(test.(NotExpr).getOperand(), left, right, k, isLt, isFalse)
)
}
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
private predicate compares_ge(
Expr test, Expr left, Expr right, int k, boolean isGe, boolean testIsTrue
) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
}
/**
* If `test => part` and `part => left < right + k` then `test => left < right + k`.
* Similarly for the case where `test` evaluates false.
*/
private predicate logical_comparison_lt(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(boolean partIsTrue, Expr part | test.impliesValue(part, partIsTrue, testIsTrue) |
compares_lt(part, left, right, k, isLt, partIsTrue)
)
}
/** Rearrange various simple comparisons into `left < right + k` form. */
private predicate simple_comparison_lt(ComparisonOperation cmp, Expr left, Expr right, int k) {
left = cmp.getLeftOperand() and
cmp.getOperator() = "<" and
right = cmp.getRightOperand() and
k = 0
or
left = cmp.getLeftOperand() and
cmp.getOperator() = "<=" and
right = cmp.getRightOperand() and
k = 1
or
right = cmp.getLeftOperand() and
cmp.getOperator() = ">" and
left = cmp.getRightOperand() and
k = 0
or
right = cmp.getLeftOperand() and
cmp.getOperator() = ">=" and
left = cmp.getRightOperand() and
k = 1
}
private predicate complex_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
sub_lt(cmp, left, right, k, isLt, testIsTrue)
or
add_lt(cmp, left, right, k, isLt, testIsTrue)
}
// left - x < right + c => left < right + (c+x)
// left < (right - x) + c => left < right + (c-x)
private predicate sub_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(SubExpr lhs, int c, int x |
compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRightOperand()) and
k = c + x
)
or
exists(SubExpr rhs, int c, int x |
compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRightOperand()) and
k = c - x
)
}
// left + x < right + c => left < right + (c-x)
// left < (right + x) + c => left < right + (c+x)
private predicate add_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(AddExpr lhs, int c, int x |
compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
) and
k = c - x
)
or
exists(AddExpr rhs, int c, int x |
compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
) and
k = c + x
)
}
/** The `int` value of integer constant expression. */
private int int_value(Expr e) {
e.getUnderlyingType() instanceof IntegralType and
result = e.getValue().toInt()
}
/** An `SsaDefinition` with an additional predicate `isLt`. */
class GuardedSsa extends SsaDefinition {
/** Holds if this `SsaDefinition` is guarded such that `this(var) < gt + k` is `testIsTrue` in `block`. */
predicate isLt(StackVariable var, Expr gt, int k, BasicBlock block, boolean testIsTrue) {
exists(Expr luse, GuardCondition test | this.getAUse(var) = luse |
test.ensuresLt(luse, gt, k, block, testIsTrue)
)
}
}

View File

@@ -0,0 +1,703 @@
/**
* Provides classes and predicates for reasoning about guards and the control
* flow elements controlled by those guards.
*/
import cpp
import semmle.code.cpp.ir.IR
/**
* Holds if `block` consists of an `UnreachedInstruction`.
*
* We avoiding reporting an unreached block as being controlled by a guard. The unreached block
* has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock`
* and the `IRBlock`.
*/
pragma[noinline]
private predicate isUnreachedBlock(IRBlock block) {
block.getFirstInstruction() instanceof UnreachedInstruction
}
/**
* A Boolean condition in the AST that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements.
*/
cached
class GuardCondition extends Expr {
cached
GuardCondition() {
exists(IRGuardCondition ir | this = ir.getUnconvertedResultExpression())
or
// no binary operators in the IR
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
or
// the IR short-circuits if(!x)
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | this.(NotExpr).getOperand() = ir.getAST())
}
/**
* Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached
predicate controls(BasicBlock controlled, boolean testIsTrue) { none() }
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
none()
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
* If `isLessThan = false` then this implies `left >= right + k`.
*/
cached
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
none()
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
* If `areEqual = false` then this implies `left != right + k`.
*/
cached
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() }
}
/**
* A binary logical operator in the AST that guards one or more basic blocks.
*/
private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
GuardConditionFromBinaryLogicalOperator() {
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
this = binop and
lhs = binop.getLeftOperand() and
rhs = binop.getRightOperand() and
lhs.controls(controlled, testIsTrue) and
rhs.controls(controlled, testIsTrue)
)
}
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
part.comparesLt(left, right, k, isLessThan, partIsTrue)
)
}
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
part.comparesEq(left, right, k, areEqual, partIsTrue)
)
}
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(boolean testIsTrue |
comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
}
/**
* A `!` operator in the AST that guards one or more basic blocks, and does not have a corresponding
* IR instruction.
*/
private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr {
GuardConditionFromShortCircuitNot() {
not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | getOperand() = ir.getAST())
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
}
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot())
}
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot())
}
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
}
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot())
}
}
/**
* A Boolean condition in the AST that guards one or more basic blocks and has a corresponding IR
* instruction.
*/
private class GuardConditionFromIR extends GuardCondition {
IRGuardCondition ir;
GuardConditionFromIR() { this = ir.getUnconvertedResultExpression() }
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
// This condition must determine the flow of control; that is, this
// node must be a top-level condition.
this.controlsBlock(controlled, testIsTrue)
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(Instruction li, Instruction ri |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesLt(li.getAUse(), ri.getAUse(), k, isLessThan, testIsTrue)
)
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
* If `isLessThan = false` then this implies `left >= right + k`.
*/
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(Instruction li, Instruction ri, boolean testIsTrue |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesLt(li.getAUse(), ri.getAUse(), k, isLessThan, testIsTrue) and
this.controls(block, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
exists(Instruction li, Instruction ri |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesEq(li.getAUse(), ri.getAUse(), k, areEqual, testIsTrue)
)
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
* If `areEqual = false` then this implies `left != right + k`.
*/
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(Instruction li, Instruction ri, boolean testIsTrue |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesEq(li.getAUse(), ri.getAUse(), k, areEqual, testIsTrue) and
this.controls(block, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(IRBlock irb |
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled and
not isUnreachedBlock(irb)
)
}
}
/**
* A Boolean condition in the IR that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements. Note that `&&` and `||`
* don't have an explicit representation in the IR, and therefore will not appear as
* IRGuardConditions.
*/
cached
class IRGuardCondition extends Instruction {
ConditionalBranchInstruction branch;
cached
IRGuardCondition() { branch = get_branch_for_condition(this) }
/**
* Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached
predicate controls(IRBlock controlled, boolean testIsTrue) {
// This condition must determine the flow of control; that is, this
// node must be a top-level condition.
this.controlsBlock(controlled, testIsTrue)
or
exists(IRGuardCondition ne |
this = ne.(LogicalNotInstruction).getUnary() and
ne.controls(controlled, testIsTrue.booleanNot())
)
}
/**
* Holds if the control-flow edge `(pred, succ)` may be taken only if
* the value of this condition is `testIsTrue`.
*/
cached
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
pred.getASuccessor() = succ and
controls(pred, testIsTrue)
or
succ = getBranchSuccessor(testIsTrue) and
branch.getCondition() = this and
branch.getBlock() = pred
}
/**
* Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
*
* This predicate is intended to help with situations in which an inference can only be made
* based on an edge between a block with multiple successors and a block with multiple
* predecessors. For example, in the following situation, an inference can be made about the
* value of `x` at the end of the `if` statement, but there is no block which is controlled by
* the `if` statement when `x >= y`.
* ```
* if (x < y) {
* x = y;
* }
* return x;
* ```
*/
private IRBlock getBranchSuccessor(boolean testIsTrue) {
branch.getCondition() = this and
(
testIsTrue = true and
result.getFirstInstruction() = branch.getTrueSuccessor()
or
testIsTrue = false and
result.getFirstInstruction() = branch.getFalseSuccessor()
)
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesLt(Operand left, Operand right, int k, boolean isLessThan, boolean testIsTrue) {
compares_lt(this, left, right, k, isLessThan, testIsTrue)
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
* If `isLessThan = false` then this implies `left >= right + k`.
*/
cached
predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` on the edge from
* `pred` to `succ`. If `isLessThan = false` then this implies `left >= right + k`.
*/
cached
predicate ensuresLtEdge(
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean isLessThan
) {
exists(boolean testIsTrue |
compares_lt(this, left, right, k, isLessThan, testIsTrue) and
this.controlsEdge(pred, succ, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
compares_eq(this, left, right, k, areEqual, testIsTrue)
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
* If `areEqual = false` then this implies `left != right + k`.
*/
cached
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
exists(boolean testIsTrue |
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` on the edge from
* `pred` to `succ`. If `areEqual = false` then this implies `left != right + k`.
*/
cached
predicate ensuresEqEdge(
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
) {
exists(boolean testIsTrue |
compares_eq(this, left, right, k, areEqual, testIsTrue) and
this.controlsEdge(pred, succ, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
not isUnreachedBlock(controlled) and
//
// For this block to control the block `controlled` with `testIsTrue` the
// following must hold: Execution must have passed through the test; that
// is, `this` must strictly dominate `controlled`. Execution must have
// passed through the `testIsTrue` edge leaving `this`.
//
// Although "passed through the true edge" implies that
// `getBranchSuccessor(true)` dominates `controlled`, the reverse is not
// true, as flow may have passed through another edge to get to
// `getBranchSuccessor(true)`, so we need to assert that
// `getBranchSuccessor(true)` dominates `controlled` *and* that all
// predecessors of `getBranchSuccessor(true)` are either `this` or
// dominated by `getBranchSuccessor(true)`.
//
// For example, in the following snippet:
//
// if (x)
// controlled;
// false_successor;
// uncontrolled;
//
// `false_successor` dominates `uncontrolled`, but not all of its
// predecessors are `this` (`if (x)`) or dominated by itself. Whereas in
// the following code:
//
// if (x)
// while (controlled)
// also_controlled;
// false_successor;
// uncontrolled;
//
// the block `while (controlled)` is controlled because all of its
// predecessors are `this` (`if (x)`) or (in the case of `also_controlled`)
// dominated by itself.
//
// The additional constraint on the predecessors of the test successor implies
// that `this` strictly dominates `controlled` so that isn't necessary to check
// directly.
exists(IRBlock succ |
succ = this.getBranchSuccessor(testIsTrue) and
this.hasDominatingEdgeTo(succ) and
succ.dominates(controlled)
)
}
/**
* Holds if `(this, succ)` is an edge that dominates `succ`, that is, all other
* predecessors of `succ` are dominated by `succ`. This implies that `this` is the
* immediate dominator of `succ`.
*
* This is a necessary and sufficient condition for an edge to dominate anything,
* and in particular `bb1.hasDominatingEdgeTo(bb2) and bb2.dominates(bb3)` means
* that the edge `(bb1, bb2)` dominates `bb3`.
*/
private predicate hasDominatingEdgeTo(IRBlock succ) {
exists(IRBlock branchBlock | branchBlock = this.getBranchBlock() |
branchBlock.immediatelyDominates(succ) and
branchBlock.getASuccessor() = succ and
forall(IRBlock pred | pred = succ.getAPredecessor() and pred != branchBlock |
succ.dominates(pred)
or
// An unreachable `pred` is vacuously dominated by `succ` since all
// paths from the entry to `pred` go through `succ`. Such vacuous
// dominance is not included in the `dominates` predicate since that
// could cause quadratic blow-up.
not pred.isReachableFromFunctionEntry()
)
)
}
pragma[noinline]
private IRBlock getBranchBlock() { result = branch.getBlock() }
}
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
result.getCondition() = guard
or
exists(LogicalNotInstruction cond |
result = get_branch_for_condition(cond) and cond.getUnary() = guard
)
}
/**
* Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`.
*
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
*/
private predicate compares_eq(
Instruction test, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
areEqual = true and testIsTrue = eq
or
areEqual = false and testIsTrue = eq.booleanNot()
)
or
// I think this is handled by forwarding in controlsBlock.
//or
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
/* a == b + k => b == a - k */
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
or
complex_eq(test, left, right, k, areEqual, testIsTrue)
or
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, isFalse)
)
}
/** Rearrange various simple comparisons into `left == right + k` form. */
private predicate simple_comparison_eq(
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual
) {
left = cmp.getLeftOperand() and
cmp instanceof CompareEQInstruction and
right = cmp.getRightOperand() and
k = 0 and
areEqual = true
or
left = cmp.getLeftOperand() and
cmp instanceof CompareNEInstruction and
right = cmp.getRightOperand() and
k = 0 and
areEqual = false
}
private predicate complex_eq(
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
) {
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
or
add_eq(cmp, left, right, k, areEqual, testIsTrue)
}
/*
* Simplification of inequality expressions
* Simplify conditions in the source to the canonical form l < r + k.
*/
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
private predicate compares_lt(
Instruction test, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
) {
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
or
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
or
complex_lt(test, left, right, k, isLt, testIsTrue)
or
/* (not (left < right + k)) => (left >= right + k) */
exists(boolean isGe | isLt = isGe.booleanNot() |
compares_ge(test, left, right, k, isGe, testIsTrue)
)
or
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, isFalse)
)
}
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
private predicate compares_ge(
Instruction test, Operand left, Operand right, int k, boolean isGe, boolean testIsTrue
) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
}
/** Rearrange various simple comparisons into `left < right + k` form. */
private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Operand right, int k) {
left = cmp.getLeftOperand() and
cmp instanceof CompareLTInstruction and
right = cmp.getRightOperand() and
k = 0
or
left = cmp.getLeftOperand() and
cmp instanceof CompareLEInstruction and
right = cmp.getRightOperand() and
k = 1
or
right = cmp.getLeftOperand() and
cmp instanceof CompareGTInstruction and
left = cmp.getRightOperand() and
k = 0
or
right = cmp.getLeftOperand() and
cmp instanceof CompareGEInstruction and
left = cmp.getRightOperand() and
k = 1
}
private predicate complex_lt(
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
) {
sub_lt(cmp, left, right, k, isLt, testIsTrue)
or
add_lt(cmp, left, right, k, isLt, testIsTrue)
}
// left - x < right + c => left < right + (c+x)
// left < (right - x) + c => left < right + (c-x)
private predicate sub_lt(
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
) {
exists(SubInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(SubInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
}
// left + x < right + c => left < right + (c-x)
// left < (right + x) + c => left < right + (c+x)
private predicate add_lt(
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
) {
exists(AddInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(AddInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
}
// left - x == right + c => left == right + (c+x)
// left == (right - x) + c => left == right + (c-x)
private predicate sub_eq(
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
) {
exists(SubInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(SubInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
}
// left + x == right + c => left == right + (c-x)
// left == (right + x) + c => left == right + (c+x)
private predicate add_eq(
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
) {
exists(AddInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(AddInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
}
/** The int value of integer constant expression. */
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }

View File

@@ -0,0 +1,393 @@
/**
* DEPRECATED: Use `StackVariableReachability` instead.
*/
import cpp
/**
* DEPRECATED: Use `StackVariableReachability` instead.
*
* A reachability analysis for control-flow nodes involving stack variables.
* This defines sources, sinks, and any other configurable aspect of the
* analysis. Multiple analyses can coexist. To create an analysis, extend this
* class with a subclass whose characteristic predicate is a unique singleton
* string. For example, write
*
* ```
* class MyAnalysisConfiguration extends LocalScopeVariableReachability {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Override `isBarrier`.
* }
* ```
*
* Then, to query whether there is flow between some source and sink, call the
* `reaches` predicate on an instance of `MyAnalysisConfiguration`.
*/
abstract deprecated class LocalScopeVariableReachability extends string {
bindingset[this]
LocalScopeVariableReachability() { length() >= 0 }
/** Holds if `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v);
/** Holds if `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v);
/** Holds if `node` is a barrier for the reachability analysis using variable `v`. */
abstract predicate isBarrier(ControlFlowNode node, LocalScopeVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier. This is (almost) equivalent to the following QL predicate but
* uses basic blocks internally for better performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSink(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* sink = source.getASuccessor() and isSource(source, v)
* or
* exists(ControlFlowNode mid | reachesImpl(source, v, mid) |
* not isBarrier(mid, v)
* and
* sink = mid.getASuccessor()
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
/*
* Implementation detail: the predicates in this class are a generalization of
* those in DefinitionsAndUses.qll, and should be kept in sync.
*
* Unfortunately, caching of abstract predicates does not work well, so the
* predicates in DefinitionsAndUses.qll cannot use this library.
*/
exists(BasicBlock bb, int i |
isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
isSink(sink, v) and
not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
)
or
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
bbSuccessorEntryReaches(bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry)
|
bbEntryReachesLocally(succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not isBarrier(succ.getNode(_), v) and
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
exists(int n |
node = bb.getNode(n) and
isSink(node, v)
|
not exists(this.firstBarrierIndexIn(bb, v))
or
n <= this.firstBarrierIndexIn(bb, v)
)
}
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
result = min(int m | isBarrier(bb.getNode(m), v))
}
}
/**
* Holds if `bb` contains the entry point `loop` for a loop at position `i`.
* The condition of that loop is provably true upon entry but not provably
* true in general (if it were, the false-successor had already been removed
* from the CFG).
*
* Examples:
* ```
* for (int i = 0; i < 2; i++) { } // always true upon entry
* for (int i = 0; true; i++) { } // always true
* ```
*/
private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, ControlFlowNode loop) {
exists(Expr condition |
loopConditionAlwaysTrueUponEntry(loop, condition) and
not conditionAlwaysTrue(condition) and
bb.getNode(i) = loop
)
}
/**
* Basic block `pred` contains all or part of the condition belonging to a loop,
* and there is an edge from `pred` to `succ` that concludes the condition.
* If the edge corrseponds with the loop condition being found to be `true`, then
* `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
* within a complex loop condition are not matched by this predicate.
*/
private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
BasicBlock pred, BasicBlock succ, boolean skipsLoop
) {
exists(Expr cond |
loopConditionAlwaysTrueUponEntry(_, cond) and
cond.getAChild*() = pred.getEnd() and
succ = pred.getASuccessor() and
not cond.getAChild*() = succ.getStart() and
(
succ = pred.getAFalseSuccessor() and
skipsLoop = true
or
succ = pred.getATrueSuccessor() and
skipsLoop = false
)
)
}
/**
* Loop invariant for `bbSuccessorEntryReaches`:
*
* - `succ` is a successor of `pred`.
* - `predSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `pred` (via `succ`) skips the first loop where the condition is
* provably true upon entry.
* - `succSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `succ` skips the first loop where the condition is provably true
* upon entry.
* - If `pred` contains the entry point of a loop where the condition
* is provably true upon entry, then `succ` is not allowed to skip
* that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`).
*/
predicate bbSuccessorEntryReachesLoopInvariant(
BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry,
boolean succSkipsFirstLoopAlwaysTrueUponEntry
) {
succ = pred.getASuccessor() and
(succSkipsFirstLoopAlwaysTrueUponEntry = true or succSkipsFirstLoopAlwaysTrueUponEntry = false) and
(
// The edge from `pred` to `succ` is from a loop condition provably
// true upon entry, so the value of `predSkipsFirstLoopAlwaysTrueUponEntry`
// is determined by whether the true edge or the false edge is chosen,
// regardless of the value of `succSkipsFirstLoopAlwaysTrueUponEntry`.
bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry)
or
// The edge from `pred` to `succ` is _not_ from a loop condition provably
// true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry`
// and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same.
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, _) and
succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and
// Moreover, if `pred` contains the entry point of a loop where the
// condition is provably true upon entry, then `succ` is not allowed
// to skip that loop, and hence `succSkipsFirstLoopAlwaysTrueUponEntry = false`.
(
bbLoopEntryConditionAlwaysTrueAt(pred, _, _)
implies
succSkipsFirstLoopAlwaysTrueUponEntry = false
)
)
}
/**
* DEPRECATED: Use `StackVariableReachabilityWithReassignment` instead.
*
* Reachability analysis for control-flow nodes involving stack variables.
* Unlike `LocalScopeVariableReachability`, this analysis takes variable
* reassignments into account.
*
* This class is used like `LocalScopeVariableReachability`, except that
* subclasses should override `isSourceActual` and `isSinkActual` instead of
* `isSource` and `isSink`, and that there is a `reachesTo` predicate in
* addition to `reaches`.
*/
abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends LocalScopeVariableReachability {
bindingset[this]
LocalScopeVariableReachabilityWithReassignment() { length() >= 0 }
/** Override this predicate rather than `isSource` (`isSource` is used internally). */
abstract predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v);
/** Override this predicate rather than `isSink` (`isSink` is used internally). */
abstract predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier, taking reassignments into account. This is (almost) equivalent
* to the following QL predicate, but uses basic blocks internally for better
* performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSinkActual(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* isSourceActual(source, v)
* and
* (
* sink = source.getASuccessor()
* or
* exists(ControlFlowNode mid, SemanticStackVariable v0 | reachesImpl(source, v0, mid) |
* // ordinary successor
* not isBarrier(mid, v) and
* sink = mid.getASuccessor() and
* v = v0
* or
* // reassigned from v0 to v
* exprDefinition(v, mid, v0.getAnAccess()) and
* sink = mid.getASuccessor()
* )
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
reachesTo(source, v, sink, _)
}
/**
* As `reaches`, but also specifies the last variable it was reassigned to (`v0`).
*/
predicate reachesTo(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
) {
exists(ControlFlowNode def |
actualSourceReaches(source, v, def, v0) and
LocalScopeVariableReachability.super.reaches(def, v0, sink) and
isSinkActual(sink, v0)
)
}
private predicate actualSourceReaches(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
isSourceActual(source, v) and def = source and v0 = v
or
exists(ControlFlowNode source1, SemanticStackVariable v1 |
actualSourceReaches(source, v, source1, v1)
|
reassignment(source1, v1, def, v0)
)
}
private predicate reassignment(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
LocalScopeVariableReachability.super.reaches(source, v, def) and
exprDefinition(v0, def, v.getAnAccess())
}
final override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
isSourceActual(node, v)
or
// Reassignment generates a new (non-actual) source
reassignment(_, _, node, v)
}
final override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
isSinkActual(node, v)
or
// Reassignment generates a new (non-actual) sink
exprDefinition(_, node, v.getAnAccess())
}
}
/**
* DEPRECATED: Use `StackVariableReachabilityExt` instead.
*
* Same as `LocalScopeVariableReachability`, but `isBarrier` works on control-flow
* edges rather than nodes and is therefore parameterized by the original
* source node as well. Otherwise, this class is used like
* `LocalScopeVariableReachability`.
*/
abstract deprecated class LocalScopeVariableReachabilityExt extends string {
bindingset[this]
LocalScopeVariableReachabilityExt() { length() >= 0 }
/** `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v);
/** `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v);
/** `node` is a barrier for the reachability analysis using variable `v` and starting from `source`. */
abstract predicate isBarrier(
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
);
/** See `LocalScopeVariableReachability.reaches`. */
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
exists(BasicBlock bb, int i |
isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
isSink(sink, v) and
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
k in [i .. j - 1]
)
)
or
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
bbSuccessorEntryReaches(source, bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry) and
not isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
bbEntryReachesLocally(source, succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
isSource(source, v) and
exists(int n | node = bb.getNode(n) and isSink(node, v) |
not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
)
}
}

View File

@@ -0,0 +1,291 @@
/**
* Provides classes and predicates for working with null values and checks for nullness.
*/
import cpp
import DefinitionsAndUses
/**
* A C/C++ literal whose value is considered null.
*/
abstract class NullValue extends Expr { }
/**
* A C/C++ literal whose value is zero.
*/
class Zero extends NullValue {
Zero() { this.(Literal).getValue() = "0" }
}
/**
* Holds if `var` is null when `checkExpr` evaluates to a true value.
*/
cached
predicate nullCheckExpr(Expr checkExpr, Variable var) {
exists(LocalScopeVariable v, AnalysedExpr expr |
var = v and
checkExpr = expr
|
exists(NotExpr notexpr, AnalysedExpr child |
expr = notexpr and notexpr.getOperand() = child and validCheckExpr(child, v)
)
or
exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i |
expr = eq and
eq.getChild(i) = child and
validCheckExpr(child, v) and
eq.getChild(1 - i) = zero
)
or
exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i |
expr = neq and
neq.getChild(i) = child and
nullCheckExpr(child, v) and
neq.getChild(1 - i) = zero
)
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
op.getRightOperand() = child and
nullCheckExpr(child, v)
)
or
exists(AssignExpr assign, AnalysedExpr child |
expr = assign and
assign.getRValue() = child and
nullCheckExpr(child, v) and
not expr.isDef(v)
)
or
exists(FunctionCall fc, AnalysedExpr child |
expr = fc and
fc.getTarget().hasGlobalName("__builtin_expect") and
fc.getArgument(0) = child and
nullCheckExpr(child, v)
)
)
}
/**
* Holds if `var` is non-null when `checkExpr` evaluates to a true value.
*/
cached
predicate validCheckExpr(Expr checkExpr, Variable var) {
exists(AnalysedExpr expr, LocalScopeVariable v |
v = var and
expr = checkExpr
|
expr.isUse(v)
or
expr.isDef(v)
or
exists(NotExpr notexpr, AnalysedExpr child |
expr = notexpr and notexpr.getOperand() = child and nullCheckExpr(child, v)
)
or
exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i |
expr = eq and
eq.getChild(i) = child and
nullCheckExpr(child, v) and
eq.getChild(1 - i) = zero
)
or
exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i |
expr = neq and
neq.getChild(i) = child and
validCheckExpr(child, v) and
neq.getChild(1 - i) = zero
)
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
op.getRightOperand() = child and
validCheckExpr(child, v)
)
or
exists(AssignExpr assign, AnalysedExpr child |
expr = assign and
assign.getRValue() = child and
validCheckExpr(child, v) and
not expr.isDef(v)
)
or
exists(FunctionCall fc, AnalysedExpr child |
expr = fc and
fc.getTarget().hasGlobalName("__builtin_expect") and
fc.getArgument(0) = child and
validCheckExpr(child, v)
)
)
}
/**
* An expression that has been extended with member predicates that provide
* information about the role of this expression in nullness checks.
*/
class AnalysedExpr extends Expr {
/**
* Holds if `v` is null when this expression evaluates to a true value.
*/
predicate isNullCheck(LocalScopeVariable v) { nullCheckExpr(this, v) }
/**
* Holds if `v` is non-null when this expression evaluates to a true value.
*/
predicate isValidCheck(LocalScopeVariable v) { validCheckExpr(this, v) }
/**
* Gets a successor of `this` in the control flow graph, where that successor
* is among the nodes to which control may flow when `this` tests `v` to be
* _null_.
*/
ControlFlowNode getNullSuccessor(LocalScopeVariable v) {
this.isNullCheck(v) and result = this.getATrueSuccessor()
or
this.isValidCheck(v) and result = this.getAFalseSuccessor()
}
/**
* Gets a successor of `this` in the control flow graph, where that successor
* is among the nodes to which control may flow when `this` tests `v` to be
* _not null_.
*/
ControlFlowNode getNonNullSuccessor(LocalScopeVariable v) {
this.isNullCheck(v) and result = this.getAFalseSuccessor()
or
this.isValidCheck(v) and result = this.getATrueSuccessor()
}
/**
* DEPRECATED: Use `getNonNullSuccessor` instead, which does the same.
*/
deprecated ControlFlowNode getValidSuccessor(LocalScopeVariable v) {
this.isValidCheck(v) and result = this.getATrueSuccessor()
or
this.isNullCheck(v) and result = this.getAFalseSuccessor()
}
/**
* Holds if this is a `VariableAccess` of `v` nested inside a condition.
*/
predicate isUse(LocalScopeVariable v) {
this.inCondition() and
this = v.getAnAccess()
}
/**
* Holds if this is an `Assignment` to `v` nested inside a condition.
*/
predicate isDef(LocalScopeVariable v) {
this.inCondition() and
this.(Assignment).getLValue() = v.getAnAccess()
}
/**
* Holds if `this` occurs at or below the controlling expression of an `if`,
* `while`, `?:`, or similar.
*/
predicate inCondition() {
this.isCondition() or
this.getParent().(AnalysedExpr).inCondition()
}
}
/**
* Holds if `var` is likely to be null at `node`.
*/
cached
predicate checkedNull(Variable var, ControlFlowNode node) {
exists(LocalScopeVariable v | v = var |
exists(AnalysedExpr e | e.getNullSuccessor(v) = node)
or
exists(ControlFlowNode pred |
pred = node.getAPredecessor() and
not pred.(AnalysedExpr).isDef(v) and
checkedNull(v, pred)
)
)
}
/**
* Holds if `var` is likely to be non-null at `node`.
*/
cached
predicate checkedValid(Variable var, ControlFlowNode node) {
exists(LocalScopeVariable v | v = var |
exists(AnalysedExpr e | e.getNonNullSuccessor(v) = node)
or
exists(ControlFlowNode pred |
pred = node.getAPredecessor() and
not pred.(AnalysedExpr).isDef(v) and
checkedValid(v, pred)
)
)
}
/**
* Holds if `val` is a null literal or a call to a function that may return a
* null literal.
*/
predicate nullValue(Expr val) {
val instanceof NullValue
or
callMayReturnNull(val)
or
nullValue(val.(Assignment).getRValue())
}
/**
* Holds if the evaluation of `n` may have the effect of, directly or
* indirectly, assigning a null literal to `var`.
*/
predicate nullInit(Variable v, ControlFlowNode n) {
exists(Initializer init |
init = n and
nullValue(init.getExpr()) and
v.getInitializer() = init
)
or
exists(AssignExpr assign |
assign = n and
nullValue(assign.getRValue()) and
assign.getLValue() = v.getAnAccess()
)
}
/**
* Holds if `call` may, directly or indirectly, evaluate to a null literal.
*/
predicate callMayReturnNull(Call call) {
exists(Options opts |
if opts.overrideReturnsNull(call)
then opts.returnsNull(call)
else mayReturnNull(call.(FunctionCall).getTarget())
)
}
/**
* Holds if `f` may, directly or indirectly, return a null literal.
*/
predicate mayReturnNull(Function f) {
f.hasGlobalOrStdName("malloc")
or
f.hasGlobalOrStdName("calloc")
or
// f.hasGlobalName("strchr")
// or
// f.hasGlobalName("strstr")
// or
exists(ReturnStmt ret |
nullValue(ret.getExpr()) and
ret.getEnclosingFunction() = f
)
or
exists(ReturnStmt ret, Expr returned, ControlFlowNode init, LocalScopeVariable v |
ret.getExpr() = returned and
nullInit(v, init) and
definitionUsePair(v, init, returned) and
not checkedValid(v, returned) and
ret.getEnclosingFunction() = f
)
}

View File

@@ -0,0 +1,161 @@
/**
* Provides classes and predicates for SSA representation (Static Single Assignment form).
*/
import cpp
import semmle.code.cpp.controlflow.Dominance
import SSAUtils
/**
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
* This class provides the standard SSA logic.
*/
library class StandardSSA extends SSAHelper {
StandardSSA() { this = 0 }
}
/**
* A definition of one or more SSA variables, including phi node definitions.
* An _SSA variable_, as defined in the literature, is effectively the pair of
* an `SsaDefinition d` and a `StackVariable v`, written `(d, v)` in this
* documentation. Note that definitions and uses can be coincident due to the
* presence of parameter definitions and phi nodes.
*
* Not all `StackVariable`s of a function have SSA definitions. If the variable
* has its address taken, either explicitly or implicitly, then it is excluded
* from analysis. `SsaDefinition`s are not generated in locations that are
* statically seen to be unreachable.
*/
class SsaDefinition extends ControlFlowNodeBase {
SsaDefinition() { exists(StandardSSA x | x.ssa_defn(_, this, _, _)) }
/**
* Gets a variable corresponding to an SSA StackVariable defined by
* this definition.
*/
StackVariable getAVariable() { exists(StandardSSA x | x.ssa_defn(result, this, _, _)) }
/**
* Gets a string representation of the SSA variable represented by the pair
* `(this, v)`.
*/
string toString(StackVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(StackVariable v) { exists(StandardSSA x | result = x.getAUse(this, v)) }
/**
* Gets the control-flow node for this definition. This will usually be the
* control-flow node that assigns to this variable as a side effect, but
* there are some exceptions. If `this` is defined by initialization, the
* result is the value of `Initializer.getExpr()` for that initialization.
* If `this` is a function parameter (see `definedByParameter`), the result
* will be the function entry point. If `this` variable is defined by being
* passed as a reference in a function call, including overloaded
* operators, the result will be the `VariableAccess` expression for this
* parameter. If `this` is a phi node (see `isPhiNode`), the result will be
* the node where control flow is joined from multiple paths.
*/
ControlFlowNode getDefinition() { result = this }
/** Gets the `BasicBlock` containing this definition. */
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
/** Gets the location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
/** Holds if the SSA variable `(this, p)` is defined by parameter `p`. */
predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() }
/**
* Holds if the SSA variable `(result, v)` is an input to the phi definition
* `(this, v)`.
*/
SsaDefinition getAPhiInput(StackVariable v) {
this.isPhiNode(v) and
result.reachesEndOfBB(v, this.(BasicBlock).getAPredecessor())
}
/**
* Gets the expression assigned to the SSA variable `(this, v)`, if any,
* when it is not a phi definition. The following is an exhaustive list of
* expressions that may be the result of this predicate.
*
* - The contained expression of an `Initializer`.
* - The right-hand side of an `AssignExpr`.
* - An `AssignOperation`.
* - A `CrementOperation`.
*
* In all cases except `PostfixCrementOperation`, the variable `v` will be
* equal to the result of this predicate after evaluation of
* `this.getDefinition()`.
*
* If the SSA variable is defined in other ways than those four (such as
* function parameters or `f(&x)`) there is no result. These cases are
* instead covered via `definedByParameter` and `getDefinition`,
* respectively.
*/
Expr getDefiningValue(StackVariable v) {
exists(ControlFlowNode def | def = this.getDefinition() |
def = v.getInitializer().getExpr() and def = result
or
exists(AssignExpr assign |
def = assign and
assign.getLValue() = v.getAnAccess() and
result = assign.getRValue()
)
or
exists(AssignOperation assign |
def = assign and
assign.getLValue() = v.getAnAccess() and
result = assign
)
or
exists(CrementOperation crement |
def = crement and
crement.getOperand() = v.getAnAccess() and
result = crement
)
)
}
/** Holds if `(this, v)` reaches the end of basic block `b`. */
predicate reachesEndOfBB(StackVariable v, BasicBlock b) {
exists(StandardSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b))
}
/**
* Gets a definition that ultimately defines this variable and is not
* itself a phi node.
*/
SsaDefinition getAnUltimateSsaDefinition(StackVariable v) {
result = this.getAPhiInput(v).getAnUltimateSsaDefinition(v)
or
v = this.getAVariable() and
not this.isPhiNode(v) and
result = this
}
/**
* Gets a possible defining expression for `v` at this SSA definition,
* recursing backwards through phi definitions. Not all definitions have a
* defining expression---see the documentation for `getDefiningValue`.
*/
Expr getAnUltimateDefiningValue(StackVariable v) {
result = this.getAnUltimateSsaDefinition(v).getDefiningValue(v)
}
/**
* DEPRECATED: this is the old name for `getAnUltimateDefiningValue`. The
* name was confusing as it seemed analogous to `getDefinition` rather than
* `getDefiningValue`. The SSA libraries for other languages use the name
* `getAnUltimateSsaDefinition` to refer to a predicate named
* `getAnUltimateSsaDefinition` in this class.
*/
deprecated Expr getAnUltimateDefinition(StackVariable v) {
result = this.getAnUltimateDefiningValue(v)
}
}

View File

@@ -0,0 +1,311 @@
/**
* Provides classes and predicates for use in the SSA library.
*/
import cpp
import semmle.code.cpp.controlflow.Dominance
import semmle.code.cpp.controlflow.SSA // must be imported for proper caching of SSAHelper
import semmle.code.cpp.rangeanalysis.RangeSSA // must be imported for proper caching of SSAHelper
/**
* The dominance frontier of a block `x` is the set of all blocks `w` such that
* `x` dominates a predecessor of `w` but does not strictly dominate `w`.
*
* This implementation is equivalent to:
*
* bbDominates(x, w.getAPredecessor()) and not bbStrictlyDominates(x, w)
*/
private predicate dominanceFrontier(BasicBlock x, BasicBlock w) {
x = w.getAPredecessor() and not bbIDominates(x, w)
or
exists(BasicBlock prev | dominanceFrontier(prev, w) |
bbIDominates(x, prev) and
not bbIDominates(x, w)
)
}
/**
* Extended version of `definition` that also includes parameters.
*/
predicate var_definition(StackVariable v, ControlFlowNode node) {
not addressTakenVariable(v) and
not unreachable(node) and
(
if isReferenceVar(v)
then
// Assignments to reference variables modify the referenced
// value, not the reference itself. So reference variables only
// have two kinds of definition: initializers and parameters.
node = v.getInitializer().getExpr()
else definition(v, node)
)
or
v instanceof Parameter and
exists(BasicBlock b |
b.getStart() = node and
not exists(b.getAPredecessor()) and
b = v.(Parameter).getFunction().getEntryPoint()
)
}
/**
* Stack variables that have their address taken are excluded from the
* analysis because the pointer could be used to change the value at
* any moment.
*/
private predicate addressTakenVariable(StackVariable var) {
// If the type of the variable is a reference type, then it is safe (as
// far as SSA is concerned) to take its address, because this does not
// enable the variable to be modified indirectly. Obviously the
// referenced value can change, but that is not the same thing as
// changing which value the reference points to. SSA tracks the latter,
// but the target of a reference is immutable so reference variables
// always have exactly one definition.
not isReferenceVar(var) and
// Find a VariableAccess that takes the address of `var`.
exists(VariableAccess va |
va = var.getAnAccess() and
va.isAddressOfAccessNonConst() and
// If the address is passed to a function then we will trust that it
// is only used to modify the variable for the duration of the
// function call.
not exists(Call call | call.passesByReferenceNonConst(_, va))
)
}
/**
* Holds if `v` is a stack-allocated reference-typed local variable. We don't
* build SSA for such variables since they are likely to change values even
* when not syntactically mentioned. For the same reason,
* `addressTakenVariable` is used to prevent tracking variables that may be
* aliased by such a reference.
*
* Reference-typed parameters are treated as if they weren't references.
* That's because it's in practice highly unlikely that they alias other data
* accessible from the function body.
*/
private predicate isReferenceVar(StackVariable v) {
v.getUnspecifiedType() instanceof ReferenceType and
not v instanceof Parameter
}
/**
* This predicate is the same as `var_definition`, but annotated with
* the basic block and index of the control flow node.
*/
private predicate variableUpdate(StackVariable v, ControlFlowNode n, BasicBlock b, int i) {
var_definition(v, n) and n = b.getNode(i)
}
private predicate ssa_use(StackVariable v, VariableAccess node, BasicBlock b, int index) {
useOfVar(v, node) and b.getNode(index) = node
}
private predicate live_at_start_of_bb(StackVariable v, BasicBlock b) {
exists(int i | ssa_use(v, _, b, i) | not exists(int j | variableUpdate(v, _, b, j) | j < i))
or
live_at_exit_of_bb(v, b) and not variableUpdate(v, _, b, _)
}
pragma[noinline]
private predicate live_at_exit_of_bb(StackVariable v, BasicBlock b) {
live_at_start_of_bb(v, b.getASuccessor())
}
/** Common SSA logic for standard SSA and range-analysis SSA. */
cached
library class SSAHelper extends int {
/* 0 = StandardSSA, 1 = RangeSSA */
cached
SSAHelper() { this in [0 .. 1] }
/**
* Override to insert a custom phi node for variable `v` at the start of
* basic block `b`.
*/
cached
predicate custom_phi_node(StackVariable v, BasicBlock b) { none() }
/**
* Remove any custom phi nodes that are invalid.
*/
private predicate sanitized_custom_phi_node(StackVariable v, BasicBlock b) {
custom_phi_node(v, b) and
not addressTakenVariable(v) and
not isReferenceVar(v) and
b.isReachable()
}
/**
* Holds if there is a phi node for variable `v` at the start of basic block
* `b`.
*/
cached
predicate phi_node(StackVariable v, BasicBlock b) {
frontier_phi_node(v, b) or sanitized_custom_phi_node(v, b)
}
/**
* A phi node is required for variable `v` at the start of basic block `b`
* if there exists a basic block `x` such that `b` is in the dominance
* frontier of `x` and `v` is defined in `x` (including phi-nodes as
* definitions). This is known as the iterated dominance frontier. See
* Modern Compiler Implementation by Andrew Appel.
*/
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
live_at_start_of_bb(v, b)
}
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
phi_node(v, b)
or
variableUpdate(v, _, b, _)
}
/**
* Holds if `v` is defined, for the purpose of SSA, at `node`, which is at
* position `index` in block `b`. This includes definitions from phi nodes.
*/
cached
predicate ssa_defn(StackVariable v, ControlFlowNode node, BasicBlock b, int index) {
phi_node(v, b) and b.getStart() = node and index = -1
or
variableUpdate(v, node, b, index)
}
/*
* The construction of SSA form ensures that each use of a variable is
* dominated by its definition. A definition of an SSA variable therefore
* reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition
* that dominates the node. If two definitions dominate a node then one must
* dominate the other, so therefore the definition of _closest_ is given by the
* dominator tree. Thus, reaching definitions can be calculated in terms of
* dominance.
*/
/**
* A ranking of the indices `i` at which there is an SSA definition or use of
* `v` in the basic block `b`.
*
* Basic block indices are translated to rank indices in order to skip
* irrelevant indices at which there is no definition or use when traversing
* basic blocks.
*/
private predicate defUseRank(StackVariable v, BasicBlock b, int rankix, int i) {
i = rank[rankix](int j | ssa_defn(v, _, b, j) or ssa_use(v, _, b, j))
}
/**
* Gets the maximum rank index for the given variable `v` and basic block
* `b`. This will be the number of defs/uses of `v` in `b` plus one, where
* the extra rank at the end represents a position past the last node in
* the block.
*/
private int lastRank(StackVariable v, BasicBlock b) {
result = max(int rankix | defUseRank(v, b, rankix, _)) + 1
}
/**
* Holds if SSA variable `(v, def)` is defined at rank index `rankix` in
* basic block `b`.
*/
private predicate ssaDefRank(StackVariable v, ControlFlowNode def, BasicBlock b, int rankix) {
exists(int i |
ssa_defn(v, def, b, i) and
defUseRank(v, b, rankix, i)
)
}
/**
* Holds if SSA variable `(v, def)` reaches the rank index `rankix` in its
* own basic block `b` before being overwritten by another definition of
* `v` that comes _at or after_ the reached node. Reaching a node means
* that the definition is visible to any _use_ at that node.
*/
private predicate ssaDefReachesRank(StackVariable v, ControlFlowNode def, BasicBlock b, int rankix) {
// A definition should not reach its own node unless a loop allows it.
// When nodes are both definitions and uses for the same variable, the
// use is understood to happen _before_ the definition. Phi nodes are
// at rankidx -1 and will therefore always reach the first node in the
// basic block.
ssaDefRank(v, def, b, rankix - 1)
or
ssaDefReachesRank(v, def, b, rankix - 1) and
rankix <= lastRank(v, b) and // Without this, the predicate would be infinite.
not ssaDefRank(v, _, b, rankix - 1) // Range is inclusive of but not past next def.
}
/** Holds if SSA variable `(v, def)` reaches the end of block `b`. */
cached
predicate ssaDefinitionReachesEndOfBB(StackVariable v, ControlFlowNode def, BasicBlock b) {
live_at_exit_of_bb(v, b) and ssaDefReachesRank(v, def, b, lastRank(v, b))
or
exists(BasicBlock idom |
ssaDefinitionReachesEndOfBB(v, def, idom) and
noDefinitionsSinceIDominator(v, idom, b)
)
}
/**
* Helper predicate for ssaDefinitionReachesEndOfBB. If there is no
* definition of `v` in basic block `b`, then any definition of `v`
* that reaches the end of `idom` (the immediate dominator of `b`) also
* reaches the end of `b`.
*/
pragma[noinline]
private predicate noDefinitionsSinceIDominator(StackVariable v, BasicBlock idom, BasicBlock b) {
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
live_at_exit_of_bb(v, b) and
not ssa_defn(v, _, b, _)
}
/**
* Holds if SSA variable `(v, def)` reaches `use` within the same basic
* block, where `use` is a `VariableAccess` of `v`.
*/
private predicate ssaDefinitionReachesUseWithinBB(StackVariable v, ControlFlowNode def, Expr use) {
exists(BasicBlock b, int rankix, int i |
ssaDefReachesRank(v, def, b, rankix) and
defUseRank(v, b, rankix, i) and
ssa_use(v, use, b, i)
)
}
/**
* Holds if SSA variable `(v, def)` reaches the control-flow node `use`.
*/
private predicate ssaDefinitionReaches(StackVariable v, ControlFlowNode def, Expr use) {
ssaDefinitionReachesUseWithinBB(v, def, use)
or
exists(BasicBlock b |
ssa_use(v, use, b, _) and
ssaDefinitionReachesEndOfBB(v, def, b.getAPredecessor()) and
not ssaDefinitionReachesUseWithinBB(v, _, use)
)
}
/**
* Gets a string representation of the SSA variable represented by the pair
* `(node, v)`.
*/
cached
string toString(ControlFlowNode node, StackVariable v) {
if phi_node(v, node.(BasicBlock))
then result = "SSA phi(" + v.getName() + ")"
else (
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
)
}
/**
* Holds if SSA variable `(v, def)` reaches `result`, where `result` is an
* access of `v`.
*/
cached
VariableAccess getAUse(ControlFlowNode def, StackVariable v) {
ssaDefinitionReaches(v, def, result) and
ssa_use(v, result, _, _)
}
}

View File

@@ -0,0 +1,388 @@
/**
* Provides a library for working with local (intra-procedural) control-flow
* reachability involving stack variables.
*/
import cpp
/**
* A reachability analysis for control-flow nodes involving stack variables.
* This defines sources, sinks, and any other configurable aspect of the
* analysis. Multiple analyses can coexist. To create an analysis, extend this
* class with a subclass whose characteristic predicate is a unique singleton
* string. For example, write
*
* ```
* class MyAnalysisConfiguration extends StackVariableReachability {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Override `isBarrier`.
* }
* ```
*
* Then, to query whether there is flow between some source and sink, call the
* `reaches` predicate on an instance of `MyAnalysisConfiguration`.
*/
abstract class StackVariableReachability extends string {
bindingset[this]
StackVariableReachability() { length() >= 0 }
/** Holds if `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, StackVariable v);
/** Holds if `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, StackVariable v);
/** Holds if `node` is a barrier for the reachability analysis using variable `v`. */
abstract predicate isBarrier(ControlFlowNode node, StackVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier. This is (almost) equivalent to the following QL predicate but
* uses basic blocks internally for better performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSink(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* sink = source.getASuccessor() and isSource(source, v)
* or
* exists(ControlFlowNode mid | reachesImpl(source, v, mid) |
* not isBarrier(mid, v)
* and
* sink = mid.getASuccessor()
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
/*
* Implementation detail: the predicates in this class are a generalization of
* those in DefinitionsAndUses.qll, and should be kept in sync.
*
* Unfortunately, caching of abstract predicates does not work well, so the
* predicates in DefinitionsAndUses.qll cannot use this library.
*/
exists(BasicBlock bb, int i |
isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
isSink(sink, v) and
not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
)
or
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
bbSuccessorEntryReaches(bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry)
|
bbEntryReachesLocally(succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not isBarrier(succ.getNode(_), v) and
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
exists(int n |
node = bb.getNode(n) and
isSink(node, v)
|
not exists(this.firstBarrierIndexIn(bb, v))
or
n <= this.firstBarrierIndexIn(bb, v)
)
}
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
result = min(int m | isBarrier(bb.getNode(m), v))
}
}
/**
* Holds if `bb` contains the entry point `loop` for a loop at position `i`.
* The condition of that loop is provably true upon entry but not provably
* true in general (if it were, the false-successor had already been removed
* from the CFG).
*
* Examples:
* ```
* for (int i = 0; i < 2; i++) { } // always true upon entry
* for (int i = 0; true; i++) { } // always true
* ```
*/
private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, ControlFlowNode loop) {
exists(Expr condition |
loopConditionAlwaysTrueUponEntry(loop, condition) and
not conditionAlwaysTrue(condition) and
bb.getNode(i) = loop
)
}
/**
* Basic block `pred` contains all or part of the condition belonging to a loop,
* and there is an edge from `pred` to `succ` that concludes the condition.
* If the edge corrseponds with the loop condition being found to be `true`, then
* `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
* within a complex loop condition are not matched by this predicate.
*/
private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
BasicBlock pred, BasicBlock succ, boolean skipsLoop
) {
exists(Expr cond |
loopConditionAlwaysTrueUponEntry(_, cond) and
cond.getAChild*() = pred.getEnd() and
succ = pred.getASuccessor() and
not cond.getAChild*() = succ.getStart() and
(
succ = pred.getAFalseSuccessor() and
skipsLoop = true
or
succ = pred.getATrueSuccessor() and
skipsLoop = false
)
)
}
/**
* Loop invariant for `bbSuccessorEntryReaches`:
*
* - `succ` is a successor of `pred`.
* - `predSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `pred` (via `succ`) skips the first loop where the condition is
* provably true upon entry.
* - `succSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `succ` skips the first loop where the condition is provably true
* upon entry.
* - If `pred` contains the entry point of a loop where the condition
* is provably true upon entry, then `succ` is not allowed to skip
* that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`).
*/
predicate bbSuccessorEntryReachesLoopInvariant(
BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry,
boolean succSkipsFirstLoopAlwaysTrueUponEntry
) {
succ = pred.getASuccessor() and
(succSkipsFirstLoopAlwaysTrueUponEntry = true or succSkipsFirstLoopAlwaysTrueUponEntry = false) and
(
// The edge from `pred` to `succ` is from a loop condition provably
// true upon entry, so the value of `predSkipsFirstLoopAlwaysTrueUponEntry`
// is determined by whether the true edge or the false edge is chosen,
// regardless of the value of `succSkipsFirstLoopAlwaysTrueUponEntry`.
bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry)
or
// The edge from `pred` to `succ` is _not_ from a loop condition provably
// true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry`
// and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same.
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, _) and
succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and
// Moreover, if `pred` contains the entry point of a loop where the
// condition is provably true upon entry, then `succ` is not allowed
// to skip that loop, and hence `succSkipsFirstLoopAlwaysTrueUponEntry = false`.
(
bbLoopEntryConditionAlwaysTrueAt(pred, _, _)
implies
succSkipsFirstLoopAlwaysTrueUponEntry = false
)
)
}
/**
* Reachability analysis for control-flow nodes involving stack variables.
* Unlike `StackVariableReachability`, this analysis takes variable
* reassignments into account.
*
* This class is used like `StackVariableReachability`, except that
* subclasses should override `isSourceActual` and `isSinkActual` instead of
* `isSource` and `isSink`, and that there is a `reachesTo` predicate in
* addition to `reaches`.
*/
abstract class StackVariableReachabilityWithReassignment extends StackVariableReachability {
bindingset[this]
StackVariableReachabilityWithReassignment() { length() >= 0 }
/** Override this predicate rather than `isSource` (`isSource` is used internally). */
abstract predicate isSourceActual(ControlFlowNode node, StackVariable v);
/** Override this predicate rather than `isSink` (`isSink` is used internally). */
abstract predicate isSinkActual(ControlFlowNode node, StackVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier, taking reassignments into account. This is (almost) equivalent
* to the following QL predicate, but uses basic blocks internally for better
* performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSinkActual(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* isSourceActual(source, v)
* and
* (
* sink = source.getASuccessor()
* or
* exists(ControlFlowNode mid, SemanticStackVariable v0 | reachesImpl(source, v0, mid) |
* // ordinary successor
* not isBarrier(mid, v) and
* sink = mid.getASuccessor() and
* v = v0
* or
* // reassigned from v0 to v
* exprDefinition(v, mid, v0.getAnAccess()) and
* sink = mid.getASuccessor()
* )
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
reachesTo(source, v, sink, _)
}
/**
* As `reaches`, but also specifies the last variable it was reassigned to (`v0`).
*/
predicate reachesTo(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
) {
exists(ControlFlowNode def |
actualSourceReaches(source, v, def, v0) and
StackVariableReachability.super.reaches(def, v0, sink) and
isSinkActual(sink, v0)
)
}
private predicate actualSourceReaches(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
isSourceActual(source, v) and def = source and v0 = v
or
exists(ControlFlowNode source1, SemanticStackVariable v1 |
actualSourceReaches(source, v, source1, v1)
|
reassignment(source1, v1, def, v0)
)
}
private predicate reassignment(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
StackVariableReachability.super.reaches(source, v, def) and
exprDefinition(v0, def, v.getAnAccess())
}
final override predicate isSource(ControlFlowNode node, StackVariable v) {
isSourceActual(node, v)
or
// Reassignment generates a new (non-actual) source
reassignment(_, _, node, v)
}
final override predicate isSink(ControlFlowNode node, StackVariable v) {
isSinkActual(node, v)
or
// Reassignment generates a new (non-actual) sink
exprDefinition(_, node, v.getAnAccess())
}
}
/**
* Same as `StackVariableReachability`, but `isBarrier` works on control-flow
* edges rather than nodes and is therefore parameterized by the original
* source node as well. Otherwise, this class is used like
* `StackVariableReachability`.
*/
abstract class StackVariableReachabilityExt extends string {
bindingset[this]
StackVariableReachabilityExt() { length() >= 0 }
/** `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, StackVariable v);
/** `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, StackVariable v);
/** `node` is a barrier for the reachability analysis using variable `v` and starting from `source`. */
abstract predicate isBarrier(
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
);
/** See `StackVariableReachability.reaches`. */
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
exists(BasicBlock bb, int i |
isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
isSink(sink, v) and
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
k in [i .. j - 1]
)
)
or
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
bbSuccessorEntryReaches(source, bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry) and
not isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
bbEntryReachesLocally(source, succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
isSource(source, v) and
exists(int n | node = bb.getNode(n) and isSink(node, v) |
not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
)
}
}

View File

@@ -0,0 +1,179 @@
// NOTE: There are two copies of this file, and they must be kept identical:
// - semmle/code/cpp/controlflow/SubBasicBlocks.qll
// - semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll
//
// The second one is a private copy of the `SubBasicBlocks` library for
// internal use by the data flow library. Having an extra copy prevents
// non-monotonic recursion errors in queries that use both the data flow
// library and the `SubBasicBlocks` library.
/**
* Provides the `SubBasicBlock` class, used for partitioning basic blocks in
* smaller pieces.
*/
import cpp
/**
* An abstract class that directs where in the control-flow graph a new
* `SubBasicBlock` must start. If a `ControlFlowNode` is an instance of this
* class, that node is guaranteed to be the first node in a `SubBasicBlock`.
* If multiple libraries use the `SubBasicBlock` library, basic blocks may be
* split in more places than either library expects, but nothing should break
* as a direct result of that.
*/
abstract class SubBasicBlockCutNode extends ControlFlowNode {
SubBasicBlockCutNode() {
// Some control-flow nodes are not in any basic block. This includes
// `Conversion`s, expressions that are evaluated at compile time, default
// arguments, and `Function`s without implementation.
exists(this.getBasicBlock())
}
}
/**
* A block that can be smaller than or equal to a `BasicBlock`. Use this class
* when `ControlFlowNode` is too fine-grained and `BasicBlock` too
* coarse-grained. Their successor graph is like that of basic blocks, except
* that the blocks are split up with an extra edge right before any instance of
* the abstract class `SubBasicBlockCutNode`. Users of this library must
* therefore extend `SubBasicBlockCutNode` to direct where basic blocks will be
* split up.
*/
class SubBasicBlock extends ControlFlowNodeBase {
SubBasicBlock() {
this instanceof BasicBlock
or
this instanceof SubBasicBlockCutNode
}
/** Gets the basic block in which this `SubBasicBlock` is contained. */
BasicBlock getBasicBlock() { result = this.(ControlFlowNode).getBasicBlock() }
/**
* Holds if this `SubBasicBlock` comes first in its basic block. This is the
* only condition under which a `SubBasicBlock` may have multiple
* predecessors.
*/
predicate firstInBB() { exists(BasicBlock bb | this.getRankInBasicBlock(bb) = 1) }
/**
* Holds if this `SubBasicBlock` comes last in its basic block. This is the
* only condition under which a `SubBasicBlock` may have multiple successors.
*/
predicate lastInBB() {
exists(BasicBlock bb | this.getRankInBasicBlock(bb) = countSubBasicBlocksInBasicBlock(bb))
}
/**
* Gets the (1-based) rank of this `SubBasicBlock` among the other `SubBasicBlock`s in
* its containing basic block `bb`, where `bb` is equal to `getBasicBlock()`.
*/
int getRankInBasicBlock(BasicBlock bb) {
exists(int thisIndexInBB |
thisIndexInBB = this.getIndexInBasicBlock(bb) and
thisIndexInBB = rank[result](int i | i = any(SubBasicBlock n).getIndexInBasicBlock(bb))
)
}
/**
* DEPRECATED: use `getRankInBasicBlock` instead. Note that this predicate
* returns a 0-based position, while `getRankInBasicBlock` returns a 1-based
* position.
*/
deprecated int getPosInBasicBlock(BasicBlock bb) { result = getRankInBasicBlock(bb) - 1 }
pragma[noinline]
private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) }
/** Gets a successor in the control-flow graph of `SubBasicBlock`s. */
SubBasicBlock getASuccessor() {
this.lastInBB() and
result = this.getBasicBlock().getASuccessor()
or
exists(BasicBlock bb | result.getRankInBasicBlock(bb) = this.getRankInBasicBlock(bb) + 1)
}
/**
* Gets the `index`th control-flow node in this `SubBasicBlock`. Indexes
* start from 0, and the node at index 0 always exists and compares equal
* to `this`.
*/
ControlFlowNode getNode(int index) {
exists(BasicBlock bb |
exists(int outerIndex |
result = bb.getNode(outerIndex) and
index = outerToInnerIndex(bb, outerIndex)
)
)
}
/**
* Gets the index of the node in this `SubBasicBlock` that has `indexInBB` in
* `bb`, where `bb` is equal to `getBasicBlock()`.
*/
// This predicate is factored out of `getNode` to ensure a good join order.
// It's sensitive to bad magic, so it has `pragma[nomagic]` on it. For
// example, it can get very slow if `getNode` is pragma[nomagic], which could
// mean it might get very slow if `getNode` is used in the wrong context.
pragma[nomagic]
private int outerToInnerIndex(BasicBlock bb, int indexInBB) {
indexInBB = result + this.getIndexInBasicBlock(bb) and
result = [0 .. this.getNumberOfNodes() - 1]
}
/** Gets a control-flow node in this `SubBasicBlock`. */
ControlFlowNode getANode() { result = this.getNode(_) }
/** Holds if `this` contains `node`. */
predicate contains(ControlFlowNode node) { node = this.getANode() }
/** Gets a predecessor in the control-flow graph of `SubBasicBlock`s. */
SubBasicBlock getAPredecessor() { result.getASuccessor() = this }
/**
* Gets a node such that the control-flow edge `(this, result)` may be taken
* when the final node of this `SubBasicBlock` is a conditional expression
* and evaluates to true.
*/
SubBasicBlock getATrueSuccessor() {
this.lastInBB() and
result = this.getBasicBlock().getATrueSuccessor()
}
/**
* Gets a node such that the control-flow edge `(this, result)` may be taken
* when the final node of this `SubBasicBlock` is a conditional expression
* and evaluates to false.
*/
SubBasicBlock getAFalseSuccessor() {
this.lastInBB() and
result = this.getBasicBlock().getAFalseSuccessor()
}
/**
* Gets the number of control-flow nodes in this `SubBasicBlock`. There is
* always at least one.
*/
int getNumberOfNodes() {
exists(BasicBlock bb |
if this.lastInBB()
then result = bb.length() - this.getIndexInBasicBlock(bb)
else result = this.getASuccessor().getIndexInBasicBlock(bb) - this.getIndexInBasicBlock(bb)
)
}
/** Gets the last control-flow node in this `SubBasicBlock`. */
ControlFlowNode getEnd() { result = this.getNode(this.getNumberOfNodes() - 1) }
/** Gets the first control-flow node in this `SubBasicBlock`. */
ControlFlowNode getStart() { result = this }
/** Gets the function that contains this `SubBasicBlock`. */
pragma[noinline]
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
}
/** Gets the number of `SubBasicBlock`s in the given basic block. */
private int countSubBasicBlocksInBasicBlock(BasicBlock bb) {
result = strictcount(SubBasicBlock sbb | sbb.getBasicBlock() = bb)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/**
* INTERNAL: use the `BasicBlocks` library instead.
* This library defines `PrimitiveBasicBlock`s, an intermediate stage in the
* computation of `BasicBlock`s.
*/
/*
* Unlike `BasicBlock`s, `PrimitiveBasicBlock`s are constructed using
* the primitive `successors_extended` relation only. That is, impossible
* edges removed in `successors_adapted` are still taken into account.
*
* `PrimitiveBasicBlock`s are used as helper entities for actually
* constructing `successors_adapted`, hence the need for two "layers"
* of basic blocks. In addition to being used to construct
* `successors_adapted`, the relations for `BasicBlocks` (e.g.,
* `basic_block_entry_node`) can reuse the relations computed here
* (e.g, `primitive_basic_block_entry_node`), as most `BasicBlock`s
* will coincide with `PrimitiveBasicBlock`s.
*/
import cpp
private class Node = ControlFlowNodeBase;
import Cached
cached
private module Cached {
/** Holds if `node` is the entry node of a primitive basic block. */
cached
predicate primitive_basic_block_entry_node(Node node) {
// The entry point of the CFG is the start of a BB.
exists(Function f | f.getEntryPoint() = node)
or
// If the node has more than one predecessor,
// or the node's predecessor has more than one successor,
// then the node is the start of a new primitive basic block.
strictcount(Node pred | successors_extended(pred, node)) > 1
or
exists(ControlFlowNode pred | successors_extended(pred, node) |
strictcount(ControlFlowNode other | successors_extended(pred, other)) > 1
)
or
// If the node has zero predecessors then it is the start of
// a BB. However, the C++ AST contains many nodes with zero
// predecessors and zero successors, which are not members of
// the CFG. So we exclude all of those trivial BBs by requiring
// that the node have at least one successor.
not successors_extended(_, node) and successors_extended(node, _)
or
// An exception handler is always the start of a new basic block. We
// don't generate edges for [possible] exceptions, but in practice control
// flow could reach the handler from anywhere inside the try block that
// could throw an exception of a corresponding type. A `Handler` usually
// needs to be considered reachable (see also `BasicBlock.isReachable`).
node instanceof Handler
}
/** Holds if `n2` follows `n1` in a `PrimitiveBasicBlock`. */
private predicate member_step(Node n1, Node n2) {
successors_extended(n1, n2) and
not n2 instanceof PrimitiveBasicBlock
}
/** Holds if `node` is the `pos`th control-flow node in primitive basic block `bb`. */
cached
predicate primitive_basic_block_member(Node node, PrimitiveBasicBlock bb, int pos) {
primitive_basic_block_entry_node(bb) and node = bb and pos = 0
or
exists(Node prev |
member_step(prev, node) and
primitive_basic_block_member(prev, bb, pos - 1)
)
}
/** Gets the number of control-flow nodes in the primitive basic block `bb`. */
cached
int primitive_bb_length(PrimitiveBasicBlock bb) {
result = strictcount(Node node | primitive_basic_block_member(node, bb, _))
}
/** Successor relation for primitive basic blocks. */
cached
predicate primitive_bb_successor(PrimitiveBasicBlock pred, PrimitiveBasicBlock succ) {
exists(Node last |
primitive_basic_block_member(last, pred, primitive_bb_length(pred) - 1) and
successors_extended(last, succ)
)
}
}
/**
* A primitive basic block in the C/C++ control-flow graph constructed using
* the primitive `successors_extended` relation only.
*/
class PrimitiveBasicBlock extends Node {
PrimitiveBasicBlock() { primitive_basic_block_entry_node(this) }
predicate contains(Node node) { primitive_basic_block_member(node, this, _) }
Node getNode(int pos) { primitive_basic_block_member(result, this, pos) }
Node getANode() { primitive_basic_block_member(result, this, _) }
PrimitiveBasicBlock getASuccessor() { primitive_bb_successor(this, result) }
PrimitiveBasicBlock getAPredecessor() { primitive_bb_successor(result, this) }
int length() { result = primitive_bb_length(this) }
}

View File

@@ -0,0 +1,24 @@
/**
* Provides a library for local (intra-procedural) and global (inter-procedural)
* data flow analysis: deciding whether data can flow from a _source_ to a
* _sink_.
*
* Unless configured otherwise, _flow_ means that the exact value of
* the source may reach the sink. We do not track flow across pointer
* dereferences or array indexing. To track these types of flow, where the
* exact value may not be preserved, import
* `semmle.code.cpp.dataflow.TaintTracking`.
*
* To use global (interprocedural) data flow, extend the class
* `DataFlow::Configuration` as documented on that class. To use local
* (intraprocedural) data flow between expressions, call
* `DataFlow::localExprFlow`. For more general cases of local data flow, call
* `DataFlow::localFlow` or `DataFlow::localFlowStep` with arguments of type
* `DataFlow::Node`.
*/
import cpp
module DataFlow {
import semmle.code.cpp.dataflow.internal.DataFlowImpl
}

View File

@@ -0,0 +1,16 @@
/**
* Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow2 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl2
}

View File

@@ -0,0 +1,16 @@
/**
* Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow3 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl3
}

View File

@@ -0,0 +1,16 @@
/**
* Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use
* this class when data-flow configurations must depend on each other. Two
* classes extending `DataFlow::Configuration` should never depend on each
* other, but one of them should instead depend on a
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
* `DataFlow4::Configuration`.
*
* See `semmle.code.cpp.dataflow.DataFlow` for the full documentation.
*/
import cpp
module DataFlow4 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl4
}

View File

@@ -0,0 +1,361 @@
/**
* Provides a local analysis for identifying where a variable address or value
* may escape an _expression tree_, meaning that it is assigned to a variable,
* passed to a function, or similar.
*/
/*
* Maintainer note: this file is one of several files that are similar but not
* identical. Many changes to this file will also apply to the others:
* - AddressConstantExpression.qll
* - AddressFlow.qll
* - EscapesTree.qll
*/
private import cpp
/**
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
* template functions, these functions are essentially casts, so we treat them
* as such.
*/
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
/**
* Holds if `f` is an instantiation of `std::addressof`, which effectively
* converts a reference to a pointer.
*/
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
or
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
or
// When an object is implicitly converted to a reference to one of its base
// classes, it gets two `Conversion`s: there is first an implicit
// `CStyleCast` to its base class followed by a `ReferenceToExpr` to a
// reference to its base class. Whereas an explicit cast to the base class
// would produce an rvalue, which would not be convertible to an lvalue
// reference, this implicit cast instead produces an lvalue. The following
// case ensures that we propagate the property of being an lvalue through
// such casts.
lvalueIn.getConversion() = lvalueOut and
lvalueOut.(CStyleCast).isImplicit()
}
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
lvalueToLvalueStepPure(lvalueIn, lvalueOut)
or
// C++ only
lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted()
or
// C++ only
lvalueIn = lvalueOut.(Assignment).getLValue().getFullyConverted()
}
private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) {
pointerIn = lvalueOut.(ArrayExpr).getArrayBase().getFullyConverted()
or
pointerIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted()
or
pointerIn = lvalueOut.(PointerFieldAccess).getQualifier().getFullyConverted()
}
private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) {
lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion)
or
lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted()
}
private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
(
pointerOut instanceof PointerAddExpr
or
pointerOut instanceof PointerSubExpr
) and
pointerIn = pointerOut.getAChild().getFullyConverted() and
pointerIn.getUnspecifiedType() instanceof PointerType
or
pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted()
or
pointerIn.getConversion() = pointerOut.(Cast)
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
or
pointerIn = pointerOut.(CommaExpr).getRightOperand().getFullyConverted()
or
pointerIn = pointerOut.(StmtExpr).getResultExpr().getFullyConverted()
}
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
}
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
}
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
pointerOut =
any(FunctionCall call |
stdAddressOf(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
}
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
referenceOut =
any(FunctionCall call |
stdIdentityFunction(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
or
referenceIn.getConversion() = referenceOut.(Cast)
or
referenceIn.getConversion() = referenceOut.(ParenthesisExpr)
}
private predicate lvalueFromVariableAccess(VariableAccess va, Expr lvalue) {
// Base case for non-reference types.
lvalue = va and
not va.getConversion() instanceof ReferenceDereferenceExpr
or
// Base case for reference types where we pretend that they are
// non-reference types. The type of the target of `va` can be `ReferenceType`
// or `FunctionReferenceType`.
lvalue = va.getConversion().(ReferenceDereferenceExpr)
or
// lvalue -> lvalue
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToLvalueStep(prev, lvalue)
)
or
// pointer -> lvalue
exists(Expr prev |
pointerFromVariableAccess(va, prev) and
pointerToLvalueStep(prev, lvalue)
)
or
// reference -> lvalue
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToLvalueStep(prev, lvalue)
)
}
private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
// pointer -> pointer
exists(Expr prev |
pointerFromVariableAccess(va, prev) and
pointerToPointerStep(prev, pointer)
)
or
// reference -> pointer
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToPointerStep(prev, pointer)
)
or
// lvalue -> pointer
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToPointerStep(prev, pointer)
)
}
private predicate referenceFromVariableAccess(VariableAccess va, Expr reference) {
// reference -> reference
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToReferenceStep(prev, reference)
)
or
// lvalue -> reference
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToReferenceStep(prev, reference)
)
}
private predicate addressMayEscapeAt(Expr e) {
exists(Call call |
e = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget())
or
e = call.getQualifier().getFullyConverted() and
e.getUnderlyingType() instanceof PointerType
)
or
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())
or
exists(Initializer init | e = init.getExpr().getFullyConverted())
or
exists(ConstructorFieldInit init | e = init.getExpr().getFullyConverted())
or
exists(ReturnStmt ret | e = ret.getExpr().getFullyConverted())
or
exists(ThrowExpr throw | e = throw.getExpr().getFullyConverted())
or
exists(AggregateLiteral agg | e = agg.getAChild().getFullyConverted())
or
exists(AsmStmt asm | e = asm.getAChild().(Expr).getFullyConverted())
}
private predicate addressMayEscapeMutablyAt(Expr e) {
addressMayEscapeAt(e) and
exists(Type t | t = e.getType().stripTopLevelSpecifiers() |
t instanceof PointerType and
not t.(PointerType).getBaseType().isConst()
or
t instanceof ReferenceType and
not t.(ReferenceType).getBaseType().isConst()
or
// If the address has been cast to an integral type, conservatively assume that it may eventually be cast back to a
// pointer to non-const type.
t instanceof IntegralType
or
// If we go through a temporary object step, we can take a reference to a temporary const pointer
// object, where the pointer doesn't point to a const value
exists(TemporaryObjectExpr temp, PointerType pt |
temp.getConversion() = e.(ReferenceToExpr) and
pt = temp.getType().stripTopLevelSpecifiers()
|
not pt.getBaseType().isConst()
)
)
}
private predicate lvalueMayEscapeAt(Expr e) {
// A call qualifier, like `q` in `q.f()`, is special in that the address of
// `q` escapes even though `q` is not a pointer or a reference.
exists(Call call |
e = call.getQualifier().getFullyConverted() and
e.getType().getUnspecifiedType() instanceof Class
)
}
private predicate lvalueMayEscapeMutablyAt(Expr e) {
lvalueMayEscapeAt(e) and
// A qualifier of a call to a const member function is converted to a const
// class type.
not e.getType().isConst()
}
private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
pointerFromVariableAccess(va, e)
or
referenceFromVariableAccess(va, e)
or
// `e` could be a pointer that is converted to a reference as the final step,
// meaning that we pass a value that is two dereferences away from referring
// to `va`. This happens, for example, with `void std::vector::push_back(T&&
// value);` when called as `v.push_back(&x)`, for a variable `x`. It
// can also happen when taking a reference to a const pointer to a
// (potentially non-const) value.
exists(Expr pointerValue |
pointerFromVariableAccess(va, pointerValue) and
e = pointerValue.getConversion().(ReferenceToExpr)
)
}
import EscapesTree_Cached
cached
private module EscapesTree_Cached {
/**
* Holds if `e` is a fully-converted expression that evaluates to an address
* derived from the address of `va` and is stored in a variable or passed
* across functions. This means `e` is the `Expr.getFullyConverted`-form of:
*
* - The right-hand side of an assignment or initialization;
* - A function argument or return value;
* - The argument to `throw`.
* - An entry in an `AggregateLiteral`, including the compiler-generated
* `ClassAggregateLiteral` that initializes a `LambdaExpression`; or
* - An expression in an inline assembly statement.
*
* This predicate includes pointers or reference to `const` types. See
* `variableAddressEscapesTreeNonConst` for a version of this predicate that
* does not.
*
* If `va` has reference type, the escape analysis concerns the value pointed
* to by the reference rather than the reference itself. The C++ language does
* not allow taking the address of a reference in any way, so this predicate
* would never produce any results for the reference itself. Callers that are
* not interested in the value referred to by references should exclude
* variable accesses to reference-typed values.
*/
cached
predicate variableAddressEscapesTree(VariableAccess va, Expr e) {
addressMayEscapeAt(e) and
addressFromVariableAccess(va, e)
or
lvalueMayEscapeAt(e) and
lvalueFromVariableAccess(va, e)
}
/**
* Holds if `e` is a fully-converted expression that evaluates to a non-const
* address derived from the address of `va` and is stored in a variable or
* passed across functions. This means `e` is the `Expr.getFullyConverted`-form
* of:
*
* - The right-hand side of an assignment or initialization;
* - A function argument or return value;
* - The argument to `throw`.
* - An entry in an `AggregateLiteral`, including the compiler-generated
* `ClassAggregateLiteral` that initializes a `LambdaExpression`; or
* - An expression in an inline assembly statement.
*
* This predicate omits pointers or reference to `const` types. See
* `variableAddressEscapesTree` for a version of this predicate that includes
* those.
*
* If `va` has reference type, the escape analysis concerns the value pointed
* to by the reference rather than the reference itself. The C++ language
* offers no way to take the address of a reference, so this predicate will
* never produce any results for the reference itself. Callers that are not
* interested in the value referred to by references should exclude variable
* accesses to reference-typed values.
*/
cached
predicate variableAddressEscapesTreeNonConst(VariableAccess va, Expr e) {
addressMayEscapeMutablyAt(e) and
addressFromVariableAccess(va, e)
or
lvalueMayEscapeMutablyAt(e) and
lvalueFromVariableAccess(va, e)
}
/**
* Holds if `e` is a fully-converted expression that evaluates to an lvalue
* derived from `va` and is used for reading from or assigning to. This is in
* contrast with a variable access that is used for taking an address (`&x`)
* or simply discarding its value (`x;`).
*
* This analysis does not propagate across assignments or calls. The analysis
* is also not concerned with whether the lvalue `e` is converted to an rvalue
* -- to examine that, use the relevant member predicates on `Expr`.
*
* If `va` has reference type, the analysis concerns the value pointed to by
* the reference rather than the reference itself. The expression `e` may be a
* `Conversion`.
*/
cached
predicate variableAccessedAsValue(VariableAccess va, Expr e) {
lvalueFromVariableAccess(va, e) and
not lvalueToLvalueStepPure(e, _) and
not lvalueToPointerStep(e, _) and
not lvalueToReferenceStep(e, _) and
not e = any(ExprInVoidContext eivc | e = eivc.getConversion*())
}
}

View File

@@ -0,0 +1,39 @@
/**
* DEPRECATED: Recursion through `DataFlow::Configuration` is impossible in
* any supported tooling. There is no need for this module because it's
* impossible to accidentally depend on recursion through
* `DataFlow::Configuration` in current releases.
*
* When this module is imported, recursive use of `DataFlow::Configuration` is
* disallowed. Importing this module will guarantee the absence of such
* recursion, which is unsupported and will be unconditionally disallowed in a
* future release.
*
* Recursive use of `DataFlow{2..4}::Configuration` is always disallowed, so no
* import is needed for those.
*/
import cpp
private import semmle.code.cpp.dataflow.DataFlow
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
* Four copies are available: `DataFlow` through `DataFlow4`.
*/
abstract private class ConfigurationRecursionPrevention extends DataFlow::Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
strictcount(DataFlow::Node n | this.isSource(n)) < 0
or
strictcount(DataFlow::Node n | this.isSink(n)) < 0
or
strictcount(DataFlow::Node n1, DataFlow::Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,298 @@
/**
* Provides utilities for determining which expressions contain
* stack addresses.
*/
import cpp
import semmle.code.cpp.controlflow.SSA
/**
* A stack address flows to `use`.
* The simplest case is when `use` is the expression `&var`, but
* assignments are also handled. For example:
*
* x = &var;
* y = x;
* ...y... // use of &var
*
* `useType` is the type of data which we believe was allocated on the
* stack. It is particularly important when dealing with pointers. Consider
* this example:
*
* int x[10];
* int *y = new int[10];
* ... = &x[1];
* ... = &y[1];
*
* In this example, x and y are both stack variables. But &x[1] is a
* pointer to the stack and &y[1] is a pointer to the heap. The difference
* is that the type of x is int[10], but the type of y is int*. This
* information is stored in `useType`.
*
* `source` is the origin of the stack address. It is only used to improve
* the quality of the error messages.
*
* `isLocal` is true if the stack address came from the current
* function. It is false if the stack address arrived via a function
* parameter. This information is only used to improve the quality of the
* error messages.
*/
predicate stackPointerFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) {
// Arrays in C are convertible to pointers. For example, if the type of x
// is int[10] then it is convertible to int*. The same rule applies to
// multidimensional arrays: if the type of y is int[2][3][4], then it is
// convertible to int(*)[3][4]. This conversion is what happens under the
// hood when we index an array. For example, x[5] is equivalent to
// *(x+5), so x is first converted to a pointer and then the pointer
// arithmetic is applied to the pointer.
exists(ArrayType arrayType |
stackReferenceFlowsToUse(use, arrayType, source, isLocal) and
useType = getExprPtrType(use.getConversion()).getBaseType()
)
or
// Address of: &x
stackReferenceFlowsToUse(use.(AddressOfExpr).getOperand(), useType, source, isLocal)
or
// Pointer subtract: if p is a pointer to a stack address, then p-1 is
// too.
stackPointerFlowsToUse(use.(PointerSubExpr).getLeftOperand(), useType, source, isLocal)
or
// Pointer add: if p is a pointer to a stack address, then p+1 is too.
stackPointerFlowsToUse(use.(PointerAddExpr).getAnOperand(), useType, source, isLocal)
or
// Indirect use of a stack address.
exists(SsaDefinition def, StackVariable var |
stackPointerFlowsToDef(def, var, useType, source, isLocal) and
use = def.getAUse(var)
)
or
// Use of a stack address which arrived via one of the function's
// parameters. We do not use the original pointer source here because it
// could lead to an extremely large number of results if the function has
// many callers. Instead we set `source` to the use of the parameter.
exists(SsaDefinition def, Parameter param |
pointerParamFlowsToDef(def, param, useType) and
use = def.getAUse(param) and
source = use and
isLocal = false
)
or
// Similar to the parameter case above. If a member function is called
// on an object which is allocated on the stack, then the "this" pointer
// contains a stack address.
exists(ThisExpr thisExpr |
use = thisExpr and
memberFcnMightRunOnStack(thisExpr.getEnclosingFunction(), useType) and
source = use and
isLocal = false
)
}
/**
* Helper function for stackPointerFlowsToUse. Gets the type of a pointer
* expression.
*/
cached
private PointerType getExprPtrType(Expr use) { result = use.getUnspecifiedType() }
predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) {
// Stack variables
exists(StackVariable var |
use = source and
source = var.getAnAccess() and
isLocal = true and
// If the type of the variable is a reference type, such as int&, then
// we need to look at its definition to determine whether it contains a
// stack reference. This is handled by stackReferenceFlowsToDef, below.
not isReferenceVariable(var) and
// There is a subtlety relating to ArrayType which is hidden by the
// simplicity of the line below. Consider this example:
//
// int arraytest(int p[3][4][5]) {
// int x[3][4][5] = { ... };
// return x[1][2][3] + p[1][2][3];
// }
//
// The types of `x` and `p` are the same, but `x` is stack allocated
// and `p` is not. So we want `useType` to be an ArrayType for `x` and
// a PointerType for `p`. Luckily, this conversion happens
// automatically when the variable is used. So we get the correct type
// provided that we get it from `use` rather than from `var`.
useType = use.getUnspecifiedType()
)
or
// Accessing the field of a class, struct, or union.
exists(FieldAccess access, Class classType |
use = access and useType = access.getUnspecifiedType()
|
// Handle both x.f and x->f:
stackReferenceFlowsToUse(access.getQualifier(), classType, source, isLocal) or
stackPointerFlowsToUse(access.getQualifier(), classType, source, isLocal)
)
or
// Array indexing: x[i].
// Note that array types are converted to pointers in
// stackPointerFlowsToUse.
stackPointerFlowsToUse(use.(ArrayExpr).getArrayBase(), useType, source, isLocal)
or
// Pointer dereference: *x
stackPointerFlowsToUse(use.(PointerDereferenceExpr).getOperand(), useType, source, isLocal)
or
// Indirect use of a stack reference, via a reference variable.
exists(SsaDefinition def, StackVariable var |
stackReferenceFlowsToDef(def, var, useType, source, isLocal) and
use = def.getAUse(var)
)
or
// Use of a stack reference which arrived via one of the function's
// parameters. We do not use the original reference source here because
// it could lead to an extremely large number of results if the function
// has many callers. Instead we set `source` to the use of the parameter.
exists(SsaDefinition def, Parameter param |
referenceParamFlowsToDef(def, param, useType) and
use = def.getAUse(param) and
source = use and
isLocal = false
)
}
/**
* Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack
* addresses through SSA definitions.
*/
predicate stackPointerFlowsToDef(
SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) {
stackPointerFlowsToUse(def.getDefiningValue(var), useType, source, isLocal)
or
// Increment/decrement operators.
exists(VariableAccess access |
access = def.(CrementOperation).getOperand() and
var = access.getTarget() and
stackPointerFlowsToUse(access, useType, source, isLocal)
)
or
// Transitive closure over phi definitions.
stackPointerFlowsToDef(def.getAPhiInput(var), var, useType, source, isLocal)
}
/**
* Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack
* references through SSA definitions. This predicate is almost identical
* to stackPointerFlowsToDef, except it handles references types, such as
* int&, rather than pointers.
*/
predicate stackReferenceFlowsToDef(
SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) {
// Check that the type of the variable is a reference type and delegate
// the rest of the work to stackReferenceFlowsToDef_Impl.
isReferenceVariable(var) and
stackReferenceFlowsToDef_Impl(def, var, useType, source, isLocal)
}
/**
* stackReferenceFlowsToDef delegates most of the work to this
* predicate.
*/
predicate stackReferenceFlowsToDef_Impl(
SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) {
stackReferenceFlowsToUse(def.getDefiningValue(var), useType, source, isLocal)
or
// Increment/decrement operators.
exists(VariableAccess access |
access = def.(CrementOperation).getOperand() and
var = access.getTarget() and
stackReferenceFlowsToUse(access, useType, source, isLocal)
)
or
// Transitive closure over phi definitions.
stackReferenceFlowsToDef(def.getAPhiInput(var), var, useType, source, isLocal)
}
/** The type of the variable is a reference type, such as int&. */
predicate isReferenceVariable(StackVariable var) {
var.getUnspecifiedType() instanceof ReferenceType
}
/**
* Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack
* addresses which arrived through one of the function's parameters. This
* predicate is very similar to stackPointerFlowsToDef but they cannot be
* merged, because we cannot identify a sensible source expression here.
*/
predicate pointerParamFlowsToDef(SsaDefinition def, Parameter param, Type useType) {
// Only include a parameter definition if we can find a call site which
// might pass it a stack address.
exists(FunctionCall call |
call.getTarget() = param.getFunction() and
stackPointerFlowsToUse(call.getArgument(param.getIndex()), useType, _, _) and
def.definedByParameter(param)
)
or
// Transitive closure over phi definitions.
pointerParamFlowsToDef(def.getAPhiInput(param), param, useType)
}
/**
* Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack
* addresses which arrived through one of the function's parameters. This
* predicate is very similar to stackPointerFlowsToDef but they cannot be
* merged, because we cannot identify a sensible source expression here.
*/
predicate referenceParamFlowsToDef(SsaDefinition def, Parameter param, Type useType) {
// Only include a parameter definition if we can find a call site which
// might pass it a stack reference.
exists(FunctionCall call |
call.getTarget() = param.getFunction() and
stackReferenceFlowsToUse(call.getArgument(param.getIndex()), useType, _, _) and
def.definedByParameter(param)
)
or
// Transitive closure over phi definitions.
referenceParamFlowsToDef(def.getAPhiInput(param), param, useType)
}
/**
* Holds if this member function might be called on an object which
* is allocated on the stack.
*/
predicate memberFcnMightRunOnStack(MemberFunction fcn, Type useType) {
exists(FunctionCall call | call.getTarget() = fcn |
// Call of the form `x.f()` where `x` is allocated on the stack.
stackReferenceFlowsToUse(call.getQualifier(), useType, _, _)
or
// Call of the form `x->f()` where `x` is allocated on the stack.
stackPointerFlowsToUse(call.getQualifier(), useType, _, _)
)
or
// Constructor calls need to be treated as a special case, because
// `call.getQualifier()` is empty.
constructorMightRunOnStack(fcn) and
useType = fcn.getDeclaringType().getUnspecifiedType()
}
/**
* Helper predicate for memberFcnMightRunOnStack. Function calls
* to constructors need to be treated as a special case, because
* `call.getQualifier()` is empty. Instead, we need to check whether
* the constructor is called from an initializer. There are several
* kinds of initializers to consider.
*/
predicate constructorMightRunOnStack(Constructor constructor) {
exists(ConstructorCall call | call.getTarget() = constructor |
// Call to a constructor from a stack variable's initializer.
exists(StackVariable var | var.getInitializer().getExpr() = call)
or
// Call to a constructor from another constructor which might
// also run on the stack.
constructorMightRunOnStack(call.getEnclosingFunction()) and
(
call instanceof ConstructorDirectInit or
call instanceof ConstructorVirtualInit or
call instanceof ConstructorDelegationInit or
exists(ConstructorFieldInit init | init.getExpr() = call)
)
)
}

View File

@@ -0,0 +1,29 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*
* To use global (interprocedural) taint tracking, extend the class
* `TaintTracking::Configuration` as documented on that class. To use local
* (intraprocedural) taint tracking between expressions, call
* `TaintTracking::localExprTaint`. For more general cases of local taint
* tracking, call `TaintTracking::localTaint` or
* `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`.
*/
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.DataFlow2
module TaintTracking {
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
private import semmle.code.cpp.dataflow.TaintTracking2
/**
* DEPRECATED: Use TaintTracking2::Configuration instead.
*/
deprecated class Configuration2 = TaintTracking2::Configuration;
}

View File

@@ -0,0 +1,15 @@
/**
* Provides a `TaintTracking2` module, which is a copy of the `TaintTracking`
* module. Use this class when data-flow configurations or taint-tracking
* configurations must depend on each other. Two classes extending
* `DataFlow::Configuration` should never depend on each other, but one of them
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
*
* See `semmle.code.cpp.dataflow.TaintTracking` for the full documentation.
*/
module TaintTracking2 {
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
}

View File

@@ -0,0 +1,388 @@
/**
* Provides a local analysis for identifying where a variable address
* is effectively taken. Array-like offsets are allowed to pass through but
* not field-like offsets.
*
* This library is specialized to meet the needs of `FlowVar.qll`.
*/
/*
* Maintainer note: this file is one of several files that are similar but not
* identical. Many changes to this file will also apply to the others:
* - AddressConstantExpression.qll
* - AddressFlow.qll
* - EscapesTree.qll
*/
private import cpp
private import semmle.code.cpp.models.interfaces.PointerWrapper
/**
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
* template functions, these functions are essentially casts, so we treat them
* as such.
*/
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
/**
* Holds if `f` is an instantiation of `std::addressof`, which effectively
* converts a reference to a pointer.
*/
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
or
// When an object is implicitly converted to a reference to one of its base
// classes, it gets two `Conversion`s: there is first an implicit
// `CStyleCast` to its base class followed by a `ReferenceToExpr` to a
// reference to its base class. Whereas an explicit cast to the base class
// would produce an rvalue, which would not be convertible to an lvalue
// reference, this implicit cast instead produces an lvalue. The following
// case ensures that we propagate the property of being an lvalue through
// such casts.
lvalueIn.getConversion() = lvalueOut and
lvalueOut.(CStyleCast).isImplicit()
}
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
lvalueToLvalueStepPure(lvalueIn, lvalueOut)
or
// C++ only
lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted()
or
// C++ only
lvalueIn = lvalueOut.(Assignment).getLValue().getFullyConverted()
}
private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) {
pointerIn = lvalueOut.(ArrayExpr).getArrayBase().getFullyConverted()
or
pointerIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted()
}
private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) {
lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion)
or
lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted()
}
private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
(
pointerOut instanceof PointerAddExpr
or
pointerOut instanceof PointerSubExpr
) and
pointerIn = pointerOut.getAChild().getFullyConverted() and
pointerIn.getUnspecifiedType() instanceof PointerType
or
pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted()
or
pointerIn.getConversion() = pointerOut.(Cast)
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
or
pointerIn = pointerOut.(CommaExpr).getRightOperand().getFullyConverted()
or
pointerIn = pointerOut.(StmtExpr).getResultExpr().getFullyConverted()
}
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
or
exists(PointerWrapper wrapper, Call call | call = referenceOut |
referenceOut.getUnspecifiedType() instanceof ReferenceType and
call = wrapper.getAnUnwrapperFunction().getACallToThisFunction() and
lvalueIn = call.getQualifier().getFullyConverted()
)
}
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
}
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
pointerOut =
any(FunctionCall call |
stdAddressOf(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
or
exists(CopyConstructor copy, Call call | call = pointerOut |
copy.getDeclaringType() instanceof PointerWrapper and
call.getTarget() = copy and
// The 0'th argument is the value being copied.
referenceIn = call.getArgument(0).getFullyConverted()
)
}
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
referenceOut =
any(FunctionCall call |
stdIdentityFunction(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
or
referenceIn.getConversion() = referenceOut.(Cast)
or
referenceIn.getConversion() = referenceOut.(ParenthesisExpr)
}
private predicate assignmentTo(Expr updated, ControlFlowNode node) {
updated = node.(Assignment).getLValue().getFullyConverted()
or
updated = node.(CrementOperation).getOperand().getFullyConverted()
}
private predicate lvalueToUpdate(Expr lvalue, Expr outer, ControlFlowNode node) {
(
exists(Call call | node = call |
outer = call.getQualifier().getFullyConverted() and
outer.getUnspecifiedType() instanceof Class and
not (
call.getTarget().hasSpecifier("const") and
// Given the following program:
// ```
// struct C {
// void* data_;
// void* data() const { return data; }
// };
// C c;
// memcpy(c.data(), source, 16)
// ```
// the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
// `C::data` has a const specifier. So we further place the restriction that the type returned
// by `call` should not be of the form `const T*` (for some deeply const type `T`).
call.getType().isDeeplyConstBelow()
)
)
or
assignmentTo(outer, node)
or
exists(DotFieldAccess fa |
// `fa.otherField = ...` or `f(&fa)` or similar
outer = fa.getQualifier().getFullyConverted() and
valueToUpdate(fa, _, node)
)
) and
lvalue = outer
or
exists(Expr lvalueMid |
lvalueToLvalueStep(lvalue, lvalueMid) and
lvalueToUpdate(lvalueMid, outer, node)
)
or
exists(Expr pointerMid |
lvalueToPointerStep(lvalue, pointerMid) and
pointerToUpdate(pointerMid, outer, node)
)
or
exists(Expr referenceMid |
lvalueToReferenceStep(lvalue, referenceMid) and
referenceToUpdate(referenceMid, outer, node)
)
}
private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node) {
(
exists(Call call | node = call |
outer = call.getAnArgument().getFullyConverted() and
exists(PointerType pt | pt = outer.getType().stripTopLevelSpecifiers() |
not pt.getBaseType().isConst()
)
or
outer = call.getQualifier().getFullyConverted() and
outer.getUnspecifiedType() instanceof PointerType and
not (
call.getTarget().hasSpecifier("const") and
// See the `lvalueToUpdate` case for an explanation of this conjunct.
call.getType().isDeeplyConstBelow()
)
or
// Pointer wrappers behave as raw pointers for dataflow purposes.
outer = call.getAnArgument().getFullyConverted() and
exists(PointerWrapper wrapper | wrapper = outer.getType().stripTopLevelSpecifiers() |
not wrapper.pointsToConst()
)
or
outer = call.getQualifier().getFullyConverted() and
outer.getUnspecifiedType() instanceof PointerWrapper and
not (
call.getTarget().hasSpecifier("const") and
call.getType().isDeeplyConstBelow()
)
)
or
exists(PointerFieldAccess fa |
// `fa.otherField = ...` or `f(&fa)` or similar
outer = fa.getQualifier().getFullyConverted() and
valueToUpdate(fa, _, node)
)
) and
pointer = outer
or
exists(Expr lvalueMid |
pointerToLvalueStep(pointer, lvalueMid) and
lvalueToUpdate(lvalueMid, outer, node)
)
or
exists(Expr pointerMid |
pointerToPointerStep(pointer, pointerMid) and
pointerToUpdate(pointerMid, outer, node)
)
}
private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode node) {
exists(Call call |
node = call and
outer = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget()) and
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
not rt.getBaseType().isConst() or
rt.getBaseType().getUnspecifiedType() =
any(PointerWrapper wrapper | not wrapper.pointsToConst())
)
) and
reference = outer
or
exists(Expr lvalueMid |
referenceToLvalueStep(reference, lvalueMid) and
lvalueToUpdate(lvalueMid, outer, node)
)
or
exists(Expr pointerMid |
referenceToPointerStep(reference, pointerMid) and
pointerToUpdate(pointerMid, outer, node)
)
or
exists(Expr referenceMid |
referenceToReferenceStep(reference, referenceMid) and
referenceToUpdate(referenceMid, outer, node)
)
}
private predicate lvalueFromVariableAccess(VariableAccess va, Expr lvalue) {
// Base case for non-reference types.
lvalue = va and
not va.getConversion() instanceof ReferenceDereferenceExpr
or
// Base case for reference types where we pretend that they are
// non-reference types. The type of the target of `va` can be `ReferenceType`
// or `FunctionReferenceType`.
lvalue = va.getConversion().(ReferenceDereferenceExpr)
or
// lvalue -> lvalue
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToLvalueStep(prev, lvalue)
)
or
// pointer -> lvalue
exists(Expr prev |
pointerFromVariableAccess(va, prev) and
pointerToLvalueStep(prev, lvalue)
)
or
// reference -> lvalue
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToLvalueStep(prev, lvalue)
)
}
private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
// pointer -> pointer
exists(Expr prev |
pointerFromVariableAccess(va, prev) and
pointerToPointerStep(prev, pointer)
)
or
// reference -> pointer
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToPointerStep(prev, pointer)
)
or
// lvalue -> pointer
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToPointerStep(prev, pointer)
)
}
private predicate referenceFromVariableAccess(VariableAccess va, Expr reference) {
// reference -> reference
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToReferenceStep(prev, reference)
)
or
// lvalue -> reference
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
lvalueToReferenceStep(prev, reference)
)
}
/**
* Holds if `node` is a control-flow node that may modify `inner` (or what it
* points to) through `outer`. The two expressions may be `Conversion`s. Plain
* assignments to variables are not included in this predicate since they are
* assumed to be analyzed by SSA or similar means.
*
* For example, in `f(& (*a).x)`, there are two results:
* - `inner` = `... .x`, `outer` = `&...`, `node` = `f(...)`.
* - `inner` = `a`, `outer` = `(...)`, `node` = `f(...)`.
*/
cached
predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) {
(
lvalueToUpdate(inner, outer, node)
or
pointerToUpdate(inner, outer, node)
or
referenceToUpdate(inner, outer, node)
) and
(
inner instanceof VariableAccess and
// Don't track non-field assignments
not (assignmentTo(outer, _) and outer.(VariableAccess).getTarget() instanceof StackVariable)
or
inner instanceof ThisExpr
or
inner instanceof Call
// `inner` could also be `*` or `ReferenceDereferenceExpr`, but we
// can't do anything useful with those at the moment.
)
}
/**
* Holds if `e` is a fully-converted expression that evaluates to an lvalue
* derived from `va` and is used for reading from or assigning to. This is in
* contrast with a variable access that is used for taking an address (`&x`)
* or simply discarding its value (`x;`).
*
* This analysis does not propagate across assignments or calls, and unlike
* `variableAccessedAsValue` in `semmle.code.cpp.dataflow.EscapesTree` it
* propagates through array accesses but not field accesses. The analysis is
* also not concerned with whether the lvalue `e` is converted to an rvalue --
* to examine that, use the relevant member predicates on `Expr`.
*
* If `va` has reference type, the analysis concerns the value pointed to by
* the reference rather than the reference itself. The expression `e` may be a
* `Conversion`.
*/
predicate variablePartiallyAccessed(VariableAccess va, Expr e) {
lvalueFromVariableAccess(va, e) and
not lvalueToLvalueStepPure(e, _) and
not lvalueToPointerStep(e, _) and
not lvalueToReferenceStep(e, _) and
not e = any(ExprInVoidContext eivc | e = eivc.getConversion*())
}

View File

@@ -0,0 +1,65 @@
private import cpp
/**
* Gets a function that might be called by `call`.
*/
Function viableCallable(Call call) {
result = call.getTarget()
or
// If the target of the call does not have a body in the snapshot, it might
// be because the target is just a header declaration, and the real target
// will be determined at run time when the caller and callee are linked
// together by the operating system's dynamic linker. In case a _unique_
// function with the right signature is present in the database, we return
// that as a potential callee.
exists(string qualifiedName, int nparams |
callSignatureWithoutBody(qualifiedName, nparams, call) and
functionSignatureWithBody(qualifiedName, nparams, result) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
}
/**
* Holds if `f` is a function with a body that has name `qualifiedName` and
* `nparams` parameter count. See `functionSignature`.
*/
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
functionSignature(f, qualifiedName, nparams) and
exists(f.getBlock())
}
/**
* Holds if the target of `call` is a function _with no definition_ that has
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
*/
pragma[noinline]
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, Call call) {
exists(Function target |
target = call.getTarget() and
not exists(target.getBlock()) and
functionSignature(target, qualifiedName, nparams)
)
}
/**
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
* an approximation of its signature for the purpose of matching functions that
* might be the same across link targets.
*/
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
qualifiedName = f.getQualifiedName() and
nparams = f.getNumberOfParameters() and
not f.isStatic()
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(Call call, Function f) { none() }
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
Function viableImplInCallContext(Call call, Call ctx) { none() }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
/**
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getEnclosingCallable()) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueType(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getNodeType(n)) and
c != 1 and
msg = "Node should have one type but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
msg = "Local flow step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = n.getEnclosingCallable() and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
n.getEnclosingCallable() != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a result of `getPreUpdateNode` to be an
// instance of `PostUpdateNode`.
private Node getPre(PostUpdateNode n) {
result = n.getPreUpdateNode()
or
none()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
getPre(n) = n and
msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a `PostUpdateNode` to be the target of
// `simpleLocalFlowStep`.
private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
query predicate postWithInFlow(Node n, string msg) {
isPostUpdateNode(n) and
simpleLocalFlowStep(_, n) and
msg = "PostUpdateNode should not be the target of local flow."
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
/**
* Provides C++-specific definitions for use in the data flow library.
*/
module Private {
import DataFlowPrivate
import DataFlowDispatch
}
module Public {
import DataFlowUtil
}

View File

@@ -0,0 +1,285 @@
private import cpp
private import DataFlowUtil
private import DataFlowDispatch
private import FlowVar
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
result.asExpr() = call.getQualifier()
or
result.(PreObjectInitializerNode).getExpr().(ConstructorCall) = call
// This does not include the implicit `this` argument on auto-generated
// base class destructor calls as those do not have an AST element.
}
/** An argument to a call. */
private class Argument extends Expr {
Call call;
int pos;
Argument() { call.getArgument(pos) = this }
/** Gets the call that has this argument. */
Call getCall() { result = call }
/** Gets the position of this argument. */
int getPosition() { result = pos }
}
/**
* A data flow node that occurs as the argument of a call and is passed as-is
* to the callable. Arguments that are wrapped in an implicit varargs array
* creation are not included, but the implicitly created array is.
* Instance arguments are also included.
*/
class ArgumentNode extends Node {
ArgumentNode() {
exists(Argument arg | this.asExpr() = arg) or
this = getInstanceArgument(_)
}
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
predicate argumentOf(DataFlowCall call, int pos) {
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
or
pos = -1 and this = getInstanceArgument(call)
}
/** Gets the call in which this node is an argument. */
DataFlowCall getCall() { this.argumentOf(result, _) }
}
private newtype TReturnKind =
TNormalReturnKind() or
TRefReturnKind(int i) { exists(Parameter parameter | i = parameter.getIndex()) }
/**
* A return kind. A return kind describes how a value can be returned
* from a callable. For C++, this is simply a function return.
*/
class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */
string toString() {
this instanceof TNormalReturnKind and
result = "return"
or
this instanceof TRefReturnKind and
result = "ref"
}
}
/** A data flow node that represents a returned value in the called function. */
abstract class ReturnNode extends Node {
/** Gets the kind of this returned value. */
abstract ReturnKind getKind();
}
/** A `ReturnNode` that occurs as the result of a `ReturnStmt`. */
private class NormalReturnNode extends ReturnNode, ExprNode {
NormalReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
/** Gets the kind of this returned value. */
override ReturnKind getKind() { result = TNormalReturnKind() }
}
/**
* A `ReturnNode` that occurs as a result of a definition of a reference
* parameter reaching the end of a function body.
*/
private class RefReturnNode extends ReturnNode, RefParameterFinalValueNode {
/** Gets the kind of this returned value. */
override ReturnKind getKind() { result = TRefReturnKind(this.getParameter().getIndex()) }
}
/** A data flow node that represents the output of a call at the call site. */
abstract class OutNode extends Node {
/** Gets the underlying call. */
abstract DataFlowCall getCall();
}
private class ExprOutNode extends OutNode, ExprNode {
ExprOutNode() { this.getExpr() instanceof Call }
/** Gets the underlying call. */
override DataFlowCall getCall() { result = this.getExpr() }
}
private class RefOutNode extends OutNode, DefinitionByReferenceOrIteratorNode {
/** Gets the underlying call. */
override DataFlowCall getCall() { result = this.getArgument().getParent() }
}
/**
* Gets a node that can read the value returned from `call` with return kind
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result = call.getNode() and
kind = TNormalReturnKind()
or
exists(int i |
result.(DefinitionByReferenceOrIteratorNode).getArgument() = call.getArgument(i) and
kind = TRefReturnKind(i)
)
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
predicate jumpStep(Node n1, Node n2) { none() }
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
exists(ClassAggregateLiteral aggr, Field field |
// The following line requires `node2` to be both an `ExprNode` and a
// `PostUpdateNode`, which means it must be an `ObjectInitializerNode`.
node2.asExpr() = aggr and
f.(FieldContent).getField() = field and
aggr.getFieldExpr(field) = node1.asExpr()
)
or
exists(FieldAccess fa |
exists(Assignment a |
node1.asExpr() = a and
a.getLValue() = fa
) and
node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
f.(FieldContent).getField() = fa.getTarget()
)
or
exists(ConstructorFieldInit cfi |
node2.getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = cfi and
f.(FieldContent).getField() = cfi.getTarget() and
node1.asExpr() = cfi.getExpr()
)
}
/**
* Holds if data can flow from `node1` to `node2` via a read of `f`.
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
predicate readStep(Node node1, Content f, Node node2) {
exists(FieldAccess fr |
node1.asExpr() = fr.getQualifier() and
fr.getTarget() = f.(FieldContent).getField() and
fr = node2.asExpr() and
not fr = any(AssignExpr a).getLValue()
)
}
/**
* Holds if values stored inside content `c` are cleared at node `n`.
*/
predicate clearsContent(Node n, Content c) {
none() // stub implementation
}
/** Gets the type of `n` used for type pruning. */
Type getNodeType(Node n) {
suppressUnusedNode(n) and
result instanceof VoidType // stub implementation
}
/** Gets a string representation of a type returned by `getNodeType`. */
string ppReprType(Type t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
predicate compatibleTypes(Type t1, Type t2) {
any() // stub implementation
}
private predicate suppressUnusedNode(Node n) { any() }
//////////////////////////////////////////////////////////////////////////////
// Java QL library compatibility wrappers
//////////////////////////////////////////////////////////////////////////////
/** A node that performs a type cast. */
class CastNode extends Node {
CastNode() { none() } // stub implementation
}
class DataFlowCallable = Function;
class DataFlowExpr = Expr;
class DataFlowType = Type;
/** A function call relevant for data flow. */
class DataFlowCall extends Expr {
DataFlowCall() { this instanceof Call }
/**
* Gets the nth argument for this call.
*
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
*/
Expr getArgument(int n) { result = this.(Call).getArgument(n) }
/** Gets the data flow node corresponding to this call. */
ExprNode getNode() { result.getExpr() = this }
/** Gets the enclosing callable of this call. */
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/** The unit type. */
private newtype TUnit = TMkUnit()
/** The trivial type with a single element. */
class Unit extends TUnit {
/** Gets a textual representation of this element. */
string toString() { result = "unit" }
}
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }

View File

@@ -0,0 +1,841 @@
/**
* Provides C++-specific definitions for use in the data flow library.
*/
private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
private import semmle.code.cpp.dataflow.internal.AddressFlow
cached
private newtype TNode =
TExprNode(Expr e) or
TPartialDefinitionNode(PartialDefinition pd) or
TPreObjectInitializerNode(Expr e) {
e instanceof ConstructorCall
or
e instanceof ClassAggregateLiteral
} or
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
TPreConstructorInitThis(ConstructorFieldInit cfi) or
TPostConstructorInitThis(ConstructorFieldInit cfi) or
TInnerPartialDefinitionNode(Expr e) {
exists(PartialDefinition def, Expr outer |
def.definesExpressions(e, outer) and
// This condition ensures that we don't get two post-update nodes sharing
// the same pre-update node.
e != outer
)
} or
TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
TRefParameterFinalValueNode(Parameter p) { exists(FlowVar var | var.reachesRefParameter(p)) }
/**
* A node in a data flow graph.
*
* A node can be either an expression, a parameter, or an uninitialized local
* variable. Such nodes are created with `DataFlow::exprNode`,
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
*/
class Node extends TNode {
/** Gets the function to which this node belongs. */
Function getFunction() { none() } // overridden in subclasses
/**
* INTERNAL: Do not use. Alternative name for `getFunction`.
*/
final Function getEnclosingCallable() { result = this.getFunction() }
/** Gets the type of this node. */
Type getType() { none() } // overridden in subclasses
/**
* Gets the expression corresponding to this node, if any. This predicate
* only has a result on nodes that represent the value of evaluating the
* expression. For data flowing _out of_ an expression, like when an
* argument is passed by reference, use `asDefiningArgument` instead of
* `asExpr`.
*/
Expr asExpr() { result = this.(ExprNode).getExpr() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
/**
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
* This predicate should be used instead of `asExpr` when referring to the
* value of a reference argument _after_ the call has returned. For example,
* in `f(&x)`, this predicate will have `&x` as its result for the `Node`
* that represents the new value of `x`.
*/
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/**
* Gets the expression that is partially defined by this node, if any.
*
* Partial definitions are created for field stores (`x.y = taint();` is a partial
* definition of `x`), and for calls that may change the value of an object (so
* `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
* a partial definition of `&x`).
*/
Expr asPartialDefinition() {
this.(PartialDefinitionNode).getPartialDefinition().definesExpressions(_, result)
}
/**
* Gets the uninitialized local variable corresponding to this node, if
* any.
*/
LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() }
/** Gets a textual representation of this element. */
string toString() { none() } // overridden by subclasses
/** Gets the location of this element. */
Location getLocation() { none() } // overridden by subclasses
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Gets an upper bound on the type of this node.
*/
Type getTypeBound() { result = getType() }
}
/**
* An expression, viewed as a node in a data flow graph.
*/
class ExprNode extends Node, TExprNode {
Expr expr;
ExprNode() { this = TExprNode(expr) }
override Function getFunction() { result = expr.getEnclosingFunction() }
override Type getType() { result = expr.getType() }
override string toString() { result = expr.toString() }
override Location getLocation() { result = expr.getLocation() }
/** Gets the expression corresponding to this node. */
Expr getExpr() { result = expr }
}
abstract class ParameterNode extends Node, TNode {
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)
* position. The implicit `this` parameter is considered to have index `-1`.
*/
abstract predicate isParameterOf(Function f, int i);
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
Parameter param;
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
override Function getFunction() { result = param.getFunction() }
override Type getType() { result = param.getType() }
override string toString() { result = param.toString() }
override Location getLocation() { result = param.getLocation() }
/** Gets the parameter corresponding to this node. */
Parameter getParameter() { result = param }
override predicate isParameterOf(Function f, int i) { f.getParameter(i) = param }
}
class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
MemberFunction f;
ImplicitParameterNode() { this = TInstanceParameterNode(f) }
override Function getFunction() { result = f }
override Type getType() { result = f.getDeclaringType() }
override string toString() { result = "this" }
override Location getLocation() { result = f.getLocation() }
override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 }
}
/**
* INTERNAL: do not use.
*
* A node that represents the value of a variable after a function call that
* may have changed the variable because it's passed by reference or because an
* iterator for it was passed by value or by reference.
*/
class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode {
Expr inner;
Expr argument;
DefinitionByReferenceOrIteratorNode() {
this.getPartialDefinition().definesExpressions(inner, argument) and
(
this.getPartialDefinition() instanceof DefinitionByReference
or
this.getPartialDefinition() instanceof DefinitionByIterator
)
}
override Function getFunction() { result = inner.getEnclosingFunction() }
override Type getType() { result = inner.getType() }
override Location getLocation() { result = argument.getLocation() }
override ExprNode getPreUpdateNode() { result.getExpr() = argument }
/** Gets the argument corresponding to this node. */
Expr getArgument() { result = argument }
/** Gets the parameter through which this value is assigned. */
Parameter getParameter() {
exists(FunctionCall call, int i |
argument = call.getArgument(i) and
result = call.getTarget().getParameter(i)
)
}
}
/**
* A node that represents the value of a variable after a function call that
* may have changed the variable because it's passed by reference.
*
* A typical example would be a call `f(&x)`. Firstly, there will be flow into
* `x` from previous definitions of `x`. Secondly, there will be a
* `DefinitionByReferenceNode` to represent the value of `x` after the call has
* returned. This node will have its `getArgument()` equal to `&x`.
*/
class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode {
override VariablePartialDefinition pd;
override string toString() { result = "ref arg " + argument.toString() }
}
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
*/
class UninitializedNode extends Node, TUninitializedNode {
LocalVariable v;
UninitializedNode() { this = TUninitializedNode(v) }
override Function getFunction() { result = v.getFunction() }
override Type getType() { result = v.getType() }
override string toString() { result = v.toString() }
override Location getLocation() { result = v.getLocation() }
/** Gets the uninitialized local variable corresponding to this node. */
LocalVariable getLocalVariable() { result = v }
}
/** INTERNAL: do not use. The final value of a non-const ref parameter. */
class RefParameterFinalValueNode extends Node, TRefParameterFinalValueNode {
Parameter p;
RefParameterFinalValueNode() { this = TRefParameterFinalValueNode(p) }
override Function getFunction() { result = p.getFunction() }
override Type getType() { result = p.getType() }
override string toString() { result = p.toString() }
override Location getLocation() { result = p.getLocation() }
Parameter getParameter() { result = p }
}
/**
* A node associated with an object after an operation that might have
* changed its state.
*
* This can be either the argument to a callable after the callable returns
* (which might have mutated the argument), or the qualifier of a field after
* an update to the field.
*
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
* to the value before the update with the exception of `ClassInstanceExpr`,
* which represents the value after the constructor has run.
*/
abstract class PostUpdateNode extends Node {
/**
* Gets the node before the state update.
*/
abstract Node getPreUpdateNode();
override Function getFunction() { result = getPreUpdateNode().getFunction() }
override Type getType() { result = getPreUpdateNode().getType() }
override Location getLocation() { result = getPreUpdateNode().getLocation() }
}
abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
PartialDefinition pd;
PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
override Location getLocation() { result = pd.getActualLocation() }
PartialDefinition getPartialDefinition() { result = pd }
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
}
private class VariablePartialDefinitionNode extends PartialDefinitionNode {
override VariablePartialDefinition pd;
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
}
/**
* INTERNAL: do not use.
*
* A synthetic data flow node used for flow into a collection when an iterator
* write occurs in a callee.
*/
private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
override IteratorPartialDefinition pd;
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
}
/**
* A post-update node on the `e->f` in `f(&e->f)` (and other forms).
*/
private class InnerPartialDefinitionNode extends TInnerPartialDefinitionNode, PostUpdateNode {
Expr e;
InnerPartialDefinitionNode() { this = TInnerPartialDefinitionNode(e) }
override ExprNode getPreUpdateNode() { result.getExpr() = e }
override Function getFunction() { result = e.getEnclosingFunction() }
override Type getType() { result = e.getType() }
override string toString() { result = e.toString() + " [inner post update]" }
override Location getLocation() { result = e.getLocation() }
}
/**
* A node representing the temporary value of an object that was just
* constructed by a constructor call or an aggregate initializer. This is only
* for objects, not for pointers to objects.
*
* These expressions are their own post-update nodes but instead have synthetic
* pre-update nodes.
*/
private class ObjectInitializerNode extends PostUpdateNode, TExprNode {
PreObjectInitializerNode pre;
ObjectInitializerNode() {
// If a `Node` is associated with a `PreObjectInitializerNode`, then it's
// an `ObjectInitializerNode`.
pre.getExpr() = this.asExpr()
}
override PreObjectInitializerNode getPreUpdateNode() { result = pre }
// No override of `toString` since these nodes already have a `toString` from
// their overlap with `ExprNode`.
}
/**
* INTERNAL: do not use.
*
* A synthetic data-flow node that plays the role of a temporary object that
* has not yet been initialized.
*/
class PreObjectInitializerNode extends Node, TPreObjectInitializerNode {
Expr getExpr() { this = TPreObjectInitializerNode(result) }
override Function getFunction() { result = getExpr().getEnclosingFunction() }
override Type getType() { result = getExpr().getType() }
override Location getLocation() { result = getExpr().getLocation() }
override string toString() { result = getExpr().toString() + " [pre init]" }
}
/**
* A synthetic data-flow node that plays the role of the post-update `this`
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
* equivalent to the `this` _after_ the field has been assigned.
*/
private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorInitThis {
override PreConstructorInitThis getPreUpdateNode() {
this = TPostConstructorInitThis(result.getConstructorFieldInit())
}
override string toString() {
result = getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]"
}
}
/**
* INTERNAL: do not use.
*
* A synthetic data-flow node that plays the role of the pre-update `this`
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
* equivalent to the `this` _before_ the field has been assigned.
*/
class PreConstructorInitThis extends Node, TPreConstructorInitThis {
ConstructorFieldInit getConstructorFieldInit() { this = TPreConstructorInitThis(result) }
override Constructor getFunction() { result = getConstructorFieldInit().getEnclosingFunction() }
override PointerType getType() {
result.getBaseType() = getConstructorFieldInit().getEnclosingFunction().getDeclaringType()
}
override Location getLocation() { result = getConstructorFieldInit().getLocation() }
override string toString() { result = getConstructorFieldInit().toString() + " [pre-this]" }
}
/**
* Gets the `Node` corresponding to the value of evaluating `e`. For data
* flowing _out of_ an expression, like when an argument is passed by
* reference, use `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
*/
ParameterNode parameterNode(Parameter p) { result.(ExplicitParameterNode).getParameter() = p }
/**
* Gets the `Node` corresponding to a definition by reference of the variable
* that is passed as `argument` of a call.
*/
DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
result.getArgument() = argument
}
/**
* Gets the `Node` corresponding to the value of an uninitialized local
* variable `v`.
*/
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
private module ThisFlow {
/**
* Gets the 0-based index of `thisNode` in `b`, where `thisNode` is an access
* to `this` that may or may not have an associated `PostUpdateNode`. To make
* room for synthetic nodes that access `this`, the index may not correspond
* to an actual `ControlFlowNode`.
*/
private int basicBlockThisIndex(BasicBlock b, Node thisNode) {
// The implicit `this` parameter node is given a very negative offset to
// make space for any `ConstructorFieldInit`s there may be between it and
// the block contents.
thisNode.(ImplicitParameterNode).getFunction().getBlock() = b and
result = -2147483648
or
// Place the synthetic `this` node for a `ConstructorFieldInit` at a
// negative offset in the first basic block, between the
// `ImplicitParameterNode` and the first statement.
exists(Constructor constructor, int i |
thisNode.(PreConstructorInitThis).getConstructorFieldInit() = constructor.getInitializer(i) and
result = -2147483648 + 1 + i and
b = thisNode.getFunction().getBlock()
)
or
b.getNode(result) = thisNode.asExpr().(ThisExpr)
}
private int thisRank(BasicBlock b, Node thisNode) {
thisNode = rank[result](Node n, int i | i = basicBlockThisIndex(b, n) | n order by i)
}
private int lastThisRank(BasicBlock b) { result = max(thisRank(b, _)) }
private predicate thisAccessBlockReaches(BasicBlock b1, BasicBlock b2) {
exists(basicBlockThisIndex(b1, _)) and b2 = b1.getASuccessor()
or
exists(BasicBlock mid |
thisAccessBlockReaches(b1, mid) and
b2 = mid.getASuccessor() and
not exists(basicBlockThisIndex(mid, _))
)
}
predicate adjacentThisRefs(Node n1, Node n2) {
exists(BasicBlock b | thisRank(b, n1) + 1 = thisRank(b, n2))
or
exists(BasicBlock b1, BasicBlock b2 |
lastThisRank(b1) = thisRank(b1, n1) and
thisAccessBlockReaches(b1, b2) and
thisRank(b2, n2) = 1
)
}
}
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
cached
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
simpleLocalFlowStep(nodeFrom, nodeTo)
or
// Field flow is not strictly a "step" but covers the whole function
// transitively. There's no way to get a step-like relation out of the global
// data flow library, so we just have to accept some big steps here.
FieldFlow::fieldFlow(nodeFrom, nodeTo)
}
/**
* INTERNAL: do not use.
*
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
*/
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or
// Assignment -> LValue post-update node
//
// This is used for assignments whose left-hand side is not a variable
// assignment or a storeStep but is still modeled by other means. It could be
// a call to `operator*` or `operator[]` where taint should flow to the
// post-update node of the qualifier.
exists(AssignExpr assign |
nodeFrom.asExpr() = assign and
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
)
or
// Node -> FlowVar -> VariableAccess
exists(FlowVar var |
(
exprToVarStep(nodeFrom.asExpr(), var)
or
varSourceBaseCase(var, nodeFrom.asParameter())
or
varSourceBaseCase(var, nodeFrom.asUninitialized())
or
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
) and
varToNodeStep(var, nodeTo)
)
or
// Expr -> DefinitionByReferenceNode
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
or
// `this` -> adjacent-`this`
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
or
// post-update-`this` -> following-`this`-ref
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
or
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
// from which there is field flow to `x` via reverse read.
exists(PartialDefinition def, Expr inner, Expr outer |
def.definesExpressions(inner, outer) and
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
)
or
// Reverse flow: data that flows from the post-update node of a reference
// returned by a function call, back into the qualifier of that function.
// This allows data to flow 'in' through references returned by a modeled
// function such as `operator[]`.
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
call.getTarget() = f and
inModel.isReturnValueDeref() and
outModel.isQualifierObject() and
f.hasDataFlow(inModel, outModel) and
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
nodeTo.asDefiningArgument() = call.getQualifier()
)
}
/**
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
/**
* Holds if data can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
/**
* Holds if the initial value of `v`, if it is a source, flows to `var`.
*/
private predicate varSourceBaseCase(FlowVar var, Variable v) { var.definedByInitialValue(v) }
/**
* Holds if `var` is defined by an assignment-like operation that causes flow
* directly from `assignedExpr` to `var`, _and_ `assignedExpr` evaluates to
* the same value as what is assigned to `var`.
*/
private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
exists(ControlFlowNode operation |
var.definedByExpr(assignedExpr, operation) and
not operation instanceof PostfixCrementOperation
)
}
/**
* Holds if the node `n` is an access of the variable `var`.
*/
private predicate varToNodeStep(FlowVar var, Node n) {
n.asExpr() = var.getAnAccess()
or
var.reachesRefParameter(n.(RefParameterFinalValueNode).getParameter())
}
/**
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
* where `toExpr` is the immediate AST parent of `fromExpr`. For example,
* data flows from `x` and `y` to `b ? x : y`.
*/
private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
toExpr = any(ConditionalExpr cond | fromExpr = cond.getThen() or fromExpr = cond.getElse())
or
toExpr = any(AssignExpr assign | fromExpr = assign.getRValue())
or
toExpr = any(CommaExpr comma | fromExpr = comma.getRightOperand())
or
toExpr = any(PostfixCrementOperation op | fromExpr = op.getOperand())
or
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
or
toExpr.(AddressOfExpr).getOperand() = fromExpr
or
// This rule enables flow from an array to its elements. Example: `a` to
// `a[i]` or `*a`, where `a` is an array type. It does not enable flow from a
// pointer to its indirection as in `p[i]` where `p` is a pointer type.
exists(Expr toConverted |
variablePartiallyAccessed(fromExpr, toConverted) and
toExpr = toConverted.getUnconverted() and
not toExpr = fromExpr
)
or
toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr
or
// The following case is needed to track the qualifier object for flow
// through fields. It gives flow from `T(x)` to `new T(x)`. That's not
// strictly _data_ flow but _taint_ flow because the type of `fromExpr` is
// `T` while the type of `toExpr` is `T*`.
//
// This discrepancy is an artifact of how `new`-expressions are represented
// in the database in a way that slightly varies from what the standard
// specifies. In the C++ standard, there is no constructor call expression
// `T(x)` after `new`. Instead there is a type `T` and an optional
// initializer `(x)`.
toExpr.(NewExpr).getInitializer() = fromExpr
or
// A lambda expression (`[captures](params){body}`) is just a thin wrapper
// around the desugared closure creation in the form of a
// `ClassAggregateLiteral` (`{ capture1, ..., captureN }`).
toExpr.(LambdaExpression).getInitializer() = fromExpr
or
// Data flow through a function model.
toExpr =
any(Call call |
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel |
f.hasDataFlow(inModel, outModel) and
(
exists(int iIn |
inModel.isParameterDeref(iIn) and
call.passesByReference(iIn, fromExpr)
)
or
exists(int iIn |
inModel.isParameter(iIn) and
fromExpr = call.getArgument(iIn)
)
or
inModel.isQualifierObject() and
fromExpr = call.getQualifier()
or
inModel.isQualifierAddress() and
fromExpr = call.getQualifier()
) and
call.getTarget() = f and
// AST dataflow treats a reference as if it were the referred-to object, while the dataflow
// models treat references as pointers. If the return type of the call is a reference, then
// look for data flow the the referred-to object, rather than the reference itself.
if call.getType().getUnspecifiedType() instanceof ReferenceType
then outModel.isReturnValueDeref()
else outModel.isReturnValue()
)
)
}
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
outModel.isParameterDeref(argOutIndex) and
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
inModel.isParameterDeref(argInIndex) and
call.passesByReference(argInIndex, exprIn)
or
inModel.isParameter(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
}
private module FieldFlow {
private import DataFlowImplCommon
private import DataFlowImplLocal
private import DataFlowPrivate
/**
* A configuration for finding local-only flow through fields. This uses the
* `Configuration` class in the dedicated `DataFlowImplLocal` copy of the
* shared library that's not user-exposed directly.
*
* To keep the flow local to a single function, we put barriers on parameters
* and return statements. Sources and sinks are the values that go into and
* out of fields, respectively.
*/
private class FieldConfiguration extends Configuration {
FieldConfiguration() { this = "FieldConfiguration" }
override predicate isSource(Node source) {
storeStep(source, _, _)
or
// Also mark `foo(a.b);` as a source when `a.b` may be overwritten by `foo`.
readStep(_, _, any(Node node | node.asExpr() = source.asDefiningArgument()))
}
override predicate isSink(Node sink) { readStep(_, _, sink) }
override predicate isBarrier(Node node) { node instanceof ParameterNode }
override predicate isBarrierOut(Node node) {
node.asExpr().getParent() instanceof ReturnStmt
or
node.asExpr().getParent() instanceof ThrowExpr
}
}
predicate fieldFlow(Node node1, Node node2) {
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
// This configuration should not be able to cross function boundaries, but
// we double-check here just to be sure.
getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2)
}
}
VariableAccess getAnAccessToAssignedVariable(Expr assign) {
(
assign instanceof Assignment
or
assign instanceof CrementOperation
) and
exists(FlowVar var |
var.definedByExpr(_, assign) and
result = var.getAnAccess()
)
}
private newtype TContent =
TFieldContent(Field f) or
TCollectionContent() or
TArrayContent()
/**
* A description of the way data may be stored inside an object. Examples
* include instance fields, the contents of a collection object, or the contents
* of an array.
*/
class Content extends TContent {
/** Gets a textual representation of this element. */
abstract string toString();
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
}
/** A reference through an instance field. */
class FieldContent extends Content, TFieldContent {
Field f;
FieldContent() { this = TFieldContent(f) }
Field getField() { result = f }
override string toString() { result = f.toString() }
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
}
/** A reference through an array. */
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "[]" }
}
/** A reference through the contents of some collection-like container. */
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "<element>" }
}
/**
* A guard that validates some expression.
*
* To use this in a configuration, extend the class and provide a
* characteristic predicate precisely specifying the guard, and override
* `checks` to specify what is being validated and in which branch.
*
* It is important that all extending classes in scope are disjoint.
*/
class BarrierGuard extends GuardCondition {
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
abstract predicate checks(Expr e, boolean b);
/** Gets a node guarded by this guard. */
final ExprNode getAGuardedNode() {
exists(SsaDefinition def, Variable v, boolean branch |
result.getExpr() = def.getAUse(v) and
this.checks(def.getAUse(v), branch) and
this.controls(result.getExpr().getBasicBlock(), branch)
)
}
}

View File

@@ -0,0 +1,895 @@
/**
* Provides a class for handling variables in the data flow analysis.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
private import semmle.code.cpp.dataflow.internal.AddressFlow
private import semmle.code.cpp.models.implementations.Iterator
private import semmle.code.cpp.models.interfaces.PointerWrapper
/**
* A conceptual variable that is assigned only once, like an SSA variable. This
* class is used for tracking data flow through variables, where the desired
* semantics is sometimes different from what the SSA library provides. Unlike
* SSA, there are no _phi_ nodes; instead, each `VariableAccess` may be
* associated with more than one `FlowVar`.
*
* Each instance of this class corresponds to a modification or an initial
* value of a variable. A `FlowVar` has exactly one result for either
* `definedByExpr` or `definedByInitialValue`. The documentation on those two
* member predicates explains how a `FlowVar` relates to syntactic constructs of
* the language.
*/
cached
class FlowVar extends TFlowVar {
/**
* Gets a `VariableAccess` that _may_ take its value from `this`. Consider
* the following snippet.
*
* ```
* int x = 0;
* if (b) { f(x); x = 1; }
* g(x)
* ```
*
* The access to `x` in `f(x)` may take its value from the `FlowVar`
* corresponding to `int x = 0`, while the access to `x` in `g(x)` may take
* its value from the `FlowVar` corresponding to `int x = 0` or the `FlowVar`
* corresponding to `x = 1`. The `x` in `x = 1` is not considered to be an
* access.
*/
cached
abstract VariableAccess getAnAccess();
/**
* Holds if this `FlowVar` corresponds to a modification occurring when `node` is
* evaluated, receiving a value best described by `e`. The following is an
* exhaustive list of cases where this may happen.
*
* - `node` is an `Initializer` and `e` is its contained expression.
* - `node` is an `AssignExpr`, and `e` is its right-hand side.
* - `node` is an `AssignOperation`, and `e` is `node`.
* - `node` is a `CrementOperation`, and `e` is `node`. The case where
* `node instanceof PostCrementOperation` is an exception to the rule that
* `this` contains the value of `e` after the evaluation of `node`.
*/
cached
abstract predicate definedByExpr(Expr e, ControlFlowNode node);
/**
* Holds if this `FlowVar` is a `PartialDefinition` whose outer defined
* expression is `e`. For example, in `f(&x)`, the outer defined expression
* is `&x`.
*/
cached
abstract predicate definedPartiallyAt(Expr e);
/**
* Holds if this `FlowVar` is a definition of a reference parameter `p` that
* persists until the function returns.
*/
cached
abstract predicate reachesRefParameter(Parameter p);
/**
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
* is an exhaustive list of cases where this may happen.
*
* - `v` is a parameter, and `this` contains the value of the parameter at
* the entry point of its function body.
* - `v` is an uninitialized local variable, and `v` contains its (arbitrary)
* value before it is reassigned. If it can be statically determined that a
* local variable is always overwritten before it is used, there is no
* `FlowVar` instance for the uninitialized value of that variable.
*/
cached
abstract predicate definedByInitialValue(StackVariable v);
/** Gets a textual representation of this element. */
cached
abstract string toString();
/** Gets the location of this element. */
cached
abstract Location getLocation();
}
/**
* Provides classes and predicates dealing with "partial definitions".
*
* In contrast to a normal "definition", which provides a new value for
* something, a partial definition is an expression that may affect a
* value, but does not necessarily replace it entirely. For example:
* ```
* x.y = 1; // a partial definition of the object `x`.
* x.y.z = 1; // a partial definition of the objects `x` and `x.y`.
* x.setY(1); // a partial definition of the object `x`.
* setY(&x); // a partial definition of the object `x`.
* ```
*/
private module PartialDefinitions {
abstract class PartialDefinition extends Expr {
ControlFlowNode node;
abstract deprecated predicate partiallyDefines(Variable v);
abstract deprecated predicate partiallyDefinesThis(ThisExpr e);
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
*/
ControlFlowNode getSubBasicBlockStart() { result = node }
/**
* Holds if this `PartialDefinition` defines variable `v` at control-flow
* node `cfn`.
*/
// does this work with a dispred?
pragma[noinline]
abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn);
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
*
* For example, in `f(& (*a).x)`, there are two results:
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
abstract predicate definesExpressions(Expr inner, Expr outer);
/**
* Gets the location of this element, adjusted to avoid unknown locations
* on compiler-generated `ThisExpr`s.
*/
Location getActualLocation() {
not exists(this.getLocation()) and result = this.getParent().getLocation()
or
this.getLocation() instanceof UnknownLocation and
result = this.getParent().getLocation()
or
result = this.getLocation() and not result instanceof UnknownLocation
}
}
class IteratorPartialDefinition extends PartialDefinition {
Variable collection;
Expr innerDefinedExpr;
IteratorPartialDefinition() {
innerDefinedExpr = getInnerDefinedExpr(this, node) and
(
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
or
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
collection instanceof IteratorParameter
) and
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
or
// iterators passed by value without a copy constructor
exists(Call call |
call = node and
call.getAnArgument() = innerDefinedExpr and
innerDefinedExpr = this and
this = getAnIteratorAccess(collection) and
not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
)
or
// iterators passed by value with a copy constructor
exists(Call call, ConstructorCall copy |
copy.getTarget() instanceof CopyConstructor and
call = node and
call.getAnArgument() = copy and
copy.getArgument(0) = getAnIteratorAccess(collection) and
innerDefinedExpr = this and
this = copy and
not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
)
}
deprecated override predicate partiallyDefines(Variable v) { v = collection }
deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() }
override predicate definesExpressions(Expr inner, Expr outer) {
inner = innerDefinedExpr and
outer = this
}
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
v = collection and
cfn = node
}
}
private Expr getInnerDefinedExpr(Expr e, ControlFlowNode node) {
not e instanceof Conversion and
exists(Expr convertedInner |
valueToUpdate(convertedInner, e.getFullyConverted(), node) and
result = convertedInner.getUnconverted()
)
}
class VariablePartialDefinition extends PartialDefinition {
Expr innerDefinedExpr;
VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) }
deprecated override predicate partiallyDefines(Variable v) {
innerDefinedExpr = v.getAnAccess()
}
deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
*
* For example, in `f(& (*a).x)`, there are two results:
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
override predicate definesExpressions(Expr inner, Expr outer) {
inner = innerDefinedExpr and
outer = this
}
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
innerDefinedExpr = v.getAnAccess() and
cfn = node
}
}
/**
* A partial definition that's a definition via an output iterator.
*/
class DefinitionByIterator extends IteratorPartialDefinition {
DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
/**
* A partial definition that's a definition by reference.
*/
class DefinitionByReference extends VariablePartialDefinition {
DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
}
import PartialDefinitions
private import FlowVar_internal
/**
* Provides classes and predicates that ought to be private but cannot use the
* `private` annotation because they may be referred to by unit tests.
*/
module FlowVar_internal {
/**
* For various reasons, not all variables handled perfectly by the SSA library.
* Ideally, this predicate should become larger as the SSA library improves.
* Before we can remove the `BlockVar` class completely, the SSA library needs
* the following improvements.
* - Considering uninitialized local variables to be definitions.
* - Supporting fields, globals and statics like the Java SSA library does.
* - Supporting all local variables, even if their address is taken by
* address-of, reference assignments, or lambdas.
* - Understanding that assignment to a field of a local struct is a
* definition of the struct but not a complete overwrite. This is what the
* IR library uses chi nodes for.
*/
predicate fullySupportedSsaVariable(Variable v) {
v = any(SsaDefinition def).getAVariable() and
// A partially-defined variable is handled using the partial definitions logic.
not any(PartialDefinition p).partiallyDefinesVariableAt(v, _) and
// SSA variables do not exist before their first assignment, but one
// feature of this data flow library is to track where uninitialized data
// ends up.
not mayBeUsedUninitialized(v, _) and
// If `v` may be a variable that is always overwritten in a loop that
// always executes at least once, we give it special treatment in
// `BlockVar`, somewhat analogous to unrolling the first iteration of the
// loop.
not exists(AlwaysTrueUponEntryLoop loop | loop.alwaysAssignsBeforeLeavingCondition(_, _, v)) and
// The SSA library has a theoretically accurate treatment of reference types,
// treating them as immutable, but for data flow it gives better results in
// practice to make the variable synonymous with its contents.
not v.getUnspecifiedType() instanceof ReferenceType and
not v instanceof IteratorParameter and
not v instanceof PointerWrapperParameter
}
/**
* Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value.
* See the documentation for `FlowVar.definedByInitialValue`.
*/
predicate blockVarDefinedByVariable(SubBasicBlock sbb, StackVariable v) {
sbb = v.(Parameter).getFunction().getEntryPoint()
or
exists(DeclStmt declStmt |
declStmt.getDeclaration(_) = v.(LocalVariable) and
sbb.contains(declStmt) and
mayBeUsedUninitialized(v, _)
)
}
newtype TFlowVar =
TSsaVar(SsaDefinition def, StackVariable v) {
fullySupportedSsaVariable(v) and
v = def.getAVariable()
} or
TBlockVar(SubBasicBlock sbb, Variable v) {
not fullySupportedSsaVariable(v) and
not v instanceof Field and // Fields are interprocedural data flow, not local
reachable(sbb) and
(
initializer(v, sbb.getANode())
or
assignmentLikeOperation(sbb, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb))
or
blockVarDefinedByVariable(sbb, v)
)
}
/**
* A variable whose analysis is backed by the SSA library.
*/
class SsaVar extends TSsaVar, FlowVar {
SsaDefinition def;
StackVariable v;
SsaVar() { this = TSsaVar(def, v) }
override VariableAccess getAnAccess() {
// There is no need to know about direct accesses to phi nodes because
// the data flow library will never see those, and the `FlowVar` library
// is only meant to be used by the data flow library.
this.isNonPhi() and
(
// This base case could be included in the transitive case by changing
// `+` to `*`, but that's slower because it goes through the `TSsaVar`
// indirection.
result = def.getAUse(v)
or
exists(SsaDefinition descendentDef |
getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and
result = descendentDef.getAUse(v)
)
)
or
parameterUsedInConstructorFieldInit(v, result) and
def.definedByParameter(v)
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
e = def.getDefiningValue(v) and
(
if def.getDefinition() = v.getInitializer().getExpr()
then node = v.getInitializer()
else node = def.getDefinition()
)
}
override predicate definedPartiallyAt(Expr e) { none() }
override predicate definedByInitialValue(StackVariable param) {
def.definedByParameter(param) and
param = v
}
// `fullySupportedSsaVariable` excludes reference types
override predicate reachesRefParameter(Parameter p) { none() }
/**
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
* library will never directly use an `SsaVar` that comes from a phi node,
* so such values are purely for implementation use.
*/
private predicate isNonPhi() {
// This implementation is positively phrased in terms of these two helper
// predicates because it performs better than phrasing it negatively in
// terms of `def.isPhiNode`.
this.definedByExpr(_, _)
or
this.definedByInitialValue(_)
}
/**
* Holds if `result` might have the same value as `this` because `result` is
* a phi node with `this` as input.
*/
private SsaVar getASuccessorSsaVar() {
exists(SsaDefinition succDef |
result = TSsaVar(succDef, v) and
def = succDef.getAPhiInput(v)
)
}
override string toString() { result = def.toString(v) }
override Location getLocation() { result = def.getLocation() }
}
/**
* A variable whose analysis is backed by a simple control flow analysis that
* does not perform as well as the SSA library but gives better results for
* data flow purposes in some cases.
*/
class BlockVar extends TBlockVar, FlowVar {
SubBasicBlock sbb;
Variable v;
BlockVar() { this = TBlockVar(sbb, v) }
override VariableAccess getAnAccess() {
variableAccessInSBB(v, getAReachedBlockVarSBB(this), result) and
result != sbb
or
parameterUsedInConstructorFieldInit(v, result) and
sbb = v.(Parameter).getFunction().getEntryPoint()
}
override predicate reachesRefParameter(Parameter p) {
parameterIsNonConstReference(p) and
p = v and
// This definition reaches the exit node of the function CFG
getAReachedBlockVarSBB(this).getANode() = p.getFunction()
}
override predicate definedByInitialValue(StackVariable lsv) {
blockVarDefinedByVariable(sbb, lsv) and
lsv = v
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
assignmentLikeOperation(node, v, e) and
node = sbb
or
// We pick the defining `ControlFlowNode` of an `Initializer` to be its
// expression rather than the `Initializer` itself. That's because the
// `Initializer` of a `ConditionDeclExpr` is for historical reasons not
// part of the CFG and therefore ends up in the wrong basic block.
initializer(v, e) and
node = e and
node = sbb.getANode()
}
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinition p |
p.partiallyDefinesVariableAt(v, sbb) and
p.definesExpressions(_, e)
)
}
override string toString() {
exists(Expr e |
this.definedByExpr(e, _) and
result = "assignment to " + v
)
or
this.definedByInitialValue(_) and
result = "initial value of " + v
or
exists(Expr partialDef |
this.definedPartiallyAt(partialDef) and
result = "partial definition at " + partialDef
)
or
// impossible case
not this.definedByExpr(_, _) and
not this.definedByInitialValue(_) and
not this.definedPartiallyAt(_) and
result = "undefined " + v
}
override Location getLocation() { result = sbb.getStart().getLocation() }
}
/** Type-specialized version of `getEnclosingElement`. */
private ControlFlowNode getCFNParent(ControlFlowNode node) { result = node.getEnclosingElement() }
/**
* A for-loop or while-loop whose condition is always true upon entry but not
* always true after the first iteration.
*/
class AlwaysTrueUponEntryLoop extends Stmt {
AlwaysTrueUponEntryLoop() {
this.(WhileStmt).conditionAlwaysTrueUponEntry() and
not this.(WhileStmt).conditionAlwaysTrue()
or
this.(ForStmt).conditionAlwaysTrueUponEntry() and
not this.(ForStmt).conditionAlwaysTrue()
}
/**
* Holds if this loop always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
* `v` must be used outside the loop.
*/
predicate alwaysAssignsBeforeLeavingCondition(
BasicBlock bbInside, BasicBlock bbOutside, Variable v
) {
v = this.getARelevantVariable() and
this.bbInLoopCondition(bbInside) and
not this.bbInLoop(bbOutside) and
bbOutside = bbInside.getASuccessor() and
not reachesWithoutAssignment(bbInside, v)
}
/**
* Gets a variable that is assigned in this loop and read outside the loop.
*/
Variable getARelevantVariable() {
result = this.getAVariableAssignedInLoop() and
exists(VariableAccess va |
va.getTarget() = result and
readAccess(va) and
exists(BasicBlock bb | bb = va.getBasicBlock() | not this.bbInLoop(bb))
)
}
/** Gets a variable that is assigned in this loop. */
pragma[noinline]
private Variable getAVariableAssignedInLoop() {
exists(BasicBlock bbAssign |
assignmentLikeOperation(bbAssign.getANode(), result, _) and
this.bbInLoop(bbAssign)
)
}
private predicate bbInLoopCondition(BasicBlock bb) {
getCFNParent*(bb.getANode()) = this.(Loop).getCondition()
}
private predicate bbInLoop(BasicBlock bb) {
bbDominates(this.(Loop).getStmt(), bb)
or
bbInLoopCondition(bb)
}
/** Holds if `sbb` is inside this loop. */
predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
/**
* Holds if `bb` is a basic block inside this loop where `v` has not been
* overwritten at the end of `bb`.
*/
private predicate reachesWithoutAssignment(BasicBlock bb, Variable v) {
(
// For the type of loop we are interested in, the body is always a
// basic block.
bb = this.(Loop).getStmt() and
v = this.getARelevantVariable()
or
reachesWithoutAssignment(bb.getAPredecessor(), v) and
this.bbInLoop(bb)
) and
not assignsToVar(bb, v)
}
}
pragma[noinline]
private predicate assignsToVar(BasicBlock bb, Variable v) {
assignmentLikeOperation(bb.getANode(), v, _) and
exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable())
}
/**
* Holds if `loop` always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
* `v` must be used outside the loop.
*/
predicate skipLoop(
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, Variable v, AlwaysTrueUponEntryLoop loop
) {
exists(BasicBlock bbInside, BasicBlock bbOutside |
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
bbInside = sbbInside.getBasicBlock() and
bbOutside = sbbOutside.getBasicBlock() and
sbbInside.lastInBB() and
sbbOutside.firstInBB()
)
}
// The noopt is needed to avoid getting `variableLiveInSBB` joined in before
// `getASuccessor`.
pragma[noopt]
private SubBasicBlock getAReachedBlockVarSBB(TBlockVar start) {
exists(Variable v |
start = TBlockVar(result, v) and
variableLiveInSBB(result, v) and
not largeVariable(v, _, _)
)
or
exists(SubBasicBlock mid, SubBasicBlock sbbDef, Variable v |
mid = getAReachedBlockVarSBB(start) and
start = TBlockVar(sbbDef, v) and
result = mid.getASuccessor() and
variableLiveInSBB(result, v) and
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
not assignmentLikeOperation(result, v, _)
)
}
/**
* Holds if `v` may have too many combinations of definitions and reached
* blocks for us the feasibly compute its def-use relation.
*/
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
// Convert to float to avoid int overflow (32-bit two's complement)
liveBlocks.(float) * defs.(float) > 100000.0
}
/**
* Holds if a value held in `v` at the start of `sbb` (or after the first
* assignment, if that assignment is to `v`) might later be read.
*/
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
variableAccessInSBB(v, sbb, _)
or
// Non-const reference parameters are live at the end of the function
parameterIsNonConstReference(v) and
sbb.contains(v.(Parameter).getFunction())
or
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
variableLiveInSBB(succ, v) and
not variableNotLiveBefore(succ, v)
)
}
predicate parameterIsNonConstReference(Parameter p) {
exists(ReferenceType refType |
refType = p.getUnderlyingType() and
(
not refType.getBaseType().isConst()
or
// A field of a parameter of type `const std::shared_ptr<A>& p` can still be changed even though
// the base type of the reference is `const`.
refType.getBaseType().getUnspecifiedType() =
any(PointerWrapper wrapper | not wrapper.pointsToConst())
)
)
or
p instanceof IteratorParameter
or
p instanceof PointerWrapperParameter
}
/**
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
*/
private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) {
assignmentLikeOperation(sbb, v, _)
or
// Liveness of `v` is killed when going backwards from a block that declares it
exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds))
}
/** Holds if `va` is a read access to `v` in `sbb`, where `v` is modeled by `BlockVar`. */
pragma[noinline]
private predicate variableAccessInSBB(Variable v, SubBasicBlock sbb, VariableAccess va) {
exists(TBlockVar(_, v)) and
va.getTarget() = v and
va = sbb.getANode() and
not overwrite(va, _)
}
/**
* A local variable that is uninitialized immediately after its declaration.
*/
class UninitializedLocalVariable extends LocalVariable, StackVariable {
UninitializedLocalVariable() { not this.hasInitializer() }
}
/** Holds if `va` may be an uninitialized access to `v`. */
predicate mayBeUsedUninitialized(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va.getTarget() = v and
readAccess(va) and
va = bb.getNode(vaIndex) and
notAccessedAtStartOfBB(v, bb) and
(
vaIndex < indexOfFirstOverwriteInBB(v, bb)
or
not exists(indexOfFirstOverwriteInBB(v, bb))
)
)
}
/**
* Holds if `v` has not been accessed at the start of `bb`, for a variable
* `v` where `allReadsDominatedByOverwrite(v)` does not hold.
*/
predicate notAccessedAtStartOfBB(UninitializedLocalVariable v, BasicBlock bb) {
// Start from a BB containing an uninitialized variable
bb.getANode().(DeclStmt).getDeclaration(_) = v and
// Only consider variables for which initialization before reading cannot
// be proved by simpler means. This predicate is expensive in time and
// space, whereas `allReadsDominatedByOverwrite` is cheap.
not allReadsDominatedByOverwrite(v)
or
exists(BasicBlock pred |
pred = bb.getAPredecessor() and
notAccessedAtStartOfBB(v, pred) and
// Stop searching when `v` is accessed.
not pred.getANode() = v.getAnAccess()
)
}
/**
* Holds if all read accesses of `v` are dominated by an overwrite of `v`.
*/
predicate allReadsDominatedByOverwrite(UninitializedLocalVariable v) {
forall(VariableAccess va |
va.getTarget() = v and
readAccess(va)
|
dominatedByOverwrite(v, va)
)
}
/** Holds if `va` accesses `v` and is dominated by an overwrite of `v`. */
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va = bb.getNode(vaIndex) and
va.getTarget() = v and
vaIndex > indexOfFirstOverwriteInBB(v, bb)
or
va = bb.getNode(vaIndex) and
va.getTarget() = v and
bbStrictlyDominates(getAnOverwritingBB(v), bb)
)
}
/** Gets a basic block in which `v` is overwritten. */
BasicBlock getAnOverwritingBB(UninitializedLocalVariable v) {
exists(indexOfFirstOverwriteInBB(v, result))
}
int indexOfFirstOverwriteInBB(LocalVariable v, BasicBlock bb) {
result = min(int i | overwrite(v.getAnAccess(), bb.getNode(i)))
}
/**
* Holds if the value of the variable accessed at `va` may affect the execution
* of the program.
*/
predicate readAccess(VariableAccess va) {
reachable(va) and
not overwrite(va, _) and
not va = any(SizeofExprOperator so).getAChild+() and
not va = any(AlignofExprOperator ao).getAChild+()
}
/**
* Holds if the value of the variable accessed at `va` is completely
* overwritten at `node`, where `va` is uniquely determined by `node`.
*/
predicate overwrite(VariableAccess va, ControlFlowNode node) {
va = node.(AssignExpr).getLValue()
}
/**
* Holds if `v` is modified through `va` as a side effect of evaluating
* `node`, receiving a value best described by `assignedExpr`.
* Assignment-like operations are those that desugar to a non-overloaded `=`
* assignment: `=`, `+=`, `++`, `--`, etc.
*
* This corresponds to `FlowVar::definedByExpr`, except that the case where
* `node instanceof Initializer` is covered by `initializer` instead of this
* predicate.
*/
predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) {
// Together, the two following cases cover `Assignment`
node =
any(AssignExpr ae |
v.getAnAccess() = ae.getLValue() and
assignedExpr = ae.getRValue()
)
or
node =
any(AssignOperation ao |
v.getAnAccess() = ao.getLValue() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
// `x` will contain the same value that `x += 1` evaluated to.
assignedExpr = ao
)
or
// This case does not add further data flow paths, except if a
// `PrefixCrementOperation` is itself a source
node =
any(CrementOperation op |
v.getAnAccess() = op.getOperand() and
assignedExpr = op
)
}
Expr getAnIteratorAccess(Variable collection) {
exists(
Call c, SsaDefinition def, Variable iterator, FunctionInput input, FunctionOutput output
|
c.getTarget().(GetIteratorFunction).getsIterator(input, output) and
(
(
input.isQualifierObject() or
input.isQualifierAddress()
) and
c.getQualifier() = collection.getAnAccess()
or
exists(int index |
input.isParameter(index) or
input.isParameterDeref(index)
|
c.getArgument(index) = collection.getAnAccess()
)
) and
output.isReturnValue() and
def.getAnUltimateDefiningValue(iterator) = c and
result = def.getAUse(iterator)
)
or
exists(Call crement |
crement = result and
[crement.getQualifier(), crement.getArgument(0)] = getAnIteratorAccess(collection) and
crement.getTarget().getName() = ["operator++", "operator--"]
)
}
class IteratorParameter extends Parameter {
IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
}
class PointerWrapperParameter extends Parameter {
PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper }
}
/**
* Holds if `v` is initialized to have value `assignedExpr`.
*/
predicate initializer(LocalVariable v, Expr assignedExpr) {
assignedExpr = v.getInitializer().getExpr()
}
/**
* Holds if `p` is a parameter to a constructor that is used in a
* `ConstructorFieldInit` at `va`. This ignores the corner case that `p`
* might have been overwritten to have a different value before this happens.
*/
predicate parameterUsedInConstructorFieldInit(Parameter p, VariableAccess va) {
exists(Constructor constructor |
constructor.getInitializer(_).(ConstructorFieldInit).getExpr().getAChild*() = va and
va = p.getAnAccess()
// We don't require that `constructor` has `p` as a parameter because
// that follows from the two other conditions and would likely just
// confuse the join orderer.
)
}
/**
* A point in a basic block where a non-SSA variable may have a different value
* than it did elsewhere in the same basic block. Extending this class
* configures the `SubBasicBlocks` library as needed for the implementation of
* this library.
*/
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
DataFlowSubBasicBlockCutNode() {
exists(Variable v | not fullySupportedSsaVariable(v) |
assignmentLikeOperation(this, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this))
// It is not necessary to cut the basic blocks at `Initializer` nodes
// because the affected variable can have no _other_ value before its
// initializer. It is not necessary to cut basic blocks at procedure
// entries for the sake of `Parameter`s since we are already guaranteed
// to have a `SubBasicBlock` starting at procedure entry.
)
}
}
}
/* module FlowVar_internal */

Some files were not shown because too many files have changed in this diff Show More