Main Content

CERT C++: 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 you ignore the implicit scaling in pointer arithmetic.

For instance, the defect can occur in these 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.

When you perform arithmetic operations on a pointer, the argument is implicitly scaled by the size of the data type of the pointed variable. 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.The implicit scaling in pointer arithmetic depends on the type of an object. Performing these scaled arithmetic and then changing the pointer type by casting might produce unintended results.Perform the pointer arithmetic after the casting operation.

Fix

The fix depends on the root cause of the defect. Often, the Result Details pane shows a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the Result Details pane does not show the event history, investigate the root cause of the defect by checking previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.

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
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdio.h>



enum { INTBUFSIZE = 80 };
extern int getdata (void);
int buf[INTBUFSIZE];
void foo (void)
{
  int *buf_ptr = buf;

  while (buf_ptr < (buf + sizeof (buf))) {  //Noncompliant
    *buf_ptr++ = getdata ();
  }
}

In this example, the operation sizeof(buf) is used for obtaining a pointer to the end of the array buf. The output of sizeof(buf) is scaled by int. Because pointer arithmetic is implicitly scaled, the output of sizeof(buf) is again scaled by int when it is added to buf, resulting in unexpected behavior. Polyspace® flags the use of sizeof operator.

Correction — Remove sizeof Operator

One possible correction is to use unscaled numbers as offsets.

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



enum { INTBUFSIZE = 80 };
extern int getdata (void);
int buf[INTBUFSIZE];
void foo (void)
{
  int *buf_ptr = buf;

  while (buf_ptr < (buf + INTBUFSIZE)) {  
    *buf_ptr++ = getdata ();
  }
}
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: 04. Containers (CTR)

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.