mirror of
https://github.com/github/codeql.git
synced 2026-02-08 03:01:10 +01:00
Add codeql sources (a2371370ff)
This commit is contained in:
1
repo-tests/codeql.txt
Normal file
1
repo-tests/codeql.txt
Normal file
@@ -0,0 +1 @@
|
||||
a2371370ff8260e789342e0ac759bc67ed401702
|
||||
4
repo-tests/codeql/cpp/ql/examples/qlpack.yml
Normal file
4
repo-tests/codeql/cpp/ql/examples/qlpack.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: codeql/cpp-examples
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
16
repo-tests/codeql/cpp/ql/examples/snippets/addressof.ql
Normal file
16
repo-tests/codeql/cpp/ql/examples/snippets/addressof.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/addressof
|
||||
* @name Address of reference variable
|
||||
* @description Finds address-of expressions (`&`) that take the address
|
||||
* of a reference variable
|
||||
* @tags addressof
|
||||
* reference
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from AddressOfExpr addr, VariableAccess access
|
||||
where
|
||||
access = addr.getOperand() and
|
||||
access.getTarget().getType() instanceof ReferenceType
|
||||
select addr
|
||||
17
repo-tests/codeql/cpp/ql/examples/snippets/arrayaccess.ql
Normal file
17
repo-tests/codeql/cpp/ql/examples/snippets/arrayaccess.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/arrayaccess
|
||||
* @name Array access
|
||||
* @description Finds array access expressions with an index expression
|
||||
* consisting of a postfix increment (`++`) expression.
|
||||
* @tags array
|
||||
* access
|
||||
* index
|
||||
* postfix
|
||||
* increment
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ArrayExpr a
|
||||
where a.getArrayOffset() instanceof PostfixIncrExpr
|
||||
select a
|
||||
17
repo-tests/codeql/cpp/ql/examples/snippets/castexpr.ql
Normal file
17
repo-tests/codeql/cpp/ql/examples/snippets/castexpr.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/castexpr
|
||||
* @name Cast expressions
|
||||
* @description Finds casts from a floating point type to an integer type
|
||||
* @tags cast
|
||||
* integer
|
||||
* float
|
||||
* type
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Cast c
|
||||
where
|
||||
c.getExpr().getType() instanceof FloatingPointType and
|
||||
c.getType() instanceof IntegralType
|
||||
select c
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/catch-exception
|
||||
* @name Catch exception
|
||||
* @description Finds places where we catch exceptions of type `parse_error`
|
||||
* @tags catch
|
||||
* try
|
||||
* exception
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from CatchBlock catch
|
||||
// `stripType` converts `const parse_error &` to `parse_error`.
|
||||
where catch.getParameter().getType().stripType().hasName("parse_error")
|
||||
select catch
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/constructor-call
|
||||
* @name Call to constructor
|
||||
* @description Finds places where we call `new MyClass(...)`
|
||||
* @tags call
|
||||
* constructor
|
||||
* new
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from NewExpr new, Constructor c
|
||||
where
|
||||
c = new.getInitializer().(ConstructorCall).getTarget() and
|
||||
c.getName() = "MyClass"
|
||||
select new
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @id cpp/examples/derives-from-class
|
||||
* @name Class derives from
|
||||
* @description Finds classes that derive from `std::exception`
|
||||
* @tags base
|
||||
* class
|
||||
* derive
|
||||
* inherit
|
||||
* override
|
||||
* subtype
|
||||
* supertype
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Class type
|
||||
where
|
||||
type.getABaseClass+().hasName("exception") and
|
||||
type.getNamespace().getName() = "std"
|
||||
select type
|
||||
14
repo-tests/codeql/cpp/ql/examples/snippets/emptyblock.ql
Normal file
14
repo-tests/codeql/cpp/ql/examples/snippets/emptyblock.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/emptyblock
|
||||
* @name Empty blocks
|
||||
* @description Finds empty block statements
|
||||
* @tags empty
|
||||
* block
|
||||
* statement
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from BlockStmt blk
|
||||
where blk.getNumStmt() = 0
|
||||
select blk
|
||||
17
repo-tests/codeql/cpp/ql/examples/snippets/emptythen.ql
Normal file
17
repo-tests/codeql/cpp/ql/examples/snippets/emptythen.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/emptythen
|
||||
* @name If statements with empty then branch
|
||||
* @description Finds `if` statements where the `then` branch is
|
||||
* an empty block statement
|
||||
* @tags if
|
||||
* then
|
||||
* empty
|
||||
* conditional
|
||||
* branch
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from IfStmt i
|
||||
where i.getThen().(BlockStmt).getNumStmt() = 0
|
||||
select i
|
||||
18
repo-tests/codeql/cpp/ql/examples/snippets/eq_true.ql
Normal file
18
repo-tests/codeql/cpp/ql/examples/snippets/eq_true.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/eq-true
|
||||
* @name Equality test on boolean
|
||||
* @description Finds tests like `==true`, `!=true`
|
||||
* @tags equal
|
||||
* comparison
|
||||
* test
|
||||
* boolean
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EqualityOperation eq, Expr trueExpr
|
||||
where
|
||||
trueExpr = eq.getAnOperand() and
|
||||
trueExpr.getType() instanceof BoolType and
|
||||
trueExpr.getValue().toInt() = 1
|
||||
select eq
|
||||
17
repo-tests/codeql/cpp/ql/examples/snippets/field_access.ql
Normal file
17
repo-tests/codeql/cpp/ql/examples/snippets/field_access.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/field-access
|
||||
* @name Access of field
|
||||
* @description Finds reads of `aDate` (defined on class `Order`)
|
||||
* @tags access
|
||||
* field
|
||||
* read
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Field f, FieldAccess access
|
||||
where
|
||||
f.hasName("aDate") and
|
||||
f.getDeclaringType().hasName("Order") and
|
||||
f = access.getTarget()
|
||||
select access
|
||||
18
repo-tests/codeql/cpp/ql/examples/snippets/function_call.ql
Normal file
18
repo-tests/codeql/cpp/ql/examples/snippets/function_call.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/function-call
|
||||
* @name Call to function
|
||||
* @description Finds calls to `std::map<...>::find()`
|
||||
* @tags call
|
||||
* function
|
||||
* method
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FunctionCall call, Function fcn
|
||||
where
|
||||
call.getTarget() = fcn and
|
||||
fcn.getDeclaringType().getSimpleName() = "map" and
|
||||
fcn.getDeclaringType().getNamespace().getName() = "std" and
|
||||
fcn.hasName("find")
|
||||
select call
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/integer-literal
|
||||
* @name Integer literal
|
||||
* @description Finds places where we use the integer literal `2`
|
||||
* @tags integer
|
||||
* literal
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Literal literal
|
||||
where
|
||||
literal.getType() instanceof IntType and
|
||||
literal.getValue().toInt() = 2
|
||||
select literal
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/mutualrecursion
|
||||
* @name Mutual recursion
|
||||
* @description Finds pairs of functions that call each other
|
||||
* @tags function
|
||||
* method
|
||||
* recursion
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Function m, Function n
|
||||
where
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = m and c.getTarget() = n) and
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = n and c.getTarget() = m) and
|
||||
m != n
|
||||
select m, n
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/override-method
|
||||
* @name Override of method
|
||||
* @description Finds methods that override `std::exception::what()`
|
||||
* @tags function
|
||||
* method
|
||||
* override
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction override, MemberFunction base
|
||||
where
|
||||
base.getName() = "what" and
|
||||
base.getDeclaringType().getName() = "exception" and
|
||||
base.getDeclaringType().getNamespace().getName() = "std" and
|
||||
override.overrides+(base)
|
||||
select override
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/returnstatement
|
||||
* @name Return statements
|
||||
* @description Finds return statements that return `0`
|
||||
* @tags return
|
||||
* statement
|
||||
* literal
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ReturnStmt r
|
||||
where r.getExpr().(Literal).getValue().toInt() = 0
|
||||
select r
|
||||
13
repo-tests/codeql/cpp/ql/examples/snippets/singletonblock.ql
Normal file
13
repo-tests/codeql/cpp/ql/examples/snippets/singletonblock.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/singletonblock
|
||||
* @name Singleton blocks
|
||||
* @description Finds block statements containing a single statement
|
||||
* @tags block
|
||||
* statement
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from BlockStmt b
|
||||
where b.getNumStmt() = 1
|
||||
select b
|
||||
17
repo-tests/codeql/cpp/ql/examples/snippets/switchcase.ql
Normal file
17
repo-tests/codeql/cpp/ql/examples/snippets/switchcase.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/switchcase
|
||||
* @name Switch statement case missing
|
||||
* @description Finds switch statements with a missing enum constant case
|
||||
* and no default case
|
||||
* @tags switch
|
||||
* case
|
||||
* enum
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EnumSwitch es, EnumConstant ec
|
||||
where
|
||||
ec = es.getAMissingCase() and
|
||||
not es.hasDefaultCase()
|
||||
select es, ec
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/ternaryconditional
|
||||
* @name Conditional expressions
|
||||
* @description Finds conditional expressions of the form `... ? ... : ...`
|
||||
* where the types of the resulting expressions differ
|
||||
* @tags conditional
|
||||
* ternary
|
||||
* type
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ConditionalExpr e
|
||||
where e.getThen().getType() != e.getElse().getType()
|
||||
select e
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/throw-exception
|
||||
* @name Throw exception of type
|
||||
* @description Finds places where we throw `parse_error` or one of its sub-types
|
||||
* @tags base
|
||||
* class
|
||||
* throw
|
||||
* exception
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ThrowExpr throw
|
||||
where throw.getType().(Class).getABaseClass*().getName() = "parse_error"
|
||||
select throw
|
||||
14
repo-tests/codeql/cpp/ql/examples/snippets/todocomment.ql
Normal file
14
repo-tests/codeql/cpp/ql/examples/snippets/todocomment.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/todocomment
|
||||
* @name TODO comments
|
||||
* @description Finds comments containing the word "TODO"
|
||||
* @tags comment
|
||||
* matches
|
||||
* TODO
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Comment c
|
||||
where c.getContents().matches("%TODO%")
|
||||
select c
|
||||
15
repo-tests/codeql/cpp/ql/examples/snippets/toomanyparams.ql
Normal file
15
repo-tests/codeql/cpp/ql/examples/snippets/toomanyparams.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/toomanyparams
|
||||
* @name Functions with many parameters
|
||||
* @description Finds functions or methods with more than 10 parameters
|
||||
* @tags function
|
||||
* method
|
||||
* parameter
|
||||
* argument
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Function fcn
|
||||
where fcn.getNumberOfParameters() > 10
|
||||
select fcn
|
||||
16
repo-tests/codeql/cpp/ql/examples/snippets/unusedlocalvar.ql
Normal file
16
repo-tests/codeql/cpp/ql/examples/snippets/unusedlocalvar.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedlocalvar
|
||||
* @name Unused local variable
|
||||
* @description Finds local variables that are not accessed
|
||||
* @tags variable
|
||||
* local
|
||||
* access
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from LocalScopeVariable v
|
||||
where
|
||||
not v instanceof Parameter and
|
||||
not exists(v.getAnAccess())
|
||||
select v
|
||||
18
repo-tests/codeql/cpp/ql/examples/snippets/unusedmethod.ql
Normal file
18
repo-tests/codeql/cpp/ql/examples/snippets/unusedmethod.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedmethod
|
||||
* @name Unused private method
|
||||
* @description Finds private non-virtual methods that are not accessed
|
||||
* @tags method
|
||||
* access
|
||||
* private
|
||||
* virtual
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction fcn
|
||||
where
|
||||
fcn.isPrivate() and
|
||||
not fcn.isVirtual() and
|
||||
not exists(FunctionCall call | fcn = call.getTarget())
|
||||
select fcn.getDefinition()
|
||||
13
repo-tests/codeql/cpp/ql/examples/snippets/unusedparam.ql
Normal file
13
repo-tests/codeql/cpp/ql/examples/snippets/unusedparam.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedparam
|
||||
* @name Unused parameter
|
||||
* @description Finds parameters that are not accessed
|
||||
* @tags parameter
|
||||
* access
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Parameter p
|
||||
where p.isNamed() and not exists(p.getAnAccess())
|
||||
select p
|
||||
21
repo-tests/codeql/cpp/ql/examples/snippets/voidreturntype.ql
Normal file
21
repo-tests/codeql/cpp/ql/examples/snippets/voidreturntype.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @id cpp/examples/voidreturntype
|
||||
* @name Const method without return type
|
||||
* @description Finds const methods whose return type is `void`
|
||||
* @tags const
|
||||
* function
|
||||
* method
|
||||
* modifier
|
||||
* specifier
|
||||
* return
|
||||
* type
|
||||
* void
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction m
|
||||
where
|
||||
m.hasSpecifier("const") and
|
||||
m.getType() instanceof VoidType
|
||||
select m
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/volatilevariable
|
||||
* @name Variable declared volatile
|
||||
* @description Finds variables with a `volatile` modifier
|
||||
* @tags variable
|
||||
* volatile
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Variable f
|
||||
where f.isVolatile()
|
||||
select f
|
||||
112
repo-tests/codeql/cpp/ql/lib/DefaultOptions.qll
Normal file
112
repo-tests/codeql/cpp/ql/lib/DefaultOptions.qll
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Provides default predicates that specify information about
|
||||
* the behavior of the program being analyzed.
|
||||
*
|
||||
* This can be overridden for particular code bases in `Options.qll`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dataflow
|
||||
import semmle.code.cpp.commons.Synchronization
|
||||
private import semmle.code.cpp.controlflow.internal.ConstantExprs
|
||||
private import Options as CustomOptions
|
||||
|
||||
/**
|
||||
* Default predicates that specify information about the behavior of
|
||||
* the program being analyzed.
|
||||
*/
|
||||
class Options extends string {
|
||||
Options() { this = "Options" }
|
||||
|
||||
/**
|
||||
* Holds if we wish to override the "may return NULL" inference for this
|
||||
* call. If this holds, then rather than trying to infer whether this
|
||||
* call might return NULL, we will instead test whether `returnsNull`
|
||||
* holds for it.
|
||||
*
|
||||
* By default, this handles the `Xstrdup` function used by the CVS
|
||||
* project.
|
||||
*/
|
||||
predicate overrideReturnsNull(Call call) {
|
||||
// Used in CVS:
|
||||
call.(FunctionCall).getTarget().hasGlobalName("Xstrdup")
|
||||
or
|
||||
CustomOptions::overrideReturnsNull(call) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this call may return NULL. This is only used if
|
||||
* `overrideReturnsNull` holds for the call.
|
||||
*
|
||||
* By default, this handles the `Xstrdup` function used by the CVS
|
||||
* project.
|
||||
*/
|
||||
predicate returnsNull(Call call) {
|
||||
// Used in CVS:
|
||||
call.(FunctionCall).getTarget().hasGlobalName("Xstrdup") and
|
||||
nullValue(call.getArgument(0))
|
||||
or
|
||||
CustomOptions::returnsNull(call) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a call to this function will never return.
|
||||
*
|
||||
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
|
||||
* `longjmp`, `__builtin_unreachable` and any function with a
|
||||
* `noreturn` attribute.
|
||||
*/
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
|
||||
])
|
||||
or
|
||||
CustomOptions::exits(f) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if evaluating expression `e` will never return, or can be assumed
|
||||
* to never return. For example:
|
||||
* ```
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
predicate exprExits(Expr e) {
|
||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
||||
CustomOptions::exprExits(e) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if function `f` should always have its return value checked.
|
||||
*
|
||||
* By default holds only for `fgets`.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) {
|
||||
f.hasGlobalOrStdName("fgets") or
|
||||
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if it is reasonable to ignore the return value of function
|
||||
* call `fc`.
|
||||
*
|
||||
* By default holds for calls to `select` where the first argument is
|
||||
* `0` (commonly used as a way of sleeping), and any call inside a
|
||||
* macro expansion.
|
||||
*/
|
||||
predicate okToIgnoreReturnValue(FunctionCall fc) {
|
||||
fc.isInMacroExpansion()
|
||||
or
|
||||
// common way of sleeping using select:
|
||||
fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0"
|
||||
or
|
||||
CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll
|
||||
}
|
||||
}
|
||||
|
||||
Options getOptions() { any() }
|
||||
154
repo-tests/codeql/cpp/ql/lib/Options.qll
Normal file
154
repo-tests/codeql/cpp/ql/lib/Options.qll
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Provides custom predicates that specify information about
|
||||
* the behavior of the program being analyzed.
|
||||
*
|
||||
* By default they fall back to the reasonable defaults provided in
|
||||
* `DefaultOptions.qll`, but by modifying this file, you can customize
|
||||
* the standard analyses to give better results for your project.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Customizable predicates that specify information about
|
||||
* the behavior of the program being analyzed.
|
||||
*/
|
||||
class CustomOptions extends Options {
|
||||
/**
|
||||
* Holds if we wish to override the "may return NULL" inference for this
|
||||
* call. If this holds, then rather than trying to infer whether this
|
||||
* call might return NULL, we will instead test whether `returnsNull`
|
||||
* holds for it.
|
||||
*
|
||||
* By default, this handles the `Xstrdup` function used by the CVS
|
||||
* project.
|
||||
*/
|
||||
override predicate overrideReturnsNull(Call call) { Options.super.overrideReturnsNull(call) }
|
||||
|
||||
/**
|
||||
* Holds if this call may return NULL. This is only used if
|
||||
* `overrideReturnsNull` holds for the call.
|
||||
*
|
||||
* By default, this handles the `Xstrdup` function used by the CVS
|
||||
* project.
|
||||
*/
|
||||
override predicate returnsNull(Call call) { Options.super.returnsNull(call) }
|
||||
|
||||
/**
|
||||
* Holds if a call to this function will never return.
|
||||
*
|
||||
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
|
||||
* `longjmp`, `error`, `__builtin_unreachable` and any function with a
|
||||
* `noreturn` attribute.
|
||||
*/
|
||||
override predicate exits(Function f) { Options.super.exits(f) }
|
||||
|
||||
/**
|
||||
* Holds if evaluating expression `e` will never return, or can be assumed
|
||||
* to never return. For example:
|
||||
* ```
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
override predicate exprExits(Expr e) { Options.super.exprExits(e) }
|
||||
|
||||
/**
|
||||
* Holds if function `f` should always have its return value checked.
|
||||
*
|
||||
* By default holds only for `fgets`.
|
||||
*/
|
||||
override predicate alwaysCheckReturnValue(Function f) { Options.super.alwaysCheckReturnValue(f) }
|
||||
|
||||
/**
|
||||
* Holds if it is reasonable to ignore the return value of function
|
||||
* call `fc`.
|
||||
*
|
||||
* By default holds for calls to `select` where the first argument is
|
||||
* `0` (commonly used as a way of sleeping), and any call inside a
|
||||
* macro expansion.
|
||||
*/
|
||||
override predicate okToIgnoreReturnValue(FunctionCall fc) {
|
||||
Options.super.okToIgnoreReturnValue(fc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that acts as a mutex.
|
||||
*/
|
||||
class CustomMutexType extends MutexType {
|
||||
CustomMutexType() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a call that always locks mutex `arg`
|
||||
* of this type.
|
||||
*/
|
||||
override predicate mustlockAccess(FunctionCall fc, Expr arg) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a call that tries to lock mutex `arg`
|
||||
* of this type, but may return without success.
|
||||
*/
|
||||
override predicate trylockAccess(FunctionCall fc, Expr arg) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a call that unlocks mutex `arg`
|
||||
* of this type.
|
||||
*/
|
||||
override predicate unlockAccess(FunctionCall fc, Expr arg) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.overrideReturnsNull` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate overrideReturnsNull(Call call) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.returnsNull` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate returnsNull(Call call) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.exits` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate exits(Function f) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.exprExits` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate exprExits(Expr e) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.alwaysCheckReturnValue` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: customize `CustomOptions.okToIgnoreReturnValue` instead.
|
||||
*
|
||||
* This predicate is required to support backwards compatibility for
|
||||
* older `Options.qll` files. It should not be removed or modified by
|
||||
* end users.
|
||||
*/
|
||||
predicate okToIgnoreReturnValue(FunctionCall fc) { none() }
|
||||
74
repo-tests/codeql/cpp/ql/lib/cpp.qll
Normal file
74
repo-tests/codeql/cpp/ql/lib/cpp.qll
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with C/C++ code.
|
||||
*
|
||||
* Where the documentation refers to the standards, it gives
|
||||
* references to the freely-available drafts.
|
||||
*
|
||||
* For C++11, these references are of the form [N3337 5.3.2/1], and the
|
||||
* corresponding draft of the standard can be downloaded from
|
||||
* https://github.com/cplusplus/draft/raw/master/papers/n3337.pdf
|
||||
*
|
||||
* For C++14, these references are of the form [N4140 5.3.2/1], and the
|
||||
* corresponding draft of the standard can be downloaded from
|
||||
* https://github.com/cplusplus/draft/raw/master/papers/n4140.pdf
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.File
|
||||
import semmle.code.cpp.Linkage
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Compilation
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Namespace
|
||||
import semmle.code.cpp.Specifier
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.Include
|
||||
import semmle.code.cpp.Macro
|
||||
import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.TypedefType
|
||||
import semmle.code.cpp.Class
|
||||
import semmle.code.cpp.Struct
|
||||
import semmle.code.cpp.Union
|
||||
import semmle.code.cpp.Enum
|
||||
import semmle.code.cpp.Member
|
||||
import semmle.code.cpp.Field
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.MemberFunction
|
||||
import semmle.code.cpp.Parameter
|
||||
import semmle.code.cpp.Variable
|
||||
import semmle.code.cpp.Initializer
|
||||
import semmle.code.cpp.FriendDecl
|
||||
import semmle.code.cpp.exprs.Expr
|
||||
import semmle.code.cpp.exprs.ArithmeticOperation
|
||||
import semmle.code.cpp.exprs.BitwiseOperation
|
||||
import semmle.code.cpp.exprs.LogicalOperation
|
||||
import semmle.code.cpp.exprs.ComparisonOperation
|
||||
import semmle.code.cpp.exprs.Assignment
|
||||
import semmle.code.cpp.exprs.Cast
|
||||
import semmle.code.cpp.exprs.Access
|
||||
import semmle.code.cpp.exprs.Call
|
||||
import semmle.code.cpp.exprs.Lambda
|
||||
import semmle.code.cpp.exprs.Literal
|
||||
import semmle.code.cpp.exprs.BuiltInOperations
|
||||
import semmle.code.cpp.stmts.Stmt
|
||||
import semmle.code.cpp.stmts.Block
|
||||
import semmle.code.cpp.metrics.MetricNamespace
|
||||
import semmle.code.cpp.metrics.MetricClass
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
import semmle.code.cpp.metrics.MetricFunction
|
||||
import semmle.code.cpp.commons.CommonType
|
||||
import semmle.code.cpp.commons.Printf
|
||||
import semmle.code.cpp.commons.VoidContext
|
||||
import semmle.code.cpp.commons.NULL
|
||||
import semmle.code.cpp.commons.PolymorphicClass
|
||||
import semmle.code.cpp.commons.Alloc
|
||||
import semmle.code.cpp.commons.StructLikeClass
|
||||
import semmle.code.cpp.controlflow.ControlFlowGraph
|
||||
import semmle.code.cpp.XML
|
||||
import semmle.code.cpp.Diagnostics
|
||||
import semmle.code.cpp.Comments
|
||||
import semmle.code.cpp.Preprocessor
|
||||
import semmle.code.cpp.Iteration
|
||||
import semmle.code.cpp.NameQualifiers
|
||||
import semmle.code.cpp.ObjectiveC
|
||||
import semmle.code.cpp.exprs.ObjectiveC
|
||||
import DefaultOptions
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* EXPERIMENTAL: The API of this module may change without notice.
|
||||
*
|
||||
* Provides a class for modeling `RangeSsaDefinition`s with a restricted range.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: The API of this class may change without notice.
|
||||
*
|
||||
* An SSA definition for which a range can be deduced. As with
|
||||
* `RangeSsaDefinition` and `SsaDefinition`, instances of this class
|
||||
* correspond to points in the program where one or more variables are defined
|
||||
* or have their value constrained in some way.
|
||||
*
|
||||
* Extend this class to add functionality to the range analysis library.
|
||||
*/
|
||||
abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
|
||||
/**
|
||||
* Holds if this `SimpleRangeAnalysisDefinition` adds range information for
|
||||
* `v`. Because a `SimpleRangeAnalysisDefinition` is just a point in the
|
||||
* program, it's possible that more than one variable might be defined at
|
||||
* this point. This predicate clarifies which variable(s) should get range
|
||||
* information from `this`.
|
||||
*
|
||||
* This predicate **must be overridden** to hold for any `v` that can show
|
||||
* up in the other members of `SimpleRangeAnalysisDefinition`. Conversely,
|
||||
* the other members **must be accurate** for any `v` in this predicate.
|
||||
*/
|
||||
abstract predicate hasRangeInformationFor(StackVariable v);
|
||||
|
||||
/**
|
||||
* Holds if `(this, v)` depends on the range of the unconverted expression
|
||||
* `e`. This information is used to inform the range analysis about cyclic
|
||||
* dependencies. Without this information, range analysis might work for
|
||||
* simple cases but will go into infinite loops on complex code.
|
||||
*
|
||||
* For example, when modelling the definition by reference in a call to an
|
||||
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
|
||||
* depends on `e`.
|
||||
*/
|
||||
abstract predicate dependsOnExpr(StackVariable v, Expr e);
|
||||
|
||||
/**
|
||||
* Gets the lower bound of the variable `v` defined by this definition.
|
||||
*
|
||||
* Implementations of this predicate should use
|
||||
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
|
||||
* recursive calls to get the bounds of their dependencies.
|
||||
*/
|
||||
abstract float getLowerBounds(StackVariable v);
|
||||
|
||||
/**
|
||||
* Gets the upper bound of the variable `v` defined by this definition.
|
||||
*
|
||||
* Implementations of this predicate should use
|
||||
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
|
||||
* recursive calls to get the bounds of their dependencies.
|
||||
*/
|
||||
abstract float getUpperBounds(StackVariable v);
|
||||
}
|
||||
|
||||
import SimpleRangeAnalysisInternal
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* EXPERIMENTAL: The API of this module may change without notice.
|
||||
*
|
||||
* Provides a class for modeling `Expr`s with a restricted range.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: The API of this class may change without notice.
|
||||
*
|
||||
* An expression for which a range can be deduced. Extend this class to add
|
||||
* functionality to the range analysis library.
|
||||
*/
|
||||
abstract class SimpleRangeAnalysisExpr extends Expr {
|
||||
/**
|
||||
* Gets the lower bound of the expression.
|
||||
*
|
||||
* Implementations of this predicate should use
|
||||
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
|
||||
* recursive calls to get the bounds of their children.
|
||||
*/
|
||||
abstract float getLowerBounds();
|
||||
|
||||
/**
|
||||
* Gets the upper bound of the expression.
|
||||
*
|
||||
* Implementations of this predicate should use
|
||||
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
|
||||
* recursive calls to get the bounds of their children.
|
||||
*/
|
||||
abstract float getUpperBounds();
|
||||
|
||||
/**
|
||||
* Holds if the range this expression depends on the definition `srcDef` for
|
||||
* StackVariable `srcVar`.
|
||||
*
|
||||
* Because this predicate cannot be recursive, most implementations should
|
||||
* override `dependsOnChild` instead.
|
||||
*/
|
||||
predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this expression depends on the range of its unconverted
|
||||
* subexpression `child`. This information is used to inform the range
|
||||
* analysis about cyclic dependencies. Without this information, range
|
||||
* analysis might work for simple cases but will go into infinite loops on
|
||||
* complex code.
|
||||
*
|
||||
* For example, when modeling a function call whose return value depends on
|
||||
* all of its arguments, implement this predicate as
|
||||
* `child = this.getAnArgument()`.
|
||||
*/
|
||||
abstract predicate dependsOnChild(Expr child);
|
||||
}
|
||||
|
||||
import SimpleRangeAnalysisInternal
|
||||
|
||||
/**
|
||||
* This class exists to prevent the QL front end from emitting compile errors
|
||||
* inside `SimpleRangeAnalysis.qll` about certain conjuncts being empty
|
||||
* because the overrides of `SimpleRangeAnalysisExpr` that happen to be in
|
||||
* scope do not make use of every feature it offers.
|
||||
*/
|
||||
private class Empty extends SimpleRangeAnalysisExpr {
|
||||
Empty() {
|
||||
// This predicate is complicated enough that the QL type checker doesn't
|
||||
// see it as empty but simple enough that the optimizer should.
|
||||
this = this and none()
|
||||
}
|
||||
|
||||
override float getLowerBounds() { none() }
|
||||
|
||||
override float getUpperBounds() { none() }
|
||||
|
||||
override predicate dependsOnChild(Expr child) { none() }
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* Provides precise tracking of how big the memory pointed to by pointers is.
|
||||
* For each pointer, we start tracking (starting from the allocation or an array declaration)
|
||||
* 1) how long is the chunk of memory allocated
|
||||
* 2) where the current pointer is in this chunk of memory
|
||||
* As computing this information is obviously not possible for all pointers,
|
||||
* we do not guarantee the existence of length/offset information for all pointers.
|
||||
* However, when it exists it is guaranteed to be accurate.
|
||||
*
|
||||
* The length and offset are tracked in a similar way to the Rangeanalysis.
|
||||
* Each length is a `ValueNumber + delta`, and each Offset is an `Operand + delta`.
|
||||
* We choose to track a `ValueNumber` for length, because the Rangeanalysis offers
|
||||
* integer bounds on instructions and operands in terms of `ValueNumber`s,
|
||||
* and `Operand` for offset because integer bounds on `Operand`s are
|
||||
* tighter than bounds on `Instruction`s.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.models.interfaces.Allocation
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.RangeUtils
|
||||
|
||||
private newtype TLength =
|
||||
TZeroLength() or
|
||||
TVNLength(ValueNumber vn) {
|
||||
not vn.getAnInstruction() instanceof ConstantInstruction and
|
||||
exists(Instruction i |
|
||||
vn.getAnInstruction() = i and
|
||||
(
|
||||
i.getResultIRType() instanceof IRSignedIntegerType or
|
||||
i.getResultIRType() instanceof IRUnsignedIntegerType
|
||||
)
|
||||
|
|
||||
i instanceof PhiInstruction
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
|
||||
or
|
||||
i.getAUse() instanceof ArgumentOperand
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Array lengths are represented in a ValueNumber | Zero + delta format.
|
||||
* This class keeps track of the ValueNumber or Zero.
|
||||
* The delta is tracked in the predicate `knownArrayLength`.
|
||||
*/
|
||||
class Length extends TLength {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* This length class corresponds to an array having a constant length
|
||||
* that is tracked by the delta value.
|
||||
*/
|
||||
class ZeroLength extends Length, TZeroLength {
|
||||
override string toString() { result = "ZeroLength" }
|
||||
}
|
||||
|
||||
/**
|
||||
* This length class corresponds to an array having variable length, i.e. the
|
||||
* length is tracked by a value number. One example is an array having length
|
||||
* `count` for an integer variable `count` in the program.
|
||||
*/
|
||||
class VNLength extends Length, TVNLength {
|
||||
ValueNumber vn;
|
||||
|
||||
VNLength() { this = TVNLength(vn) }
|
||||
|
||||
/** Gets an instruction with this value number bound. */
|
||||
Instruction getInstruction() { this = TVNLength(valueNumber(result)) }
|
||||
|
||||
ValueNumber getValueNumber() { result = vn }
|
||||
|
||||
override string toString() { result = "VNLength(" + vn.getExampleInstruction().toString() + ")" }
|
||||
}
|
||||
|
||||
private newtype TOffset =
|
||||
TZeroOffset() or
|
||||
TOpOffset(Operand op) {
|
||||
op.getAnyDef().getResultIRType() instanceof IRSignedIntegerType or
|
||||
op.getAnyDef().getResultIRType() instanceof IRUnsignedIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* This class describes the offset of a pointer in a chunk of memory.
|
||||
* It is either an `Operand` or zero, an additional integer delta is added later.
|
||||
*/
|
||||
class Offset extends TOffset {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a fixed offset, only specified by a delta.
|
||||
*/
|
||||
class ZeroOffset extends Offset, TZeroOffset {
|
||||
override string toString() { result = "ZeroOffset" }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents an offset of an operand.
|
||||
*/
|
||||
class OpOffset extends Offset, TOpOffset {
|
||||
Operand op;
|
||||
|
||||
OpOffset() { this = TOpOffset(op) }
|
||||
|
||||
Operand getOperand() { result = op }
|
||||
|
||||
override string toString() { result = "OpOffset(" + op.getDef().toString() + ")" }
|
||||
}
|
||||
|
||||
private int getBaseSizeForPointerType(PointerType type) { result = type.getBaseType().getSize() }
|
||||
|
||||
/**
|
||||
* Holds if pointer `prev` that points at offset `prevOffset + prevOffsetDelta`
|
||||
* steps to `array` that points to `offset + offsetDelta` in one step.
|
||||
* This predicate does not contain any recursive steps.
|
||||
*/
|
||||
bindingset[prevOffset, prevOffsetDelta]
|
||||
predicate simpleArrayLengthStep(
|
||||
Instruction array, Offset offset, int offsetDelta, Instruction prev, Offset prevOffset,
|
||||
int prevOffsetDelta
|
||||
) {
|
||||
// array assign
|
||||
array.(CopyInstruction).getSourceValue() = prev and
|
||||
offset = prevOffset and
|
||||
offsetDelta = prevOffsetDelta
|
||||
or
|
||||
// pointer add with constant
|
||||
array.(PointerAddInstruction).getLeft() = prev and
|
||||
offset = prevOffset and
|
||||
offsetDelta = prevOffsetDelta + getConstantValue(array.(PointerAddInstruction).getRight())
|
||||
or
|
||||
// pointer add with variable
|
||||
array.(PointerAddInstruction).getLeft() = prev and
|
||||
prevOffset instanceof ZeroOffset and
|
||||
offset.(OpOffset).getOperand() = array.(PointerAddInstruction).getRightOperand() and
|
||||
offsetDelta = prevOffsetDelta and
|
||||
not exists(getConstantValue(array.(PointerAddInstruction).getRight()))
|
||||
or
|
||||
// pointer sub with constant
|
||||
array.(PointerSubInstruction).getLeft() = prev and
|
||||
offset = prevOffset and
|
||||
offsetDelta = prevOffsetDelta - getConstantValue(array.(PointerSubInstruction).getRight())
|
||||
or
|
||||
// array to pointer decay
|
||||
array.(ConvertInstruction).getUnary() = prev and
|
||||
array.getConvertedResultExpression() instanceof ArrayToPointerConversion and
|
||||
offset = prevOffset and
|
||||
offsetDelta = prevOffsetDelta
|
||||
or
|
||||
// cast of pointer to pointer with the same element size
|
||||
exists(PointerType fromTyp, PointerType toTyp |
|
||||
array.(PtrToPtrCastInstruction).getUnary() = prev and
|
||||
prev.getResultLanguageType().hasType(fromTyp, false) and
|
||||
array.getResultLanguageType().hasType(toTyp, false) and
|
||||
offset = prevOffset and
|
||||
offsetDelta = prevOffsetDelta and
|
||||
if fromTyp instanceof VoidPointerType
|
||||
then getBaseSizeForPointerType(toTyp) = 1
|
||||
else (
|
||||
if toTyp instanceof VoidPointerType
|
||||
then getBaseSizeForPointerType(fromTyp) = 1
|
||||
else getBaseSizeForPointerType(toTyp) = getBaseSizeForPointerType(fromTyp)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a `sizeExpr` of malloc into a variable part (`lengthExpr`) and an integer offset (`delta`).
|
||||
*/
|
||||
private predicate deconstructMallocSizeExpr(Expr sizeExpr, Expr lengthExpr, int delta) {
|
||||
sizeExpr instanceof AddExpr and
|
||||
exists(Expr constantExpr |
|
||||
lengthExpr = sizeExpr.(AddExpr).getAnOperand() and
|
||||
constantExpr = sizeExpr.(AddExpr).getAnOperand() and
|
||||
lengthExpr != constantExpr and
|
||||
delta = constantExpr.getValue().toInt()
|
||||
)
|
||||
or
|
||||
sizeExpr instanceof SubExpr and
|
||||
exists(Expr constantExpr |
|
||||
lengthExpr = sizeExpr.(SubExpr).getLeftOperand() and
|
||||
constantExpr = sizeExpr.(SubExpr).getRightOperand() and
|
||||
delta = -constantExpr.getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the instruction `array` is a dynamic memory allocation of `length`+`delta` elements.
|
||||
*/
|
||||
private predicate allocation(Instruction array, Length length, int delta) {
|
||||
exists(AllocationExpr alloc, Type ptrTyp |
|
||||
array.getUnconvertedResultExpression() = alloc and
|
||||
array.getResultLanguageType().hasType(ptrTyp, false) and
|
||||
// ensure that we have the same type of the allocation and the pointer
|
||||
ptrTyp.stripTopLevelSpecifiers().(PointerType).getBaseType().getUnspecifiedType() =
|
||||
alloc.getAllocatedElementType().getUnspecifiedType() and
|
||||
// ensure that the size multiplier of the allocation is the same as the
|
||||
// size of the type we are allocating
|
||||
alloc.getSizeMult() = getBaseSizeForPointerType(ptrTyp) and
|
||||
(
|
||||
length instanceof ZeroLength and
|
||||
delta = alloc.getSizeExpr().getValue().toInt()
|
||||
or
|
||||
not exists(alloc.getSizeExpr().getValue().toInt()) and
|
||||
(
|
||||
exists(Expr lengthExpr |
|
||||
deconstructMallocSizeExpr(alloc.getSizeExpr(), lengthExpr, delta) and
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr
|
||||
)
|
||||
or
|
||||
not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and
|
||||
delta = 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `array` is declared as an array with length `length + lengthDelta`
|
||||
*/
|
||||
private predicate arrayDeclaration(Instruction array, Length length, int lengthDelta) {
|
||||
(
|
||||
array instanceof VariableAddressInstruction or
|
||||
array instanceof FieldAddressInstruction
|
||||
) and
|
||||
exists(ArrayType type | array.getResultLanguageType().hasType(type, _) |
|
||||
length instanceof ZeroLength and
|
||||
lengthDelta = type.getArraySize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `array` is declared as an array or allocated
|
||||
* with length `length + lengthDelta`
|
||||
*/
|
||||
predicate arrayAllocationOrDeclaration(Instruction array, Length length, int lengthDelta) {
|
||||
allocation(array, length, lengthDelta)
|
||||
or
|
||||
// declaration of variable of array type
|
||||
arrayDeclaration(array, length, lengthDelta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the instruction `array` represents a pointer to a chunk of memory that holds
|
||||
* `length + lengthDelta` elements, using only local analysis.
|
||||
* `array` points at `offset + offsetDelta` in the chunk of memory.
|
||||
* The pointer is in-bounds if `offset + offsetDelta < length + lengthDelta` and
|
||||
* `offset + offsetDelta >= 0` holds.
|
||||
* The pointer is out-of-bounds if `offset + offsetDelta >= length + lengthDelta`
|
||||
* or `offset + offsetDelta < 0` holds.
|
||||
* All pointers in this predicate are guaranteed to be non-null,
|
||||
* but are not guaranteed to be live.
|
||||
*/
|
||||
predicate knownArrayLength(
|
||||
Instruction array, Length length, int lengthDelta, Offset offset, int offsetDelta
|
||||
) {
|
||||
arrayAllocationOrDeclaration(array, length, lengthDelta) and
|
||||
offset instanceof ZeroOffset and
|
||||
offsetDelta = 0
|
||||
or
|
||||
// simple step (no phi nodes)
|
||||
exists(Instruction prev, Offset prevOffset, int prevOffsetDelta |
|
||||
knownArrayLength(prev, length, lengthDelta, prevOffset, prevOffsetDelta) and
|
||||
simpleArrayLengthStep(array, offset, offsetDelta, prev, prevOffset, prevOffsetDelta)
|
||||
)
|
||||
or
|
||||
// merge control flow after phi node - but only if all the bounds agree
|
||||
forex(Instruction input | array.(PhiInstruction).getAnInput() = input |
|
||||
knownArrayLength(input, length, lengthDelta, offset, offsetDelta)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
private newtype TBound =
|
||||
TBoundZero() or
|
||||
TBoundValueNumber(ValueNumber vn) {
|
||||
exists(Instruction i |
|
||||
vn.getAnInstruction() = i and
|
||||
(
|
||||
i.getResultIRType() instanceof IRIntegerType or
|
||||
i.getResultIRType() instanceof IRAddressType
|
||||
) and
|
||||
not vn.getAnInstruction() instanceof ConstantInstruction
|
||||
|
|
||||
i instanceof PhiInstruction
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
i instanceof VariableAddressInstruction
|
||||
or
|
||||
i instanceof FieldAddressInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
|
||||
or
|
||||
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
|
||||
or
|
||||
i.getAUse() instanceof ArgumentOperand
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound that may be inferred for an expression plus/minus an integer delta.
|
||||
*/
|
||||
abstract class Bound extends TBound {
|
||||
abstract string toString();
|
||||
|
||||
/** Gets an expression that equals this bound plus `delta`. */
|
||||
abstract Instruction getInstruction(int delta);
|
||||
|
||||
/** Gets an expression that equals this bound. */
|
||||
Instruction getInstruction() { result = getInstruction(0) }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* The bound that corresponds to the integer 0. This is used to represent all
|
||||
* integer bounds as bounds are always accompanied by an added integer delta.
|
||||
*/
|
||||
class ZeroBound extends Bound, TBoundZero {
|
||||
override string toString() { result = "0" }
|
||||
|
||||
override Instruction getInstruction(int delta) {
|
||||
result.(ConstantValueInstruction).getValue().toInt() = delta
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* A bound corresponding to the value of an `Instruction`.
|
||||
*/
|
||||
class ValueNumberBound extends Bound, TBoundValueNumber {
|
||||
ValueNumber vn;
|
||||
|
||||
ValueNumberBound() { this = TBoundValueNumber(vn) }
|
||||
|
||||
/** Gets an `Instruction` that equals this bound. */
|
||||
override Instruction getInstruction(int delta) {
|
||||
this = TBoundValueNumber(valueNumber(result)) and delta = 0
|
||||
}
|
||||
|
||||
override string toString() { result = vn.getExampleInstruction().toString() }
|
||||
|
||||
override Location getLocation() { result = vn.getLocation() }
|
||||
|
||||
/** Gets the value number that equals this bound. */
|
||||
ValueNumber getValueNumber() { result = vn }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
//
|
||||
// Import each extension we want to enable
|
||||
import extensions.SubtractSelf
|
||||
import extensions.ConstantBitwiseAndExprRange
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* This library proves that a subset of pointer dereferences in a program are
|
||||
* safe, i.e. in-bounds.
|
||||
* It does so by first defining what a pointer dereference is (on the IR
|
||||
* `Instruction` level), and then using the array length analysis and the range
|
||||
* analysis together to prove that some of these pointer dereferences are safe.
|
||||
*
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
|
||||
* in the program.
|
||||
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
||||
* the array length analysis.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the address of memory that `i` accesses.
|
||||
* Only holds if `i` dereferences a pointer, not when the computation of the
|
||||
* memory address is constant, or if the address of a local variable is loaded/stored to.
|
||||
*/
|
||||
private Instruction getMemoryAddressInstruction(Instruction i) {
|
||||
(
|
||||
result = i.(FieldAddressInstruction).getObjectAddress() or
|
||||
result = i.(LoadInstruction).getSourceAddress() or
|
||||
result = i.(StoreInstruction).getDestinationAddress()
|
||||
) and
|
||||
not result instanceof FieldAddressInstruction and
|
||||
not result instanceof VariableAddressInstruction and
|
||||
not result instanceof ConstantValueInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* All instructions that dereference a pointer.
|
||||
*/
|
||||
class PointerDereferenceInstruction extends Instruction {
|
||||
PointerDereferenceInstruction() { exists(getMemoryAddressInstruction(this)) }
|
||||
|
||||
Instruction getAddress() { result = getMemoryAddressInstruction(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ptrDeref` can be proven to always access allocated memory.
|
||||
*/
|
||||
predicate inBounds(PointerDereferenceInstruction ptrDeref) {
|
||||
exists(Length length, int lengthDelta, Offset offset, int offsetDelta |
|
||||
knownArrayLength(ptrDeref.getAddress(), length, lengthDelta, offset, offsetDelta) and
|
||||
// lower bound - note that we treat a pointer that accesses an array of
|
||||
// length 0 as on upper-bound violation, but not as a lower-bound violation
|
||||
(
|
||||
offset instanceof ZeroOffset and
|
||||
offsetDelta >= 0
|
||||
or
|
||||
offset instanceof OpOffset and
|
||||
exists(int lowerBoundDelta |
|
||||
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), lowerBoundDelta,
|
||||
/*upper*/ false, _) and
|
||||
lowerBoundDelta + offsetDelta >= 0
|
||||
)
|
||||
) and
|
||||
// upper bound
|
||||
(
|
||||
// both offset and length are only integers
|
||||
length instanceof ZeroLength and
|
||||
offset instanceof ZeroOffset and
|
||||
offsetDelta < lengthDelta
|
||||
or
|
||||
exists(int lengthBound |
|
||||
// array length is variable+integer, and there's a fixed (integer-only)
|
||||
// lower bound on the variable, so we can guarantee this access is always in-bounds
|
||||
length instanceof VNLength and
|
||||
offset instanceof ZeroOffset and
|
||||
boundedInstruction(length.(VNLength).getInstruction(), any(ZeroBound b), lengthBound,
|
||||
/* upper*/ false, _) and
|
||||
offsetDelta < lengthBound + lengthDelta
|
||||
)
|
||||
or
|
||||
exists(int offsetBoundDelta |
|
||||
length instanceof ZeroLength and
|
||||
offset instanceof OpOffset and
|
||||
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), offsetBoundDelta,
|
||||
/* upper */ true, _) and
|
||||
// offset <= offsetBoundDelta, so offset + offsetDelta <= offsetDelta + offsetBoundDelta
|
||||
// Thus, in-bounds if offsetDelta + offsetBoundDelta < lengthDelta
|
||||
// as we have length instanceof ZeroLength
|
||||
offsetDelta + offsetBoundDelta < lengthDelta
|
||||
)
|
||||
or
|
||||
exists(ValueNumberBound b, int offsetBoundDelta |
|
||||
length instanceof VNLength and
|
||||
offset instanceof OpOffset and
|
||||
b.getValueNumber() = length.(VNLength).getValueNumber() and
|
||||
// It holds that offset <= length + offsetBoundDelta
|
||||
boundedOperand(offset.(OpOffset).getOperand(), b, offsetBoundDelta, /*upper*/ true, _) and
|
||||
// it also holds that
|
||||
offsetDelta < lengthDelta - offsetBoundDelta
|
||||
// taking both inequalities together we get
|
||||
// offset <= length + offsetBoundDelta
|
||||
// => offset + offsetDelta <= length + offsetBoundDelta + offsetDelta < length + offsetBoundDelta + lengthDelta - offsetBoundDelta
|
||||
// as required
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,624 @@
|
||||
/**
|
||||
* Provides classes and predicates for range analysis.
|
||||
*
|
||||
* An inferred bound can either be a specific integer or a `ValueNumber`
|
||||
* representing the abstract value of a set of `Instruction`s.
|
||||
*
|
||||
* If an inferred bound relies directly on a condition, then this condition is
|
||||
* reported as the reason for the bound.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library tackles range analysis as a flow problem. Consider e.g.:
|
||||
* ```
|
||||
* len = arr.length;
|
||||
* if (x < len) { ... y = x-1; ... y ... }
|
||||
* ```
|
||||
* In this case we would like to infer `y <= arr.length - 2`, and this is
|
||||
* accomplished by tracking the bound through a sequence of steps:
|
||||
* ```
|
||||
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
|
||||
* ```
|
||||
*
|
||||
* In its simplest form the step relation `I1 --> I2` relates two `Instruction`s
|
||||
* such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate
|
||||
* step relation handling lower bounds). Examples of such steps include
|
||||
* assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x`
|
||||
* guarded by the condition.
|
||||
*
|
||||
* In order to handle subtractions and additions with constants, and strict
|
||||
* comparisons, the step relation is augmented with an integer delta. With this
|
||||
* generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer
|
||||
* such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds
|
||||
* to the predicate `boundFlowStep`.
|
||||
*
|
||||
* The complete range analysis is then implemented as the transitive closure of
|
||||
* the step relation summing the deltas along the way. If `I1` transitively
|
||||
* steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an
|
||||
* interesting bound equal to the value of `I1` then `I2 <= B + delta`. This
|
||||
* corresponds to the predicate `boundedInstruction`.
|
||||
*
|
||||
* Bounds come in two forms: either they are relative to zero (and thus provide
|
||||
* a constant bound), or they are relative to some program value. This value is
|
||||
* represented by the `ValueNumber` class, each instance of which represents a
|
||||
* set of `Instructions` that must have the same value.
|
||||
*
|
||||
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
|
||||
* There are essentially two cases:
|
||||
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
|
||||
* - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
|
||||
* The first case is for whenever a bound can be proven without taking looping
|
||||
* into account. The second case is relevant when `x2` comes from a back-edge
|
||||
* where we can prove that the variable has been non-increasing through the
|
||||
* loop-iteration as this means that any upper bound that holds prior to the
|
||||
* loop also holds for the variable during the loop.
|
||||
* This generalizes to a phi node with `n` inputs, so if
|
||||
* `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
|
||||
* also have `x0 <= B + delta` if we can prove either:
|
||||
* - `xj <= B + d` with `d <= delta` or
|
||||
* - `xj <= x0 + d` with `d <= 0`
|
||||
* for each input `xj`.
|
||||
*
|
||||
* As all inferred bounds can be related directly to a path in the source code
|
||||
* the only source of non-termination is if successive redundant (and thereby
|
||||
* increasingly worse) bounds are calculated along a loop in the source code.
|
||||
* We prevent this by weakening the bound to a small finite set of bounds when
|
||||
* a path follows a second back-edge (we postpone weakening till the second
|
||||
* back-edge as a precise bound might require traversing a loop once).
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import RangeUtils
|
||||
private import SignAnalysis
|
||||
import Bound
|
||||
|
||||
cached
|
||||
private module RangeAnalysisCache {
|
||||
cached
|
||||
module RangeAnalysisPublic {
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `i` and this is the best such delta.
|
||||
* - `upper = true` : `i <= b + delta`
|
||||
* - `upper = false` : `i >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
cached
|
||||
predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedInstruction(i, b, delta, upper, _, _, reason) and
|
||||
bestInstructionBound(i, b, delta, upper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
cached
|
||||
predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedOperandCand(op, b, delta, upper, reason) and
|
||||
bestOperandBound(op, b, delta, upper)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
|
||||
*/
|
||||
cached
|
||||
predicate possibleReason(IRGuardCondition guard) {
|
||||
guard = boundFlowCond(_, _, _, _, _)
|
||||
or
|
||||
guard = eqFlowCond(_, _, _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
private import RangeAnalysisCache
|
||||
import RangeAnalysisPublic
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) {
|
||||
delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op`.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedNonPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
or
|
||||
boundedPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*/
|
||||
private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) {
|
||||
delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `vn` equals `bound + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `vn == bound + delta`
|
||||
* - `isEq = false` : `vn != bound + delta`
|
||||
*/
|
||||
private IRGuardCondition eqFlowCond(
|
||||
ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue
|
||||
) {
|
||||
result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1 + delta` is a valid bound for `op2`.
|
||||
* - `upper = true` : `op2 <= op1 + delta`
|
||||
* - `upper = false` : `op2 >= op1 + delta`
|
||||
*/
|
||||
private predicate boundFlowStepSsa(
|
||||
NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason
|
||||
) {
|
||||
exists(IRGuardCondition guard, boolean testIsTrue |
|
||||
guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
|
||||
guard.controls(op2.getUse().getBlock(), testIsTrue) and
|
||||
reason = TCondReason(guard)
|
||||
)
|
||||
or
|
||||
exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast |
|
||||
valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and
|
||||
guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and
|
||||
guard.controls(op2.getUse().getBlock(), testIsTrue) and
|
||||
reason = TCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `vn` is bounded by `bound + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `upper = true` : `vn <= bound + delta`
|
||||
* - `upper = false` : `vn >= bound + delta`
|
||||
*/
|
||||
private IRGuardCondition boundFlowCond(
|
||||
ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue
|
||||
) {
|
||||
exists(int d |
|
||||
result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and
|
||||
// `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need
|
||||
// `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as
|
||||
// it does here, so we don't need to account for it.
|
||||
if upper = true then delta = d - 1 else delta = d
|
||||
)
|
||||
or
|
||||
result = eqFlowCond(vn, bound, delta, true, testIsTrue) and
|
||||
(upper = true or upper = false)
|
||||
}
|
||||
|
||||
private newtype TReason =
|
||||
TNoReason() or
|
||||
TCondReason(IRGuardCondition guard) { possibleReason(guard) }
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class Reason extends TReason {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class NoReason extends Reason, TNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
class CondReason extends Reason, TCondReason {
|
||||
IRGuardCondition getCond() { this = TCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `typ` is a small integral type with the given lower and upper bounds.
|
||||
*/
|
||||
private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) {
|
||||
typ.isSigned() and typ.getByteSize() = 1 and lowerbound = -128 and upperbound = 127
|
||||
or
|
||||
typ.isUnsigned() and typ.getByteSize() = 1 and lowerbound = 0 and upperbound = 255
|
||||
or
|
||||
typ.isSigned() and typ.getByteSize() = 2 and lowerbound = -32768 and upperbound = 32767
|
||||
or
|
||||
typ.isUnsigned() and typ.getByteSize() = 2 and lowerbound = 0 and upperbound = 65535
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast to a small integral type that may overflow or underflow.
|
||||
*/
|
||||
private class NarrowingCastInstruction extends ConvertInstruction {
|
||||
NarrowingCastInstruction() {
|
||||
not this instanceof SafeCastInstruction and
|
||||
typeBound(getResultIRType(), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(getResultIRType(), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(getResultIRType(), _, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op + delta` is a valid bound for `i`.
|
||||
* - `upper = true` : `i <= op + delta`
|
||||
* - `upper = false` : `i >= op + delta`
|
||||
*/
|
||||
private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) {
|
||||
valueFlowStep(i, op, delta) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
i.(SafeCastInstruction).getAnOperand() = op and
|
||||
delta = 0 and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(Operand x |
|
||||
i.(AddInstruction).getAnOperand() = op and
|
||||
i.(AddInstruction).getAnOperand() = x and
|
||||
op != x
|
||||
|
|
||||
not exists(getValue(getConstantValue(op.getUse()))) and
|
||||
not exists(getValue(getConstantValue(x.getUse()))) and
|
||||
if strictlyPositive(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if positive(x)
|
||||
then upper = false and delta = 0
|
||||
else
|
||||
if strictlyNegative(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if negative(x)
|
||||
then upper = true and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
exists(Operand x |
|
||||
exists(SubInstruction sub |
|
||||
i = sub and
|
||||
sub.getLeftOperand() = op and
|
||||
sub.getRightOperand() = x
|
||||
)
|
||||
|
|
||||
// `x` with constant value is covered by valueFlowStep
|
||||
not exists(getValue(getConstantValue(x.getUse()))) and
|
||||
if strictlyPositive(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if positive(x)
|
||||
then upper = true and delta = 0
|
||||
else
|
||||
if strictlyNegative(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if negative(x)
|
||||
then upper = false and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true
|
||||
or
|
||||
i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true
|
||||
or
|
||||
i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true
|
||||
or
|
||||
i.(BitOrInstruction).getAnOperand() = op and
|
||||
positiveInstruction(i) and
|
||||
delta = 0 and
|
||||
upper = false
|
||||
// TODO: min, max, rand
|
||||
}
|
||||
|
||||
private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) {
|
||||
exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 |
|
||||
i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k
|
||||
or
|
||||
exists(ShiftLeftInstruction i |
|
||||
i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) {
|
||||
exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 |
|
||||
exists(DivInstruction i |
|
||||
i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k
|
||||
)
|
||||
or
|
||||
exists(ShiftRightInstruction i |
|
||||
i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b` is a valid bound for `op`
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedNonPhiOperand(
|
||||
NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
exists(NonPhiOperand op2, int d1, int d2 |
|
||||
boundFlowStepSsa(op, op2, d1, upper, reason) and
|
||||
boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and
|
||||
delta = d1 + d2
|
||||
)
|
||||
or
|
||||
boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
or
|
||||
exists(int d, Reason r1, Reason r2 |
|
||||
boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2)
|
||||
|
|
||||
unequalOperand(op, b, d, r1) and
|
||||
(
|
||||
upper = true and delta = d - 1
|
||||
or
|
||||
upper = false and delta = d + 1
|
||||
) and
|
||||
(
|
||||
reason = r1
|
||||
or
|
||||
reason = r2 and not r2 instanceof NoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1 + delta` is a valid bound for `op2`.
|
||||
* - `upper = true` : `op2 <= op1 + delta`
|
||||
* - `upper = false` : `op2 >= op1 + delta`
|
||||
*/
|
||||
private predicate boundFlowStepPhi(
|
||||
PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason
|
||||
) {
|
||||
op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and
|
||||
(upper = true or upper = false) and
|
||||
reason = TNoReason() and
|
||||
delta = 0
|
||||
or
|
||||
exists(IRGuardCondition guard, boolean testIsTrue |
|
||||
guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
|
||||
guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and
|
||||
reason = TCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundedPhiOperand(
|
||||
PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 |
|
||||
boundFlowStepPhi(op, op2, d1, upper, r1) and
|
||||
boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and
|
||||
delta = d1 + d2 and
|
||||
(if r1 instanceof NoReason then reason = r2 else reason = r1)
|
||||
)
|
||||
or
|
||||
boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
or
|
||||
exists(int d, Reason r1, Reason r2 |
|
||||
boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2)
|
||||
|
|
||||
unequalOperand(op, b, d, r1) and
|
||||
(
|
||||
upper = true and delta = d - 1
|
||||
or
|
||||
upper = false and delta = d + 1
|
||||
) and
|
||||
(
|
||||
reason = r1
|
||||
or
|
||||
reason = r2 and not r2 instanceof NoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `op2 != op1 + delta` at `pos`. */
|
||||
private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) {
|
||||
exists(IRGuardCondition guard, boolean testIsTrue |
|
||||
guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and
|
||||
guard.controls(op2.getUse().getBlock(), testIsTrue) and
|
||||
reason = TCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op != b + delta` at `pos`.
|
||||
*/
|
||||
private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) {
|
||||
exists(Operand op2, int d1, int d2 |
|
||||
unequalFlowStep(op, op2, d1, reason) and
|
||||
boundedNonPhiOperand(op2, b, d2, true, _, _, _) and
|
||||
boundedNonPhiOperand(op2, b, d2, false, _, _, _) and
|
||||
delta = d1 + d2
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundedPhiCandValidForEdge(
|
||||
PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
Reason reason, PhiInputOperand op
|
||||
) {
|
||||
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta)
|
||||
or
|
||||
exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta)
|
||||
or
|
||||
selfBoundedPhiInp(phi, op, upper)
|
||||
)
|
||||
}
|
||||
|
||||
/** Weakens a delta to lie in the range `[-1..1]`. */
|
||||
bindingset[delta, upper]
|
||||
private int weakenDelta(boolean upper, int delta) {
|
||||
delta in [-1 .. 1] and result = delta
|
||||
or
|
||||
upper = true and result = -1 and delta < -1
|
||||
or
|
||||
upper = false and result = 1 and delta > 1
|
||||
}
|
||||
|
||||
private predicate boundedPhiInp(
|
||||
PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge,
|
||||
int origdelta, Reason reason
|
||||
) {
|
||||
phi.getAnOperand() = op and
|
||||
exists(int d, boolean fromBackEdge0 |
|
||||
boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
b.(ValueNumberBound).getInstruction() = op.getDef() and
|
||||
d = 0 and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge0 = false and
|
||||
origdelta = 0 and
|
||||
reason = TNoReason()
|
||||
|
|
||||
if backEdge(phi, op)
|
||||
then
|
||||
fromBackEdge = true and
|
||||
(
|
||||
fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
|
||||
or
|
||||
fromBackEdge0 = false and delta = d
|
||||
)
|
||||
else (
|
||||
delta = d and fromBackEdge = fromBackEdge0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiInp1(
|
||||
PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper
|
||||
) {
|
||||
boundedPhiInp(phi, op, b, delta, upper, _, _, _)
|
||||
}
|
||||
|
||||
private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) {
|
||||
exists(int d, ValueNumberBound phibound |
|
||||
phibound.getInstruction() = phi and
|
||||
boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and
|
||||
(
|
||||
upper = true and d <= 0
|
||||
or
|
||||
upper = false and d >= 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiCand(
|
||||
PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
exists(PhiInputOperand op |
|
||||
boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value being cast has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound within the bounds of the resulting type.
|
||||
* For `upper = true` this means that the cast will not overflow and for
|
||||
* `upper = false` this means that the cast will not underflow.
|
||||
*/
|
||||
private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) {
|
||||
exists(int bound |
|
||||
boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _)
|
||||
|
|
||||
upper = true and bound <= cast.getUpperBound()
|
||||
or
|
||||
upper = false and bound >= cast.getLowerBound()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedCastExpr(
|
||||
NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge,
|
||||
int origdelta, Reason reason
|
||||
) {
|
||||
boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `i`.
|
||||
* - `upper = true` : `i <= b + delta`
|
||||
* - `upper = false` : `i >= b + delta`
|
||||
*/
|
||||
private predicate boundedInstruction(
|
||||
Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
i instanceof PhiInstruction and
|
||||
forex(PhiInputOperand op | op = i.getAnOperand() |
|
||||
boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op)
|
||||
)
|
||||
or
|
||||
i = b.getInstruction(delta) and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TNoReason()
|
||||
or
|
||||
exists(Operand mid, int d1, int d2 |
|
||||
boundFlowStep(i, mid, d1, upper) and
|
||||
boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
|
||||
delta = d1 + d2 and
|
||||
not exists(getValue(getConstantValue(i)))
|
||||
)
|
||||
or
|
||||
exists(Operand mid, int factor, int d |
|
||||
boundFlowStepMul(i, mid, factor) and
|
||||
boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof ZeroBound and
|
||||
delta = d * factor and
|
||||
not exists(getValue(getConstantValue(i)))
|
||||
)
|
||||
or
|
||||
exists(Operand mid, int factor, int d |
|
||||
boundFlowStepDiv(i, mid, factor) and
|
||||
boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
d >= 0 and
|
||||
b instanceof ZeroBound and
|
||||
delta = d / factor and
|
||||
not exists(getValue(getConstantValue(i)))
|
||||
)
|
||||
or
|
||||
exists(NarrowingCastInstruction cast |
|
||||
cast = i and
|
||||
safeNarrowingCast(cast, upper.booleanNot()) and
|
||||
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
// TODO: move this dependency
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
|
||||
// TODO: move this out of test code
|
||||
language[monotonicAggregates]
|
||||
IntValue getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt()
|
||||
or
|
||||
exists(BinaryInstruction binInstr, IntValue left, IntValue right |
|
||||
binInstr = instr and
|
||||
left = getConstantValue(binInstr.getLeft()) and
|
||||
right = getConstantValue(binInstr.getRight()) and
|
||||
(
|
||||
binInstr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
binInstr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
binInstr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
binInstr instanceof DivInstruction and result = div(left, right)
|
||||
)
|
||||
)
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result =
|
||||
max(PhiInputOperand operand |
|
||||
operand = phi.getAnOperand()
|
||||
|
|
||||
getConstantValue(operand.getDef())
|
||||
) and
|
||||
result =
|
||||
min(PhiInputOperand operand |
|
||||
operand = phi.getAnOperand()
|
||||
|
|
||||
getConstantValue(operand.getDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate valueFlowStep(Instruction i, Operand op, int delta) {
|
||||
i.(CopyInstruction).getSourceValueOperand() = op and delta = 0
|
||||
or
|
||||
exists(Operand x |
|
||||
i.(AddInstruction).getAnOperand() = op and
|
||||
i.(AddInstruction).getAnOperand() = x and
|
||||
op != x
|
||||
|
|
||||
delta = getValue(getConstantValue(x.getDef()))
|
||||
)
|
||||
or
|
||||
exists(Operand x |
|
||||
i.(SubInstruction).getLeftOperand() = op and
|
||||
i.(SubInstruction).getRightOperand() = x
|
||||
|
|
||||
delta = -getValue(getConstantValue(x.getDef()))
|
||||
)
|
||||
or
|
||||
exists(Operand x |
|
||||
i.(PointerAddInstruction).getAnOperand() = op and
|
||||
i.(PointerAddInstruction).getAnOperand() = x and
|
||||
op != x
|
||||
|
|
||||
delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef()))
|
||||
)
|
||||
or
|
||||
exists(Operand x |
|
||||
i.(PointerSubInstruction).getLeftOperand() = op and
|
||||
i.(PointerSubInstruction).getRightOperand() = x
|
||||
|
|
||||
delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef()))
|
||||
)
|
||||
}
|
||||
|
||||
predicate backEdge(PhiInstruction phi, PhiInputOperand op) {
|
||||
phi.getAnOperand() = op and
|
||||
phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of
|
||||
* range analysis.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) {
|
||||
fromtyp.getByteSize() < totyp.getByteSize() and
|
||||
(
|
||||
fromtyp.isUnsigned()
|
||||
or
|
||||
totyp.isSigned()
|
||||
)
|
||||
or
|
||||
fromtyp.getByteSize() <= totyp.getByteSize() and
|
||||
(
|
||||
fromtyp.isSigned() and
|
||||
totyp.isSigned()
|
||||
or
|
||||
fromtyp.isUnsigned() and
|
||||
totyp.isUnsigned()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which casts from one pointer type to another.
|
||||
*/
|
||||
class PtrToPtrCastInstruction extends ConvertInstruction {
|
||||
PtrToPtrCastInstruction() {
|
||||
getResultIRType() instanceof IRAddressType and
|
||||
getUnary().getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which casts from one integer type to another in a way
|
||||
* that cannot overflow or underflow.
|
||||
*/
|
||||
class SafeIntCastInstruction extends ConvertInstruction {
|
||||
SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which does not invalidate bounds determined by
|
||||
* range analysis.
|
||||
*/
|
||||
class SafeCastInstruction extends ConvertInstruction {
|
||||
SafeCastInstruction() {
|
||||
this instanceof PtrToPtrCastInstruction or
|
||||
this instanceof SafeIntCastInstruction
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
* Provides sign analysis to determine whether expression are always positive
|
||||
* or negative.
|
||||
*
|
||||
* The analysis is implemented as an abstract interpretation over the
|
||||
* three-valued domain `{negative, zero, positive}`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import SignAnalysisCached
|
||||
|
||||
private newtype TSign =
|
||||
TNeg() or
|
||||
TZero() or
|
||||
TPos()
|
||||
|
||||
private class Sign extends TSign {
|
||||
string toString() {
|
||||
result = "-" and this = TNeg()
|
||||
or
|
||||
result = "0" and this = TZero()
|
||||
or
|
||||
result = "+" and this = TPos()
|
||||
}
|
||||
|
||||
Sign inc() {
|
||||
this = TNeg() and result = TNeg()
|
||||
or
|
||||
this = TNeg() and result = TZero()
|
||||
or
|
||||
this = TZero() and result = TPos()
|
||||
or
|
||||
this = TPos() and result = TPos()
|
||||
}
|
||||
|
||||
Sign dec() { result.inc() = this }
|
||||
|
||||
Sign neg() {
|
||||
this = TNeg() and result = TPos()
|
||||
or
|
||||
this = TZero() and result = TZero()
|
||||
or
|
||||
this = TPos() and result = TNeg()
|
||||
}
|
||||
|
||||
Sign bitnot() {
|
||||
this = TNeg() and result = TPos()
|
||||
or
|
||||
this = TNeg() and result = TZero()
|
||||
or
|
||||
this = TZero() and result = TNeg()
|
||||
or
|
||||
this = TPos() and result = TNeg()
|
||||
}
|
||||
|
||||
Sign add(Sign s) {
|
||||
this = TZero() and result = s
|
||||
or
|
||||
s = TZero() and result = this
|
||||
or
|
||||
this = s and this = result
|
||||
or
|
||||
this = TPos() and s = TNeg()
|
||||
or
|
||||
this = TNeg() and s = TPos()
|
||||
}
|
||||
|
||||
Sign mul(Sign s) {
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = TZero() and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign div(Sign s) {
|
||||
result = TZero() and s = TNeg()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign rem(Sign s) {
|
||||
result = TZero() and s = TNeg()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = this and s = TNeg()
|
||||
or
|
||||
result = this and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitand(Sign s) {
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = TZero() and s = TZero()
|
||||
or
|
||||
result = TZero() and this = TPos()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitor(Sign s) {
|
||||
result = TZero() and this = TZero() and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TNeg()
|
||||
or
|
||||
result = TNeg() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TZero()
|
||||
or
|
||||
result = TPos() and this = TZero() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitxor(Sign s) {
|
||||
result = TZero() and this = s
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result = s and this = TZero()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign lshift(Sign s) {
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
this != TZero() and s != TZero()
|
||||
}
|
||||
|
||||
Sign rshift(Sign s) {
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TNeg()
|
||||
or
|
||||
result != TNeg() and this = TPos() and s != TZero()
|
||||
}
|
||||
|
||||
Sign urshift(Sign s) {
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result != TZero() and this = TNeg() and s != TZero()
|
||||
or
|
||||
result != TNeg() and this = TPos() and s != TZero()
|
||||
}
|
||||
}
|
||||
|
||||
private Sign certainInstructionSign(Instruction inst) {
|
||||
exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i |
|
||||
i < 0 and result = TNeg()
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype CastKind =
|
||||
TWiden() or
|
||||
TSame() or
|
||||
TNarrow()
|
||||
|
||||
private CastKind getCastKind(ConvertInstruction ci) {
|
||||
exists(int fromSize, int toSize |
|
||||
toSize = ci.getResultSize() and
|
||||
fromSize = ci.getUnary().getResultSize()
|
||||
|
|
||||
fromSize < toSize and
|
||||
result = TWiden()
|
||||
or
|
||||
fromSize = toSize and
|
||||
result = TSame()
|
||||
or
|
||||
fromSize > toSize and
|
||||
result = TNarrow()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bindBool(boolean bool) {
|
||||
bool = true or
|
||||
bool = false
|
||||
}
|
||||
|
||||
private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) {
|
||||
result = TZero() and
|
||||
(
|
||||
bindBool(fromSigned) and
|
||||
bindBool(toSigned) and
|
||||
s = TZero()
|
||||
or
|
||||
bindBool(fromSigned) and
|
||||
bindBool(toSigned) and
|
||||
ck = TNarrow()
|
||||
)
|
||||
or
|
||||
result = TPos() and
|
||||
(
|
||||
bindBool(fromSigned) and
|
||||
bindBool(toSigned) and
|
||||
s = TPos()
|
||||
or
|
||||
bindBool(fromSigned) and
|
||||
bindBool(toSigned) and
|
||||
s = TNeg() and
|
||||
ck = TNarrow()
|
||||
or
|
||||
fromSigned = true and
|
||||
toSigned = false and
|
||||
s = TNeg()
|
||||
)
|
||||
or
|
||||
result = TNeg() and
|
||||
(
|
||||
fromSigned = true and
|
||||
toSigned = true and
|
||||
s = TNeg()
|
||||
or
|
||||
fromSigned = false and
|
||||
toSigned = true and
|
||||
s = TPos() and
|
||||
ck != TWiden()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the sign of `e` is too complicated to determine. */
|
||||
private predicate unknownSign(Instruction i) {
|
||||
// REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than
|
||||
// the ones we don't understand. Currently, if we try to compute the sign of an instruction that
|
||||
// we don't understand, and it isn't on this list, we incorrectly compute the sign as "none"
|
||||
// instead of "+,0,-".
|
||||
// Even better, we could track the state of each instruction as a power set of {non-negative,
|
||||
// non-positive, non-zero}, which would mean that the representation of the sign of an unknown
|
||||
// value would be the empty set.
|
||||
(
|
||||
i instanceof UninitializedInstruction
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
or
|
||||
i instanceof BuiltInOperationInstruction
|
||||
or
|
||||
i instanceof CallInstruction
|
||||
or
|
||||
i instanceof ChiInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `bounded`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict
|
||||
) {
|
||||
exists(int adjustment, Operand compared |
|
||||
valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
|
||||
(
|
||||
isStrict = true and
|
||||
adjustment = 0
|
||||
or
|
||||
isStrict = false and
|
||||
adjustment = 1
|
||||
) and
|
||||
comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict
|
||||
) {
|
||||
exists(int adjustment, Operand compared |
|
||||
valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
|
||||
(
|
||||
isStrict = true and
|
||||
adjustment = 0
|
||||
or
|
||||
isStrict = false and
|
||||
adjustment = 1
|
||||
) and
|
||||
comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `bounded = eqbound`
|
||||
* - `isEq = false` : `bounded != eqbound`
|
||||
*/
|
||||
private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) {
|
||||
exists(Operand compared |
|
||||
valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
|
||||
guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
upperBound(comp, bound, op, _) or
|
||||
eqBound(comp, bound, op, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
lowerBound(comp, bound, op, _) or
|
||||
eqBound(comp, bound, op, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
lowerBound(comp, bound, op, _) or
|
||||
upperBound(comp, bound, op, _) or
|
||||
eqBound(comp, bound, op, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
posBound(comp, bound, op) and TPos() = operandSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
negBound(comp, bound, op) and TNeg() = operandSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
|
||||
lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound)
|
||||
or
|
||||
lowerBound(comp, bound, op, false) and TZero() = operandSign(bound)
|
||||
or
|
||||
upperBound(comp, bound, op, _) and TPos() = operandSign(bound)
|
||||
or
|
||||
upperBound(comp, bound, op, false) and TZero() = operandSign(bound)
|
||||
or
|
||||
eqBound(comp, bound, op, true) and TZero() = operandSign(bound)
|
||||
or
|
||||
eqBound(comp, bound, op, false) and TZero() != operandSign(bound)
|
||||
}
|
||||
|
||||
private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) }
|
||||
|
||||
private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) {
|
||||
lhs = binaryOpLhsSign(i) and
|
||||
rhs = binaryOpRhsSign(i)
|
||||
}
|
||||
|
||||
private Sign unguardedOperandSign(Operand operand) {
|
||||
result = instructionSign(operand.getDef()) and
|
||||
not hasGuard(operand, result)
|
||||
}
|
||||
|
||||
private Sign guardedOperandSign(Operand operand) {
|
||||
result = instructionSign(operand.getDef()) and
|
||||
hasGuard(operand, result)
|
||||
}
|
||||
|
||||
private Sign guardedOperandSignOk(Operand operand) {
|
||||
result = TPos() and
|
||||
forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) |
|
||||
posBoundOk(guard, bound, operand)
|
||||
)
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) |
|
||||
negBoundOk(guard, bound, operand)
|
||||
)
|
||||
or
|
||||
result = TZero() and
|
||||
forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) |
|
||||
zeroBoundOk(guard, bound, operand)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(Operand op, Sign s) {
|
||||
s = TPos() and posBound(_, _, op)
|
||||
or
|
||||
s = TNeg() and negBound(_, _, op)
|
||||
or
|
||||
s = TZero() and zeroBound(_, _, op)
|
||||
}
|
||||
|
||||
cached
|
||||
module SignAnalysisCached {
|
||||
/**
|
||||
* Gets a sign that `operand` may have at `pos`, taking guards into account.
|
||||
*/
|
||||
cached
|
||||
Sign operandSign(Operand operand) {
|
||||
result = unguardedOperandSign(operand)
|
||||
or
|
||||
result = guardedOperandSign(operand) and
|
||||
result = guardedOperandSignOk(operand)
|
||||
or
|
||||
// `result` is unconstrained if the definition is inexact. Then any sign is possible.
|
||||
operand.isDefinitionInexact()
|
||||
}
|
||||
|
||||
cached
|
||||
Sign instructionSign(Instruction i) {
|
||||
result = certainInstructionSign(i)
|
||||
or
|
||||
not exists(certainInstructionSign(i)) and
|
||||
not (
|
||||
result = TNeg() and
|
||||
i.getResultIRType().(IRIntegerType).isUnsigned()
|
||||
) and
|
||||
(
|
||||
unknownSign(i)
|
||||
or
|
||||
exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned |
|
||||
i = ci and
|
||||
prior = ci.getUnary() and
|
||||
(
|
||||
if ci.getResultIRType().(IRIntegerType).isSigned()
|
||||
then toSigned = true
|
||||
else toSigned = false
|
||||
) and
|
||||
(
|
||||
if prior.getResultIRType().(IRIntegerType).isSigned()
|
||||
then fromSigned = true
|
||||
else fromSigned = false
|
||||
) and
|
||||
result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci))
|
||||
)
|
||||
or
|
||||
result = operandSign(i.(CopyInstruction).getSourceValueOperand())
|
||||
or
|
||||
result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot()
|
||||
or
|
||||
result = operandSign(i.(NegateInstruction).getAnOperand()).neg()
|
||||
or
|
||||
exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) |
|
||||
i instanceof AddInstruction and result = s1.add(s2)
|
||||
or
|
||||
i instanceof SubInstruction and result = s1.add(s2.neg())
|
||||
or
|
||||
i instanceof MulInstruction and result = s1.mul(s2)
|
||||
or
|
||||
i instanceof DivInstruction and result = s1.div(s2)
|
||||
or
|
||||
i instanceof RemInstruction and result = s1.rem(s2)
|
||||
or
|
||||
i instanceof BitAndInstruction and result = s1.bitand(s2)
|
||||
or
|
||||
i instanceof BitOrInstruction and result = s1.bitor(s2)
|
||||
or
|
||||
i instanceof BitXorInstruction and result = s1.bitxor(s2)
|
||||
or
|
||||
i instanceof ShiftLeftInstruction and result = s1.lshift(s2)
|
||||
or
|
||||
i instanceof ShiftRightInstruction and
|
||||
i.getResultIRType().(IRIntegerType).isSigned() and
|
||||
result = s1.rshift(s2)
|
||||
or
|
||||
i instanceof ShiftRightInstruction and
|
||||
not i.getResultIRType().(IRIntegerType).isSigned() and
|
||||
result = s1.urshift(s2)
|
||||
)
|
||||
or
|
||||
// use hasGuard here?
|
||||
result = operandSign(i.(PhiInstruction).getAnOperand())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `i` can be positive and cannot be negative. */
|
||||
predicate positiveInstruction(Instruction i) {
|
||||
instructionSign(i) = TPos() and
|
||||
not instructionSign(i) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `i` at `pos` can be positive at and cannot be negative. */
|
||||
predicate positive(Operand op) {
|
||||
operandSign(op) = TPos() and
|
||||
not operandSign(op) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `i` can be negative and cannot be positive. */
|
||||
predicate negativeInstruction(Instruction i) {
|
||||
instructionSign(i) = TNeg() and
|
||||
not instructionSign(i) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `i` at `pos` can be negative and cannot be positive. */
|
||||
predicate negative(Operand op) {
|
||||
operandSign(op) = TNeg() and
|
||||
not operandSign(op) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `i` is strictly positive. */
|
||||
predicate strictlyPositiveInstruction(Instruction i) {
|
||||
instructionSign(i) = TPos() and
|
||||
not instructionSign(i) = TNeg() and
|
||||
not instructionSign(i) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `i` is strictly positive at `pos`. */
|
||||
predicate strictlyPositive(Operand op) {
|
||||
operandSign(op) = TPos() and
|
||||
not operandSign(op) = TNeg() and
|
||||
not operandSign(op) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `i` is strictly negative. */
|
||||
predicate strictlyNegativeInstruction(Instruction i) {
|
||||
instructionSign(i) = TNeg() and
|
||||
not instructionSign(i) = TPos() and
|
||||
not instructionSign(i) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `i` is strictly negative at `pos`. */
|
||||
predicate strictlyNegative(Operand op) {
|
||||
operandSign(op) = TNeg() and
|
||||
not operandSign(op) = TPos() and
|
||||
not operandSign(op) = TZero()
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
private import cpp
|
||||
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Holds if `e` is a constant or if it is a variable with a constant value
|
||||
*/
|
||||
float evaluateConstantExpr(Expr e) {
|
||||
result = e.getValue().toFloat()
|
||||
or
|
||||
exists(SsaDefinition defn, StackVariable sv |
|
||||
defn.getAUse(sv) = e and
|
||||
result = defn.getDefiningValue(sv).getValue().toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The current implementation for `BitwiseAndExpr` only handles cases where both operands are
|
||||
* either unsigned or non-negative constants. This class not only covers these cases, but also
|
||||
* adds support for `&` expressions between a signed integer with a non-negative range and a
|
||||
* non-negative constant. It also adds support for `&=` for the same set of cases as `&`.
|
||||
*/
|
||||
private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
|
||||
ConstantBitwiseAndExprRange() {
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(BitwiseAndExpr).getLeftOperand() and
|
||||
r = this.(BitwiseAndExpr).getRightOperand()
|
||||
or
|
||||
l = this.(AssignAndExpr).getLValue() and
|
||||
r = this.(AssignAndExpr).getRValue()
|
||||
|
|
||||
// No operands can be negative constants
|
||||
not (evaluateConstantExpr(l) < 0 or evaluateConstantExpr(r) < 0) and
|
||||
// At least one operand must be a non-negative constant
|
||||
(evaluateConstantExpr(l) >= 0 or evaluateConstantExpr(r) >= 0)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLeftOperand() {
|
||||
result = this.(BitwiseAndExpr).getLeftOperand() or
|
||||
result = this.(AssignAndExpr).getLValue()
|
||||
}
|
||||
|
||||
Expr getRightOperand() {
|
||||
result = this.(BitwiseAndExpr).getRightOperand() or
|
||||
result = this.(AssignAndExpr).getRValue()
|
||||
}
|
||||
|
||||
override float getLowerBounds() {
|
||||
// If an operand can have negative values, the lower bound is unconstrained.
|
||||
// Otherwise, the lower bound is zero.
|
||||
exists(float lLower, float rLower |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
(
|
||||
(lLower < 0 or rLower < 0) and
|
||||
result = exprMinVal(this)
|
||||
or
|
||||
// This technically results in two lowerBounds when an operand range is negative, but
|
||||
// that's fine since `exprMinVal(x) <= 0`. We can't use an if statement here without
|
||||
// non-monotonic recursion issues
|
||||
result = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override float getUpperBounds() {
|
||||
// If an operand can have negative values, the upper bound is unconstrained.
|
||||
// Otherwise, the upper bound is the minimum of the upper bounds of the operands
|
||||
exists(float lLower, float lUpper, float rLower, float rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
(
|
||||
(lLower < 0 or rLower < 0) and
|
||||
result = exprMaxVal(this)
|
||||
or
|
||||
// This technically results in two upperBounds when an operand range is negative, but
|
||||
// that's fine since `exprMaxVal(b) >= result`. We can't use an if statement here without
|
||||
// non-monotonic recursion issues
|
||||
result = rUpper.minimum(lUpper)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
|
||||
private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
|
||||
SelfSub() {
|
||||
// Match `x - x` but not `myInt - (unsigned char)myInt`.
|
||||
getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
|
||||
getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
|
||||
}
|
||||
|
||||
override float getLowerBounds() { result = 0 }
|
||||
|
||||
override float getUpperBounds() { result = 0 }
|
||||
|
||||
override predicate dependsOnChild(Expr child) { none() }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import experimental.semmle.code.cpp.security.PrivateData
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
|
||||
module PrivateCleartextWrite {
|
||||
/**
|
||||
* A data flow source for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Source extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow sink for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A sanitizer for private information flowing unencrypted to an external location.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
|
||||
class ProtectSanitizer extends Sanitizer {
|
||||
ProtectSanitizer() {
|
||||
exists(Function m, string s |
|
||||
this.getExpr().(FunctionCall).getTarget() = m and
|
||||
m.getName().regexpMatch("(?i).*" + s + ".*")
|
||||
|
|
||||
s = "protect" or s = "encode" or s = "encrypt"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WriteConfig extends TaintTracking::Configuration {
|
||||
WriteConfig() { this = "Write configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
}
|
||||
|
||||
class WriteSink extends Sink {
|
||||
WriteSink() {
|
||||
exists(FileWrite f, BufferWrite b |
|
||||
this.asExpr() = f.getASource()
|
||||
or
|
||||
this.asExpr() = b.getAChild()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying private data and functions for security.
|
||||
*
|
||||
* 'Private' data in general is anything that would compromise user privacy if exposed. This
|
||||
* library tries to guess where private data may either be stored in a variable or produced by a
|
||||
* function.
|
||||
*
|
||||
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
|
||||
* to credentials.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** A string for `match` that identifies strings that look like they represent private data. */
|
||||
private string privateNames() {
|
||||
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
|
||||
// Government identifiers, such as Social Security Numbers
|
||||
result = "%social%security%number%" or
|
||||
// Contact information, such as home addresses and telephone numbers
|
||||
result = "%postcode%" or
|
||||
result = "%zipcode%" or
|
||||
// result = "%telephone%" or
|
||||
// Geographic location - where the user is (or was)
|
||||
result = "%latitude%" or
|
||||
result = "%longitude%" or
|
||||
// Financial data - such as credit card numbers, salary, bank accounts, and debts
|
||||
result = "%creditcard%" or
|
||||
result = "%salary%" or
|
||||
result = "%bankaccount%" or
|
||||
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
|
||||
// result = "%email%" or
|
||||
// result = "%mobile%" or
|
||||
result = "%employer%" or
|
||||
// Health - medical conditions, insurance status, prescription records
|
||||
result = "%medical%"
|
||||
}
|
||||
|
||||
/** An expression that might contain private data. */
|
||||
abstract class PrivateDataExpr extends Expr { }
|
||||
|
||||
/** A functiond call that might produce private data. */
|
||||
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
|
||||
PrivateFunctionCall() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to a variable that might contain private data. */
|
||||
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
|
||||
PrivateVariableAccess() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
60
repo-tests/codeql/cpp/ql/lib/external/ExternalArtifact.qll
vendored
Normal file
60
repo-tests/codeql/cpp/ql/lib/external/ExternalArtifact.qll
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Provides classes for working with external data.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An external data item.
|
||||
*/
|
||||
class ExternalData extends @externalDataElement {
|
||||
/** Gets the path of the file this data was loaded from. */
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the path of the file this data was loaded from, with its
|
||||
* extension replaced by `.ql`.
|
||||
*/
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
|
||||
/** Gets the number of fields in this data item. */
|
||||
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
|
||||
|
||||
/** Gets the value of the `i`th field of this data item. */
|
||||
string getField(int i) { externalData(this, _, i, result) }
|
||||
|
||||
/** Gets the integer value of the `i`th field of this data item. */
|
||||
int getFieldAsInt(int i) { result = getField(i).toInt() }
|
||||
|
||||
/** Gets the floating-point value of the `i`th field of this data item. */
|
||||
float getFieldAsFloat(int i) { result = getField(i).toFloat() }
|
||||
|
||||
/** Gets the value of the `i`th field of this data item, interpreted as a date. */
|
||||
date getFieldAsDate(int i) { result = getField(i).toDate() }
|
||||
|
||||
/** Gets a textual representation of this data item. */
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
/** Gets a textual representation of this data item, starting with the `n`th field. */
|
||||
private string buildTupleString(int n) {
|
||||
n = getNumFields() - 1 and result = getField(n)
|
||||
or
|
||||
n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* External data with a location, and a message, as produced by tools that used to produce QLDs.
|
||||
*/
|
||||
class DefectExternalData extends ExternalData {
|
||||
DefectExternalData() {
|
||||
this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and
|
||||
this.getNumFields() = 2
|
||||
}
|
||||
|
||||
/** Gets the URL associated with this data item. */
|
||||
string getURL() { result = getField(0) }
|
||||
|
||||
/** Gets the message associated with this data item. */
|
||||
string getMessage() { result = getField(1) }
|
||||
}
|
||||
7
repo-tests/codeql/cpp/ql/lib/qlpack.yml
Normal file
7
repo-tests/codeql/cpp/ql/lib/qlpack.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.0.2
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/cpp-upgrades: 0.0.2
|
||||
@@ -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
|
||||
@@ -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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Class.qll
Normal file
1171
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Class.qll
Normal file
File diff suppressed because it is too large
Load Diff
54
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Comments.qll
Normal file
54
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Compilation.qll
Normal file
115
repo-tests/codeql/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, _, _) }
|
||||
}
|
||||
720
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal file
720
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal file
@@ -0,0 +1,720 @@
|
||||
/**
|
||||
* 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) {
|
||||
exists(getTemplateArgumentValue(index)) and
|
||||
result = getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/** 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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Diagnostics.qll
Normal file
68
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Element.qll
Normal file
297
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enclosing.qll
Normal file
63
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enum.qll
Normal file
204
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Field.qll
Normal file
126
repo-tests/codeql/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()
|
||||
}
|
||||
}
|
||||
448
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/File.qll
Normal file
448
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/File.qll
Normal file
@@ -0,0 +1,448 @@
|
||||
/**
|
||||
* 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://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#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() { result = this.getBaseName() }
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length() - 1]) and
|
||||
result = name.suffix(firstDotPos + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length()]) and
|
||||
result = name.prefix(firstDotPos)
|
||||
)
|
||||
or
|
||||
this.getAbsolutePath() = "" and
|
||||
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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/FriendDecl.qll
Normal file
69
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Function.qll
Normal file
876
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Include.qll
Normal file
69
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Initializer.qll
Normal file
54
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Iteration.qll
Normal file
60
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Linkage.qll
Normal file
51
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Location.qll
Normal file
173
repo-tests/codeql/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://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Macro.qll
Normal file
351
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Member.qll
Normal file
6
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll
Normal file
517
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll
Normal file
167
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Namespace.qll
Normal file
249
repo-tests/codeql/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 }
|
||||
}
|
||||
@@ -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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll
Normal file
196
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PODType03.qll
Normal file
125
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Parameter.qll
Normal file
176
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll
Normal file
294
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Print.qll
Normal file
413
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.ql
Normal file
21
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.qll
Normal file
884
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Specifier.qll
Normal file
318
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Struct.qll
Normal file
75
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/TestFile.qll
Normal file
89
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
1745
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
File diff suppressed because it is too large
Load Diff
130
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/TypedefType.qll
Normal file
130
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Union.qll
Normal file
70
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/UserType.qll
Normal file
132
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Variable.qll
Normal file
608
repo-tests/codeql/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
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/XML.qll
Executable file
343
repo-tests/codeql/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://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
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() }
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
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.
|
||||
*/
|
||||
predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
c = v.getDeclaringType() and
|
||||
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
@@ -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" }
|
||||
}
|
||||
@@ -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" }
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -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()) }
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
private import semmle.code.cpp.models.implementations.Strcat
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
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 calls...
|
||||
exists(Call c, Function f, int i |
|
||||
e = c and
|
||||
f = c.getTarget() and
|
||||
not functionArgumentMustBeNullTerminated(f, i) and
|
||||
c.getAnArgumentSubExpr(i) = va
|
||||
|
|
||||
// library function
|
||||
not f.hasEntryPoint()
|
||||
or
|
||||
// function where the relevant parameter is potentially added a null terminator
|
||||
mayAddNullTerminator(_, f.getParameter(i).getAnAccess())
|
||||
or
|
||||
// varargs function
|
||||
f.isVarargs() and i >= f.getNumberOfParameters()
|
||||
or
|
||||
// function containing assembler code
|
||||
exists(AsmStmt s | s.getEnclosingFunction() = f)
|
||||
or
|
||||
// function where the relevant parameter is returned (leaking it to be potentially null terminated elsewhere)
|
||||
DataFlow::localFlow(DataFlow::parameterNode(f.getParameter(i)),
|
||||
DataFlow::exprNode(any(ReturnStmt rs).getExpr()))
|
||||
)
|
||||
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
|
||||
// String argument to a formatting function (such as `printf`)
|
||||
exists(int n, FormatLiteral fl |
|
||||
fc.(FormattingFunctionCall).getConversionArgument(n) = va and
|
||||
fl = fc.(FormattingFunctionCall).getFormat() and
|
||||
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
|
||||
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
|
||||
not fl.hasPrecision(n) // exclude: `%.*s`
|
||||
)
|
||||
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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
1225
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
Normal file
1225
repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user