Files
codeql/cpp/ql/lib/semmle/code/cpp/Field.qll
2025-01-29 18:19:17 +00:00

172 lines
5.2 KiB
Plaintext

/**
* Provides classes representing C structure members and C++ non-static member variables.
*/
import semmle.code.cpp.Variable
import semmle.code.cpp.Enum
private predicate hasAFieldWithOffset(Class c, Field f, int offset) {
// Base case: `f` is a field in `c`.
f = c.getAField() and
offset = f.getByteOffset() and
not f.getUnspecifiedType().(Class).hasDefinition()
or
// Otherwise, we find the struct that is a field of `c` which then has
// the field `f` as a member.
exists(Field g |
g = c.getAField() and
// Find the field with the largest offset that's less than or equal to
// offset. That's the struct we need to search recursively.
g =
max(Field cand, int candOffset |
cand = c.getAField() and
candOffset = cand.getByteOffset() and
offset >= candOffset
|
cand order by candOffset
) and
hasAFieldWithOffset(g.getUnspecifiedType(), f, offset - g.getByteOffset())
)
}
/**
* 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 occurrence 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(this.getDeclaringType()) + this.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())
)
}
/**
* Gets the offset (in bytes) of this field starting at `c`.
*
* For example, consider:
* ```cpp
* struct S1 {
* int a;
* void* b;
* };
*
* struct S2 {
* S1 s1;
* char c;
* };
* ```
* If `f` represents the field `s1` and `c` represents the class `S2` then
* `f.getOffsetInClass(S2) = 0` holds. Likewise, if `f` represents the
* field `a`, then `f.getOffsetInClass(c) = 0` holds.
*/
int getOffsetInClass(Class c) { hasAFieldWithOffset(c, this, result) }
}
/**
* 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() { this.hasName("(unnamed bitfield)") }
override predicate isInitializable() {
// Anonymous bitfields are not initializable.
not this.isAnonymous()
}
}