diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions4.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions4.qll new file mode 100644 index 000000000..057d79792 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions4.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Preconditions4Query = TInvalidAssignmentToErrnoQuery() + +predicate isPreconditions4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `invalidAssignmentToErrno` query + Preconditions4Package::invalidAssignmentToErrnoQuery() and + queryId = + // `@id` for the `invalidAssignmentToErrno` query + "cpp/misra/invalid-assignment-to-errno" and + ruleId = "RULE-22-4-1" and + category = "required" +} + +module Preconditions4Package { + Query invalidAssignmentToErrnoQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidAssignmentToErrno` query + TQueryCPP(TPreconditions4PackageQuery(TInvalidAssignmentToErrnoQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 6d44eb693..6cefa01d8 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -45,6 +45,7 @@ import OrderOfEvaluation import OutOfBounds import Pointers import Preconditions1 +import Preconditions4 import Representation import Scope import SideEffects1 @@ -105,6 +106,7 @@ newtype TCPPQuery = TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or TPointersPackageQuery(PointersQuery q) or TPreconditions1PackageQuery(Preconditions1Query q) or + TPreconditions4PackageQuery(Preconditions4Query q) or TRepresentationPackageQuery(RepresentationQuery q) or TScopePackageQuery(ScopeQuery q) or TSideEffects1PackageQuery(SideEffects1Query q) or @@ -165,6 +167,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or isPointersQueryMetadata(query, queryId, ruleId, category) or isPreconditions1QueryMetadata(query, queryId, ruleId, category) or + isPreconditions4QueryMetadata(query, queryId, ruleId, category) or isRepresentationQueryMetadata(query, queryId, ruleId, category) or isScopeQueryMetadata(query, queryId, ruleId, category) or isSideEffects1QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/Errno.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/Errno.qll new file mode 100644 index 000000000..be5f5cf20 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/Errno.qll @@ -0,0 +1,6 @@ +import cpp + +predicate isErrno(VariableAccess va) { + va.getTarget().hasName("errno") or + va.getTarget().hasName("__errno") +} diff --git a/cpp/common/test/includes/standard-library/errno.h b/cpp/common/test/includes/standard-library/errno.h index be17eeb0b..fe6e9e02c 100644 --- a/cpp/common/test/includes/standard-library/errno.h +++ b/cpp/common/test/includes/standard-library/errno.h @@ -2,4 +2,7 @@ #define _GHLIBCPP_ERRNO int __errno; #define errno __errno +#define EINVAL 22 +#define ERANGE 34 +#define EDOM 33 #endif // _GHLIBCPP_ERRNO \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/string b/cpp/common/test/includes/standard-library/string index cb76a7742..3f60c1838 100644 --- a/cpp/common/test/includes/standard-library/string +++ b/cpp/common/test/includes/standard-library/string @@ -248,6 +248,8 @@ public: int compare(size_type pos1, size_type n1, const charT *s) const; int compare(size_type pos1, size_type n1, const charT *s, size_type n2) const; + bool empty() const noexcept; + void reserve(size_type new_cap = 0); }; diff --git a/cpp/misra/src/rules/RULE-22-4-1/InvalidAssignmentToErrno.ql b/cpp/misra/src/rules/RULE-22-4-1/InvalidAssignmentToErrno.ql new file mode 100644 index 000000000..652a032f6 --- /dev/null +++ b/cpp/misra/src/rules/RULE-22-4-1/InvalidAssignmentToErrno.ql @@ -0,0 +1,37 @@ +/** + * @id cpp/misra/invalid-assignment-to-errno + * @name RULE-22-4-1: The literal value zero shall be the only value assigned to errno + * @description C++ provides better options for error handling than the use of errno. Errno should + * not be used for reporting errors within project code. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-22-4-1 + * scope/single-translation-unit + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.standardlibrary.Errno +import codingstandards.cpp.Literals + +from Assignment assign, VariableAccess errno, Expr rvalue, string message +where + not isExcluded(assign, Preconditions4Package::invalidAssignmentToErrnoQuery()) and + assign.getLValue() = errno and + isErrno(errno) and + assign.getRValue().getExplicitlyConverted() = rvalue and + ( + not rvalue instanceof LiteralZero and + message = + "Assignment to 'errno' with value '" + rvalue.toString() + + "' that is not a zero integer literal." + or + assign instanceof AssignOperation and + message = + "Compound assignment to 'errno' with operator '" + assign.getOperator() + "' is not allowed." + ) +select assign, message diff --git a/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.expected b/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.expected new file mode 100644 index 000000000..407c95869 --- /dev/null +++ b/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.expected @@ -0,0 +1,37 @@ +| test.cpp:26:3:26:13 | ... = ... | Assignment to 'errno' with value '0.0' that is not a zero integer literal. | +| test.cpp:27:3:27:14 | ... = ... | Assignment to 'errno' with value '0.0' that is not a zero integer literal. | +| test.cpp:31:3:31:11 | ... = ... | Assignment to 'errno' with value '1' that is not a zero integer literal. | +| test.cpp:32:3:32:12 | ... = ... | Assignment to 'errno' with value '42' that is not a zero integer literal. | +| test.cpp:33:3:33:12 | ... = ... | Assignment to 'errno' with value '- ...' that is not a zero integer literal. | +| test.cpp:39:3:39:22 | ... = ... | Assignment to 'errno' with value '42' that is not a zero integer literal. | +| test.cpp:49:3:49:12 | ... = ... | Assignment to 'errno' with value 'l1' that is not a zero integer literal. | +| test.cpp:50:3:50:12 | ... = ... | Assignment to 'errno' with value 'l2' that is not a zero integer literal. | +| test.cpp:51:3:51:12 | ... = ... | Assignment to 'errno' with value 'l3' that is not a zero integer literal. | +| test.cpp:52:3:52:12 | ... = ... | Assignment to 'errno' with value 'l4' that is not a zero integer literal. | +| test.cpp:53:3:53:12 | ... = ... | Assignment to 'errno' with value 'l5' that is not a zero integer literal. | +| test.cpp:57:3:57:16 | ... = ... | Assignment to 'errno' with value '22' that is not a zero integer literal. | +| test.cpp:58:3:58:16 | ... = ... | Assignment to 'errno' with value '34' that is not a zero integer literal. | +| test.cpp:59:3:59:14 | ... = ... | Assignment to 'errno' with value '33' that is not a zero integer literal. | +| test.cpp:63:3:63:15 | ... = ... | Assignment to 'errno' with value '... + ...' that is not a zero integer literal. | +| test.cpp:64:3:64:15 | ... = ... | Assignment to 'errno' with value '... - ...' that is not a zero integer literal. | +| test.cpp:65:3:65:15 | ... = ... | Assignment to 'errno' with value '... * ...' that is not a zero integer literal. | +| test.cpp:66:3:66:35 | ... = ... | Assignment to 'errno' with value '... - ...' that is not a zero integer literal. | +| test.cpp:70:3:70:11 | ... = ... | Assignment to 'errno' with value '5' that is not a zero integer literal. | +| test.cpp:71:3:71:12 | ... += ... | Compound assignment to 'errno' with operator '+=' is not allowed. | +| test.cpp:72:3:72:12 | ... -= ... | Assignment to 'errno' with value '5' that is not a zero integer literal. | +| test.cpp:72:3:72:12 | ... -= ... | Compound assignment to 'errno' with operator '-=' is not allowed. | +| test.cpp:73:3:73:12 | ... *= ... | Compound assignment to 'errno' with operator '*=' is not allowed. | +| test.cpp:74:3:74:12 | ... /= ... | Assignment to 'errno' with value '1' that is not a zero integer literal. | +| test.cpp:74:3:74:12 | ... /= ... | Compound assignment to 'errno' with operator '/=' is not allowed. | +| test.cpp:81:3:81:14 | ... = ... | Assignment to 'errno' with value 'call to operator()' that is not a zero integer literal. | +| test.cpp:82:3:82:14 | ... = ... | Assignment to 'errno' with value 'call to operator()' that is not a zero integer literal. | +| test.cpp:86:3:86:29 | ... = ... | Assignment to 'errno' with value 'static_cast...' that is not a zero integer literal. | +| test.cpp:87:3:87:31 | ... = ... | Assignment to 'errno' with value 'static_cast...' that is not a zero integer literal. | +| test.cpp:88:3:88:16 | ... = ... | Assignment to 'errno' with value '(int)...' that is not a zero integer literal. | +| test.cpp:89:3:89:16 | ... = ... | Assignment to 'errno' with value '(int)...' that is not a zero integer literal. | +| test.cpp:108:3:108:40 | ... = ... | Assignment to 'errno' with value 'reinterpret_cast...' that is not a zero integer literal. | +| test.cpp:110:3:110:35 | ... = ... | Assignment to 'errno' with value 'reinterpret_cast...' that is not a zero integer literal. | +| test.cpp:111:3:111:35 | ... = ... | Assignment to 'errno' with value 'reinterpret_cast...' that is not a zero integer literal. | +| test.cpp:113:3:113:40 | ... = ... | Assignment to 'errno' with value 'reinterpret_cast...' that is not a zero integer literal. | +| test.cpp:122:3:122:13 | ... = ... | Assignment to 'errno' with value '48' that is not a zero integer literal. | +| test.cpp:128:3:128:14 | ... = ... | Assignment to 'errno' with value '1' that is not a zero integer literal. | diff --git a/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.qlref b/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.qlref new file mode 100644 index 000000000..cd8c13591 --- /dev/null +++ b/cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.qlref @@ -0,0 +1 @@ +rules/RULE-22-4-1/InvalidAssignmentToErrno.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-22-4-1/test.cpp b/cpp/misra/test/rules/RULE-22-4-1/test.cpp new file mode 100644 index 000000000..d3886dc9d --- /dev/null +++ b/cpp/misra/test/rules/RULE-22-4-1/test.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +void errnoSettingFunction(); +void handleError(); +void f(); + +#define OK 0 +#define CUSTOM_ERROR 42 +#define ZERO_MACRO 0 + +void test_literal_zero_assignment() { + errno = 0; // COMPLIANT +} + +void test_different_zero_literal_formats() { + errno = 0; // COMPLIANT - decimal zero literal + errno = 0x0; // COMPLIANT - hexadecimal zero literal + errno = 00; // COMPLIANT - octal zero literal + errno = 0b0; // COMPLIANT - binary zero literal +} + +void test_floating_point_zero_literals() { + errno = 0.0; // NON_COMPLIANT - floating point literal, not integer literal + errno = 0.0f; // NON_COMPLIANT - floating point literal, not integer literal +} + +void test_non_zero_literal_assignment() { + errno = 1; // NON_COMPLIANT + errno = 42; // NON_COMPLIANT + errno = -1; // NON_COMPLIANT +} + +void test_macro_assignments() { + errno = OK; // COMPLIANT - expands to literal 0 + errno = ZERO_MACRO; // COMPLIANT - expands to literal 0 + errno = CUSTOM_ERROR; // NON_COMPLIANT - expands to non-zero literal +} + +void test_variable_assignments() { + std::uint32_t l1 = 0; + const std::uint32_t l2 = 0; + constexpr std::uint32_t l3 = 0; + std::uint32_t l4 = 42; + const std::uint32_t l5 = 42; + + errno = l1; // NON_COMPLIANT - variable, not literal + errno = l2; // NON_COMPLIANT - constant variable, not literal + errno = l3; // NON_COMPLIANT - constexpr variable, not literal + errno = l4; // NON_COMPLIANT - variable with non-zero value + errno = l5; // NON_COMPLIANT - constant variable with non-zero value +} + +void test_standard_error_macros() { + errno = EINVAL; // NON_COMPLIANT - standard error macro + errno = ERANGE; // NON_COMPLIANT - standard error macro + errno = EDOM; // NON_COMPLIANT - standard error macro +} + +void test_expressions() { + errno = 0 + 0; // NON_COMPLIANT - expression, not literal + errno = 1 - 1; // NON_COMPLIANT - expression, not literal + errno = 0 * 5; // NON_COMPLIANT - expression, not literal + errno = sizeof(int) - sizeof(int); // NON_COMPLIANT - expression, not literal +} + +void test_compound_assignments() { + errno = 5; // NON_COMPLIANT - initial assignment to non-zero value + errno += 0; // NON_COMPLIANT - compound assignment, not simple assignment + errno -= 5; // NON_COMPLIANT - compound assignment, not simple assignment + errno *= 0; // NON_COMPLIANT - compound assignment, not simple assignment + errno /= 1; // NON_COMPLIANT - compound assignment, not simple assignment +} + +void test_function_return_values() { + auto l1 = []() { return 0; }; + auto l2 = []() { return 42; }; + + errno = l1(); // NON_COMPLIANT - function return value, not literal + errno = l2(); // NON_COMPLIANT - function return value, not literal +} + +void test_cast_expressions() { + errno = static_cast(0); // NON_COMPLIANT - cast expression + errno = static_cast(0.0); // NON_COMPLIANT - cast expression + errno = (int)0; // NON_COMPLIANT - C-style cast + errno = int(0); // NON_COMPLIANT - functional cast +} + +void test_reading_errno_is_allowed() { + std::uint32_t l1 = errno; // COMPLIANT - reading errno is allowed + if (errno != 0) { // COMPLIANT - reading errno is allowed + handleError(); + } + + errnoSettingFunction(); + std::uint32_t l2 = 0; + if (errno != l2) { // COMPLIANT - reading errno is allowed + handleError(); + } +} + +void test_pointer_and_null_assignments() { +label: + static const int x = 0; + errno = reinterpret_cast(nullptr); // NON_COMPLIANT - nullptr is + // not an integer literal + errno = reinterpret_cast(&x); // NON_COMPLIANT - pointer cast to integer + errno = reinterpret_cast(&f); // NON_COMPLIANT - pointer cast to + // integer + errno = reinterpret_cast(&&label); // NON_COMPLIANT - pointer + // cast to integer + errno = NULL; // NON_COMPLIANT[FALSE_NEGATIVE] - NULL may expand to 0 but not + // literal +} + +void test_character_literals() { + errno = '\0'; // NON_COMPLIANT[FALSE_NEGATIVE] - character literal, not + // integer literal + errno = '0'; // NON_COMPLIANT - character '0' has value 48 +} + +void test_boolean_literals() { + errno = false; // NON_COMPLIANT[FALSE_NEGATIVE] - boolean literal, not integer + // literal + errno = true; // NON_COMPLIANT - boolean literal with non-zero value +} \ No newline at end of file diff --git a/rule_packages/cpp/Preconditions4.json b/rule_packages/cpp/Preconditions4.json new file mode 100644 index 000000000..21bdfc930 --- /dev/null +++ b/rule_packages/cpp/Preconditions4.json @@ -0,0 +1,25 @@ +{ + "MISRA-C++-2023": { + "RULE-22-4-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "C++ provides better options for error handling than the use of errno. Errno should not be used for reporting errors within project code.", + "kind": "problem", + "name": "The literal value zero shall be the only value assigned to errno", + "precision": "very-high", + "severity": "error", + "short_name": "InvalidAssignmentToErrno", + "tags": [ + "scope/single-translation-unit", + "maintainability" + ] + } + ], + "title": "The literal value zero shall be the only value assigned to errno" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 25c287ecc..a2eb7eddd 100644 --- a/rules.csv +++ b/rules.csv @@ -987,7 +987,7 @@ cpp,MISRA-C++-2023,RULE-21-10-1,Yes,Required,Decidable,Single Translation Unit,T cpp,MISRA-C++-2023,RULE-21-10-2,Yes,Required,Decidable,Single Translation Unit,The standard header file shall not be used,ERR52-CPP,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-21-10-3,Yes,Required,Decidable,Single Translation Unit,The facilities provided by the standard header file shall not be used,M18-7-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-22-3-1,Yes,Required,Decidable,Single Translation Unit,The assert macro shall not be used with a constant-expression,,Preconditions,Easy, -cpp,MISRA-C++-2023,RULE-22-4-1,Yes,Required,Decidable,Single Translation Unit,The literal value zero shall be the only value assigned to errno,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-22-4-1,Yes,Required,Decidable,Single Translation Unit,The literal value zero shall be the only value assigned to errno,,Preconditions4,Easy, cpp,MISRA-C++-2023,RULE-23-11-1,Yes,Advisory,Decidable,Single Translation Unit,The raw pointer constructors of std::shared_ptr and std::unique_ptr should not be used,,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-24-5-1,Yes,Required,Decidable,Single Translation Unit,The character handling functions from and shall not be used,,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-24-5-2,Yes,Required,Decidable,Single Translation Unit,"The C++ Standard Library functions memcpy, memmove and memcmp from shall not be used",,BannedAPIs,Easy,