Modify rules S946,S2107,S3470,S3490,S5312,S6183: Fix links & minor grammar improvements (#3737)
This commit is contained in:
parent
2c7f2531a5
commit
fa678815a7
@ -1,4 +1,4 @@
|
||||
Partially-initialized objects are surprising to the `class` users
|
||||
Partially initialized objects are surprising to the `class` users
|
||||
and might lead to hard-to-catch bugs.
|
||||
``class``es with constructors are expected to have all members initialized after their constructor finishes.
|
||||
|
||||
@ -27,23 +27,23 @@ struct PartInit {
|
||||
};
|
||||
----
|
||||
|
||||
This leads to undefined behavior in benign-looking code like on the example below.
|
||||
This leads to undefined behavior in benign-looking code, like in the example below.
|
||||
In this particular case, garbage value may be printed,
|
||||
or a compiler may optimize away the print statement completely.
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
PartInit pi(1);
|
||||
std::cout <<pi.y; // Undefined behavior
|
||||
std::cout << pi.y; // Undefined behavior
|
||||
----
|
||||
|
||||
For this reason, constructors should always initialize all data members of a class.
|
||||
|
||||
While in some cases data members are initialized by their default constructor,
|
||||
While in some cases, data members are initialized by their default constructor,
|
||||
in others, they are left with garbage.
|
||||
|
||||
Types with a ``++default++``ed or implicit trivial default constructor follow the aggregate initialization syntax:
|
||||
if you omit them in the initialization list, they will not be initialized.
|
||||
if you omit them from the initialization list, they will not be initialized.
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
@ -125,7 +125,7 @@ struct ContainerPartial {
|
||||
|
||||
=== What is the potential impact?
|
||||
|
||||
It is a common expectation that an object be in a fully-initialized state after its construction.
|
||||
It is a common expectation that an object is in a fully-initialized state after its construction.
|
||||
A partially initialized object breaks this assumption.
|
||||
|
||||
This comes with all the risks associated with uninitialized variables,
|
||||
@ -141,7 +141,7 @@ include::../../../shared_content/cfamily/garbage_value_impact.adoc[]
|
||||
Aggregate classes do not initialize most of their data members
|
||||
(unless you explicitly value initialize them with `x{}` or `x()`)
|
||||
but allow their users to use nice and flexible initialization syntax.
|
||||
They will be ignored by this rule (but are the subject of rule S5558).
|
||||
This rule ignores them.
|
||||
|
||||
== How to fix it
|
||||
|
||||
|
@ -19,7 +19,7 @@ ____
|
||||
. The behavior of a {cpp} program is undefined if it adds declarations or definitions to namespace posix or to a namespace within namespace posix unless otherwise specified. The namespace posix is reserved for use by ISO/IEC 9945 and other POSIX standards.
|
||||
____
|
||||
|
||||
You may think that it's legitimate to reopen ``++std++`` to define a version of extension points (``++std::swap++``, ``++std::hash++``...) that work with your types, but it's not necessary: If you call these extension points according to the correct pattern (see for instance S5963 for ``++swap++``), user-defined version will be found too.
|
||||
You may think that it's legitimate to reopen ``++std++`` to define a version of extension points (``++std::swap++``, ``++std::hash++``...) that work with your types, but it's not necessary: If you call these extension points according to the correct pattern, the user-defined version will be found too.
|
||||
|
||||
|
||||
This rule raises an issue for any modification of the standard ``++std++`` and ``++posix++`` namespaces.
|
||||
@ -48,7 +48,7 @@ namespace expanded_std {
|
||||
}
|
||||
namespace MyNamespace {
|
||||
class MyType {/*...*/};
|
||||
void swap(MyType &m1, MyType &m2); // See also S5963 to see how to properly call it
|
||||
void swap(MyType &m1, MyType &m2);
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -3,17 +3,17 @@
|
||||
All special member functions (default constructor, copy and move constructors, copy and move assignment operators, destructor) can be automatically generated by the compiler if you don't prevent it (for many classes, it is good practice to organize your code so that you can use these default versions, see S4963).
|
||||
|
||||
|
||||
There are cases where it's still useful to manually write such a function, because the default implementation is not doing what you need. But if the manually written function is equivalent to the default implementation, this is an issue:
|
||||
There are cases where it's still useful to manually write such a function because the default implementation is not doing what you need. But when the manually written function is equivalent to the default implementation, this is an issue because:
|
||||
|
||||
* It's more code to write, test and maintain for no good reason
|
||||
* Writing the code of those functions correctly is surprisingly difficult
|
||||
* It's more code to write, test, and maintain for no good reason
|
||||
* Correctly writing the code of those functions is surprisingly difficult
|
||||
* Once you write one such function, you will typically have to write several (see S3624)
|
||||
* If you want your class to be _trivial_ or to be an _aggregate_, those functions cannot be user-provided anyways
|
||||
|
||||
In most cases, you should just remove the code of the redundant function. In some cases, the compiler will not automatically generate the default version of the function, but you can force it to do so by using the ``++= default++`` syntax.
|
||||
|
||||
|
||||
For default constructors, you will often be able to use the default version if you use in-class initialization instead of the initializer list (see S5424). You will have to make it explicitly defaulted if your class has any other constructor.
|
||||
For default constructors, you can often use the default version if you use in-class initialization instead of the initializer list. You must make it explicitly defaulted if your class has any other constructor.
|
||||
|
||||
|
||||
For destructors, you may want to use the ``++=default++`` syntax to be able to declare it as virtual (see S1235).
|
||||
@ -70,6 +70,7 @@ struct Book {
|
||||
== Resources
|
||||
|
||||
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md#c30-define-a-destructor-if-a-class-needs-an-explicit-action-at-object-destruction[C.30: Define a destructor if a class needs an explicit action at object destruction]
|
||||
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md#c48-prefer-in-class-initializers-to-member-initializers-in-constructors-for-constant-initializers[C.48: Prefer in-class initializers to member initializers in constructors for constant initializers]
|
||||
|
||||
|
||||
ifdef::env-github,rspecator-view[]
|
||||
|
@ -3,9 +3,6 @@
|
||||
This rule is a strict implementation of a MISRA (Motor Industry Software Reliability Association) rule. MISRA defines best practices for developing safety-critical software. You can learn more about this rule in the MISRA documents referenced below.
|
||||
|
||||
|
||||
If you are not concerned with MISRA compliance or with safety-critical software, you might consider using rule S888 instead ; it targets the same kind of issue for general-purpose software.
|
||||
|
||||
|
||||
== Resources
|
||||
|
||||
* MISRA {cpp}2008, 6-5-2
|
||||
@ -16,6 +13,6 @@ ifdef::env-github,rspecator-view[]
|
||||
== Comments And Links
|
||||
(visible only on this page)
|
||||
|
||||
=== is related to: S888
|
||||
=== is related to: S888 (not for C/{cpp})
|
||||
|
||||
endif::env-github,rspecator-view[]
|
||||
|
@ -2,13 +2,13 @@ Functions from the ``++std::cmp_*++`` family should be used to compare signed an
|
||||
|
||||
== Why is this an issue?
|
||||
|
||||
Comparisons between ``++signed++`` and ``++unsigned++`` integers are dangerous because they produce counterintuitive results outside of their shared value range.
|
||||
Comparisons between ``++signed++`` and ``++unsigned++`` integers are dangerous because they produce counterintuitive results outside their shared value range.
|
||||
|
||||
When a signed integer is compared to an unsigned one, the former might be converted to unsigned.
|
||||
The conversion preserves the two's-complement bit pattern of the signed value that often corresponds to a large unsigned result.
|
||||
The expression ``++2U < -1++`` evaluates to ``++true++``, for instance.
|
||||
|
||||
{cpp}20 introduced remedy to this common pitfall: a family of ``++std::cmp_*++`` functions defined in the ``++<utility>++`` header:
|
||||
{cpp}20 introduced a remedy to this common pitfall: a family of ``++std::cmp_*++`` functions defined in the ``++<utility>++`` header:
|
||||
|
||||
* ``++std::cmp_equal++``
|
||||
* ``++std::cmp_not_equal++``
|
||||
@ -56,7 +56,7 @@ int main(int argc, char **argv) {
|
||||
if (user_input == 0xBEEF) {
|
||||
printf("Whoopsie daisy, ...\n");
|
||||
// A malicious user can craft input arguments such that the flow of control
|
||||
// passes through this call to `execl` which opens a new shell with this
|
||||
// passes through this call to `execl`, which opens a new shell with this
|
||||
// program's (possibly elevated) permissions.
|
||||
execl("/bin/bash", "bash", (char *)NULL);
|
||||
} else {
|
||||
@ -66,26 +66,26 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
----
|
||||
|
||||
The program takes as arguments a string and its size, and uses these arguments to copy the string argument into an internal buffer.
|
||||
Before copying the string into its internal buffer it checks whether the user-provided string fits into the buffer.
|
||||
The program also comprises a call to `execl` that opens a shell with the program's possibly elevated permissions -- a potentially dangerous endeavour.
|
||||
Even though the call to `execl` seems unreachable at a first glance, it can actually be reached due to signed/unsigned integer conversion.
|
||||
The program takes a string and its size as arguments and uses these arguments to copy the string argument into an internal buffer.
|
||||
Before copying the string into its internal buffer, it checks whether the user-provided string fits into the buffer.
|
||||
The program also comprises a call to `execl` that opens a shell with the program's possibly elevated permissions -- a potentially dangerous endeavor.
|
||||
Even though the call to `execl` seems unreachable at first glance, it can actually be reached due to signed/unsigned integer conversion.
|
||||
|
||||
The check for the buffer size only validates that the provided string length (`user_input`) is smaller or equal to the buffer's size.
|
||||
Since the `atoi` function returns a signed integer, a user may provide a negative number to withstand that check.
|
||||
The result of `sizeof(*)` on the other hand returns an unsigned integer which causes the expression `user_input * sizeof(char)` to be evaluated by
|
||||
On the other hand, the result of `sizeof(*)` returns an unsigned integer which causes the expression `user_input * sizeof(char)` to be evaluated by
|
||||
|
||||
. converting both operands to unsigned integers,
|
||||
. performing the multiplication, and
|
||||
. returning the result as an unsigned integer type.
|
||||
|
||||
A malicious user is hence able to provide carefully crafted negative integer and string to bypass the size check while still arriving at the appropriate size argument to not crash `memcpy`.
|
||||
This, in turn, enables the malicious user to overflow the buffer variable `buf` to override the `user_input` variable which allows the second `if` statement to be evaluated to true, eventually opening a new shell with the target program's possibly elevated permissions.
|
||||
Hence, a malicious user can provide carefully crafted negative integer and string to bypass the size check while still arriving at the appropriate size argument to not crash `memcpy`.
|
||||
In turn, this enables the malicious user to overflow the buffer variable `buf` to override the `user_input` variable, which allows the second `if` statement to be evaluated to true, eventually opening a new shell with the target program's possibly elevated permissions.
|
||||
|
||||
|
||||
== How to fix it
|
||||
|
||||
Use the appropriate function from the ``++std::cmp_*++`` family to conduct comparisons between signed and unsigned integer types.
|
||||
Use the appropriate function from the ``++std::cmp_*++`` family to compare signed and unsigned integer types.
|
||||
|
||||
|
||||
=== Code examples
|
||||
@ -131,10 +131,10 @@ bool fun(int x, std::vector<int> const& v) {
|
||||
|
||||
Note that this rule (S6183) deliberately avoids intersection with S6214.
|
||||
|
||||
While S6214 raises an issue if the signed value can be proven to be negative (in which case it is definitely a bug), S6281 will flag all *other* comparisons between signed and unsigned integers.
|
||||
While S6214 raises an issue if the signed value can be proven to be negative (in which case it is definitely a bug), S6183 will flag all *other* comparisons between signed and unsigned integers.
|
||||
Therefore, if this rule is enabled, S6214 should be enabled too.
|
||||
|
||||
The following code snippet is hence compliant with S6183, but noncompliant with S6214 which will raise an issue on this definite bug.
|
||||
The following code snippet is compliant with S6183 but noncompliant with S6214, which will raise an issue on this definite bug.
|
||||
|
||||
[source,cpp,diff-id=3,diff-type=noncompliant]
|
||||
----
|
||||
|
@ -233,14 +233,14 @@ void caller() {
|
||||
* MISRA {cpp}:2008, 7-5-2 - The address of an object with automatic storage shall not be assigned to another object that may persist after the first object has ceased to exist
|
||||
* MISRA C:2012, 18.6 - The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist
|
||||
|
||||
=== Related rules
|
||||
|
||||
ifdef::env-github,rspecator-view[]
|
||||
|
||||
=== Related rules but not implemented
|
||||
|
||||
* S837 detects attempts to return addresses of automatic variables
|
||||
* S839 ensures that functions do not return references or pointers to parameters that are passed by reference
|
||||
|
||||
|
||||
ifdef::env-github,rspecator-view[]
|
||||
|
||||
'''
|
||||
== Implementation Specification
|
||||
(visible only on this page)
|
||||
|
@ -3,7 +3,7 @@ Using garbage values will cause the program to behave
|
||||
nondeterministically at runtime.
|
||||
The program may produce a different output or crash depending on the run.
|
||||
|
||||
In some situations, loading a variable may expose a sensitive data,
|
||||
In some situations, loading a variable may expose sensitive data,
|
||||
such as a password that was previously stored in the same location,
|
||||
leading to a vulnerability that uses such a defect
|
||||
as a gadget for extracting information from the instance
|
||||
|
Loading…
x
Reference in New Issue
Block a user