Add codeql sources (a2371370ff)

This commit is contained in:
Esben Sparre Andreasen
2021-10-13 21:53:51 +02:00
parent 9826e7df1d
commit 53e17e7835
4211 changed files with 624966 additions and 0 deletions

1
repo-tests/codeql.txt Normal file
View File

@@ -0,0 +1 @@
a2371370ff8260e789342e0ac759bc67ed401702

View File

@@ -0,0 +1,4 @@
name: codeql/cpp-examples
version: 0.0.2
dependencies:
codeql/cpp-all: "*"

View 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

View 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

View 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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View 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

View 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

View 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

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

View 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

View 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

View File

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

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

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
//
// Import each extension we want to enable
import extensions.SubtractSelf
import extensions.ConstantBitwiseAndExprRange

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,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")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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() }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,173 @@
/**
* Provides classes and predicates for locations in the source code.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.File
/**
* A location of a C/C++ artifact.
*/
class Location extends @location {
/** Gets the container corresponding to this location. */
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
/** Gets the file corresponding to this location, if any. */
File getFile() { result = this.getContainer() }
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { this.fullLocationInfo(_, _, _, result, _) }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) }
/**
* Gets a textual representation of this element.
*
* The format is "file://filePath:startLine:startColumn:endLine:endColumn".
*/
string toString() {
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
toUrl(filepath, startline, startcolumn, endline, endcolumn, result)
)
}
/**
* Holds if this element is in the specified container.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline`.
*
* This predicate is similar to `hasLocationInfo`, but exposes the `Container`
* entity, rather than merely its path.
*/
predicate fullLocationInfo(
Container container, int startline, int startcolumn, int endline, int endcolumn
) {
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_stmt(this, unresolveElement(container), startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://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) }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,343 @@
/**
* Provides classes and predicates for working with XML files and their content.
*/
import semmle.files.FileSystem
private class TXMLLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
/** An XML element that has a location. */
class XMLLocatable extends @xmllocatable, TXMLLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Gets the source location for this element.
*/
deprecated Location getALocation() { result = this.getLocation() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://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() }
}

View File

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

View File

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

View File

@@ -0,0 +1,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()
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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
)
)
)
}

View File

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

File diff suppressed because it is too large Load Diff

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