mirror of
https://github.com/github/codeql.git
synced 2026-02-12 05:01:06 +01:00
Merge branch 'main' into andersfugmann/improve_upper_bound
This commit is contained in:
9
cpp/ql/lib/semmle/code/cpp/ASTConsistency.ql
Normal file
9
cpp/ql/lib/semmle/code/cpp/ASTConsistency.ql
Normal 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
|
||||
133
cpp/ql/lib/semmle/code/cpp/AutogeneratedFile.qll
Normal file
133
cpp/ql/lib/semmle/code/cpp/AutogeneratedFile.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
1171
cpp/ql/lib/semmle/code/cpp/Class.qll
Normal file
1171
cpp/ql/lib/semmle/code/cpp/Class.qll
Normal file
File diff suppressed because it is too large
Load Diff
54
cpp/ql/lib/semmle/code/cpp/Comments.qll
Normal file
54
cpp/ql/lib/semmle/code/cpp/Comments.qll
Normal 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("//%") }
|
||||
}
|
||||
115
cpp/ql/lib/semmle/code/cpp/Compilation.qll
Normal file
115
cpp/ql/lib/semmle/code/cpp/Compilation.qll
Normal 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, _, _) }
|
||||
}
|
||||
721
cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal file
721
cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal 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")
|
||||
}
|
||||
68
cpp/ql/lib/semmle/code/cpp/Diagnostics.qll
Normal file
68
cpp/ql/lib/semmle/code/cpp/Diagnostics.qll
Normal 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 }
|
||||
}
|
||||
297
cpp/ql/lib/semmle/code/cpp/Element.qll
Normal file
297
cpp/ql/lib/semmle/code/cpp/Element.qll
Normal 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, _) }
|
||||
}
|
||||
63
cpp/ql/lib/semmle/code/cpp/Enclosing.qll
Normal file
63
cpp/ql/lib/semmle/code/cpp/Enclosing.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
204
cpp/ql/lib/semmle/code/cpp/Enum.qll
Normal file
204
cpp/ql/lib/semmle/code/cpp/Enum.qll
Normal 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)) }
|
||||
}
|
||||
126
cpp/ql/lib/semmle/code/cpp/Field.qll
Normal file
126
cpp/ql/lib/semmle/code/cpp/Field.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
443
cpp/ql/lib/semmle/code/cpp/File.qll
Normal file
443
cpp/ql/lib/semmle/code/cpp/File.qll
Normal 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() }
|
||||
}
|
||||
69
cpp/ql/lib/semmle/code/cpp/FriendDecl.qll
Normal file
69
cpp/ql/lib/semmle/code/cpp/FriendDecl.qll
Normal 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() }
|
||||
}
|
||||
876
cpp/ql/lib/semmle/code/cpp/Function.qll
Normal file
876
cpp/ql/lib/semmle/code/cpp/Function.qll
Normal 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() }
|
||||
69
cpp/ql/lib/semmle/code/cpp/Include.qll
Normal file
69
cpp/ql/lib/semmle/code/cpp/Include.qll
Normal 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() }
|
||||
}
|
||||
54
cpp/ql/lib/semmle/code/cpp/Initializer.qll
Normal file
54
cpp/ql/lib/semmle/code/cpp/Initializer.qll
Normal 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() }
|
||||
}
|
||||
60
cpp/ql/lib/semmle/code/cpp/Iteration.qll
Normal file
60
cpp/ql/lib/semmle/code/cpp/Iteration.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
51
cpp/ql/lib/semmle/code/cpp/Linkage.qll
Normal file
51
cpp/ql/lib/semmle/code/cpp/Linkage.qll
Normal 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()) }
|
||||
173
cpp/ql/lib/semmle/code/cpp/Location.qll
Normal file
173
cpp/ql/lib/semmle/code/cpp/Location.qll
Normal 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) }
|
||||
}
|
||||
351
cpp/ql/lib/semmle/code/cpp/Macro.qll
Normal file
351
cpp/ql/lib/semmle/code/cpp/Macro.qll
Normal 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())
|
||||
}
|
||||
6
cpp/ql/lib/semmle/code/cpp/Member.qll
Normal file
6
cpp/ql/lib/semmle/code/cpp/Member.qll
Normal 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
|
||||
517
cpp/ql/lib/semmle/code/cpp/MemberFunction.qll
Normal file
517
cpp/ql/lib/semmle/code/cpp/MemberFunction.qll
Normal 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" }
|
||||
}
|
||||
167
cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll
Normal file
167
cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll
Normal 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() }
|
||||
}
|
||||
249
cpp/ql/lib/semmle/code/cpp/Namespace.qll
Normal file
249
cpp/ql/lib/semmle/code/cpp/Namespace.qll
Normal 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 }
|
||||
}
|
||||
69
cpp/ql/lib/semmle/code/cpp/NestedFields.qll
Normal file
69
cpp/ql/lib/semmle/code/cpp/NestedFields.qll
Normal 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 }
|
||||
}
|
||||
196
cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll
Normal file
196
cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll
Normal 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() }
|
||||
}
|
||||
125
cpp/ql/lib/semmle/code/cpp/PODType03.qll
Normal file
125
cpp/ql/lib/semmle/code/cpp/PODType03.qll
Normal 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())
|
||||
)
|
||||
}
|
||||
176
cpp/ql/lib/semmle/code/cpp/Parameter.qll
Normal file
176
cpp/ql/lib/semmle/code/cpp/Parameter.qll
Normal 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`
|
||||
}
|
||||
}
|
||||
294
cpp/ql/lib/semmle/code/cpp/Preprocessor.qll
Normal file
294
cpp/ql/lib/semmle/code/cpp/Preprocessor.qll
Normal 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() }
|
||||
}
|
||||
413
cpp/ql/lib/semmle/code/cpp/Print.qll
Normal file
413
cpp/ql/lib/semmle/code/cpp/Print.qll
Normal 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() }
|
||||
21
cpp/ql/lib/semmle/code/cpp/PrintAST.ql
Normal file
21
cpp/ql/lib/semmle/code/cpp/PrintAST.ql
Normal 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() }
|
||||
}
|
||||
884
cpp/ql/lib/semmle/code/cpp/PrintAST.qll
Normal file
884
cpp/ql/lib/semmle/code/cpp/PrintAST.qll
Normal 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"
|
||||
}
|
||||
318
cpp/ql/lib/semmle/code/cpp/Specifier.qll
Normal file
318
cpp/ql/lib/semmle/code/cpp/Specifier.qll
Normal 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() }
|
||||
75
cpp/ql/lib/semmle/code/cpp/Struct.qll
Normal file
75
cpp/ql/lib/semmle/code/cpp/Struct.qll
Normal 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") }
|
||||
}
|
||||
89
cpp/ql/lib/semmle/code/cpp/TestFile.qll
Normal file
89
cpp/ql/lib/semmle/code/cpp/TestFile.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
1745
cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
1745
cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
File diff suppressed because it is too large
Load Diff
130
cpp/ql/lib/semmle/code/cpp/TypedefType.qll
Normal file
130
cpp/ql/lib/semmle/code/cpp/TypedefType.qll
Normal 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") }
|
||||
}
|
||||
70
cpp/ql/lib/semmle/code/cpp/Union.qll
Normal file
70
cpp/ql/lib/semmle/code/cpp/Union.qll
Normal 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") }
|
||||
}
|
||||
132
cpp/ql/lib/semmle/code/cpp/UserType.qll
Normal file
132
cpp/ql/lib/semmle/code/cpp/UserType.qll
Normal 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)) }
|
||||
}
|
||||
608
cpp/ql/lib/semmle/code/cpp/Variable.qll
Normal file
608
cpp/ql/lib/semmle/code/cpp/Variable.qll
Normal 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(_) }
|
||||
}
|
||||
343
cpp/ql/lib/semmle/code/cpp/XML.qll
Executable file
343
cpp/ql/lib/semmle/code/cpp/XML.qll
Executable 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() }
|
||||
}
|
||||
66
cpp/ql/lib/semmle/code/cpp/commons/Alloc.qll
Normal file
66
cpp/ql/lib/semmle/code/cpp/commons/Alloc.qll
Normal 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 }
|
||||
60
cpp/ql/lib/semmle/code/cpp/commons/Assertions.qll
Normal file
60
cpp/ql/lib/semmle/code/cpp/commons/Assertions.qll
Normal 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
|
||||
}
|
||||
}
|
||||
124
cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
Normal file
124
cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
441
cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll
Normal file
441
cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll
Normal 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" }
|
||||
}
|
||||
97
cpp/ql/lib/semmle/code/cpp/commons/DateTime.qll
Normal file
97
cpp/ql/lib/semmle/code/cpp/commons/DateTime.qll
Normal 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" }
|
||||
}
|
||||
502
cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
Normal file
502
cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
35
cpp/ql/lib/semmle/code/cpp/commons/Environment.qll
Normal file
35
cpp/ql/lib/semmle/code/cpp/commons/Environment.qll
Normal 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
|
||||
)
|
||||
}
|
||||
111
cpp/ql/lib/semmle/code/cpp/commons/Exclusions.qll
Normal file
111
cpp/ql/lib/semmle/code/cpp/commons/Exclusions.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
42
cpp/ql/lib/semmle/code/cpp/commons/File.qll
Normal file
42
cpp/ql/lib/semmle/code/cpp/commons/File.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
11
cpp/ql/lib/semmle/code/cpp/commons/NULL.qll
Normal file
11
cpp/ql/lib/semmle/code/cpp/commons/NULL.qll
Normal 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()) }
|
||||
}
|
||||
117
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
Normal file
117
cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
Normal 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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
10
cpp/ql/lib/semmle/code/cpp/commons/PolymorphicClass.qll
Normal file
10
cpp/ql/lib/semmle/code/cpp/commons/PolymorphicClass.qll
Normal 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())
|
||||
}
|
||||
}
|
||||
1210
cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
Normal file
1210
cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
Normal file
File diff suppressed because it is too large
Load Diff
273
cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll
Normal file
273
cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
22
cpp/ql/lib/semmle/code/cpp/commons/Strcat.qll
Normal file
22
cpp/ql/lib/semmle/code/cpp/commons/Strcat.qll
Normal 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)
|
||||
]
|
||||
}
|
||||
}
|
||||
73
cpp/ql/lib/semmle/code/cpp/commons/StringAnalysis.qll
Normal file
73
cpp/ql/lib/semmle/code/cpp/commons/StringAnalysis.qll
Normal 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) }
|
||||
}
|
||||
69
cpp/ql/lib/semmle/code/cpp/commons/StructLikeClass.qll
Normal file
69
cpp/ql/lib/semmle/code/cpp/commons/StructLikeClass.qll
Normal 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()
|
||||
}
|
||||
215
cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll
Normal file
215
cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll
Normal 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())
|
||||
}
|
||||
42
cpp/ql/lib/semmle/code/cpp/commons/VoidContext.qll
Normal file
42
cpp/ql/lib/semmle/code/cpp/commons/VoidContext.qll
Normal 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
|
||||
}
|
||||
96
cpp/ql/lib/semmle/code/cpp/commons/unix/Constants.qll
Normal file
96
cpp/ql/lib/semmle/code/cpp/commons/unix/Constants.qll
Normal 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") }
|
||||
282
cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
Normal file
282
cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
Normal 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())
|
||||
}
|
||||
}
|
||||
145
cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll
Normal file
145
cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll
Normal 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
|
||||
}
|
||||
166
cpp/ql/lib/semmle/code/cpp/controlflow/Dataflow.qll
Normal file
166
cpp/ql/lib/semmle/code/cpp/controlflow/Dataflow.qll
Normal 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) }
|
||||
383
cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
Normal file
383
cpp/ql/lib/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
Normal 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
|
||||
)
|
||||
}
|
||||
110
cpp/ql/lib/semmle/code/cpp/controlflow/Dereferenced.qll
Normal file
110
cpp/ql/lib/semmle/code/cpp/controlflow/Dereferenced.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
160
cpp/ql/lib/semmle/code/cpp/controlflow/Dominance.qll
Normal file
160
cpp/ql/lib/semmle/code/cpp/controlflow/Dominance.qll
Normal 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
|
||||
}
|
||||
384
cpp/ql/lib/semmle/code/cpp/controlflow/Guards.qll
Normal file
384
cpp/ql/lib/semmle/code/cpp/controlflow/Guards.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
703
cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll
Normal file
703
cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll
Normal 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() }
|
||||
@@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
291
cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll
Normal file
291
cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll
Normal 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
|
||||
)
|
||||
}
|
||||
161
cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll
Normal file
161
cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
311
cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll
Normal file
311
cpp/ql/lib/semmle/code/cpp/controlflow/SSAUtils.qll
Normal 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, _, _)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
179
cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll
Normal file
179
cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll
Normal 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)
|
||||
}
|
||||
1410
cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll
Normal file
1410
cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll
Normal file
File diff suppressed because it is too large
Load Diff
1223
cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
Normal file
1223
cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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) }
|
||||
}
|
||||
24
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll
Normal file
24
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow.qll
Normal 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
|
||||
}
|
||||
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow2.qll
Normal file
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow2.qll
Normal 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
|
||||
}
|
||||
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow3.qll
Normal file
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow3.qll
Normal 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
|
||||
}
|
||||
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow4.qll
Normal file
16
cpp/ql/lib/semmle/code/cpp/dataflow/DataFlow4.qll
Normal 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
|
||||
}
|
||||
361
cpp/ql/lib/semmle/code/cpp/dataflow/EscapesTree.qll
Normal file
361
cpp/ql/lib/semmle/code/cpp/dataflow/EscapesTree.qll
Normal 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*())
|
||||
}
|
||||
}
|
||||
39
cpp/ql/lib/semmle/code/cpp/dataflow/RecursionPrevention.qll
Normal file
39
cpp/ql/lib/semmle/code/cpp/dataflow/RecursionPrevention.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
298
cpp/ql/lib/semmle/code/cpp/dataflow/StackAddress.qll
Normal file
298
cpp/ql/lib/semmle/code/cpp/dataflow/StackAddress.qll
Normal 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
29
cpp/ql/lib/semmle/code/cpp/dataflow/TaintTracking.qll
Normal file
29
cpp/ql/lib/semmle/code/cpp/dataflow/TaintTracking.qll
Normal 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;
|
||||
}
|
||||
15
cpp/ql/lib/semmle/code/cpp/dataflow/TaintTracking2.qll
Normal file
15
cpp/ql/lib/semmle/code/cpp/dataflow/TaintTracking2.qll
Normal 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
|
||||
}
|
||||
388
cpp/ql/lib/semmle/code/cpp/dataflow/internal/AddressFlow.qll
Normal file
388
cpp/ql/lib/semmle/code/cpp/dataflow/internal/AddressFlow.qll
Normal 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*())
|
||||
}
|
||||
@@ -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() }
|
||||
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
Normal file
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
Normal file
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
Normal file
File diff suppressed because it is too large
Load Diff
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
Normal file
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
Normal file
File diff suppressed because it is too large
Load Diff
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
Normal file
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
Normal file
File diff suppressed because it is too large
Load Diff
1273
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
Normal file
1273
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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."
|
||||
}
|
||||
}
|
||||
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
Normal file
4463
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
285
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
Normal file
285
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
Normal 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() }
|
||||
841
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
Normal file
841
cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
895
cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll
Normal file
895
cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll
Normal 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
Reference in New Issue
Block a user