exponenta event banner

Незаконно дереферентированный указатель

Указатель обнулен вне границ

Описание

Эта проверка при отмене привязки указателя определяет, имеет ли указатель значение NULL или находится за пределами его границ. Проверка выполняется только при удалении указателя, а не при переназначении другому указателю или передаче указателя функции.

Контрольное сообщение показывает смещение указателя и размер буфера в байтах. Указатель указывает за пределы своих границ, когда сумма смещения и размера указателя превышает размер буфера.

  • Буфер: При назначении адреса указателю выделяется блок памяти. Невозможно получить доступ к памяти за пределами этого блока с помощью указателя. Размер этого блока - размер буфера.

    Иногда вместо определенного значения размер может быть диапазоном. Например, при динамическом создании буфера с использованием malloc при неизвестном вводе размера Polyspace ® предполагает, что размер массива может принимать полный диапазон значений, допустимых типом входных данных.

  • Смещение: можно переместить указатель в разрешенном блоке памяти, используя арифметику указателя. Разница между начальным расположением указателя и его текущим расположением - смещение.

    Иногда вместо определенного значения смещение может быть диапазоном. Например, при доступе к массиву в цикле смещение изменяет значение в каждой итерации цикла и принимает диапазон значений по всему циклу.

Например, если указатель указывает на массив:

  • Размер буфера - это размер массива.

  • Смещение - это разность между началом массива и текущим расположением указателя.

Примеры

развернуть все

#define Size 1024

int input(void);

void main() {
    int arr[Size];
    int *p = arr;

    for (int index = 0; index < Size ; index++, p++){
        *p = input();
    }
    *p = input();
}

В этом примере:

  • До for петля, p указывает на начало массива arr.

  • После for петля, p точки за пределами массива.

Проверка запрещенного указателя при отмене привязки p после for цикл приводит к красной ошибке.

Исправление - удаление недопустимой отмены привязки

Одним из возможных исправлений является удаление незаконной отмены p после for цикл.

#define Size 1024

int input(void);

void main() {
    int arr[Size];
    int *p = arr;
 	
    for (int index = 0; index < Size ; index++, p++) {
        *p = input();
    }
}    
typedef struct S {
    int f1;
    int f2;
    int f3;
} S;

void Initialize(int *ptr) {
    *ptr = 0;
    *(ptr+1) = 0;
    *(ptr+2) = 0;
}

void main(void) {
    S myStruct;
    Initialize(&myStruct.f1);
}

В этом примере в теле Initialize, ptr является int указатель, указывающий на первое поле структуры. При попытке доступа ко второму полю через ptr, проверка Незаконно дереферентированного указателя приводит к красной ошибке.

Исправление - Избегайте доступа к памяти вне поля структуры

Одной из возможных корректировок является передача указателя на всю структуру в Initialize.

typedef struct S {
    int f1;
    int f2;
    int f3;
} S;

void Initialize(S* ptr) {
    ptr->f1 = 0;
    ptr->f2 = 0;
    ptr->f3 = 0;
}

void main(void) {
    S myStruct;
    Initialize(&myStruct);
}
#include<stdlib.h>

void main() {
    int *ptr=NULL;
    *ptr=0;
}

В этом примере: ptr присваивается значение NULL. Поэтому при отмене привязки ptr, проверка Незаконно дереферентированного указателя приводит к красной ошибке.

Исправление - Избегайте отмены привязки указателя NULL

Одной из возможных корректировок является инициализация ptr с адресом переменной вместо NULL.

void main() {
    int var;
    int *ptr=&var;
    *ptr=0;
}
int getOffset(void);

void main() {
    int *ptr = (int*) 0 + getOffset();
    if(ptr != (int*)0)
        *ptr = 0;
}

В этом примере, хотя смещение добавляется к (int*) 0, Polyspace не рассматривает результат как допустимый адрес. Поэтому при отмене привязки ptr, проверка Незаконно дереферентированного указателя приводит к красной ошибке.

struct flagCollection {
    unsigned int flag1: 1;
    unsigned int flag2: 1;
    unsigned int flag3: 1;
    unsigned int flag4: 1;
    unsigned int flag5: 1;
    unsigned int flag6: 1;
    unsigned int flag7: 1;
};

char getFlag(void);

