|
4 | 4 | <qhelp> |
5 | 5 | <overview> |
6 | 6 | <p> |
7 | | -When checking for out-of-range pointer values, we might write tests like |
8 | | -<code>p + a < p</code> and check if the value "wraps around". |
9 | | -Such a test is wrong in that it relies on the overflow of <code>p + a</code>, |
10 | | -which has undefined behavior. In fact, many optimizing compilers will remove |
11 | | -<code>p + a < p</code> altogether and replace it with the value <code>0</code> |
12 | | -(<code>false</code>). Conversely, should <code>p + a</code> <i>not</i> overflow, |
13 | | -the programmer may erroneously assume that the memory location <code>p + a</code> |
14 | | -is accessible to the program, even though it may be inaccessible (protected by |
15 | | -the operating system) or nonexistent; writing to such memory, if allowed, can |
16 | | -lead to memory corruption. |
| 7 | +The expression <code>ptr + a < ptr</code> is equivalent to <code>a < |
| 8 | +0</code>, and an optimizing compiler is likely to make that replacement, |
| 9 | +thereby removing a range check that might have been necessary for security. |
| 10 | +If `a` is known to be non-negative, the compiler can even replace <code>ptr + |
| 11 | +a < ptr</code> with <code>false</code>. |
| 12 | +</p> |
| 13 | + |
| 14 | +<p> |
| 15 | +The reason is that pointer arithmetic overflow in C/C++ is undefined |
| 16 | +behavior. The optimizing compiler can assume that the program has no |
| 17 | +undefined behavior, which means that adding a positive number to `ptr` cannot |
| 18 | +produce a pointer less than `ptr`. |
17 | 19 | </p> |
18 | 20 | </overview> |
19 | 21 | <recommendation> |
20 | 22 | <p> |
21 | 23 | To check whether an index <code>a</code> is less than the length of an array, |
22 | 24 | simply compare these two numbers as unsigned integers: <code>a < ARRAY_LENGTH</code>. |
23 | 25 | If the length of the array is defined as the difference between two pointers |
24 | | -<code>p</code> and <code>p_end</code>, write <code>a < p_end - p</code>. |
| 26 | +<code>ptr</code> and <code>p_end</code>, write <code>a < p_end - ptr</code>. |
25 | 27 | If a is <code>signed</code>, cast it to <code>unsigned</code> |
26 | 28 | in order to guard against negative <code>a</code>. For example, write |
27 | | -<code>(size_t)a < p_end - p</code>.</p> |
| 29 | +<code>(size_t)a < p_end - ptr</code>. |
| 30 | +</p> |
28 | 31 | </recommendation> |
29 | 32 | <example> |
30 | 33 | <p> |
31 | | -In the first example, an index <code>a</code>is being added to a pointer <code>p</code> |
32 | | -to an array with elements of type <code>T</code>. Since we are not provided a |
33 | | -separate pointer pointing to the end of the array, we fall back |
34 | | -on a check for address "wrap-around" to see if <code>p + a</code> points at |
35 | | -valid memory. This scheme does not work, unfortunately, since the |
36 | | -value of <code>p + a</code> is undefined if it points at invalid memory (for |
37 | | -example, outside our array). Even if <code>p + a</code> were to point to |
38 | | -some accessible memory location, it would almost certainly lie |
39 | | -<i>outside</i> the bounds of the array. |
| 34 | +An invalid check for pointer overflow is most often seen as part of checking |
| 35 | +whether a number <code>a</code> is too large by checking first if adding the |
| 36 | +number to <code>ptr</code> goes past the end of an allocation and then |
| 37 | +checking if adding it to <code>ptr</code> creates a pointer so large that it |
| 38 | +overflows and wraps around. |
40 | 39 | </p> |
| 40 | + |
41 | 41 | <sample src="PointerWrapAround-bad.cpp" /> |
| 42 | + |
| 43 | +<p> |
| 44 | +In both of these checks, the operations are performed in the wrong order. |
| 45 | +First, an expression that may lead to undefined behavior is evaluated |
| 46 | +(<code>ptr + a</code>), and then the result is checked for being in range. |
| 47 | +But once undefined behavior has happened in the pointer addition, it cannot |
| 48 | +be recovered from: it's too late to perform the range check after a possible |
| 49 | +pointer overflow. |
| 50 | +</p> |
| 51 | + |
| 52 | +<p> |
| 53 | +While it's not the subject of this query, the expression <code>ptr + a < |
| 54 | +ptr_end</code> is also an invalid range check. It's undefined behavor in |
| 55 | +C/C++ to create a pointer that points more than one past the end of an |
| 56 | +allocation. |
| 57 | +</p> |
| 58 | + |
42 | 59 | <p> |
43 | | -The next example shows how to properly check for an out-of-range pointer. |
44 | | -In order to do so, we need to obtain the value <code>ptr_end</code> |
45 | | -representing the end of the array (or the address immediately past |
46 | | -the end). We can then express the condition <code>p + a < p_end</code> as a |
47 | | -difference of two pointers, even if <code>p + a</code> happens to be undefined. |
| 60 | +The next example shows how to portably check whether a number is outside the |
| 61 | +range of an allocation between <code>ptr</code> and <code>ptr_end</code>. |
48 | 62 | </p> |
49 | 63 | <sample src="PointerWrapAround-good.cpp" /> |
50 | 64 | </example> |
|
0 commit comments