exponenta event banner

CERT C: Rec. EXP08-C

Убедитесь, что арифметика указателя используется правильно

Описание

Определение правила

Убедитесь, что арифметика указателя используется правильно. [1 ]

Внедрение Polyspace

Эта проверка проверяет наличие следующих проблем:

  • Доступ к массиву вне границ

  • Доступ указателя выходит за границы.

  • Вычитание между указателями на различные массивы.

  • Неправильное масштабирование указателя.

Примеры

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

Проблема

Эта проблема возникает, когда индекс массива выходит за пределы диапазона [0...array_size-1] во время доступа к массиву.

Риск

Доступ к массиву за пределами его границ является неопределенным поведением. Можно прочитать непредсказуемое значение или попытаться получить доступ к расположению, которое не разрешено, и обнаружить ошибку сегментации.

Зафиксировать

Исправление зависит от первопричины дефекта. Например, вы получили доступ к массиву внутри цикла, и произошла одна из следующих ситуаций:

  • Верхняя граница цикла слишком велика.

  • Используется индекс массива, который совпадает с индексом цикла, а не на единицу меньше индекса цикла.

Чтобы устранить проблему, необходимо изменить ограничение цикла или индекс массива.

Другой причиной, по которой индекс массива может превышать границы массива, является предварительное преобразование из подписанных в неподписанные целые числа. Преобразование может привести к обтеканию значения индекса, в результате чего индекс массива превысит границы массива.

Часто детали результата показывают последовательность событий, которые привели к дефекту. Исправление может быть реализовано для любого события в последовательности. Если сведения о результатах не отображают историю событий, можно выполнить обратную трассировку, щелкнув правой кнопкой мыши параметры в исходном коде и просмотреть предыдущие связанные события. См. также раздел Интерпретация результатов поиска ошибок в интерфейсе пользователя Polyspace Desktop.

См. примеры исправлений ниже.

Если вы не хотите устранять проблему, добавьте комментарии к результату или коду, чтобы избежать другой проверки. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.

Пример - Ошибка доступа к массиву вне границ
#include <stdio.h>

void fibonacci(void)
{
    int i;
    int fib[10];
 
    for (i = 0; i < 10; i++) 
       {
        if (i < 2) 
            fib[i] = 1;
         else 
            fib[i] = fib[i-1] + fib[i-2];
       }

    printf("The 10-th Fibonacci number is %i .\n", fib[i]);   
    /* Defect: Value of i is greater than allowed value of 9 */
}

Множество fib назначается размер 10. Индекс массива для fib имеет допустимые значения [0,1,2,...,9]. Переменная i имеет значение 10, когда оно выходит из for-луп. Следовательно, printf оператор пытается получить доступ fib[10] через i.

Исправление - сохранение индекса массива в пределах массива

Одной из возможных корректировок является печать fib[i-1] вместо fib[i] после for-луп.

#include <stdio.h>

void fibonacci(void)
{
   int i;
   int fib[10];

   for (i = 0; i < 10; i++) 
    {
        if (i < 2) 
            fib[i] = 1;
        else 
            fib[i] = fib[i-1] + fib[i-2];
    }

    /* Fix: Print fib[9] instead of fib[10] */
    printf("The 10-th Fibonacci number is %i .\n", fib[i-1]); 
}

printf доступ к операторам fib[9] вместо fib[10].

Проблема

Эта проблема возникает, когда указатель обнуляется за пределами его границ.

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

Риск

Переопределение указателя за пределами его границ является неопределенным поведением. Можно прочитать непредсказуемое значение или попытаться получить доступ к расположению, которое не разрешено, и обнаружить ошибку сегментации.

Зафиксировать

Исправление зависит от первопричины дефекта. Например, вы отменили ссылку на указатель внутри цикла, и произошла одна из следующих ситуаций:

  • Верхняя граница цикла слишком велика.

  • Для продвижения указателя с неправильным значением приращения указателя использовалась арифметика указателя.

Чтобы устранить проблему, необходимо изменить ограничение цикла или значение приращения указателя.

Часто детали результата показывают последовательность событий, которые привели к дефекту. Исправление может быть реализовано для любого события в последовательности. Если сведения о результатах не отображают историю событий, можно выполнить обратную трассировку, щелкнув правой кнопкой мыши параметры в исходном коде и просмотреть предыдущие связанные события. См. также раздел Интерпретация результатов поиска ошибок в интерфейсе пользователя Polyspace Desktop.

См. примеры исправлений ниже.

Если вы не хотите устранять проблему, добавьте комментарии к результату или коду, чтобы избежать другой проверки. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.

Пример - Ошибка доступа указателя за пределами границ
int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
   {
    ptr++;
    *ptr=i;
    /* Defect: ptr out of bounds for i=9 */
   }

 return(arr);
}

