IssueIncorrect 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.
Situation | Risk | Possible 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. |
FixThe 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
Operatorvoid 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
OperatorOne 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 Arithmeticint 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 ArithmeticIf 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
OperatorOne 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;
}