CERT C: Rec. EXP08-C

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

Описание

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

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

Реализация Polyspace

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

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

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

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

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

Примеры

расширить все

Проблема

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

Риск

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

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

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

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

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

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

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

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

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

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

Пример - ошибка доступа к массиву вне границ
#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]); 
}

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

Проблема

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

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

Риск

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

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

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

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

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

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

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

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

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

Пример - Доступ к указателю за пределами ошибки
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
}

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

Проблема

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

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

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

The sizeof оператор возвращает размер типа данных в количестве байт.

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

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

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

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

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

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

Пример - Использование 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 вне границ массива. Поэтому на Pointer access out of bounds появляется * ошибка операция.

Коррекция - Удаление 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). После операции полученный указатель указывает вне допустимого буфера. Когда вы высмеиваете указатель, на * появляется Pointer access out of bounds ошибка операция.

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

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

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

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

Группа: Рек. 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 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

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

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