ptr назначается адрес arr указывает на блок памяти размера 10*sizeof(int). В for-луп, ptr увеличивается в 10 раз. В последней итерации цикла ptr точки за пределами назначенного ему блока памяти. Поэтому его нельзя обособлять.

Коррекция - Проверка того, что указатель остается в пределах границ

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

int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
     {
      /* Fix: Dereference pointer before increment */
      *ptr=i;
      ptr++;
     }

 return(arr);
}

После последнего приращения, хотя ptr точки за пределами назначенного ему блока памяти, больше он не обособлен.

Проблема

Это правило создается всякий раз, когда анализ обнаруживает Subtraction or comparison between pointers to different arrays.

Риск

Это правило применяется к выражениям формы pointer_expression1 - pointer_expression2. Поведение не определено, если pointer_expression1 и pointer_expression2:

  • Не указывайте на элементы одного массива,

  • Или не указывайте на элемент за пределами массива.

Пример - Вычитание указателей
#include <stdint.h>
#include <stddef.h>

void f1 (int32_t *ptr)
{
    int32_t a1[10];
    int32_t a2[10];
    int32_t *p1 = &a1[ 1];
    int32_t *p2 = &a2[10];
    ptrdiff_t diff1, diff2, diff3;

    diff1 =  p1 - a1;   // Compliant
    diff2 =  p2 - a2;   // Compliant
    diff3 =  p1 - p2;   // Non-compliant
}

В этом примере три выражения вычитания показывают разницу между совместимыми и несоответствующими вычитаниями указателей. diff1 и diff2 вычитания совместимы, поскольку указатели указывают на один и тот же массив. diff3 вычитание несовместимо, поскольку p1 и p2 указывает на различные массивы.

Проблема

Неправильное масштабирование указателя происходит, когда Polyspace ® Bug Finder™ считает, что неявное масштабирование в арифметике указателя игнорируется.

Например, дефект может возникнуть в следующих ситуациях.

СитуацияРискВозможное исправление
Вы используете sizeof оператор в арифметических операциях над указателем.

sizeof возвращает размер типа данных в байтах.

Арифметика указателя уже неявно масштабируется по размеру типа данных указанной переменной. Следовательно, использование sizeof в арифметике указателя дает непреднамеренные результаты.

Не использовать sizeof оператор в арифметике указателя.
Для указателя выполняются арифметические операции, а затем применяется приведение.Арифметика указателя неявно масштабируется. Если это неявное масштабирование не учитывается, приведение результата арифметики указателя приводит к непреднамеренным результатам.Применить приведение перед арифметикой указателя.

Зафиксировать

Исправление зависит от первопричины дефекта. Часто детали результата показывают последовательность событий, которые привели к дефекту. Исправление может быть реализовано для любого события в последовательности. Если сведения о результатах не отображают историю событий, можно выполнить обратную трассировку, щелкнув правой кнопкой мыши параметры в исходном коде и просмотреть предыдущие связанные события. См. также раздел Интерпретация результатов поиска ошибок в интерфейсе пользователя Polyspace Desktop.

См. примеры исправлений ниже.

Если вы не хотите устранять проблему, добавьте комментарии к результату или коду, чтобы избежать другой проверки. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.

Пример - Использование sizeof Оператор
void func(void) {
    int arr[5] = {1,2,3,4,5};
    int *ptr = arr;

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

В этом примере операция 2*(sizeof(int)) возвращает вдвое больший размер, чем int переменная в байтах. Однако, поскольку арифметика указателя неявно масштабирована, количество байтов, на которое ptr является смещением 2*(sizeof(int))*(sizeof(int)).

В этом примере неверное масштабирование сдвигается ptr за пределами массива. Таким образом, ошибка доступа указателя вне границ появляется на * операция.

Исправление - Удалить sizeof Оператор

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

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

    int value_in_position_2 = *(ptr + 2);
}
Пример - Приведение следующей арифметики указателя
int func(void) {
    int x = 0;
    char r = *(char *)(&x + 1);
    return r;
}

В этом примере операция &x + 1 смещения &x около sizeof(int). После выполнения операции результирующий указатель указывает за пределы допустимого буфера. При отмене привязки указателя появляется ошибка доступа указателя за пределами границ на * операция.

Коррекция - Применить литейную форму перед арифметикой указателя

Если требуется получить доступ ко второму байту x, первый актёрский состав &x в char* и затем выполните арифметику указателя. Результирующий указатель смещен на sizeof(char) байты и все еще точки в разрешенном буфере, размер которого sizeof(int) байт.

int func(void) {
    int x = 0;
    char r = *((char *)(&x )+ 1);
    return r;
}

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

Группа: Rec. 03. Выражения (EXP)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.