mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2024-03-22 13:30:58 +08:00
Most of the bounds safety profile
This commit is contained in:
parent
9eb18fdf9e
commit
df160f3654
|
@ -11217,17 +11217,197 @@ You should know enough not to need parentheses for:
|
||||||
|
|
||||||
Complicated pointer manipulation is a major source of errors.
|
Complicated pointer manipulation is a major source of errors.
|
||||||
|
|
||||||
* Do all pointer arithmetic on a `span` (exception ++p in simple loop???)
|
##### Note
|
||||||
* Avoid pointers to pointers
|
|
||||||
* ???
|
Use `gsl::span` instead.
|
||||||
|
Pointers should [only refer to single objects](#Ri-array).
|
||||||
|
Pointer arithmetic is fragile and easy to get wrong, the source of many, many bad bugs and security violations.
|
||||||
|
`span` is a bounds-checked, safe type for accessing arrays of data.
|
||||||
|
Access into an array with known bounds using a constant as a subscript can be validated by the compiler.
|
||||||
|
|
||||||
|
##### Example, bad
|
||||||
|
|
||||||
|
void f(int* p, int count)
|
||||||
|
{
|
||||||
|
if (count < 2) return;
|
||||||
|
|
||||||
|
int* q = p + 1; // BAD
|
||||||
|
|
||||||
|
ptrdiff_t d;
|
||||||
|
int n;
|
||||||
|
d = (p - &n); // OK
|
||||||
|
d = (q - p); // OK
|
||||||
|
|
||||||
|
int n = *p++; // BAD
|
||||||
|
|
||||||
|
if (count < 6) return;
|
||||||
|
|
||||||
|
p[4] = 1; // BAD
|
||||||
|
|
||||||
|
p[count - 1] = 2; // BAD
|
||||||
|
|
||||||
|
use(&p[0], 3); // BAD
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Example, good
|
||||||
|
|
||||||
|
void f(span<int> a) // BETTER: use span in the function declaration
|
||||||
|
{
|
||||||
|
if (a.length() < 2) return;
|
||||||
|
|
||||||
|
int n = a[0]; // OK
|
||||||
|
|
||||||
|
span<int> q = a.subspan(1); // OK
|
||||||
|
|
||||||
|
if (a.length() < 6) return;
|
||||||
|
|
||||||
|
a[4] = 1; // OK
|
||||||
|
|
||||||
|
a[count - 1] = 2; // OK
|
||||||
|
|
||||||
|
use(a.data(), 3); // OK
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
Subscripting with a variable is difficult for both tools and humans to validate as safe.
|
||||||
|
`span` is a run-time bounds-checked, safe type for accessing arrays of data.
|
||||||
|
`at()` is another alternative that ensures single accesses are bounds-checked.
|
||||||
|
If iterators are needed to access an array, use the iterators from a `span` constructed over the array.
|
||||||
|
|
||||||
|
##### Example, bad
|
||||||
|
|
||||||
|
void f(array<int, 10> a, int pos)
|
||||||
|
{
|
||||||
|
a[pos / 2] = 1; // BAD
|
||||||
|
a[pos - 1] = 2; // BAD
|
||||||
|
a[-1] = 3; // BAD (but easily caught by tools) -- no replacement, just don't do this
|
||||||
|
a[10] = 4; // BAD (but easily caught by tools) -- no replacement, just don't do this
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Example, good
|
||||||
|
|
||||||
|
Use a `span`:
|
||||||
|
|
||||||
|
void f1(span<int, 10> a, int pos) // A1: Change parameter type to use span
|
||||||
|
{
|
||||||
|
a[pos / 2] = 1; // OK
|
||||||
|
a[pos - 1] = 2; // OK
|
||||||
|
}
|
||||||
|
|
||||||
|
void f2(array<int, 10> arr, int pos) // A2: Add local span and use that
|
||||||
|
{
|
||||||
|
span<int> a = {arr, pos}
|
||||||
|
a[pos / 2] = 1; // OK
|
||||||
|
a[pos - 1] = 2; // OK
|
||||||
|
}
|
||||||
|
|
||||||
|
Use a `at()`:
|
||||||
|
|
||||||
|
void f3(array<int, 10> a, int pos) // ALTERNATIVE B: Use at() for access
|
||||||
|
{
|
||||||
|
at(a, pos / 2) = 1; // OK
|
||||||
|
at(a, pos - 1) = 2; // OK
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Example, bad
|
||||||
|
|
||||||
|
void f()
|
||||||
|
{
|
||||||
|
int arr[COUNT];
|
||||||
|
for (int i = 0; i < COUNT; ++i)
|
||||||
|
arr[i] = i; // BAD, cannot use non-constant indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Example, good
|
||||||
|
|
||||||
|
Use a `span`:
|
||||||
|
|
||||||
|
void f1()
|
||||||
|
{
|
||||||
|
int arr[COUNT];
|
||||||
|
span<int> av = arr;
|
||||||
|
for (int i = 0; i < COUNT; ++i)
|
||||||
|
av[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Use a `span` and range-`for`:
|
||||||
|
|
||||||
|
void f1a()
|
||||||
|
{
|
||||||
|
int arr[COUNT];
|
||||||
|
span<int, COUNT> av = arr;
|
||||||
|
int i = 0;
|
||||||
|
for (auto& e : av)
|
||||||
|
e = i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Use `at()` for access:
|
||||||
|
|
||||||
|
void f2()
|
||||||
|
{
|
||||||
|
int arr[COUNT];
|
||||||
|
for (int i = 0; i < COUNT; ++i)
|
||||||
|
at(arr, i) = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Use a range-`for`:
|
||||||
|
|
||||||
|
void f3()
|
||||||
|
{
|
||||||
|
int arr[COUNT];
|
||||||
|
for (auto& e : arr)
|
||||||
|
e = i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
Tooling can offer rewrites of array accesses that involve dynamic index expressions to use `at()` instead:
|
||||||
|
|
||||||
|
static int a[10];
|
||||||
|
|
||||||
|
void f(int i, int j)
|
||||||
|
{
|
||||||
|
a[i + j] = 12; // BAD, could be rewritten as ...
|
||||||
|
at(a, i + j) = 12; // OK -- bounds-checked
|
||||||
|
}
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
???
|
Turning an array into a pointer (as the language does essentially always) removes opportunities for checking, so avoid it
|
||||||
|
|
||||||
|
void g(int* p);
|
||||||
|
|
||||||
|
void f()
|
||||||
|
{
|
||||||
|
int a[5];
|
||||||
|
g(a); // BAD: are we trying to pass an array?
|
||||||
|
g(&a[0]); // OK: passing one object
|
||||||
|
}
|
||||||
|
|
||||||
|
If you want to pass an array, say so:
|
||||||
|
|
||||||
|
void g(int* p, size_t length); // old (dangerous) code
|
||||||
|
|
||||||
|
void g1(span<int> av); // BETTER: get g() changed.
|
||||||
|
|
||||||
|
void f2()
|
||||||
|
{
|
||||||
|
int a[5];
|
||||||
|
span<int> av = a;
|
||||||
|
|
||||||
|
g(av.data(), av.length()); // OK, if you have no choice
|
||||||
|
g1(a); // OK -- no decay here, instead use implicit span ctor
|
||||||
|
}
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
We need a heuristic limiting the complexity of pointer arithmetic statement.
|
* Flag any arithmetic operation on an expression of pointer type that results in a value of pointer type.
|
||||||
|
* Flag any indexing expression on an expression or variable of array type (either static array or `std::array`) where the indexer is not a compile-time constant expression with a value between `0` or and the upper bound of the array.
|
||||||
|
* Flag any expression that would rely on implicit conversion of an array type to a pointer type.
|
||||||
|
|
||||||
|
This rule is part of the [bounds-safety profile](#SS-bounds).
|
||||||
|
|
||||||
|
|
||||||
### <a name="Res-order"></a>ES.43: Avoid expressions with undefined order of evaluation
|
### <a name="Res-order"></a>ES.43: Avoid expressions with undefined order of evaluation
|
||||||
|
|
||||||
|
@ -19140,197 +19320,17 @@ An implementation of this profile shall recognize the following patterns in sour
|
||||||
|
|
||||||
Bounds safety profile summary:
|
Bounds safety profile summary:
|
||||||
|
|
||||||
* [Bounds.1: Don't use pointer arithmetic. Use `span` instead](#Pro-bounds-arithmetic)
|
* <a href="Pro-bounds-arithmetic"></a>Bounds.1: Don't use pointer arithmetic. Use `span` instead:
|
||||||
* [Bounds.2: Only index into arrays using constant expressions](#Pro-bounds-arrayindex)
|
[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple).
|
||||||
* [Bounds.3: No array-to-pointer decay](#Pro-bounds-decay)
|
* <a href="Pro-bounds-arrayindex"></a>Bounds.2: Only index into arrays using constant expressions:
|
||||||
* [Bounds.4: Don't use standard library functions and types that are not bounds-checked](#Pro-bounds-stdlib)
|
[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple).
|
||||||
|
* <a href="Pro-bounds-decay"></a>Bounds.3: No array-to-pointer decay:
|
||||||
|
[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple).
|
||||||
|
* <a href="Pro-bounds-stdlib"></a>Bounds.4: Don't use standard library functions and types that are not bounds-checked:
|
||||||
|
[???](#XXX)
|
||||||
|
|
||||||
|
|
||||||
### <a name="Pro-bounds-arithmetic"></a>Bounds.1: Don't use pointer arithmetic. Use `span` instead.
|
### <a name="XXX"></a>Bounds.4: Don't use standard library functions and types that are not bounds-checked.
|
||||||
|
|
||||||
##### Reason
|
|
||||||
|
|
||||||
Pointers should only refer to single objects, and pointer arithmetic is fragile and easy to get wrong. `span` is a bounds-checked, safe type for accessing arrays of data.
|
|
||||||
|
|
||||||
##### Example, bad
|
|
||||||
|
|
||||||
void f(int* p, int count)
|
|
||||||
{
|
|
||||||
if (count < 2) return;
|
|
||||||
|
|
||||||
int* q = p + 1; // BAD
|
|
||||||
|
|
||||||
ptrdiff_t d;
|
|
||||||
int n;
|
|
||||||
d = (p - &n); // OK
|
|
||||||
d = (q - p); // OK
|
|
||||||
|
|
||||||
int n = *p++; // BAD
|
|
||||||
|
|
||||||
if (count < 6) return;
|
|
||||||
|
|
||||||
p[4] = 1; // BAD
|
|
||||||
|
|
||||||
p[count - 1] = 2; // BAD
|
|
||||||
|
|
||||||
use(&p[0], 3); // BAD
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Example, good
|
|
||||||
|
|
||||||
void f(span<int> a) // BETTER: use span in the function declaration
|
|
||||||
{
|
|
||||||
if (a.length() < 2) return;
|
|
||||||
|
|
||||||
int n = a[0]; // OK
|
|
||||||
|
|
||||||
span<int> q = a.subspan(1); // OK
|
|
||||||
|
|
||||||
if (a.length() < 6) return;
|
|
||||||
|
|
||||||
a[4] = 1; // OK
|
|
||||||
|
|
||||||
a[count - 1] = 2; // OK
|
|
||||||
|
|
||||||
use(a.data(), 3); // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Enforcement
|
|
||||||
|
|
||||||
Issue a diagnostic for any arithmetic operation on an expression of pointer type that results in a value of pointer type.
|
|
||||||
|
|
||||||
### <a name="Pro-bounds-arrayindex"></a>Bounds.2: Only index into arrays using constant expressions.
|
|
||||||
|
|
||||||
##### Reason
|
|
||||||
|
|
||||||
Dynamic accesses into arrays are difficult for both tools and humans to validate as safe. `span` is a bounds-checked, safe type for accessing arrays of data. `at()` is another alternative that ensures single accesses are bounds-checked. If iterators are needed to access an array, use the iterators from a `span` constructed over the array.
|
|
||||||
|
|
||||||
##### Example, bad
|
|
||||||
|
|
||||||
void f(array<int, 10> a, int pos)
|
|
||||||
{
|
|
||||||
a[pos / 2] = 1; // BAD
|
|
||||||
a[pos - 1] = 2; // BAD
|
|
||||||
a[-1] = 3; // BAD -- no replacement, just don't do this
|
|
||||||
a[10] = 4; // BAD -- no replacement, just don't do this
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Example, good
|
|
||||||
|
|
||||||
// ALTERNATIVE A: Use a span
|
|
||||||
|
|
||||||
// A1: Change parameter type to use span
|
|
||||||
void f1(span<int, 10> a, int pos)
|
|
||||||
{
|
|
||||||
a[pos / 2] = 1; // OK
|
|
||||||
a[pos - 1] = 2; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
// A2: Add local span and use that
|
|
||||||
void f2(array<int, 10> arr, int pos)
|
|
||||||
{
|
|
||||||
span<int> a = {arr, pos}
|
|
||||||
a[pos / 2] = 1; // OK
|
|
||||||
a[pos - 1] = 2; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
// ALTERNATIVE B: Use at() for access
|
|
||||||
void f3(array<int, 10> a, int pos)
|
|
||||||
{
|
|
||||||
at(a, pos / 2) = 1; // OK
|
|
||||||
at(a, pos - 1) = 2; // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Example, bad
|
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
|
||||||
int arr[COUNT];
|
|
||||||
for (int i = 0; i < COUNT; ++i)
|
|
||||||
arr[i] = i; // BAD, cannot use non-constant indexer
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Example, good
|
|
||||||
|
|
||||||
// ALTERNATIVE A: Use a span
|
|
||||||
void f1()
|
|
||||||
{
|
|
||||||
int arr[COUNT];
|
|
||||||
span<int> av = arr;
|
|
||||||
for (int i = 0; i < COUNT; ++i)
|
|
||||||
av[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ALTERNATIVE Aa: Use a span and range-for
|
|
||||||
void f1a()
|
|
||||||
{
|
|
||||||
int arr[COUNT];
|
|
||||||
span<int, COUNT> av = arr;
|
|
||||||
int i = 0;
|
|
||||||
for (auto& e : av)
|
|
||||||
e = i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ALTERNATIVE B: Use at() for access
|
|
||||||
void f2()
|
|
||||||
{
|
|
||||||
int arr[COUNT];
|
|
||||||
for (int i = 0; i < COUNT; ++i)
|
|
||||||
at(arr, i) = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Enforcement
|
|
||||||
|
|
||||||
Issue a diagnostic for any indexing expression on an expression or variable of array type (either static array or `std::array`) where the indexer is not a compile-time constant expression.
|
|
||||||
|
|
||||||
Issue a diagnostic for any indexing expression on an expression or variable of array type (either static array or `std::array`) where the indexer is not a value between `0` or and the upper bound of the array.
|
|
||||||
|
|
||||||
**Rewrite support**: Tooling can offer rewrites of array accesses that involve dynamic index expressions to use `at()` instead:
|
|
||||||
|
|
||||||
static int a[10];
|
|
||||||
|
|
||||||
void f(int i, int j)
|
|
||||||
{
|
|
||||||
a[i + j] = 12; // BAD, could be rewritten as ...
|
|
||||||
at(a, i + j) = 12; // OK -- bounds-checked
|
|
||||||
}
|
|
||||||
|
|
||||||
### <a name="Pro-bounds-decay"></a>Bounds.3: No array-to-pointer decay.
|
|
||||||
|
|
||||||
##### Reason
|
|
||||||
|
|
||||||
Pointers should not be used as arrays. `span` is a bounds-checked, safe alternative to using pointers to access arrays.
|
|
||||||
|
|
||||||
##### Example, bad
|
|
||||||
|
|
||||||
void g(int* p, size_t length);
|
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
|
||||||
int a[5];
|
|
||||||
g(a, 5); // BAD
|
|
||||||
g(&a[0], 1); // OK
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Example, good
|
|
||||||
|
|
||||||
void g(int* p, size_t length);
|
|
||||||
void g1(span<int> av); // BETTER: get g() changed.
|
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
|
||||||
int a[5];
|
|
||||||
span<int> av = a;
|
|
||||||
|
|
||||||
g(av.data(), av.length()); // OK, if you have no choice
|
|
||||||
g1(a); // OK -- no decay here, instead use implicit span ctor
|
|
||||||
}
|
|
||||||
|
|
||||||
##### Enforcement
|
|
||||||
|
|
||||||
Issue a diagnostic for any expression that would rely on implicit conversion of an array type to a pointer type.
|
|
||||||
|
|
||||||
### <a name="Pro-bounds-stdlib"></a>Bounds.4: Don't use standard library functions and types that are not bounds-checked.
|
|
||||||
|
|
||||||
##### Reason
|
##### Reason
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user