From df160f3654a68f79a5acdf13456c2cdfb79e6aba Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Tue, 23 May 2017 15:55:51 -0400 Subject: [PATCH] Most of the bounds safety profile --- CppCoreGuidelines.md | 388 +++++++++++++++++++++---------------------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 8db3898..c78540d 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -11217,17 +11217,197 @@ You should know enough not to need parentheses for: Complicated pointer manipulation is a major source of errors. -* Do all pointer arithmetic on a `span` (exception ++p in simple loop???) -* Avoid pointers to pointers -* ??? +##### Note + +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 a) // BETTER: use span in the function declaration + { + if (a.length() < 2) return; + + int n = a[0]; // OK + + span 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 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 a, int pos) // A1: Change parameter type to use span + { + a[pos / 2] = 1; // OK + a[pos - 1] = 2; // OK + } + + void f2(array arr, int pos) // A2: Add local span and use that + { + span a = {arr, pos} + a[pos / 2] = 1; // OK + a[pos - 1] = 2; // OK + } + +Use a `at()`: + + void f3(array 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 av = arr; + for (int i = 0; i < COUNT; ++i) + av[i] = i; + } + +Use a `span` and range-`for`: + + void f1a() + { + int arr[COUNT]; + span 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 - ??? +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 av); // BETTER: get g() changed. + + void f2() + { + int a[5]; + span 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 -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). + ### 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.1: Don't use pointer arithmetic. Use `span` instead](#Pro-bounds-arithmetic) -* [Bounds.2: Only index into arrays using constant expressions](#Pro-bounds-arrayindex) -* [Bounds.3: No array-to-pointer decay](#Pro-bounds-decay) -* [Bounds.4: Don't use standard library functions and types that are not bounds-checked](#Pro-bounds-stdlib) +* Bounds.1: Don't use pointer arithmetic. Use `span` instead: +[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple). +* Bounds.2: Only index into arrays using constant expressions: +[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple). +* Bounds.3: No array-to-pointer decay: +[Pass pointers to single objects (only)](#Ri-array) and [Keep pointer arithmetic simple](#Res-simple). +* Bounds.4: Don't use standard library functions and types that are not bounds-checked: +[???](#XXX) -### Bounds.1: Don't use pointer arithmetic. Use `span` instead. - -##### 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 a) // BETTER: use span in the function declaration - { - if (a.length() < 2) return; - - int n = a[0]; // OK - - span 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. - -### 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 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 a, int pos) - { - a[pos / 2] = 1; // OK - a[pos - 1] = 2; // OK - } - - // A2: Add local span and use that - void f2(array arr, int pos) - { - span a = {arr, pos} - a[pos / 2] = 1; // OK - a[pos - 1] = 2; // OK - } - - // ALTERNATIVE B: Use at() for access - void f3(array 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 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 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 - } - -### 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 av); // BETTER: get g() changed. - - void f() - { - int a[5]; - span 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. - -### Bounds.4: Don't use standard library functions and types that are not bounds-checked. +### Bounds.4: Don't use standard library functions and types that are not bounds-checked. ##### Reason