int main()
{
    unsigned char myFlag = getFlag();
    struct flagCollection* myFlagCollection;
    myFlagCollection = (struct flagCollection *) &myFlag;
    if (myFlagCollection->flag1 == 1)
        return 1;
    return 0;
}

В этом примере:

  • Поля flagCollection иметь тип unsigned int. Поэтому a flagCollection структура требует 32 бит памяти в 32-разрядной архитектуре, хотя сами поля занимают 7 бит.

  • Когда вы бросаете char адрес &myFlag в flagCollection указатель myFlagCollection, указателю назначается только 8 бит памяти. Следовательно, проверка указателя «Незаконно» при отмене привязки myFlagCollection приводит к появлению красной ошибки.

Коррекция - используйте правильный тип для битовых полей

Одной из возможных корректировок является использование unsigned char как тип поля flagCollection вместо unsigned int. В этом случае:

  • Структура flagCollection требуется 8 бит памяти.

  • Когда вы бросаете char адрес &myFlag в flagCollection указатель myFlagCollection, вы также назначаете 8 бит памяти указателю. Следовательно, проверка указателя «Незаконно» при отмене привязки myFlagCollection зеленый.

struct flagCollection {
    unsigned char flag1: 1;
    unsigned char flag2: 1;
    unsigned char flag3: 1;
    unsigned char flag4: 1;
    unsigned char flag5: 1;
    unsigned char flag6: 1;
    unsigned char flag7: 1;
};

char getFlag(void);

int main()
{
    unsigned char myFlag = getFlag();
    struct flagCollection* myFlagCollection;
    myFlagCollection = (struct flagCollection *) &myFlag;
    if (myFlagCollection->flag1 == 1)
        return 1;
    return 0;
}
#include <stdlib.h>

void main(void)
{
    char *p = (char*)malloc(1);
    char *q = p;
    *q = 'a';
}

В этом примере: malloc может вернуться NULL кому p. Поэтому при присвоении p кому q и отмена связи q, проверка Незаконно дереферентированного указателя приводит к красной ошибке.

Correction - Check return value of malloc для NULL

Одной из возможных корректировок является проверка p для NULL перед дереференцией q.

#include <stdlib.h>
void main(void)
{
    char *p = (char*)malloc(1);
    char *q = p;
    if(p!=NULL) *q = 'a';
}
#include <stdlib.h>

enum typeName {CHAR,INT};

typedef struct {
    enum typeName myTypeName;
    union {
        char myChar;
        int myInt;
    } myVar;
} myType;

void main() {
    myType* myTypePtr;
    myTypePtr = (myType*)malloc(sizeof(int) + sizeof(char));
    if(myTypePtr != NULL) {
        myTypePtr->myTypeName = INT;
    }
}

В этом примере:

  • Потому что профсоюз myVar имеет int переменной как полю должно быть назначено 4 байта в 32-битной архитектуре. Поэтому структура myType должен быть назначен 4 + 4 = 8 байт.

  • malloc прибыль sizeof(int) + sizeof(char)= 4 + 1 = 5 байт памятиmyTypePtr, указатель на myType структура. Поэтому, когда вы dereference myTypePtr, проверка Незаконно дереферентированного указателя возвращает красную ошибку.

Исправление - Выделение достаточного объема памяти указателю

Одной из возможных корректировок является назначение 8 байт памяти myTypePtr перед исключением.

#include <stdlib.h>

enum typeName {CHAR,INT};

typedef struct {
    enum typeName myTypeName;
    union {
        char myChar;
        int myInt;
    } myVar;
} myType;

void main() {
    myType* myTypePtr;
    myTypePtr = (myType*)malloc(sizeof(int) + sizeof(int));
    if(myTypePtr != NULL) {
        myTypePtr->myTypeName = INT;
    }
}
#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} rectangle;

typedef struct {
    int length;
    int breadth;
    int height;
} cuboid;

void main() {
    cuboid *cuboidPtr = (cuboid*)malloc(sizeof(rectangle));
    if(cuboidPtr!=NULL) {
        cuboidPtr->length = 10;
        cuboidPtr->breadth = 10;
    }
}

В этом примере: cuboidPtr получает достаточный объем памяти для размещения двух его полей. Поскольку стандарты ANSI ® C не допускают таких частичных выделений памяти, проверка указателя «Незаконно» при отмене привязки cuboidPtr приводит к появлению красной ошибки.

