Pointer arithmetic is appropriate only when the pointer argument refers to an array (see ARR37-C. Do not add or subtract an integer to a pointer to a non-array object), including an array of bytes. When performing pointer arithmetic, the size of the value to add to or subtract from a pointer is automatically scaled to the size of the type of the referenced array object. Adding or subtracting a scaled integer value to or from a pointer is invalid because it may yield a pointer that does not point to an element within or one past the end of the array. (See ARR30-C. Do not form or use out-of-bounds pointers or array subscripts.)
Adding a pointer to an array of a type other than character to the result of the sizeof
operator or offsetof
macro, which returns a size and an offset, respectively, violates this rule. However, adding an array pointer to the number of array elements, for example, by using the arr[sizeof(arr)/sizeof(arr[0])])
idiom, is allowed provided that arr
refers to an array and not a pointer.
Noncompliant Code Example
In this noncompliant code example, sizeof(buf)
is added to the array buf
. This example is noncompliant because sizeof(buf)
is scaled by int
and then scaled again when added to buf
.
enum { INTBUFSIZE = 80 }; extern int getdata(void); int buf[INTBUFSIZE]; void func(void) { int *buf_ptr = buf; while (buf_ptr < (buf + sizeof(buf))) { *buf_ptr++ = getdata(); } }
Compliant Solution
This compliant solution uses an unscaled integer to obtain a pointer to the end of the array:
enum { INTBUFSIZE = 80 }; extern int getdata(void); int buf[INTBUFSIZE]; void func(void) { int *buf_ptr = buf; while (buf_ptr < (buf + INTBUFSIZE)) { *buf_ptr++ = getdata(); } }
Noncompliant Code Example
In this noncompliant code example, skip
is added to the pointer s
. However, skip
represents the byte offset of ull_b
in struct big
. When added to s
, skip
is scaled by the size of struct big
.
#include <string.h> #include <stdlib.h> #include <stddef.h> struct big { unsigned long long ull_a; unsigned long long ull_b; unsigned long long ull_c; int si_e; int si_f; }; void func(void) { size_t skip = offsetof(struct big, ull_b); struct big *s = (struct big *)malloc(sizeof(struct big)); if (s == NULL) { /* Handle malloc() error */ } memset(s + skip, 0, sizeof(struct big) - skip); /* ... */ free(s); s = NULL; }
Compliant Solution
This compliant solution uses an unsigned char *
to calculate the offset instead of using a struct big *
, which would result in scaled arithmetic:
#include <string.h> #include <stdlib.h> #include <stddef.h> struct big { unsigned long long ull_a; unsigned long long ull_b; unsigned long long ull_c; int si_d; int si_e; }; void func(void) { size_t skip = offsetof(struct big, ull_b); unsigned char *ptr = (unsigned char *)malloc( sizeof(struct big) ); if (ptr == NULL) { /* Handle malloc() error */ } memset(ptr + skip, 0, sizeof(struct big) - skip); /* ... */ free(ptr); ptr = NULL; }
Noncompliant Code Example
In this noncompliant code example, wcslen(error_msg) * sizeof(wchar_t)
bytes are scaled by the size of wchar_t
when added to error_msg
:
#include <wchar.h> #include <stdio.h> enum { WCHAR_BUF = 128 }; void func(void) { wchar_t error_msg[WCHAR_BUF]; wcscpy(error_msg, L"Error: "); fgetws(error_msg + wcslen(error_msg) * sizeof(wchar_t), WCHAR_BUF - 7, stdin); /* ... */ }
Compliant Solution
This compliant solution does not scale the length of the string; wcslen()
returns the number of characters and the addition to error_msg
is scaled:
#include <wchar.h> #include <stdio.h> enum { WCHAR_BUF = 128 }; const wchar_t ERROR_PREFIX[7] = L"Error: "; void func(void) { const size_t prefix_len = wcslen(ERROR_PREFIX); wchar_t error_msg[WCHAR_BUF]; wcscpy(error_msg, ERROR_PREFIX); fgetws(error_msg + prefix_len, WCHAR_BUF - prefix_len, stdin); /* ... */ }
Risk Assessment
Failure to understand and properly use pointer arithmetic can allow an attacker to execute arbitrary code.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ARR39-C | High | Probable | High | P6 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Coverity | 2017.07 | BAD_SIZEOF | Partially implemented |
LDRA tool suite | 9.7.1 | 47 S, 489 S, 567 S, 64 X, 66 X, 68 X, 69 X, 70 X, 71 X | Partially implemented |
Parasoft C/C++test | 2023.1 | BD-PB-ARRAY, MISRA-101, CODSTA-189 | |
Polyspace Bug Finder | R2024a | Implicit scaling in pointer arithmetic might be ignored Standard library memory function called with invalid arguments Pointer dereferenced outside its bounds Use of A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand | |
PRQA QA-C | 9.1 | 2930, 2931, 2932, 2933, 2934 | |
PRQA QA-C++ | 4.4 | 2840, 2841, 2842, 2843, 2844, 2930, 2931, 2932, 2933, 2934 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
CERT C Secure Coding Standard | ARR30-C. Do not form or use out-of-bounds pointers or array subscripts | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | ARR37-C. Do not add or subtract an integer to a pointer to a non-array object | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Pointer Casting and Pointer Type Changes [HFC] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Pointer Arithmetic [RVG] | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 18.1 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 18.2 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 18.3 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 18.4 (advisory) | Prior to 2018-01-12: CERT: Unspecified Relationship |
CWE 2.11 | CWE-468, Incorrect Pointer Scaling | 2017-07-07: CERT: Exact |
Bibliography
[Dowd 2006] | Chapter 6, "C Language Issues" |
[Murenin 07] |