Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import Operators
import OrderOfEvaluation
import OutOfBounds
import Pointers
import Preconditions4
import Representation
import Scope
import SideEffects1
Expand Down Expand Up @@ -103,6 +104,7 @@ newtype TCPPQuery =
TOrderOfEvaluationPackageQuery(OrderOfEvaluationQuery q) or
TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or
TPointersPackageQuery(PointersQuery q) or
TPreconditions4PackageQuery(Preconditions4Query q) or
TRepresentationPackageQuery(RepresentationQuery q) or
TScopePackageQuery(ScopeQuery q) or
TSideEffects1PackageQuery(SideEffects1Query q) or
Expand Down Expand Up @@ -162,6 +164,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isOrderOfEvaluationQueryMetadata(query, queryId, ruleId, category) or
isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or
isPointersQueryMetadata(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
Expand Down
6 changes: 6 additions & 0 deletions cpp/common/src/codingstandards/cpp/standardlibrary/Errno.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import cpp

predicate isErrno(VariableAccess va) {
va.getTarget().hasName("errno") or
va.getTarget().hasName("__errno")
}
3 changes: 3 additions & 0 deletions cpp/common/test/includes/standard-library/errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
#define _GHLIBCPP_ERRNO
int __errno;
#define errno __errno
#define EINVAL 22
#define ERANGE 34
#define EDOM 33
#endif // _GHLIBCPP_ERRNO
2 changes: 2 additions & 0 deletions cpp/common/test/includes/standard-library/string
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand Down
35 changes: 35 additions & 0 deletions cpp/misra/src/rules/RULE-22-4-1/InvalidAssignmentToErrno.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @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 non-zero literal value '" + rvalue.toString() + "'."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with copilot that the message can be improved, see for example the results for line 86 and 128 in the test...
I'd remove literal as bad values are often not literal.
I also tried the following to have a value closer to the source but maybe it's better to just omit the value alltogether

Suggested change
message = "Assignment to 'errno' with non-zero literal value '" + rvalue.toString() + "'."
message = "Assignment to 'errno' with non-zero value '" + assign.getRValue() + "'."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, will do. Thanks Mauro! :)

or
assign instanceof AssignOperation and
message =
"Compound assignment to 'errno' with operator '" + assign.getOperator() + "' is not allowed."
)
select assign, message
37 changes: 37 additions & 0 deletions cpp/misra/test/rules/RULE-22-4-1/InvalidAssignmentToErrno.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
| test.cpp:26:3:26:13 | ... = ... | Assignment to 'errno' with non-zero literal value '0.0'. |
| test.cpp:27:3:27:14 | ... = ... | Assignment to 'errno' with non-zero literal value '0.0'. |
| test.cpp:31:3:31:11 | ... = ... | Assignment to 'errno' with non-zero literal value '1'. |
| test.cpp:32:3:32:12 | ... = ... | Assignment to 'errno' with non-zero literal value '42'. |
| test.cpp:33:3:33:12 | ... = ... | Assignment to 'errno' with non-zero literal value '- ...'. |
| test.cpp:39:3:39:22 | ... = ... | Assignment to 'errno' with non-zero literal value '42'. |
| test.cpp:49:3:49:12 | ... = ... | Assignment to 'errno' with non-zero literal value 'l1'. |
| test.cpp:50:3:50:12 | ... = ... | Assignment to 'errno' with non-zero literal value 'l2'. |
| test.cpp:51:3:51:12 | ... = ... | Assignment to 'errno' with non-zero literal value 'l3'. |
| test.cpp:52:3:52:12 | ... = ... | Assignment to 'errno' with non-zero literal value 'l4'. |
| test.cpp:53:3:53:12 | ... = ... | Assignment to 'errno' with non-zero literal value 'l5'. |
| test.cpp:57:3:57:16 | ... = ... | Assignment to 'errno' with non-zero literal value '22'. |
| test.cpp:58:3:58:16 | ... = ... | Assignment to 'errno' with non-zero literal value '34'. |
| test.cpp:59:3:59:14 | ... = ... | Assignment to 'errno' with non-zero literal value '33'. |
| test.cpp:63:3:63:15 | ... = ... | Assignment to 'errno' with non-zero literal value '... + ...'. |
| test.cpp:64:3:64:15 | ... = ... | Assignment to 'errno' with non-zero literal value '... - ...'. |
| test.cpp:65:3:65:15 | ... = ... | Assignment to 'errno' with non-zero literal value '... * ...'. |
| test.cpp:66:3:66:35 | ... = ... | Assignment to 'errno' with non-zero literal value '... - ...'. |
| test.cpp:70:3:70:11 | ... = ... | Assignment to 'errno' with non-zero literal value '5'. |
| 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 non-zero literal value '5'. |
| 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 non-zero literal value '1'. |
| 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 non-zero literal value 'call to operator()'. |
| test.cpp:82:3:82:14 | ... = ... | Assignment to 'errno' with non-zero literal value 'call to operator()'. |
| test.cpp:86:3:86:29 | ... = ... | Assignment to 'errno' with non-zero literal value 'static_cast<int>...'. |
| test.cpp:87:3:87:31 | ... = ... | Assignment to 'errno' with non-zero literal value 'static_cast<int>...'. |
| test.cpp:88:3:88:16 | ... = ... | Assignment to 'errno' with non-zero literal value '(int)...'. |
| test.cpp:89:3:89:16 | ... = ... | Assignment to 'errno' with non-zero literal value '(int)...'. |
| test.cpp:108:3:108:40 | ... = ... | Assignment to 'errno' with non-zero literal value 'reinterpret_cast<int>...'. |
| test.cpp:110:3:110:35 | ... = ... | Assignment to 'errno' with non-zero literal value 'reinterpret_cast<int>...'. |
| test.cpp:111:3:111:35 | ... = ... | Assignment to 'errno' with non-zero literal value 'reinterpret_cast<int>...'. |
| test.cpp:113:3:113:40 | ... = ... | Assignment to 'errno' with non-zero literal value 'reinterpret_cast<int>...'. |
| test.cpp:122:3:122:13 | ... = ... | Assignment to 'errno' with non-zero literal value '48'. |
| test.cpp:128:3:128:14 | ... = ... | Assignment to 'errno' with non-zero literal value '1'. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-22-4-1/InvalidAssignmentToErrno.ql
129 changes: 129 additions & 0 deletions cpp/misra/test/rules/RULE-22-4-1/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include <cerrno>
#include <cstdint>
#include <optional>
#include <string>

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<int>(0); // NON_COMPLIANT - cast expression
errno = static_cast<int>(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<int>(nullptr); // NON_COMPLIANT - nullptr is
// not an integer literal
errno = reinterpret_cast<int>(&x); // NON_COMPLIANT - pointer cast to integer
errno = reinterpret_cast<int>(&f); // NON_COMPLIANT - pointer cast to
// integer
errno = reinterpret_cast<int>(&&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
}
25 changes: 25 additions & 0 deletions rule_packages/cpp/Preconditions4.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
2 changes: 1 addition & 1 deletion rules.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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 <csetjmp> 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 <csignal> 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 <cctype> and <cwctype> 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 <cstring> shall not be used",,BannedAPIs,Easy,
Expand Down
Loading