Исправление - выделение полной памяти

Соблюдение стандартов ANSI C, cuboidPtr должна быть выделена полная память.

#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} rectangle;

typedef struct {
    int length;
    int breadth;
    int height;
} cuboid;

void main() {
    cuboid *cuboidPtr = (cuboid*)malloc(sizeof(cuboid));
    if(cuboidPtr!=NULL) {
        cuboidPtr->length = 10;
        cuboidPtr->breadth = 10;
    }
}
Коррекция - опция Использовать анализ в пространстве (Use Polyspace analysis)

Можно разрешить частичное выделение памяти структурам, но при этом не иметь красной ошибки указателя «Незаконно». Чтобы разрешить частичное выделение памяти, на панели Конфигурация в разделе Поведение проверки выберите Разрешить неполное или частичное выделение структур.

#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} rectangle;

typedef struct {
    int length;
    int breadth;
    int height;
} cuboid;

void main() {
    cuboid *cuboidPtr = (cuboid*)malloc(sizeof(rectangle));
    if(cuboidPtr!=NULL) {
        cuboidPtr->length = 10;
        cuboidPtr->breadth = 10;
    }
}
#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} square;


void main() {
    square mySquare;
    char* squarePtr = (char*)&mySquare.length;
//Assign zero to mySquare.length byte by byte
    for(int byteIndex=1; byteIndex<=4; byteIndex++) {
        *squarePtr=0;
        squarePtr++;
    }
//Assign zero to first byte of mySquare.breadth
    *squarePtr=0;
}

В этом примере, хотя squarePtr является char указатель, ему присваивается адрес целого числа mySquare.length. Потому что:

  • char занимает 1 байт,

  • int занимает 4 байта в 32-битной архитектуре,

squarePtr может получить доступ к четырем байтам mySquare.length через арифметику указателя. Но при обращении к первому байту другого поля mySquare.breadth, проверка Незаконно дереферентированного указателя приводит к красной ошибке.

Исправление - назначение адреса структуры вместо поля

Одной из возможных корректировок является присвоение squarePtr адрес полной структуры mySquare вместо mySquare.length. squarePtr может затем получить доступ ко всем байтам mySquare через арифметику указателя.

#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} square;


void main() {
    square mySquare;
    char* squarePtr = (char*)&mySquare;
//Assign zero to mySquare.length byte by byte
    for(int byteIndex=1; byteIndex<=4; byteIndex++) {
        *squarePtr=0;
        squarePtr++;
    }
//Assign zero to first byte of mySquare.breadth
    *squarePtr=0;
}
Коррекция - опция Использовать анализ пространства (Use Polyspace analysis) (недоступна в C++)

С помощью указателя можно перемещаться по полям структуры и не создавать красную ошибку «Недопустимый указатель». Чтобы разрешить такую навигацию, на панели Конфигурация (Configuration) в разделе Поведение проверки (Check Behavior) выберите Включить арифметику указателя для всех полей.

Этот параметр недоступен для проектов C++. В C++ арифметика указателя становится нетривиальной при работе с такими понятиями, как полиморфные типы.


#include <stdlib.h>
typedef struct {
    int length;
    int breadth;
} square;


void main() {
    square mySquare;
    char* squarePtr = (char*)&mySquare.length;
//Assign zero to mySquare.length byte by byte
    for(int byteIndex=1; byteIndex<=4; byteIndex++) {
        *squarePtr=0;
        squarePtr++;
    }
//Assign zero to first byte of mySquare.breadth
    *squarePtr=0;
}
void func2(int *ptr) {
    *ptr = 0; 
}

int* func1(void) {
    int ret = 0;
    return &ret ;
}
void main(void) {
    int* ptr = func1() ;
    func2(ptr) ;
}

В следующем коде: ptr указывает на ret. Потому что объем ret ограничивается func1, когда ptr доступен в func2, доступ запрещен. В результате проверки выполняется проверка указателя «Незаконно» красного цвета *ptr.

По умолчанию Prover™ Polyspace Code не обнаруживает функции, возвращающие указатели на локальные переменные. Для обнаружения таких случаев используйте опцию Detect stack pointer dereference outside scope (-detect-pointer-escape).

Проверить информацию

Группа: Статическая память
Язык: C | C++
Акроним: ВПЛ