diff --git a/cpp/common/src/codingstandards/cpp/UnintializedMemoryAllocation.qll b/cpp/common/src/codingstandards/cpp/UnintializedMemoryAllocation.qll new file mode 100644 index 0000000000..688b48dd02 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/UnintializedMemoryAllocation.qll @@ -0,0 +1,75 @@ +/** + * Provides models of functions in that deals with uninitialized memory. + */ + +import cpp + +abstract class UninitializedMemoryManagementFunction extends Function { + UninitializedMemoryManagementFunction() { + this.getADeclarationLocation().getFile().getShortName() = "memory" + } +} + +class UninitializedCopyFunction extends UninitializedMemoryManagementFunction { + UninitializedCopyFunction() { this.hasQualifiedName("std", "uninitialized_copy") } +} + +class UninitializedCopyNFunction extends UninitializedMemoryManagementFunction { + UninitializedCopyNFunction() { this.hasQualifiedName("std", "uninitialized_copy_n") } +} + +class UninitializedDefaultConstructFunction extends UninitializedMemoryManagementFunction { + UninitializedDefaultConstructFunction() { + this.hasQualifiedName("std", "uninitialized_default_construct") + } +} + +class UninitializedDefaultConstructNFunction extends UninitializedMemoryManagementFunction { + UninitializedDefaultConstructNFunction() { + this.hasQualifiedName("std", "uninitialized_default_construct_n") + } +} + +class UninitializedValueConstructFunction extends UninitializedMemoryManagementFunction { + UninitializedValueConstructFunction() { + this.hasQualifiedName("std", "uninitialized_value_construct") + } +} + +class UninitializedValueConstructNFunction extends UninitializedMemoryManagementFunction { + UninitializedValueConstructNFunction() { + this.hasQualifiedName("std", "uninitialized_value_construct_n") + } +} + +class UninitializedMoveFunction extends UninitializedMemoryManagementFunction { + UninitializedMoveFunction() { this.hasQualifiedName("std", "uninitialized_move") } +} + +class UninitializedMoveNFunction extends UninitializedMemoryManagementFunction { + UninitializedMoveNFunction() { this.hasQualifiedName("std", "uninitialized_move_n") } +} + +class UninitializedFillFunction extends UninitializedMemoryManagementFunction { + UninitializedFillFunction() { this.hasQualifiedName("std", "uninitialized_fill") } +} + +class UninitializedFillNFunction extends UninitializedMemoryManagementFunction { + UninitializedFillNFunction() { this.hasQualifiedName("std", "uninitialized_fill_n") } +} + +class DestroyFunction extends UninitializedMemoryManagementFunction { + DestroyFunction() { this.hasQualifiedName("std", "destroy") } +} + +class DestroyNFunction extends UninitializedMemoryManagementFunction { + DestroyNFunction() { this.hasQualifiedName("std", "destroy_n") } +} + +class DestroyAtFunction extends UninitializedMemoryManagementFunction { + DestroyAtFunction() { this.hasQualifiedName("std", "destroy_at") } +} + +class LaunderFunction extends UninitializedMemoryManagementFunction { + LaunderFunction() { this.hasQualifiedName("std", "launder") } +} diff --git a/cpp/common/src/codingstandards/cpp/allocations/CustomOperatorNewDelete.qll b/cpp/common/src/codingstandards/cpp/allocations/CustomOperatorNewDelete.qll index 69cff1d9ae..65576cb99c 100644 --- a/cpp/common/src/codingstandards/cpp/allocations/CustomOperatorNewDelete.qll +++ b/cpp/common/src/codingstandards/cpp/allocations/CustomOperatorNewDelete.qll @@ -20,7 +20,13 @@ abstract class CustomOperatorNewOrDelete extends Operator { exists(getFile().getRelativePath()) and // Not in a file called `new`, which is likely to be a copy of the standard library // as it is in our tests - not getFile().getBaseName() = "new" + not forall(File file | file = this.getADeclarationLocation().getFile() | + file.getBaseName() = "new" + ) and + ( + this.getName().regexpMatch("operator new(\\[\\])?") or + this.getName().regexpMatch("operator delete(\\[\\])?") + ) } /** diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory5.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory5.qll new file mode 100644 index 0000000000..4dcac4f243 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory5.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Memory5Query = TDynamicMemoryManagedManuallyQuery() + +predicate isMemory5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `dynamicMemoryManagedManually` query + Memory5Package::dynamicMemoryManagedManuallyQuery() and + queryId = + // `@id` for the `dynamicMemoryManagedManually` query + "cpp/misra/dynamic-memory-managed-manually" and + ruleId = "RULE-21-6-2" and + category = "required" +} + +module Memory5Package { + Query dynamicMemoryManagedManuallyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `dynamicMemoryManagedManually` query + TQueryCPP(TMemory5PackageQuery(TDynamicMemoryManagedManuallyQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory6.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory6.qll new file mode 100644 index 0000000000..0102cd7ccb --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Memory6.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Memory6Query = TAdvancedMemoryManagementUsedQuery() + +predicate isMemory6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `advancedMemoryManagementUsed` query + Memory6Package::advancedMemoryManagementUsedQuery() and + queryId = + // `@id` for the `advancedMemoryManagementUsed` query + "cpp/misra/advanced-memory-management-used" and + ruleId = "RULE-21-6-3" and + category = "required" +} + +module Memory6Package { + Query advancedMemoryManagementUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `advancedMemoryManagementUsed` query + TQueryCPP(TMemory6PackageQuery(TAdvancedMemoryManagementUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index cad86d2285..1ebfefe79b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -45,6 +45,8 @@ import Macros import Memory2 import Memory3 import Memory4 +import Memory5 +import Memory6 import MoveForward import Naming import Naming2 @@ -117,6 +119,8 @@ newtype TCPPQuery = TMemory2PackageQuery(Memory2Query q) or TMemory3PackageQuery(Memory3Query q) or TMemory4PackageQuery(Memory4Query q) or + TMemory5PackageQuery(Memory5Query q) or + TMemory6PackageQuery(Memory6Query q) or TMoveForwardPackageQuery(MoveForwardQuery q) or TNamingPackageQuery(NamingQuery q) or TNaming2PackageQuery(Naming2Query q) or @@ -189,6 +193,8 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isMemory2QueryMetadata(query, queryId, ruleId, category) or isMemory3QueryMetadata(query, queryId, ruleId, category) or isMemory4QueryMetadata(query, queryId, ruleId, category) or + isMemory5QueryMetadata(query, queryId, ruleId, category) or + isMemory6QueryMetadata(query, queryId, ruleId, category) or isMoveForwardQueryMetadata(query, queryId, ruleId, category) or isNamingQueryMetadata(query, queryId, ruleId, category) or isNaming2QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/test/includes/standard-library/cstdlib b/cpp/common/test/includes/standard-library/cstdlib index 3b1eefc4a9..e58b8fb7ee 100644 --- a/cpp/common/test/includes/standard-library/cstdlib +++ b/cpp/common/test/includes/standard-library/cstdlib @@ -13,6 +13,9 @@ using ::atoll; using ::exit; using ::free; using ::malloc; +using ::calloc; +using ::realloc; +using ::aligned_alloc; using ::quick_exit; using ::rand; using ::strtod; diff --git a/cpp/common/test/includes/standard-library/memory.h b/cpp/common/test/includes/standard-library/memory.h index 494f428422..75bb2c51d2 100644 --- a/cpp/common/test/includes/standard-library/memory.h +++ b/cpp/common/test/includes/standard-library/memory.h @@ -2,6 +2,7 @@ #define _GHLIBCPP_MEMORY #include "exception.h" #include "stddef.h" +#include "utility.h" namespace std { @@ -128,6 +129,136 @@ class bad_alloc : public exception { bad_alloc &operator=(const bad_alloc &) noexcept; virtual const char *what() const noexcept; }; + +template struct allocator { + using value_type = T1; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr allocator() noexcept = default; + constexpr allocator(const allocator &) noexcept = default; + + template constexpr allocator(const allocator &) noexcept; + + ~allocator() = default; + + T1 *allocate(std::size_t); + void deallocate(T1 *, std::size_t); +}; + +template <> struct allocator { + using value_type = void; +}; + +template struct allocator_traits { + using allocator_type = T1; + using value_type = typename T1::value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using void_pointer = void *; + using const_void_pointer = const void *; + using size_type = typename T1::size_type; + using difference_type = typename T1::difference_type; + + template using rebind_alloc = allocator; + + static pointer allocate(T1 &, size_type); + static pointer allocate(T1 &, size_type, const_void_pointer); + static void deallocate(T1 &, pointer, size_type); +}; + +// uninitialized_default_construct +template +void uninitialized_default_construct(T1, T1); + +template +void uninitialized_default_construct(T1&&, T2, T2); + +// uninitialized_default_construct_n +template +T1 uninitialized_default_construct_n(T1, T2); + +template +T2 uninitialized_default_construct_n(T1&&, T2, T3); + +// uninitialized_value_construct +template +void uninitialized_value_construct(T1, T1); + +template +void uninitialized_value_construct(T1&&, T2, T2); + +// uninitialized_value_construct_n +template +T1 uninitialized_value_construct_n(T1, T2); + +template +T2 uninitialized_value_construct_n(T1&&, T2, T3); + +// uninitialized_copy +template +T2 uninitialized_copy(T1, T1, T2); + +template +T3 uninitialized_copy(T1&&, T2, T2, T3); + +// uninitialized_copy_n +template +T3 uninitialized_copy_n(T1, T2, T3); + +template +T4 uninitialized_copy_n(T1&&, T2, T3, T4); + +// uninitialized_move +template +T2 uninitialized_move(T1, T1, T2); + +template +T3 uninitialized_move(T1&&, T2, T2, T3); + +// uninitialized_move_n +template +pair uninitialized_move_n(T1, T2, T3); + +template +pair uninitialized_move_n(T1&&, T2, T3, T4); + +// uninitialized_fill +template +void uninitialized_fill(T1, T1, const T2&); + +template +void uninitialized_fill(T1&&, T2, T2, const T3&); + +// uninitialized_fill_n +template +T1 uninitialized_fill_n(T1, T2, const T3&); + +template +T2 uninitialized_fill_n(T1&&, T2, T3, const T4&); + +// destroy_at +template +void destroy_at(T1*); + +// destroy +template +void destroy(T1, T1); + +template +void destroy(T1&&, T2, T2); + +// destroy_n +template +T1 destroy_n(T1, T2); + +template +T2 destroy_n(T1&&, T2, T3); + +// launder +template +constexpr T1* launder(T1*) noexcept; + } // namespace std -#endif // _GHLIBCPP_MEMORY \ No newline at end of file +#endif // _GHLIBCPP_MEMORY diff --git a/cpp/common/test/includes/standard-library/memory_resource b/cpp/common/test/includes/standard-library/memory_resource new file mode 100644 index 0000000000..7f40043d1f --- /dev/null +++ b/cpp/common/test/includes/standard-library/memory_resource @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/memory_resource.h b/cpp/common/test/includes/standard-library/memory_resource.h new file mode 100644 index 0000000000..5f9d41d2e1 --- /dev/null +++ b/cpp/common/test/includes/standard-library/memory_resource.h @@ -0,0 +1,114 @@ +#ifndef _GHLIBCPP_MEMORY_RESOURCE +#define _GHLIBCPP_MEMORY_RESOURCE + +#include +#include +#include +#include + +namespace std::pmr { + +class memory_resource { +public: + memory_resource() = default; + memory_resource(const memory_resource &) = default; + virtual ~memory_resource(); + + void *allocate(std::size_t, std::size_t = alignof(std::max_align_t)); + void deallocate(void *, std::size_t, std::size_t = alignof(std::max_align_t)); + +private: + virtual void *do_allocate(std::size_t, std::size_t) = 0; + virtual void do_deallocate(void *, std::size_t, std::size_t) = 0; + virtual bool do_is_equal(const memory_resource &) const noexcept = 0; +}; + +template class polymorphic_allocator { +public: + using value_type = T1; + + polymorphic_allocator() noexcept; + polymorphic_allocator(memory_resource *); + polymorphic_allocator(const polymorphic_allocator &) = default; + + template + polymorphic_allocator(const polymorphic_allocator &) noexcept; + + T1 *allocate(std::size_t); + void deallocate(T1 *, std::size_t); + + memory_resource *resource() const; + +private: + memory_resource *resource_; +}; + +struct pool_options { + std::size_t max_blocks_per_chunk = 0; + std::size_t largest_required_pool_block = 0; +}; + +class monotonic_buffer_resource : public memory_resource { +public: + explicit monotonic_buffer_resource(memory_resource *); + monotonic_buffer_resource(std::size_t, memory_resource *); + monotonic_buffer_resource(void *, std::size_t, memory_resource *); + + monotonic_buffer_resource(); + explicit monotonic_buffer_resource(std::size_t); + monotonic_buffer_resource(void *, std::size_t); + + monotonic_buffer_resource(const monotonic_buffer_resource &) = delete; + ~monotonic_buffer_resource() override; + + void release(); + memory_resource *upstream_resource() const; + +private: + void *do_allocate(std::size_t, std::size_t) override; + void do_deallocate(void *, std::size_t, std::size_t) override; + bool do_is_equal(const memory_resource &) const noexcept override; +}; + +class unsynchronized_pool_resource : public memory_resource { +public: + unsynchronized_pool_resource(const pool_options &, memory_resource *); + unsynchronized_pool_resource(); + explicit unsynchronized_pool_resource(memory_resource *); + explicit unsynchronized_pool_resource(const pool_options &); + + unsynchronized_pool_resource(const unsynchronized_pool_resource &) = delete; + ~unsynchronized_pool_resource() override; + + void release(); + memory_resource *upstream_resource() const; + pool_options options() const; + +protected: + void *do_allocate(std::size_t, std::size_t) override; + void do_deallocate(void *, std::size_t, std::size_t) override; + bool do_is_equal(const memory_resource &) const noexcept override; +}; + +class synchronized_pool_resource : public memory_resource { +public: + synchronized_pool_resource(const pool_options &, memory_resource *); + synchronized_pool_resource(); + explicit synchronized_pool_resource(memory_resource *); + explicit synchronized_pool_resource(const pool_options &); + + synchronized_pool_resource(const synchronized_pool_resource &) = delete; + ~synchronized_pool_resource() override; + + void release(); + memory_resource *upstream_resource() const; + pool_options options() const; + +protected: + void *do_allocate(std::size_t, std::size_t) override; + void do_deallocate(void *, std::size_t, std::size_t) override; + bool do_is_equal(const memory_resource &) const noexcept override; +}; +} // namespace std::pmr + +#endif // _GHLIBCPP_MEMORY_RESOURCE diff --git a/cpp/common/test/includes/standard-library/stddef.h b/cpp/common/test/includes/standard-library/stddef.h index 96e9849973..9fe9e747f2 100644 --- a/cpp/common/test/includes/standard-library/stddef.h +++ b/cpp/common/test/includes/standard-library/stddef.h @@ -13,6 +13,7 @@ typedef struct { // equivalent to that provided by clang and gcc typedef long ptrdiff_t; using nullptr_t = decltype(nullptr); using size_t = decltype(sizeof(char)); +enum class byte : unsigned char {}; } // namespace std #define offsetof(t, d) __builtin_offsetof(t, d) /*implementation-defined*/ diff --git a/cpp/common/test/includes/standard-library/stdlib.h b/cpp/common/test/includes/standard-library/stdlib.h index eb73db0627..c71c76efe9 100644 --- a/cpp/common/test/includes/standard-library/stdlib.h +++ b/cpp/common/test/includes/standard-library/stdlib.h @@ -7,6 +7,7 @@ void *calloc(size_t num, size_t size); void free(void *ptr); void *malloc(size_t size); void *realloc(void *ptr, size_t size); +void *aligned_alloc(size_t, size_t); [[noreturn]] void _Exit(int status) noexcept; [[noreturn]] void abort(void) noexcept; @@ -36,4 +37,4 @@ long double strtold(const char *str, char **endptr); int rand(void); -#endif // _GHLIBCPP_STDLIB \ No newline at end of file +#endif // _GHLIBCPP_STDLIB diff --git a/cpp/common/test/includes/standard-library/tuple.h b/cpp/common/test/includes/standard-library/tuple.h index e4ab473488..90725ca838 100644 --- a/cpp/common/test/includes/standard-library/tuple.h +++ b/cpp/common/test/includes/standard-library/tuple.h @@ -1,11 +1,12 @@ namespace std { -template class tuple {}; +// Definition +template class tuple {}; + template std::tuple make_tuple(Types &&...args); struct ignore_t { template constexpr // required since C++14 - void - operator=(T &&) const noexcept {} + void operator=(T &&) const noexcept {} }; inline const std::ignore_t ignore; // 'const' only until C++17 } // namespace std diff --git a/cpp/common/test/includes/standard-library/utility.h b/cpp/common/test/includes/standard-library/utility.h index db8d18cdb6..f4e3472b54 100644 --- a/cpp/common/test/includes/standard-library/utility.h +++ b/cpp/common/test/includes/standard-library/utility.h @@ -9,24 +9,24 @@ template typename add_rvalue_reference::type declval() noexcept; template void swap(T &a, T &b) noexcept; -template -struct tuple {}; +// Forward declaration only - defined in +template class tuple; -template struct pair : tuple { - T first; - U second; - pair(T t, U u); +template struct pair { + T first; + U second; + pair(T t, U u); }; template std::pair make_pair(T &&x, U &&y); -template +template constexpr auto get(const std::pair &p) noexcept { - if constexpr (N == 0) { - return p.first; - } else if constexpr (N == 1) { - return p.second; - } else { - static_assert(N < 2, "Index out of bounds for pair"); - } + if constexpr (N == 0) { + return p.first; + } else if constexpr (N == 1) { + return p.second; + } else { + static_assert(N < 2, "Index out of bounds for pair"); + } } -} // namespace std \ No newline at end of file +} // namespace std diff --git a/cpp/misra/src/rules/RULE-21-6-2/DynamicMemoryManagedManually.ql b/cpp/misra/src/rules/RULE-21-6-2/DynamicMemoryManagedManually.ql new file mode 100644 index 0000000000..67ed9f5a1e --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-6-2/DynamicMemoryManagedManually.ql @@ -0,0 +1,79 @@ +/** + * @id cpp/misra/dynamic-memory-managed-manually + * @name RULE-21-6-2: Dynamic memory shall be managed automatically + * @description Dynamically allocated memory must not be managed manually. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-6-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.SmartPointers + +class DynamicMemoryManagementFunction extends Function { + string description; + + DynamicMemoryManagementFunction() { + (this instanceof AllocationFunction or this instanceof AlignedAlloc) and + description = "an expression that dynamically allocates memory" + or + this instanceof DeallocationFunction and + description = "an expression that dynamically deallocates memory" + or + this instanceof AllocateOrDeallocateStdlibMemberFunction and + description = "a standard library function that manages memory manually" + or + this instanceof UniquePointerReleaseFunction and + description = "`std::unique_ptr::release`" + } + + string describe() { result = description } +} + +/** + * A function that has namespace `std` and has name `allocate` or `deallocate`, including but not limited to: + * - `std::allocator::allocate(std::size_t)` + * - `std::allocator::dellocate(T*, std::size_t)` + * - `std::pmr::memory_resource::allocate(std::size_t, std::size_t)` + * - `std::pmr::memory_resource::allocate(std::size_t, std::size_t)` + */ +class AllocateOrDeallocateStdlibMemberFunction extends MemberFunction { + AllocateOrDeallocateStdlibMemberFunction() { + this.getName() in ["allocate", "deallocate"] and + this.getNamespace().getParentNamespace*() instanceof StdNamespace + } +} + +/** + * The `std::aligned_alloc` (``) or `::aligned_alloc` (``) function. + */ +class AlignedAlloc extends Function { + AlignedAlloc() { this.hasGlobalOrStdName("aligned_alloc") } +} + +class UniquePointerReleaseFunction extends MemberFunction { + UniquePointerReleaseFunction() { this.getClassAndName("release") instanceof AutosarUniquePointer } +} + +from Expr expr, string message +where + not isExcluded(expr, Memory5Package::dynamicMemoryManagedManuallyQuery()) and + exists(DynamicMemoryManagementFunction dynamicMemoryManagementFunction | + /* ===== 1. The expression calls one of the dynamic memory management functions. ===== */ + expr = dynamicMemoryManagementFunction.getACallToThisFunction() and + message = + "This expression is a call to `" + dynamicMemoryManagementFunction.getName() + "` which is " + + dynamicMemoryManagementFunction.describe() + "." + or + /* ===== 2. The expression takes address of the dynamic memory management functions. ===== */ + expr = dynamicMemoryManagementFunction.getAnAccess() and + message = + "This expression takes address of `" + dynamicMemoryManagementFunction.getName() + + "` which is " + dynamicMemoryManagementFunction.describe() + "." + ) +select expr, message diff --git a/cpp/misra/src/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.ql b/cpp/misra/src/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.ql new file mode 100644 index 0000000000..257b9e42b1 --- /dev/null +++ b/cpp/misra/src/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.ql @@ -0,0 +1,96 @@ +/** + * @id cpp/misra/advanced-memory-management-used + * @name RULE-21-6-3: Advanced memory management shall not be used + * @description Using advanced memory management that either alters allocation and deallocation or + * constructs object construction on uninitalized memory may result in undefined + * behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-21-6-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.UnintializedMemoryAllocation +import codingstandards.cpp.allocations.CustomOperatorNewDelete + +class AdvancedMemoryManagementFunction extends Function { + string description; + + AdvancedMemoryManagementFunction() { + this instanceof NonStandardNewOrNewArrayOperator and + description = "a non-replaceable allocation function as operator `new` / `new[]`" + or + this instanceof NonStandardDeleteOrDeleteArrayOperator and + description = "a non-replaceable deallocation function as operator `delete` / `delete[]`" + or + this instanceof UninitializedMemoryManagementFunction and + description = "a function from that manages uninitialized memory" + } + + string describe() { result = description } +} + +class NonStandardNewOrNewArrayOperator extends CustomOperatorNewOrDelete { + NonStandardNewOrNewArrayOperator() { + this.getName() in ["operator new", "operator new[]"] and + not this instanceof CustomOperatorNew // `CustomOperatorNew` only detects replaceable allocation functions. + } +} + +/** + * A user-provided declaration of `new` / `new[]` / `delete` / `delete[]`. + * + * NOTE: Technically, the rule does not care if the declarations are in user-provided code, + * but for the sake of development, we want to exclude the stubs we index into the database. + */ +class UserDeclaredOperatorNewOrDelete extends FunctionDeclarationEntry { + UserDeclaredOperatorNewOrDelete() { + /* Not in the standard library */ + exists(this.getFile().getRelativePath()) and + /* Not in a file called `new`, which is likely to be a stub of the standard library */ + not this.getFile().getBaseName() = "new" and + ( + this.getName() in ["operator new", "operator new[]"] or + this.getName() in ["operator delete", "operator delete[]"] + ) + } +} + +class NonStandardDeleteOrDeleteArrayOperator extends CustomOperatorNewOrDelete { + NonStandardDeleteOrDeleteArrayOperator() { + this.getName() in ["operator delete", "operator delete[]"] and + not this instanceof CustomOperatorDelete // `CustomOperatorDelete` only detects replaceable deallocation functions. + } +} + +from Element element, string message +where + not isExcluded(element, Memory6Package::advancedMemoryManagementUsedQuery()) and + exists(AdvancedMemoryManagementFunction advancedMemoryManagementFunction | + /* 1. The element is a call to one of the advanced management functions. */ + element = advancedMemoryManagementFunction.getACallToThisFunction() and + message = + "This expression is a call to `" + advancedMemoryManagementFunction.getName() + "` which is " + + advancedMemoryManagementFunction.describe() + "." + or + /* 2. The element takes address of the advanced memory management functions. */ + element = advancedMemoryManagementFunction.getAnAccess() and + message = + "This expression takes address of `" + advancedMemoryManagementFunction.getName() + + "` which is " + advancedMemoryManagementFunction.describe() + "." + ) + or + ( + element instanceof VacuousDestructorCall or + element instanceof DestructorCall + ) and + message = "This expression is a call to a destructor." + or + element instanceof UserDeclaredOperatorNewOrDelete and + message = "This is a user-provided declaration of `new` / `new[]` / `delete` / `delete[]`." +select element, message diff --git a/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.expected b/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.expected new file mode 100644 index 0000000000..34163ca700 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.expected @@ -0,0 +1,42 @@ +| test.cpp:17:12:17:17 | new | This expression dynamically allocates or deallocates memory. | +| test.cpp:22:12:22:17 | new | This expression dynamically allocates or deallocates memory. | +| test.cpp:23:3:23:11 | call to operator delete | This expression dynamically allocates or deallocates memory. | +| test.cpp:23:3:23:11 | delete | This expression dynamically allocates or deallocates memory. | +| test.cpp:30:7:30:17 | call to malloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:32:7:32:12 | call to malloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:37:7:37:17 | call to calloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:38:30:38:35 | call to calloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:43:13:43:23 | call to malloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:44:14:44:25 | call to realloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:45:14:45:20 | call to realloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:50:14:50:31 | call to aligned_alloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:52:14:52:26 | call to aligned_alloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:59:7:59:17 | call to malloc | This expression dynamically allocates or deallocates memory. | +| test.cpp:60:3:60:11 | call to free | This expression dynamically allocates or deallocates memory. | +| test.cpp:61:3:61:6 | call to free | This expression dynamically allocates or deallocates memory. | +| test.cpp:69:18:69:25 | call to allocate | This expression uses a standard library function `std::allocator::allocate` that manages memory manually. | +| test.cpp:70:9:70:18 | call to deallocate | This expression uses a standard library function `std::allocator::deallocate` that manages memory manually. | +| test.cpp:77:12:77:27 | call to allocate | This expression uses a standard library function `std::allocator_traits>::allocate` that manages memory manually. | +| test.cpp:79:3:79:20 | call to deallocate | This expression uses a standard library function `std::allocator_traits>::deallocate` that manages memory manually. | +| test.cpp:85:10:85:17 | call to allocate | This expression uses a standard library function `std::pmr::memory_resource::allocate` that manages memory manually. | +| test.cpp:86:6:86:15 | call to deallocate | This expression uses a standard library function `std::pmr::memory_resource::deallocate` that manages memory manually. | +| test.cpp:88:17:88:24 | call to allocate | This expression uses a standard library function `std::pmr::memory_resource::allocate` that manages memory manually. | +| test.cpp:91:6:91:15 | call to deallocate | This expression uses a standard library function `std::pmr::memory_resource::deallocate` that manages memory manually. | +| test.cpp:99:18:99:25 | call to allocate | This expression uses a standard library function `std::pmr::polymorphic_allocator::allocate` that manages memory manually. | +| test.cpp:100:9:100:18 | call to deallocate | This expression uses a standard library function `std::pmr::polymorphic_allocator::deallocate` that manages memory manually. | +| test.cpp:107:17:107:24 | call to allocate | This expression uses a standard library function `std::pmr::memory_resource::allocate` that manages memory manually. | +| test.cpp:109:6:109:15 | call to deallocate | This expression uses a standard library function `std::pmr::memory_resource::deallocate` that manages memory manually. | +| test.cpp:115:26:115:33 | call to allocate | This expression uses a standard library function `std::pmr::memory_resource::allocate` that manages memory manually. | +| test.cpp:117:15:117:24 | call to deallocate | This expression uses a standard library function `std::pmr::memory_resource::deallocate` that manages memory manually. | +| test.cpp:122:24:122:31 | call to allocate | This expression uses a standard library function `std::pmr::memory_resource::allocate` that manages memory manually. | +| test.cpp:124:13:124:22 | call to deallocate | This expression uses a standard library function `std::pmr::memory_resource::deallocate` that manages memory manually. | +| test.cpp:133:13:133:20 | call to allocate | This expression uses a standard library function `std::scoped_allocator_adaptor>::allocate` that manages memory manually. | +| test.cpp:134:9:134:18 | call to deallocate | This expression uses a standard library function `std::scoped_allocator_adaptor>::deallocate` that manages memory manually. | +| test.cpp:142:17:142:23 | call to release | This expression is a call to `std::unique_ptr::release` that manages memory manually. | +| test.cpp:143:3:143:13 | call to operator delete | This expression dynamically allocates or deallocates memory. | +| test.cpp:143:3:143:13 | delete | This expression dynamically allocates or deallocates memory. | +| test.cpp:148:10:148:16 | call to release | This expression is a call to `std::unique_ptr::release` that manages memory manually. | +| test.cpp:149:3:149:15 | call to operator delete[] | This expression dynamically allocates or deallocates memory. | +| test.cpp:149:3:149:15 | delete[] | This expression dynamically allocates or deallocates memory. | +| test.cpp:155:3:155:12 | call to operator delete | This expression dynamically allocates or deallocates memory. | +| test.cpp:155:3:155:12 | delete | This expression dynamically allocates or deallocates memory. | diff --git a/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.qlref b/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.qlref new file mode 100644 index 0000000000..40a66338fb --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-2/DynamicMemoryManagedManually.qlref @@ -0,0 +1 @@ +rules/RULE-21-6-2/DynamicMemoryManagedManually.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-2/test.cpp b/cpp/misra/test/rules/RULE-21-6-2/test.cpp new file mode 100644 index 0000000000..40e2e9e7ee --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-2/test.cpp @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include +#include + +class C1 { +public: + C1() {} +}; + +// Item 1: Any non-placement form of new or delete + +void use_of_new() { + C1 x1; // COMPLIANT: no use of new + C1 x2{}; // COMPLIANT: no use of new + C1 *x3 = new C1; + // NON_COMPLIANT: use of new + C1 *x4 = new (&x1) C1; // COMPLIANT: placement new (but violates Rule 21.6.3) +} + +void use_of_delete() { + C1 *x1 = new C1; // NON_COMPLIANT: use of new + delete x1; // NON_COMPLIANT: use of delete +} + +// Item 2: Any of the functions malloc, calloc, realloc, aligned_alloc, free + +void use_of_malloc() { + C1 *x1 = static_cast( + std::malloc(sizeof(C1))); // NON_COMPLIANT: use of malloc + C1 *x2 = static_cast( + malloc(sizeof(C1))); // NON_COMPLIANT: use of malloc from global namespace +} + +void use_of_calloc() { + C1 *x1 = static_cast( + std::calloc(1, sizeof(C1))); // NON_COMPLIANT: use of calloc + C1 *x2 = static_cast(calloc( + 1, sizeof(C1))); // NON_COMPLIANT: use of calloc from global namespace +} + +void use_of_realloc() { + void *p = std::malloc(sizeof(C1)); // NON_COMPLIANT: use of malloc + void *q1 = std::realloc(p, sizeof(C1) * 2); // NON_COMPLIANT: use of realloc + void *q2 = realloc( + p, sizeof(C1) * 2); // NON_COMPLIANT: use of realloc from global namespace +} + +void use_of_aligned_alloc() { + void *x1 = std::aligned_alloc( + alignof(C1), sizeof(C1)); // NON_COMPLIANT: use of aligned_alloc + void *x2 = aligned_alloc( + alignof(C1), + sizeof(C1)); // NON_COMPLIANT: use of aligned_alloc from global namespace +} + +void use_of_free() { + C1 *c1 = static_cast( + std::malloc(sizeof(C1))); // NON_COMPLIANT: use of malloc + std::free(c1); // NON_COMPLIANT: use of free + free(c1); // NON_COMPLIANT: use of free from global namespace +} + +// Item 3: Any member function named allocate or deallocate enclosed by +// namespace std + +void use_of_std_allocator() { + std::allocator alloc; + C1 *p1 = alloc.allocate(1); // NON_COMPLIANT: std::allocator::allocate + alloc.deallocate(p1, 1); // NON_COMPLIANT: std::allocator::deallocate +} + +void use_of_allocator_traits() { + std::allocator alloc; + using Traits = std::allocator_traits>; + + C1 *p1 = Traits::allocate( + alloc, 1); // NON_COMPLIANT: std::allocator_traits::allocate + Traits::deallocate(alloc, p1, + 1); // NON_COMPLIANT: std::allocator_traits::deallocate +} + +void use_of_memory_resource(std::pmr::memory_resource &mr) { + void *p1 = + mr.allocate(sizeof(C1)); // NON_COMPLIANT: memory_resource::allocate + mr.deallocate(p1, sizeof(C1)); // NON_COMPLIANT: memory_resource::deallocate + + void *p2 = mr.allocate( + sizeof(C1), + alignof(C1)); // NON_COMPLIANT: memory_resource::allocate (with alignment) + mr.deallocate( + p2, sizeof(C1), + alignof( + C1)); // NON_COMPLIANT: memory_resource::deallocate (with alignment) +} + +void use_of_polymorphic_allocator() { + std::pmr::polymorphic_allocator alloc; + C1 *p1 = alloc.allocate(1); // NON_COMPLIANT: polymorphic_allocator::allocate + alloc.deallocate(p1, 1); // NON_COMPLIANT: polymorphic_allocator::deallocate +} + +void use_of_monotonic_buffer_resource() { + char buffer[1024]; + std::pmr::monotonic_buffer_resource mr{buffer, sizeof(buffer)}; + + void *p1 = mr.allocate( + sizeof(C1)); // NON_COMPLIANT: monotonic_buffer_resource::allocate + mr.deallocate( + p1, sizeof(C1)); // NON_COMPLIANT: monotonic_buffer_resource::deallocate +} + +void use_of_pool_resources() { + std::pmr::unsynchronized_pool_resource unsync_pool; + void *p1 = unsync_pool.allocate( + sizeof(C1)); // NON_COMPLIANT: unsynchronized_pool_resource::allocate + unsync_pool.deallocate( + p1, + sizeof(C1)); // NON_COMPLIANT: unsynchronized_pool_resource::deallocate + + std::pmr::synchronized_pool_resource sync_pool; + void *p2 = sync_pool.allocate( + sizeof(C1)); // NON_COMPLIANT: synchronized_pool_resource::allocate + sync_pool.deallocate( + p2, sizeof(C1)); // NON_COMPLIANT: synchronized_pool_resource::deallocate +} + +void use_of_scoped_allocator_adaptor() { + using Alloc = std::scoped_allocator_adaptor>; + Alloc alloc; + + C1 *p1 = + alloc.allocate(1); // NON_COMPLIANT: scoped_allocator_adaptor::allocate + alloc.deallocate(p1, + 1); // NON_COMPLIANT: scoped_allocator_adaptor::deallocate +} + +// Item 4: std::unique_ptr::release + +void use_of_unique_ptr_release() { + auto p1 = std::make_unique(); // COMPLIANT: smart pointer creation + C1 *raw1 = p1.release(); // NON_COMPLIANT: std::unique_ptr::release + delete raw1; // NON_COMPLIANT: use of delete + + auto p2 = + std::make_unique(10); // COMPLIANT: smart pointer array creation + C1 *raw2 = + p2.release(); // NON_COMPLIANT: std::unique_ptr::release (array form) + delete[] raw2; // NON_COMPLIANT: use of delete[] +} + +void delete_via_get() { + auto p1 = std::make_unique(); + C1 *raw = p1.get(); // COMPLIANT: get() is fine + delete raw; // NON_COMPLIANT: use of delete (causes double-free!) +} + +// Taking address of allocation functions (Item 2) + +void take_address_of_malloc_etc() { + std::function alloc1 = + std::malloc; // NON_COMPLIANT: implicit address-of malloc + std::function alloc2 = + &std::malloc; // NON_COMPLIANT: explicit address-of malloc + std::function alloc3 = + ::malloc; // NON_COMPLIANT: implicit address-of malloc (global) + std::function alloc4 = + &::malloc; // NON_COMPLIANT: explicit address-of malloc (global) + + std::function alloc5 = + std::calloc; // NON_COMPLIANT: implicit address-of calloc + std::function alloc6 = + &std::calloc; // NON_COMPLIANT: explicit address-of calloc + + std::function alloc7 = + std::realloc; // NON_COMPLIANT: implicit address-of realloc + std::function alloc8 = + &std::realloc; // NON_COMPLIANT: explicit address-of realloc + + std::function alloc9 = + std::aligned_alloc; // NON_COMPLIANT: implicit address-of aligned_alloc + std::function alloc10 = + &std::aligned_alloc; // NON_COMPLIANT: explicit address-of aligned_alloc + + std::function dealloc1 = + std::free; // NON_COMPLIANT: implicit address-of free + std::function dealloc2 = + &std::free; // NON_COMPLIANT: explicit address-of free + std::function dealloc3 = + ::free; // NON_COMPLIANT: implicit address-of free (global) + std::function dealloc4 = + &::free; // NON_COMPLIANT: explicit address-of free (global) +} + +// Taking address of operator new/delete (Item 1) + +void take_address_of_operator_new() { + void *(*p1)(std::size_t) = + &::operator new; // NON_COMPLIANT: address of operator new + void *(*p2)(std::size_t) = + ::operator new; // NON_COMPLIANT: implicit address of operator new + void *(*p3)(std::size_t) = + &::operator new[]; // NON_COMPLIANT: address of operator new[] + void *(*p4)(std::size_t) = + ::operator new[]; // NON_COMPLIANT: implicit address of operator new[] + + void *(*p5)(std::size_t, const std::nothrow_t &) = + &::operator new; // NON_COMPLIANT: address of nothrow operator new + void *(*p6)(std::size_t, const std::nothrow_t &) = + &::operator new[]; // NON_COMPLIANT: address of nothrow operator new[] +} + +void take_address_of_operator_delete() { + void (*p1)(void *) = + &::operator delete; // NON_COMPLIANT: address of operator delete + void (*p2)(void *) = + ::operator delete; // NON_COMPLIANT: implicit address of operator delete + void (*p3)(void *) = + &::operator delete[]; // NON_COMPLIANT: address of operator delete[] + void (*p4)(void *) = + ::operator delete[]; // NON_COMPLIANT: implicit address of operator + // delete[] + + void (*p5)(void *, const std::nothrow_t &) = + &::operator delete; // NON_COMPLIANT: address of nothrow operator delete + void (*p6)(void *, const std::nothrow_t &) = + &::operator delete[]; // NON_COMPLIANT: address of nothrow operator + // delete[] + + void (*p7)(void *, std::size_t) = + &::operator delete; // NON_COMPLIANT: address of sized operator delete + void (*p8)(void *, std::size_t) = + &::operator delete[]; // NON_COMPLIANT: address of sized operator + // delete[] +} + +// Taking address of allocate/deallocate member functions (Item 3) + +void take_address_of_allocate_deallocate() { + // std::allocator + auto p1 = + &std::allocator::allocate; // NON_COMPLIANT: address of + // std::allocator::allocate + auto p2 = + &std::allocator::deallocate; // NON_COMPLIANT: address of + // std::allocator::deallocate + + // std::allocator_traits (static member functions) + using Traits = std::allocator_traits>; + C1* (*p3)(std::allocator&, std::size_t) = + &Traits::allocate; // NON_COMPLIANT: address of + // std::allocator_traits::allocate + void (*p4)(std::allocator&, C1*, std::size_t) = + &Traits::deallocate; // NON_COMPLIANT: address of + // std::allocator_traits::deallocate + + // std::pmr::memory_resource + auto p5 = + &std::pmr::memory_resource::allocate; // NON_COMPLIANT: address of + // memory_resource::allocate + auto p6 = + &std::pmr::memory_resource::deallocate; // NON_COMPLIANT: address of + // memory_resource::deallocate + + // std::pmr::polymorphic_allocator + auto p7 = + &std::pmr::polymorphic_allocator< + C1>::allocate; // NON_COMPLIANT: address of + // polymorphic_allocator::allocate + auto p8 = + &std::pmr::polymorphic_allocator< + C1>::deallocate; // NON_COMPLIANT: address of + // polymorphic_allocator::deallocate + + // std::pmr::monotonic_buffer_resource + auto p9 = + &std::pmr::monotonic_buffer_resource:: + allocate; // NON_COMPLIANT: address of + // monotonic_buffer_resource::allocate + auto p10 = + &std::pmr::monotonic_buffer_resource:: + deallocate; // NON_COMPLIANT: address of + // monotonic_buffer_resource::deallocate + + // std::pmr::unsynchronized_pool_resource + auto p11 = + &std::pmr::unsynchronized_pool_resource:: + allocate; // NON_COMPLIANT: address of + // unsynchronized_pool_resource::allocate + auto p12 = + &std::pmr::unsynchronized_pool_resource:: + deallocate; // NON_COMPLIANT: address of + // unsynchronized_pool_resource::deallocate + + // std::pmr::synchronized_pool_resource + auto p13 = + &std::pmr::synchronized_pool_resource:: + allocate; // NON_COMPLIANT: address of + // synchronized_pool_resource::allocate + auto p14 = + &std::pmr::synchronized_pool_resource:: + deallocate; // NON_COMPLIANT: address of + // synchronized_pool_resource::deallocate + + // std::scoped_allocator_adaptor (non-static member functions) + using ScopedAlloc = std::scoped_allocator_adaptor>; + C1* (ScopedAlloc::*p15)(std::size_t) = + &ScopedAlloc::allocate; // NON_COMPLIANT: address of + // scoped_allocator_adaptor::allocate + void (ScopedAlloc::*p16)(C1*, std::size_t) = + &ScopedAlloc::deallocate; // NON_COMPLIANT: address of + // scoped_allocator_adaptor::deallocate +} + +// Taking address of std::unique_ptr::release (Item 4) + +void take_address_of_unique_ptr_release() { + auto p1 = + &std::unique_ptr::release; // NON_COMPLIANT: address of + // std::unique_ptr::release + auto p2 = + &std::unique_ptr::release; // NON_COMPLIANT: address of + // std::unique_ptr::release (array form) +} + +int main() { return 0; } diff --git a/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.expected b/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.qlref b/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.qlref new file mode 100644 index 0000000000..981cc55f9e --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-3/AdvancedMemoryManagementUsed.qlref @@ -0,0 +1 @@ +rules/RULE-21-6-3/AdvancedMemoryManagementUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-21-6-3/test.cpp b/cpp/misra/test/rules/RULE-21-6-3/test.cpp new file mode 100644 index 0000000000..d76bbc6493 --- /dev/null +++ b/cpp/misra/test/rules/RULE-21-6-3/test.cpp @@ -0,0 +1,436 @@ +#include +#include +#include +#include + +/** + * Fixture class with class-specific operator new/delete declarations and + * definitions. + */ +class C1 { +public: + // Class-specific operator declarations - NON_COMPLIANT (any signature) + void * + operator new(std::size_t size); // NON_COMPLIANT: class-specific declaration + void * + operator new[](std::size_t size); // NON_COMPLIANT: class-specific declaration + void operator delete( + void *ptr) noexcept; // NON_COMPLIANT: class-specific declaration + void operator delete[]( + void *ptr) noexcept; // NON_COMPLIANT: class-specific declaration + void *operator new( + std::size_t size, + const std::nothrow_t + &) noexcept; // NON_COMPLIANT: class-specific nothrow declaration + void *operator new(std::size_t size, + void *ptr) noexcept; // NON_COMPLIANT: class-specific + // placement declaration + void *operator new[](std::size_t size, + void *ptr) noexcept; // NON_COMPLIANT: class-specific + // placement declaration + void * + operator new(std::size_t size, + int hint); // NON_COMPLIANT: class-specific custom declaration + void * + operator new(std::size_t size, double alignment, + int pool); // NON_COMPLIANT: class-specific custom declaration + void operator delete(void *ptr, + void *) noexcept; // NON_COMPLIANT: class-specific + // placement delete declaration + void operator delete[](void *ptr, + void *) noexcept; // NON_COMPLIANT: class-specific + // placement delete[] declaration + void operator delete(void *ptr, + int hint) noexcept; // NON_COMPLIANT: class-specific + // custom delete declaration +}; + +/** + * Class-specific operator definitions - NON_COMPLIANT (any signature) + */ +void *C1::operator new(std::size_t size) { + return std::malloc(size); +} // NON_COMPLIANT: class-specific +void *C1::operator new[](std::size_t size) { + return std::malloc(size); +} // NON_COMPLIANT: class-specific +void C1::operator delete(void *ptr) noexcept { + std::free(ptr); +} // NON_COMPLIANT: class-specific +void C1::operator delete[](void *ptr) noexcept { + std::free(ptr); +} // NON_COMPLIANT: class-specific +void *C1::operator new(std::size_t size, const std::nothrow_t &) noexcept { + return std::malloc(size); +} // NON_COMPLIANT: class-specific nothrow +void *C1::operator new(std::size_t size, void *ptr) noexcept { + return ptr; +} // NON_COMPLIANT: class-specific placement +void *C1::operator new[](std::size_t size, void *ptr) noexcept { + return ptr; +} // NON_COMPLIANT: class-specific placement +void *C1::operator new(std::size_t size, int hint) { + return std::malloc(size); +} // NON_COMPLIANT: class-specific custom +void *C1::operator new(std::size_t size, double alignment, int pool) { + return std::malloc(size); +} // NON_COMPLIANT: class-specific custom + +/** + * Fixture class with class-specific operator new/delete inline definitions. + */ +class C2 { +public: + C2() {} + + // Class-specific operator inline definitions - NON_COMPLIANT (any signature) + void *operator new(std::size_t size) { + return std::malloc(size); + } // NON_COMPLIANT: class-specific inline + void *operator new[](std::size_t size) { + return std::malloc(size); + } // NON_COMPLIANT: class-specific inline + void operator delete(void *ptr) noexcept { + std::free(ptr); + } // NON_COMPLIANT: class-specific inline + void operator delete[](void *ptr) noexcept { + std::free(ptr); + } // NON_COMPLIANT: class-specific inline + void *operator new(std::size_t size, const std::nothrow_t &) noexcept { + return std::malloc(size); + } // NON_COMPLIANT: class-specific nothrow inline + void *operator new(std::size_t size, void *ptr) noexcept { + return ptr; + } // NON_COMPLIANT: class-specific placement inline + void *operator new[](std::size_t size, void *ptr) noexcept { + return ptr; + } // NON_COMPLIANT: class-specific placement inline + void *operator new(std::size_t size, int hint) { + return std::malloc(size); + } // NON_COMPLIANT: class-specific custom inline + void *operator new(std::size_t size, double alignment, int pool) { + return std::malloc(size); + } // NON_COMPLIANT: class-specific custom inline +}; + +/** + * Re-declared allocation / deallocations functions - NON_COMPLIANT + */ +void *operator new( + std::size_t size); // NON_COMPLIANT: re-declaring global replaceable +void *operator new(std::size_t size) { + return std::malloc(size); +} // NON_COMPLIANT: implementing global replaceable +void operator delete(void *ptr) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable +void *operator new[](std::size_t size) { + return std::malloc(size); +} // NON_COMPLIANT: implementing global replaceable +void operator delete[](void *ptr) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable +void *operator new(std::size_t size, const std::nothrow_t &) noexcept { + return std::malloc(size); +} // NON_COMPLIANT: implementing global replaceable nothrow +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept { + return std::malloc(size); +} // NON_COMPLIANT: implementing global replaceable nothrow +void operator delete(void *ptr, std::size_t) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable sized +void operator delete[](void *ptr, std::size_t) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable sized +void operator delete(void *ptr, const std::nothrow_t &) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable placement deallocation +void operator delete[](void *ptr, const std::nothrow_t &) noexcept { + std::free(ptr); +} // NON_COMPLIANT: implementing global replaceable placement deallocation + +/** + * Global non-standard forms - NON_COMPLIANT + * These are not in the four replaceable categories. + */ +void * +operator new(std::size_t size, + void *ptr) noexcept; // NON_COMPLIANT: user-declared placement new +void *operator new[]( + std::size_t size, + void *ptr) noexcept; // NON_COMPLIANT: user-declared placement new[] +void *operator new(std::size_t size, int hint) { + return std::malloc(size); +} // NON_COMPLIANT: custom parameter +void *operator new(std::size_t size, double alignment, int pool) { + return std::malloc(size); +} // NON_COMPLIANT: custom parameters +void operator delete( + void *ptr, + void *) noexcept; // NON_COMPLIANT: user-declared placement delete +void operator delete[]( + void *ptr, + void *) noexcept; // NON_COMPLIANT: user-declared placement delete[] +void operator delete(void *ptr, int hint) noexcept { + std::free(ptr); +} // NON_COMPLIANT: custom parameter + +/** + * Test placement new expressions. + */ +void use_placement_new() { + std::size_t size = 10; + alignas(C1) std::byte buffer[sizeof(C1)]; + C1 *p1 = new (buffer) C1; // NON_COMPLIANT: placement new expression + C1 *p2 = new (buffer) + C1{}; // NON_COMPLIANT: placement new expression with brace init + + alignas(C1) std::byte arr_buffer[sizeof(C1) * 10]; + C1 *p3 = + new (arr_buffer) C1[size]; // NON_COMPLIANT: placement new[] expression + + void *mem = std::malloc(sizeof(C1)); // NON_COMPLIANT (Rule 21.6.2): malloc + C1 *p4 = new (mem) C1; // NON_COMPLIANT: placement new expression +} + +/** + * Test custom-parameter new expressions. + */ +void use_custom_new_expressions() { + C1 *p1 = new (42) C1; // NON_COMPLIANT: uses operator new(size_t, int) + C1 *p2 = + new (3.14, 1) C1; // NON_COMPLIANT: uses operator new(size_t, double, int) +} + +/** + * Test taking address of global placement new. + */ +void take_address_of_placement_new() { + void *(*c1)(std::size_t) = + &::operator new; // COMPLIANT: address of replaceable new + void *(*c2)(std::size_t) = + ::operator new; // COMPLIANT: implicit address of replaceable new + void *(*c3)(std::size_t) = + &::operator new[]; // COMPLIANT: address of replaceable new[] + void *(*c4)(std::size_t) = + ::operator new[]; // COMPLIANT: implicit address of replaceable new[] + void *(*c5)(std::size_t, const std::nothrow_t &) = + &::operator new; // COMPLIANT: address of nothrow new + void *(*c6)(std::size_t, const std::nothrow_t &) = + &::operator new[]; // COMPLIANT: address of nothrow new[] + + // Non-compliant: taking address of non-replaceable allocation functions + void *(*p1)(std::size_t, void *) = + &::operator new; // NON_COMPLIANT: address of placement new + void *(*p2)(std::size_t, void *) = + ::operator new; // NON_COMPLIANT: implicit address of placement new + void *(*p3)(std::size_t, void *) = + &::operator new[]; // NON_COMPLIANT: address of placement new[] + void *(*p4)(std::size_t, void *) = + ::operator new[]; // NON_COMPLIANT: implicit address of placement new[] +} + +/** + * Test taking address of global placement delete. + */ +void take_address_of_placement_delete() { + // Compliant: taking address of replaceable deallocation functions + void (*c1)(void *) = + &::operator delete; // COMPLIANT: address of replaceable delete + void (*c2)(void *) = + ::operator delete; // COMPLIANT: implicit address of replaceable delete + void (*c3)(void *) = + &::operator delete[]; // COMPLIANT: address of replaceable delete[] + void (*c4)(void *) = ::operator delete[]; // COMPLIANT: implicit address of + // replaceable delete[] + void (*c5)(void *, std::size_t) = + &::operator delete; // COMPLIANT: address of sized delete + void (*c6)(void *, std::size_t) = + &::operator delete[]; // COMPLIANT: address of sized delete[] + void (*c7)(void *, const std::nothrow_t &) = + &::operator delete; // COMPLIANT: address of nothrow delete + void (*c8)(void *, const std::nothrow_t &) = + &::operator delete[]; // COMPLIANT: address of nothrow delete[] + + // Non-compliant: taking address of non-replaceable deallocation functions + void (*p1)(void *, void *) = + &::operator delete; // NON_COMPLIANT: address of placement delete + void (*p2)(void *, void *) = + ::operator delete; // NON_COMPLIANT: implicit address of placement delete + void (*p3)(void *, void *) = + &::operator delete[]; // NON_COMPLIANT: address of placement delete[] + void (*p4)(void *, void *) = + ::operator delete[]; // NON_COMPLIANT: implicit address of placement + // delete[] +} + +/** + * Test taking address of class-specific operator new. + */ +void take_address_of_class_specific_new() { + void *(*p1)(std::size_t) = + &C1::operator new; // COMPLIANT: address of class-specific replaceable + // allocation function + void *(*p2)(std::size_t) = + &C1::operator new[]; // COMPLIANT: address of class-specific replaceable + // allocation function + void *(*p3)(std::size_t, const std::nothrow_t &) = + &C1::operator new; // COMPLIANT: address of class-specific replaceable + // non-throwing allocation function + void *(*p4)(std::size_t, void *) = + &C1::operator new; // NON_COMPLIANT: address of class-specific placement + // new + void *(*p5)(std::size_t, int) = + &C1::operator new; // NON_COMPLIANT: address of class-specific custom new +} + +/** + * Test taking address of class-specific operator delete. + */ +void take_address_of_class_specific_delete() { + void (*p1)(void *) = + &C1::operator delete; // COMPLIANT: address of class-specific + // replaceable deallocation function + void (*p2)(void *) = + &C1::operator delete[]; // COMPLIANT: address of class-specific + // replaceable deallocation function + void (*p3)(void *, void *) = + &C1::operator delete; // NON_COMPLIANT: address of class-specific + // placement delete + void (*p4)(void *, void *) = + &C1::operator delete[]; // NON_COMPLIANT: address of class-specific + // placement delete[] + void (*p5)(void *, int) = + &C1::operator delete; // NON_COMPLIANT: address of class-specific custom + // delete +} + +/** + * Test std::uninitialized_default_construct and + * std::uninitialized_default_construct_n. + */ +void use_uninitialized_default_construct() { + std::size_t size = 10; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *begin = reinterpret_cast(buffer); + C1 *end = begin + size; + + std::uninitialized_default_construct(begin, end); // NON_COMPLIANT + std::uninitialized_default_construct_n(begin, size); // NON_COMPLIANT +} + +/** + * Test std::uninitialized_value_construct and + * std::uninitialized_value_construct_n. + */ +void use_uninitialized_value_construct() { + std::size_t size = 10; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *begin = reinterpret_cast(buffer); + C1 *end = begin + size; + + std::uninitialized_value_construct(begin, end); // NON_COMPLIANT + std::uninitialized_value_construct_n(begin, size); // NON_COMPLIANT +} + +/** + * Test std::uninitialized_copy and std::uninitialized_copy_n. + */ +void use_uninitialized_copy() { + std::size_t size = 10; + C1 source[10]; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *dest = reinterpret_cast(buffer); + + std::uninitialized_copy(source, source + size, dest); // NON_COMPLIANT + std::uninitialized_copy_n(source, size, dest); // NON_COMPLIANT +} + +/** + * Test std::uninitialized_move and std::uninitialized_move_n. + */ +void use_uninitialized_move() { + std::size_t size = 10; + C1 source[10]; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *dest = reinterpret_cast(buffer); + + std::uninitialized_move(source, source + size, dest); // NON_COMPLIANT + std::uninitialized_move_n(source, size, dest); // NON_COMPLIANT +} + +/** + * Test std::uninitialized_fill and std::uninitialized_fill_n. + */ +void use_uninitialized_fill() { + std::size_t size = 10; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *begin = reinterpret_cast(buffer); + C1 *end = begin + size; + C1 value; + + std::uninitialized_fill(begin, end, value); // NON_COMPLIANT + std::uninitialized_fill_n(begin, size, value); // NON_COMPLIANT +} + +/** + * Test std::destroy, std::destroy_n, and std::destroy_at. + */ +void use_destroy() { + std::size_t size = 10; + alignas(C1) std::byte buffer[sizeof(C1) * 10]; + C1 *begin = reinterpret_cast(buffer); + C1 *end = begin + size; + + std::uninitialized_default_construct(begin, end); // NON_COMPLIANT + + std::destroy(begin, end); // NON_COMPLIANT + std::destroy_n(begin, size); // NON_COMPLIANT + std::destroy_at(begin); // NON_COMPLIANT +} + +/** + * Test std::launder. + */ +void use_launder() { + alignas(C1) std::byte buffer[sizeof(C1)]; + C1 *p1 = new (buffer) C1; // NON_COMPLIANT: placement new + p1->~C1(); // NON_COMPLIANT: explicit destructor + C1 *p2 = new (buffer) C1; // NON_COMPLIANT: placement new + + C1 *p3 = std::launder(p1); // NON_COMPLIANT: use of std::launder +} + +/** + * Test explicit destructor call via pointer. + */ +void use_explicit_destructor() { + C1 obj; + C1 *p = &obj; + + p->~C1(); // NON_COMPLIANT: explicit destructor call +} + +/** + * Test explicit destructor call via reference. + */ +void use_explicit_destructor_via_reference() { + C1 obj; + C1 &ref = obj; + + ref.~C1(); // NON_COMPLIANT: explicit destructor call +} + +/** + * Test explicit destructor call in array loop. + */ +void use_explicit_destructor_array() { + std::size_t size = 10; + C1 arr[10]; + + for (std::size_t i = 0; i < size; ++i) { + arr[i].~C1(); // NON_COMPLIANT: explicit destructor call + } +} + +int main() { return 0; } diff --git a/rule_packages/cpp/Memory5.json b/rule_packages/cpp/Memory5.json new file mode 100644 index 0000000000..56c4c7eb56 --- /dev/null +++ b/rule_packages/cpp/Memory5.json @@ -0,0 +1,24 @@ +{ + "MISRA-C++-2023": { + "RULE-21-6-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Dynamically allocated memory must not be managed manually.", + "kind": "problem", + "name": "Dynamic memory shall be managed automatically", + "precision": "very-high", + "severity": "error", + "short_name": "DynamicMemoryManagedManually", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Dynamically allocated memory must not be managed manually." + } + } +} diff --git a/rule_packages/cpp/Memory6.json b/rule_packages/cpp/Memory6.json new file mode 100644 index 0000000000..a4373ed46c --- /dev/null +++ b/rule_packages/cpp/Memory6.json @@ -0,0 +1,24 @@ +{ + "MISRA-C++-2023": { + "RULE-21-6-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using advanced memory management that either alters allocation and deallocation or constructs object construction on uninitalized memory may result in undefined behavior.", + "kind": "problem", + "name": "Advanced memory management shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "AdvancedMemoryManagementUsed", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Using advanced memory management that either alters allocation and deallocation or constructs object construction on uninitalized memory may result in undefined behavior." + } + } +} diff --git a/rules.csv b/rules.csv index dcc5ee5c5f..2fc3be15ca 100644 --- a/rules.csv +++ b/rules.csv @@ -979,8 +979,8 @@ cpp,MISRA-C++-2023,RULE-21-2-2,Yes,Required,Decidable,Single Translation Unit,"T cpp,MISRA-C++-2023,RULE-21-2-3,Yes,Required,Decidable,Single Translation Unit,The library function system from shall not be used,M18-0-3,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-21-2-4,Yes,Required,Decidable,Single Translation Unit,The macro offsetof shall not be used,M18-2-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-21-6-1,Yes,Advisory,Undecidable,Single Translation Unit,Dynamic memory should not be used,DIR-4-12,Banned,Easy, -cpp,MISRA-C++-2023,RULE-21-6-2,Yes,Required,Decidable,Single Translation Unit,Dynamic memory shall be managed automatically,,Memory,Easy, -cpp,MISRA-C++-2023,RULE-21-6-3,Yes,Required,Decidable,Single Translation Unit,Advanced memory management shall not be used,,Memory,Medium, +cpp,MISRA-C++-2023,RULE-21-6-2,Yes,Required,Decidable,Single Translation Unit,Dynamic memory shall be managed automatically,,Memory5,Easy, +cpp,MISRA-C++-2023,RULE-21-6-3,Yes,Required,Decidable,Single Translation Unit,Advanced memory management shall not be used,,Memory6,Medium, cpp,MISRA-C++-2023,RULE-21-6-4,Yes,Required,Decidable,System,"If a project defines either a sized or unsized version of a global operator delete, then both shall be defined",A18-5-4,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-21-6-5,Yes,Required,Decidable,Single Translation Unit,A pointer to an incomplete class type shall not be deleted,A5-3-3,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-21-10-1,Yes,Required,Decidable,Single Translation Unit,The features of shall not be used,DCL50-CPP,BannedAPIs,Easy,