Main Content

CERT C: Rule ARR39-C

Do not add or subtract a scaled integer to a pointer

Description

Rule Definition

Do not add or subtract a scaled integer to a pointer.1

Polyspace Implementation

The rule checker checks for Incorrect pointer scaling.

Examples

expand all

Issue

Incorrect pointer scaling occurs when Polyspace® Bug Finder™ considers that you are ignoring the implicit scaling in pointer arithmetic.

For instance, the defect can occur in the following situations.

SituationRiskPossible Fix
You use the sizeof operator in arithmetic operations on a pointer.

The sizeof operator returns the size of a data type in number of bytes.

Pointer arithmetic is already implicitly scaled by the size of the data type of the pointed variable. Therefore, the use of sizeof in pointer arithmetic produces unintended results.

Do not use sizeof operator in pointer arithmetic.
You perform arithmetic operations on a pointer, and then apply a cast.Pointer arithmetic is implicitly scaled. If you do not consider this implicit scaling, casting the result of a pointer arithmetic produces unintended results.Apply the cast before the pointer arithmetic.

Fix

The fix depends on the root cause of the defect. Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Example — Use of sizeof Operator
void func(void) {
    int arr[5] = {1,2,3,4,5};
    int *ptr = arr;

    int value_in_position_2 = *(ptr + 2*(sizeof(int)));//Noncompliant //Noncompliant
}

In this example, the operation 2*(sizeof(int)) returns twice the size of an int variable in bytes. However, because pointer arithmetic is implicitly scaled, the number of bytes by which ptr is offset is 2*(sizeof(int))*(sizeof(int)).

In this example, the incorrect scaling shifts ptr outside the bounds of the array. Therefore, a Pointer access out of bounds error appears on the * operation.

Correction — Remove sizeof Operator

One possible correction is to remove the sizeof operator.

void func(void) {
    int arr[5] = {1,2,3,4,5};
    int *ptr = arr;

    int value_in_position_2 = *(ptr + 2);
}
Example — Cast Following Pointer Arithmetic
int func(void) {
    int x = 0;
    char r = *(char *)(&x + 1); //Noncompliant
    return r;
}

In this example, the operation &x + 1 offsets &x by sizeof(int). Following the operation, the resulting pointer points outside the allowed buffer. When you dereference the pointer, a Pointer access out of bounds error appears on the * operation.

Correction — Apply Cast Before Pointer Arithmetic

If you want to access the second byte of x, first cast &x to a char* pointer and then perform the pointer arithmetic. The resulting pointer is offset by sizeof(char) bytes and still points within the allowed buffer, whose size is sizeof(int) bytes.

int func(void) {
    int x = 0;
    char r = *((char *)(&x )+ 1);
    return r;
}
Example — Use of sizeof in Function Arguments
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
enum { WCHAR_BUF = 128 };
FILE* pFile;
//...
void func2_ko (void)
{
	wchar_t error_msg[WCHAR_BUF];
	wcscpy (error_msg, L"Error: ");
	fgetws (error_msg + wcslen (error_msg)   //Noncompliant
	       * sizeof (wchar_t), WCHAR_BUF - 7, pFile);
}

In this example, an error message is read from the file pointer pFile stream and copied to error_msg after an offset. The intended offset here is wcslen(error_msg), which is already implicitly scaled when it is added to the wchar pointer error_msg. Because the offset is then explicitly scaled again by using sizeof, Polyspace flags the incorrect scaling.

Correction — Remove sizeof Operator

One possible correction is to remove the sizeof operator.

#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
enum { WCHAR_BUF = 128 };
const wchar_t ERROR_PREFIX[8] = L"Error: ";
FILE* pFile;
//...

void func2_ok (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, pFile);  //Compliant
  /* ... */
}
Example — Implicitly Scaled Offset When Calling memset
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define bigNum unsigned long long

struct Collection { 
	bigNum bn_A;
	bigNum bn_B;
	bigNum bn_C;
	int ci_1;
	int ci_2;
};

void foo(void) {
	size_t offset = offsetof(struct Collection, bn_B);
	struct Collection *s = (struct Collection *)malloc(sizeof(struct Collection));
	if (s == NULL) {
		/* Handle malloc() error */
	}

	memset(s + offset, 0, sizeof(struct Collection) - offset); //Noncompliant
	/* ... */
	free(s);
	s = NULL;
}

In this example, offset is calculated by calling offsetof, and then added to s. The variable offset is the byte offset of bn_B in struct Collection. When setting memory by using memset, instead of offsetting the location by bytes, offset is implicitly scaled by the size. The implicit scaling might cause unexpected results. Polyspace raises a violation.

Correction — Calculate Offset by Using unsigned char*

The violation is caused by the fact that offset is scaled by the type of s. Avoid the violation by declaring s as unsigned char*, which is scaled by a factor of one.

#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define bigNum unsigned long long

struct Collection {
	bigNum bn_A;
	bigNum bn_B;
	bigNum bn_C;
	int ci_1;
	int ci_2;
};

void foo(void) {
	size_t offset = offsetof(struct Collection, bn_B);
	unsigned char *s = (unsigned char *)malloc(sizeof(struct Collection));
	if (s == NULL) {
		/* Handle malloc() error */
	}

	memset(s + offset, 0, sizeof(struct Collection) - offset); //Compliant
	/* ... */
	free(s);
	s = NULL;
}

Check Information

Group: Rule 06. Arrays (ARR)

Version History

Introduced in R2019a

expand all


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.