diff --git a/c/cert/src/codeql-pack.lock.yml b/c/cert/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/cert/src/codeql-pack.lock.yml +++ b/c/cert/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql index 1cbdcc4e12..a209afca6d 100644 --- a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql +++ b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql @@ -17,6 +17,7 @@ import cpp import codingstandards.c.cert +import codingstandards.cpp.types.Resolve class LiteralZero extends Literal { LiteralZero() { this.getValue() = "0" } @@ -37,21 +38,30 @@ class StdIntIntPtrType extends Type { } } +class ResolvesToStdIntIntPtrType = ResolvesTo::IgnoringSpecifiers; + +class ResolvesToVoidPointerType = ResolvesTo::IgnoringSpecifiers; + /** * Casting a pointer value to integer, excluding literal 0. * Includes implicit conversions made during declarations or assignments. */ predicate conversionBetweenPointerAndInteger(Cast cast, string message) { /* Ensure that `int` has different size than that of pointers */ - exists(IntType intType, PointerType ptrType | intType.getSize() < ptrType.getSize() | - cast.getExpr().getUnderlyingType() = intType and - cast.getUnderlyingType() = ptrType and + exists( + ResolvesTo::IgnoringSpecifiers intType, + ResolvesTo::IgnoringSpecifiers ptrType + | + intType.getSize() < ptrType.getSize() + | + cast.getExpr().getType() = intType and + cast.getType() = ptrType and if cast.isCompilerGenerated() then message = "Integer expression " + cast.getExpr() + " is implicitly cast to a pointer type." else message = "Integer expression " + cast.getExpr() + " is cast to a pointer type." or - cast.getExpr().getUnderlyingType() = ptrType and - cast.getUnderlyingType() = intType and + cast.getExpr().getType() = ptrType and + cast.getType() = intType and if cast.isCompilerGenerated() then message = "Pointer expression " + cast.getExpr() + " is implicitly cast to an integer type." @@ -61,11 +71,11 @@ predicate conversionBetweenPointerAndInteger(Cast cast, string message) { not cast.getExpr() instanceof LiteralZero and /* Compliant exception 2: variable's declared type is (u)intptr_t */ not ( - cast.getType() instanceof StdIntIntPtrType and - cast.getExpr().getType() instanceof VoidPointerType + cast.getType() instanceof ResolvesToStdIntIntPtrType and + cast.getExpr().getType() instanceof ResolvesToVoidPointerType or - cast.getType() instanceof VoidPointerType and - cast.getExpr().getType() instanceof StdIntIntPtrType + cast.getType() instanceof ResolvesToVoidPointerType and + cast.getExpr().getType() instanceof ResolvesToStdIntIntPtrType ) } diff --git a/c/cert/test/codeql-pack.lock.yml b/c/cert/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/cert/test/codeql-pack.lock.yml +++ b/c/cert/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/common/src/codeql-pack.lock.yml b/c/common/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/common/src/codeql-pack.lock.yml +++ b/c/common/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/common/test/codeql-pack.lock.yml b/c/common/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/common/test/codeql-pack.lock.yml +++ b/c/common/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/misra/src/codeql-pack.lock.yml b/c/misra/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/misra/src/codeql-pack.lock.yml +++ b/c/misra/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql index d92b4ccea6..85bcba272a 100644 --- a/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql +++ b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql @@ -16,9 +16,11 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve -predicate isThreadingObject(Type t) { t instanceof PossiblySpecified::Type } +predicate isThreadingObject(Type t) { + t instanceof ResolvesTo::IgnoringSpecifiers +} predicate validUseOfStdThreadObject(Expr e) { e.getParent() instanceof AddressOfExpr diff --git a/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql index 066cf3c295..5afd9f4547 100644 --- a/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql +++ b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.c.misra import codingstandards.c.Objects import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve from ObjectIdentity obj, StorageDuration storageDuration, Type type where @@ -25,7 +25,7 @@ where storageDuration = obj.getStorageDuration() and not storageDuration.isStatic() and type = obj.getASubObjectType() and - type instanceof PossiblySpecified::Type + type instanceof ResolvesTo::IgnoringSpecifiers select obj, "Object of type '" + obj.getType().getName() + "' has invalid storage duration type '" + storageDuration.getStorageTypeName() + "'." diff --git a/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql index f78c25f981..08f4374b62 100644 --- a/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql +++ b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.c.misra import codingstandards.c.Objects import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve import codingstandards.c.initialization.GlobalInitializationAnalysis module MutexInitializationConfig implements GlobalInitializationAnalysisConfigSig { @@ -68,8 +68,8 @@ where ) and ( if - obj.getType() instanceof PossiblySpecified::Type or - obj.getType() instanceof PossiblySpecified::Type + obj.getType() instanceof ResolvesTo::IgnoringSpecifiers or + obj.getType() instanceof ResolvesTo::IgnoringSpecifiers then description = typeString else description = typeString + " in object" ) diff --git a/c/misra/test/codeql-pack.lock.yml b/c/misra/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/misra/test/codeql-pack.lock.yml +++ b/c/misra/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/change_notes/2025-12-03-type-resolution-tracking-changes.md b/change_notes/2025-12-03-type-resolution-tracking-changes.md new file mode 100644 index 0000000000..5e5e9e4f1d --- /dev/null +++ b/change_notes/2025-12-03-type-resolution-tracking-changes.md @@ -0,0 +1,6 @@ + - `INT36-C` - `ConvertingAPointerToIntegerOrIntegerToPointer.ql`: + - Integrated new type resolution modules to fully handle typedefs and ignore cv-qualifiers during type comparisons, such as in detecting int types, pointer types, (u)intptr_t types, and void pointer types. + - `RULE-22-12`, `RULE-22-13`, `RULE-22-14` - `NonstandardUseOfThreadingObject.ql`, `ThreadingObjectWithInvalidStorageDuration.ql`, `MutexNotInitializedBeforeUse.ql`: + - Integrated new type resolution modules to handle typedefs when identifying threading object types. + - `RULE-9-5-1` - `LegacyForStatementsShouldBeSimple.ql`: + - Refactor to integrate new type resolution, no change in functionality expected. \ No newline at end of file diff --git a/cpp/autosar/src/codeql-pack.lock.yml b/cpp/autosar/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/autosar/src/codeql-pack.lock.yml +++ b/cpp/autosar/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/autosar/test/codeql-pack.lock.yml b/cpp/autosar/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/autosar/test/codeql-pack.lock.yml +++ b/cpp/autosar/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/cert/src/codeql-pack.lock.yml b/cpp/cert/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/cert/src/codeql-pack.lock.yml +++ b/cpp/cert/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/cert/test/codeql-pack.lock.yml b/cpp/cert/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/cert/test/codeql-pack.lock.yml +++ b/cpp/cert/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/src/codeql-pack.lock.yml b/cpp/common/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/common/src/codeql-pack.lock.yml +++ b/cpp/common/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/src/codingstandards/cpp/types/Resolve.qll b/cpp/common/src/codingstandards/cpp/types/Resolve.qll new file mode 100644 index 0000000000..3fd2f7db78 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Resolve.qll @@ -0,0 +1,271 @@ +private import cpp +private import qtil.Qtil +private import codingstandards.cpp.types.Specifiers +private import codeql.util.Boolean + +Type typeResolvesToTypeStep(Type type) { + result = type.(Decltype).getBaseType() + or + result = type.(TypedefType).getBaseType() +} + +module PointerTo::Type PointeeType> { + /** + * A pointer type that points to a type that resolves to the module's `PointeeType` type parameter. + */ + class Type extends Qtil::Final::Type { + Type() { getBaseType() instanceof PointeeType } + } +} + +module ReferenceOf::Type ReferencedType> { + /** + * A reference type that refers to a type that resolves to the module's `ReferencedType` type + * parameter. + */ + class Type extends Qtil::Final::Type { + Type() { getBaseType() instanceof ReferencedType } + } +} + +/** + * A module for handling complex type resolution in c++, such as a decltype of a const typedef + * of a struct type, which resolves to a const struct type. + * + * Basic usage: + * - `ResolvesTo::Exactly` is the set of class types, and typedefs/decltypes that resolve + * to class types exactly (without specifiers). + * - `resolvedType.resolve()` gets the fully resolved class type for the above. + * - `ResolvesTo::Specified` is the set of types that resolve to a specified class type. + * - `ResolvesTo::Ref` is the set of types that resolve to a reference to a class type. + * - `ResolvesTo::CvConst` is the set of types that resolve to a const class type. + * - `ResolvesTo::IgnoringSpecifiers` is the set of types that resolve to a class type + * ignoring specifiers. + * - `ResolvesTo::ExactlyOrRef` is the set of types that resolve to a class type and + * unwraps references. + * + * These module classes are preferred to the member predicates on `Type` such as `resolveTypedefs`, + * `stripSpecifiers`, `getUnderlyingType()`, etc, for the following reasons: + * - Hopefully the API is clearer and easier to use correctly. + * - Unlike `resolveTypedefs`, these classes can find types that resolve to other typedefs (via + * `ResolvesType::Exactly` etc.), instead of always resolving all typedefs. + * - The member predicates on `Type` have cases with no result. For instance, if there's a const + * typedef `const T = F`, but `const F` is never explicitly written in the code, then there is no + * matching extracted `Type` for `resolveTypedefs` to return, and it will have no result. + */ +module ResolvesTo::Type ResolvedType> { + private import cpp as cpp + + final class CppType = cpp::Type; + + /** + * A type that resolves exactly to the module's `ResolvedType` type parameter. + * + * For example, `ResolvesTo::Type` is the set of all `FooType`s and types that resolve + * (through typedefs * and/or decltypes) to `FooType`s. This does _not_ include types that resolve + * to a const `FooType` (though `FooType` itself may be const). To perform type resolution and + * check or strip specifiers, see module classes `IgnoringSpecifiers`, `Specified`, `Const`, etc. + * + * ``` + * // Example `ResolvesTo::Exactly` types: + * FooType f; // matches (a FooType) + * decltype(f); // matches (a decltype of FooType) + * typedef FooType FT; // matches (a typedef of FooType) + * FT f2; // matches (a typedef of FooType) + * decltype(f2); // matches (a decltype of typedef of FooType) + * typedef FT FT2; // matches (a typedef of typedef of FooType) + * + * // Examples types that are not `ResolvesTo::Exactly` types: + * const FooType cf; // does not match (specified FooTypes) + * FooType& rf = f; // does not match (references to FooTypes) + * NotFooType nf; // does not match (non FooTypes) + * ``` + */ + class Exactly extends CppType { + ResolvedType resolvedType; + + Exactly() { resolvedType = typeResolvesToTypeStep*(this) } + + ResolvedType resolve() { result = resolvedType } + } + + /** + * A type that resolves to a const type that in turn resolves to the module's `ResolvedType` type. + * + * For example, `ResolvesTo::CvConst` is the set of all const `FooType`s and types that + * resolve (through typedefs and/or decltypes) to const `FooType`s, including cases involving + * `const` typedefs, etc. + * + * Volatile specifiers are ignored, since const volatile types are still const. + * + * For matching both const and non-const types that resolve to `FooType`, see + * `IgnoringSpecifiers`. + * + * ``` + * // Note that this does NOT match `FooType`s that are not const. + * FooType f; // does not match (non-const) + * decltype(f) df; // does not match (non-const) + * typedef TF = FooType; // does not match (non-const) + * + * // Example `ResolvesTo::Const` types: + * const FooType cf; // matches (a const FooType) + * const volatile FooType cf; // matches (a const FooType, volatile is allowed and ignored) + * decltype(cf); // matches (a decltype of a const FooType) + * const decltype(f); // matches (a const decltype of FooType) + * const decltype(cf); // matches (a const decltype of a const FooType) + * typedef const FooType CFT; // matches (a typedef of const FooType) + * const TF ctf; // matches (a const typedef of FooType) + * + * // Additional examples types that are not `ResolvesTo::Const` types: + * const FooType &f; // does not match (reference to const FooType) + * ``` + */ + class CvConst extends Specified { + CvConst() { getASpecifier() instanceof ConstSpecifier } + } + + /** + * A type that resolves to a cv-qualified (or otherwise specified) type that in turn resolves to + * the module's `ResolvedType` type parameter. + * + * For example, `ResolvesTo::Specified` is the set of all specified `FooType`s and types + * that resolve (through typedefs and/or decltypes) to specified `FooType`s, including cases + * involving `const` and `volatile` typedefs, etc. + * + * ``` + * // Note that this does NOT match `FooType`s that are not specified. + * FooType f; // does not match (not specified) + * decltype(f) df; // does not match (not specified) + * typedef TF = FooType; // does not match (not specified) + * + * // Example `ResolvesTo::Specified` types: + * const FooType cf; // matches (a const FooType) + * volatile FooType vf; // matches (a volatile FooType) + * const volatile FooType cvf; // matches (a const volatile FooType) + * decltype(cf); // matches (a decltype of a const FooType) + * volatile decltype(f); // matches (a volatile decltype of FooType) + * const decltype(vf); // matches (a const decltype of volatile FooType) + * const decltype(cf); // matches (a const decltype of const FooType) + * typedef const FooType CFT; // matches (a typedef of const FooType) + * const TF ctf; // matches (a const typedef of FooType) + * volatile TF ctf; // matches (a volatile typedef of FooType) + * + * // Additional examples types that are not `ResolvesTo::Specified` types: + * const FooType &f; // does not match (reference to const FooType) + * ``` + */ + class Specified extends CppType { + ResolvedType resolved; + + Specified() { + resolved = typeResolvesToTypeStep*(this).(SpecifiedType).getBaseType().(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A class that resolves to the module's `ResolvedType` type parameter, ignoring specifiers. + */ + class IgnoringSpecifiers extends CppType { + ResolvedType resolved; + + IgnoringSpecifiers() { + resolved = this.(Specified).resolve() + or + resolved = this.(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to a reference that resolves to the module's `ResolvedType` type + * parameter. + * + * For example, `ResolvesTo::Ref` is the set of all references to `FooType`s and types + * that resolve (through typedefs and/or decltypes) to references to `FooType`s. + * + * ``` + * // Example `ResolvesTo::Ref` types: + * FooType &f; // matches (a reference to FooType) + * decltype(f); // matches (a decltype of reference to FooType) + * typedef FooType &FT; // matches (a typedef of ref to FooType) + * FT f2; // matches (a typedef of ref to FooType) + * decltype(f2); // matches (a decltype of typedef of ref to FooType) + * typedef FT FT2; // matches (a typedef of typedef of ref to FooType) + * + * // Examples types that are not `ResolvesTo::Ref` types: + * const FooType &cf; // does not match (specified references to FooTypes) + * FooType rf = f; // does not match (non-rerefence FooTypes) + * NotFooType &nf; // does not match (non FooTypes) + * ``` + */ + class Ref extends CppType { + ResolvedType resolved; + + Ref() { + // Note that the extractor appears to perform reference collapsing, so cases like + // `const T& &` are treated as `const T&`. + resolved = typeResolvesToTypeStep*(this).(ReferenceType).getBaseType().(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to a const reference of (or reference to const of) the module's + * `ResolvedType` type parameter. + * + * For example, `ResolvesTo::CvConstRef` is the set of all const references to `FooType`s + * and types that resolve (through typedefs and/or decltypes) to const references to `FooType`s. + * + * Volatile specifiers are ignored, since const volatile types are still const. + * + * ``` + * FooType &f; // does not match (not const) + * const FooType f; // does not match (not a reference) + * + * // Example `ResolvesTo::CvConstRef` types: + * const FooType &cf; // matches (a const reference to FooType) + * const volatile FooType &cf; // matches (a const reference to FooType, volatile is ignored) + * const decltype(f) cdf; // matches (a const decltype of reference to FooType) + * decltype(f)& dcf; // matches (a decltype of const reference to FooType) + * typedef const FooType &CFT; // matches (a typedef of const ref to FooType) + * const CFT ctf; // matches due to const collapse + * CFT &ctf; // matches due to reference collapse + * ``` + */ + class CvConstRef extends CppType { + ResolvedType resolved; + + CvConstRef() { + exists(ReferenceType refType | + // A type can be a reference to const, but a const type cannot contain a reference. + // Therefore, we only need to find reference types that resolve to const types. + // Note that the extractor appears to perform reference collapsing, so cases like + // `const T& &` are treated as `const T&`. + refType = typeResolvesToTypeStep*(this) and + resolved = refType.getBaseType().(CvConst).resolve() + ) + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to either a reference that resolves to the module's `ResolvedType` type + * parameter, or exactly to the `ResolvedType`. + */ + class ExactlyOrRef extends CppType { + ResolvedType resolved; + + ExactlyOrRef() { + resolved = this.(Ref).resolve() + or + resolved = this.(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } +} diff --git a/cpp/common/src/codingstandards/cpp/types/Specifiers.qll b/cpp/common/src/codingstandards/cpp/types/Specifiers.qll new file mode 100644 index 0000000000..187709f8c1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Specifiers.qll @@ -0,0 +1,28 @@ +import cpp + +class ConstSpecifier extends Specifier { + ConstSpecifier() { this.hasName("const") } +} + +/** + * A SpecifiedType with a `const` specifier. + * + * Note that this does *not* find all const types, as it does not resolve typedefs etc. + */ +class RawConstType extends SpecifiedType { + RawConstType() { this.getASpecifier() instanceof ConstSpecifier } +} + +/** + * Any type that is const, using the `.isConst()` member predicate. + */ +class ConstType extends Type { + ConstType() { this.isConst() } +} + +/** + * Any type that is not const, using the `.isConst()` member predicate. + */ +class NonConstType extends Type { + NonConstType() { not this.isConst() } +} diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index 0e55b3ad58..137a01c239 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -60,30 +60,6 @@ Type stripSpecifiers(Type type) { else result = type } -signature class PossiblySpecifiedBaseType extends Type; - -/** - * This module defines a class `Type` which holds for types `T` and `const/volatile T` etc. - * - * Similar to `getUnspecifiedType()`, but does not resolve typedefs. Useful for matching - * potentially qualified versions of standard typedef types, such as `const mtx_t`. - * - * Example usage: `someType.(PossiblySpecified::Type).strip()` - */ -module PossiblySpecified { - import cpp as cpp - - final class CppType = cpp::Type; - - class Type extends CppType { - BaseType baseType; - - Type() { baseType = stripSpecifiers(this) } - - BaseType strip() { result = baseType } - } -} - /** * Get the precision of an integral type, where precision is defined as the number of bits * that can be used to represent the numeric value. diff --git a/cpp/common/src/qlpack.yml b/cpp/common/src/qlpack.yml index 21663a5186..222a096067 100644 --- a/cpp/common/src/qlpack.yml +++ b/cpp/common/src/qlpack.yml @@ -3,5 +3,6 @@ version: 2.52.0-dev license: MIT dependencies: codeql/cpp-all: 4.0.3 + advanced-security/qtil: "0.0.3" dataExtensions: - ext/*.model.yml diff --git a/cpp/common/test/codeql-pack.lock.yml b/cpp/common/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/common/test/codeql-pack.lock.yml +++ b/cpp/common/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.expected b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql new file mode 100644 index 0000000000..ab8953f7b2 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql @@ -0,0 +1,68 @@ +import cpp +import utils.test.InlineExpectationsTest +import codingstandards.cpp.types.Resolve + +class FooType extends Class { + FooType() { this.hasName("Foo") } +} + +class TDFoo extends TypedefType { + TDFoo() { this.hasName("TDFoo") } +} + +class DeclFoo extends Decltype { + DeclFoo() { this.getBaseType() instanceof FooType } +} + +class ExactFoo = ResolvesTo::Exactly; + +class ConstFoo = ResolvesTo::CvConst; + +class SpecFoo = ResolvesTo::Specified; + +class MaybeSpecFoo = ResolvesTo::IgnoringSpecifiers; + +class RefFoo = ResolvesTo::Ref; + +class ConstRefFoo = ResolvesTo::CvConstRef; + +class ExactTDFoo = ResolvesTo::Exactly; + +class ExactDeclFoo = ResolvesTo::Exactly; + +predicate hasRelevantType(Type type, string s) { + type instanceof ExactFoo and s = "Foo" + or + type instanceof ConstFoo and s = "ConstFoo" + or + type instanceof SpecFoo and s = "SpecFoo" + or + type instanceof MaybeSpecFoo and s = "MaybeSpecFoo" + or + type instanceof RefFoo and s = "RefFoo" + or + type instanceof ConstRefFoo and s = "ConstRefFoo" + or + type instanceof ExactTDFoo and s = "TDFoo" + or + type instanceof ExactDeclFoo and s = "DeclFoo" +} + +module MyTest implements TestSig { + string getARelevantTag() { result = "type" } + + predicate hasActualResult(Location l, string element, string tag, string value) { + exists(Variable var, Type type | + var.getLocation().getFile().getBaseName() = "test.cpp" and + not var.isFromTemplateInstantiation(_) and + not var.isFromUninstantiatedTemplate(_) and + var.getLocation() = l and + element = var.toString() and + tag = getARelevantTag() and + type = var.getType() and + value = concat(string typestr | hasRelevantType(type, typestr) | typestr, ",") + ) + } +} + +import MakeTest diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp b/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp new file mode 100644 index 0000000000..c0bd914ad2 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp @@ -0,0 +1,72 @@ +#include + +class Foo {}; + +Foo l1; // $ type=Foo,MaybeSpecFoo +Foo &l2 = l1; // $ type=RefFoo +const Foo l3; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const Foo &l4 = l3; // $ type=ConstRefFoo +volatile Foo l5; // $ type=MaybeSpecFoo,SpecFoo +volatile Foo &l6 = l5; // $ type= +const volatile Foo l7; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const volatile Foo &l8 = l7; // $ type=ConstRefFoo + +typedef Foo TDFoo; +typedef Foo &TDFooRef; +typedef const Foo TDFooConst; +typedef const Foo &TDFooConstRef; +typedef volatile Foo TDFooVol; +typedef const volatile Foo TDFooVolConst; + +// Plain typedefs +TDFoo l9; // $ type=Foo,MaybeSpecFoo,TDFoo +TDFooConst l10; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +TDFooVol l11; // $ type=MaybeSpecFoo,SpecFoo +TDFooVolConst l12; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const TDFoo l13; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +volatile TDFoo l15; // $ type=MaybeSpecFoo,SpecFoo + +// Ref types +TDFoo &l17 = l1; // $ type=RefFoo +TDFooRef l18 = l1; // $ type=RefFoo +TDFooConstRef l19 = l3; // $ type=ConstRefFoo + +// const collapse +const TDFooConstRef l21 = l3; // $ type=ConstRefFoo +const TDFooConst l14; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const volatile TDFoo l16; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo + +// Ref collapse +TDFooRef &l22 = l1; // $ type=RefFoo +const TDFooRef &l23 = l1; // $ type=RefFoo +TDFooConstRef &l24 = l3; // $ type=ConstRefFoo + +// Cannot const qualify a ref type, const is ignored [8.3.2/1] +TDFooRef const l20 = l1; // $ type=RefFoo + +template class Declval { +public: + T type; +}; + +decltype(l1) l25 = l1; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l26; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l27 = l1; // $ type=RefFoo +decltype(Declval::type) l28; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +decltype(Declval::type) l29 = l1; // $ type=ConstRefFoo +decltype(l25) l30; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l31; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l32 = l1; // $ type=RefFoo +decltype(Declval::type) l33 = l1; // $ type=ConstRefFoo +decltype(Declval::type) l34; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo + +const decltype(Declval::type) l35; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +volatile decltype(Declval::type) l36; // $ type=MaybeSpecFoo,SpecFoo +const volatile decltype(Declval::type) + l37; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +decltype(Declval::type) &l38 = l1; // $ type=RefFoo +decltype(Declval::type) &l39 = l1; // $ type=RefFoo +decltype(Declval::type) &l40 = l1; // $ type=ConstRefFoo +decltype(Declval::type) &l41 = l1; // $ type=ConstRefFoo +const decltype(Declval::type) + l42; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo \ No newline at end of file diff --git a/cpp/common/test/library/codingstandards/cpp/types/options b/cpp/common/test/library/codingstandards/cpp/types/options new file mode 100644 index 0000000000..17bcdd919b --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -std=c++17 -nostdinc++ -I../../../../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/misra/src/codeql-pack.lock.yml b/cpp/misra/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/misra/src/codeql-pack.lock.yml +++ b/cpp/misra/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql index 9525d1f5f8..eb7759b1d8 100644 --- a/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql +++ b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql @@ -22,6 +22,8 @@ import codingstandards.cpp.Call import codingstandards.cpp.Loops import codingstandards.cpp.ast.Increment import codingstandards.cpp.misra.BuiltInTypeRules::MisraCpp23BuiltInTypes +import codingstandards.cpp.types.Resolve +import codingstandards.cpp.types.Specifiers Variable getDeclaredVariableInForLoop(ForStmt forLoop) { result = forLoop.getADeclaration().getADeclarationEntry().(VariableDeclarationEntry).getVariable() @@ -85,13 +87,11 @@ Expr getLoopStepOfForStmt(ForStmt forLoop) { predicate loopVariableAssignedToNonConstPointerOrReferenceType( ForStmt forLoop, VariableAccess loopVariableAccessInCondition, Expr assignmentRhs ) { - exists(Type targetType, DerivedType strippedType | + exists(Type targetType | isAssignment(assignmentRhs, targetType, _) and - strippedType = targetType.stripTopLevelSpecifiers() and - not strippedType.getBaseType().isConst() and ( - strippedType instanceof PointerType or - strippedType instanceof ReferenceType + targetType instanceof ResolvesTo::Type>::IgnoringSpecifiers or + targetType instanceof ResolvesTo::Type>::IgnoringSpecifiers ) | assignmentRhs.getEnclosingStmt().getParent*() = forLoop.getStmt() and diff --git a/cpp/misra/test/codeql-pack.lock.yml b/cpp/misra/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/misra/test/codeql-pack.lock.yml +++ b/cpp/misra/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: