mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
337 lines
11 KiB
Plaintext
337 lines
11 KiB
Plaintext
/**
|
|
* 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() {
|
|
exists(this) and
|
|
result instanceof UnknownLocation
|
|
}
|
|
|
|
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", "virtual", "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", "register", "static", "extern", "mutable"]) }
|
|
|
|
override string getAPrimaryQlClass() { result = "StorageClassSpecifier" }
|
|
}
|
|
|
|
/**
|
|
* A C++ access specifier: `public`, `protected`, or `private`.
|
|
*/
|
|
class AccessSpecifier extends Specifier {
|
|
AccessSpecifier() { this.hasName(["public", "protected", "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" }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ calling convention specifier: `cdecl`, `fastcall`, `stdcall`, `thiscall`,
|
|
* `vectorcall`, or `clrcall`.
|
|
*/
|
|
class CallingConventionSpecifier extends Specifier {
|
|
CallingConventionSpecifier() {
|
|
this.hasName(["cdecl", "fastcall", "stdcall", "thiscall", "vectorcall", "clrcall"])
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "CallingConventionSpecifier" }
|
|
}
|
|
|
|
/**
|
|
* 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 = this.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 = this.getNamespace() and this.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 = this.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() { this.getName() = "format" }
|
|
|
|
/**
|
|
* Gets the archetype of this format attribute, for example
|
|
* `"printf"`.
|
|
*/
|
|
string getArchetype() { result = this.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 = this.getArgument(1).getValueInt() - this.firstArgumentNumber() }
|
|
|
|
/**
|
|
* Gets the (0-based) index of the first format argument (if any),
|
|
* according to this attribute.
|
|
*/
|
|
int getFirstFormatArgIndex() {
|
|
exists(int val |
|
|
val = this.getArgument(2).getValueInt() and
|
|
result = val - this.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 constant or a token.
|
|
*/
|
|
string getValueText() {
|
|
if underlyingElement(this) instanceof @attribute_arg_constant_expr
|
|
then result = this.getValueConstant().getValue()
|
|
else attribute_arg_value(underlyingElement(this), result)
|
|
}
|
|
|
|
/**
|
|
* Gets the value of this argument, if its value is integral.
|
|
*/
|
|
int getValueInt() { result = this.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 value of this argument, if its value is a constant.
|
|
*/
|
|
Expr getValueConstant() {
|
|
attribute_arg_constant(underlyingElement(this), unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* Gets the value of this argument, if its value is an expression.
|
|
*/
|
|
Expr getValueExpr() { attribute_arg_expr(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 underlyingElement(this) instanceof @attribute_arg_empty
|
|
then result = "empty argument"
|
|
else
|
|
exists(string prefix, string tail |
|
|
(if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and
|
|
(
|
|
if underlyingElement(this) instanceof @attribute_arg_type
|
|
then tail = this.getValueType().getName()
|
|
else
|
|
if underlyingElement(this) instanceof @attribute_arg_constant_expr
|
|
then tail = this.getValueConstant().toString()
|
|
else
|
|
if underlyingElement(this) instanceof @attribute_arg_expr
|
|
then tail = this.getValueExpr().toString()
|
|
else tail = this.getValueText()
|
|
) and
|
|
result = prefix + tail
|
|
)
|
|
}
|
|
}
|