IssueIncorrect pointer
scaling occurs when you ignore the implicit scaling in pointer
arithmetic.
For instance, the defect can occur in these
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. 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. |
FixThe 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
OperatorOne 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 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;
}