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 insecure because it may result in a pointer that does not point to an element within or one past the end of the array. This is contraindicated by ARR30-C. Do not form or use out of bounds pointers or array subscripts.
Violations of this guideline are indicated when a pointer to an array is added to the result of the sizeof operator or offsetof macro, which return a size and offset, respectively. 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.
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 is 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();
  }
} | 
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();
  }
} | 
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;
} | 
The 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;
} | 
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);
  /* ... */
} | 
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 };
void func(void) {
  wchar_t error_msg[WCHAR_BUF];
  wcscpy(error_msg, L"Error: ");
  fgetws(error_msg + wcslen(error_msg), WCHAR_BUF - 7, stdin);
  /* ... */
} | 
Failure to understand and properly use pointer arithmetic can allow an attacker to execute arbitrary code.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level | 
|---|---|---|---|---|---|
| EXP41-C | High | Probable | High | P6 | L2 | 
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
| CERT C Secure Coding Standard | ARR30-C. Do not form or use out of bounds pointers or array subscripts ARR37-C. Do not add or subtract an integer to a pointer to a non-array object | 
| ISO/IEC TR 24772:2013 | Pointer Casting and Pointer Type Changes [HFC] Pointer Arithmetic [RVG] | 
| MISRA C:2012 | Rule 18.1 (required) Rule 18.2 (required) Rule 18.3 (required) Rule 18.4 (advisory) | 
| MITRE CWE | CWE 468, Incorrect pointer scaling | 
| [Dowd 06] | Chapter 6, "C Language Issues" | 
| [Murenin 07] |