exponenta event banner

Неправильное использование расширенного символьного значения

Преобразование типа данных с расширением знака вызывает непредвиденное поведение

Описание

Этот дефект возникает при преобразовании подписанного или простого char переменная, содержащая возможные отрицательные значения для более широкого целочисленного типа данных (или выполнить арифметическую операцию, которая выполняет преобразование), а затем использовать результирующее значение одним из следующих способов:

  • Для сравнения с EOF (использование == или !=)

  • Как индекс массива

  • В качестве аргумента функции обработки символов в ctype.h, например, isalpha() или isdigit()

При преобразовании подписи char переменная с отрицательным значением для более широкого типа, например int, бит знака сохраняется (расширение знака). Это может привести к конкретным проблемам даже в ситуациях, когда вы думаете, что учли бит знака.

Например, подписанный char значение -1 может представлять символ EOF (конец файла), который является недопустимым символом. Предположим, что char переменная var получает это значение. Если вы лечите var в качестве char переменная, вы можете написать специальный код, чтобы учесть это недопустимое значение символа. Однако при выполнении такой операции, как var++ (включая целочисленное продвижение), оно приводит к значению 0, которое представляет допустимое значение '\0' случайно. С помощью арифметической операции был выполнен переход от недопустимого к допустимому значению.

Даже для отрицательных значений, отличных от -1, преобразование со знаком char к подписанному int может привести к другим проблемам. Например, подписанный char значение -126 эквивалентно unsigned char значение 130 (соответствующее расширенному символу '\202'). При преобразовании значения из char кому int, бит знака сохраняется. Если затем привести результирующее значение к unsigned int, вы получаете неожиданно большое значение, 4294967170 (предполагая 32-битныйint). Если код ожидает unsigned char значение 130 в финале unsigned int переменная, можно увидеть неожиданные результаты.

Основной причиной этой проблемы является расширение знака во время преобразования в более широкий тип. В большинстве архитектур для хранения значений используется двухкомпонентное представление. В этом представлении старший бит указывает знак значения. При преобразовании в более широкий тип преобразование выполняется копированием этого знакового бита во все ведущие биты более широкого типа, так что знак сохраняется. Например, char значение -3 представлено как 11111101 (в предположении, что 8-битный char). При преобразовании в int, представление:

11111111 11111111 11111111  11111101
Значение -3 сохраняется в более широком типе int. Однако при преобразовании в unsigned int, значение (4294967293) больше не совпадает со значением unsigned char эквивалент оригинала char значение. Если вы не знаете об этой проблеме, вы можете увидеть неожиданные результаты в своем коде.

Риск

В следующих случаях функция Bug Finder помечает использование переменных после преобразования из char для более широкого типа данных или арифметической операции, которая неявно преобразует переменную в более широкий тип данных:

  • При сравнении значения переменной с EOF:

    A char значение -1 может представлять недопустимый символ EOF или допустимое расширенное символьное значение '\377' (соответствует unsigned char эквивалент, 255). После char переменная приводится к более широкому типу, такому как int, из-за расширения знака, char значение -1, представляющее одно из EOF или '\377' становится int значение -1, представляющее только EOF. unsigned char значение 255 больше не может быть восстановлено из int переменная. Функция поиска ошибок помечает эту ситуацию, чтобы можно было привести переменную к unsigned char сначала (или избегайте charКому-int преобразование или преобразование операции перед сравнением с EOF). Только тогда сравнение с EOF имеет смысл. См. раздел Расширенное знаковое значение по сравнению с EOF.

  • Если значение переменной используется в качестве индекса массива:

    После char переменная приводится к более широкому типу, такому как intиз-за расширения знака все отрицательные значения сохраняют свой знак. Если отрицательные значения используются непосредственно для доступа к массиву, это приводит к переполнению/неполному заполнению буфера. Даже при учете отрицательных значений способ их учета может привести к неправильному считыванию элементов из массива. См. раздел Расширенное знаковое значение, используемое в качестве индекса массива.

  • При передаче значения переменной в качестве аргумента функции обработки символов:

    В соответствии со стандартом C11 (раздел 7.4), если указан целый аргумент, который не может быть представлен как unsigned char или EOF, результирующее поведение не определено. Функция поиска ошибок помечает эту ситуацию, поскольку она отрицательна char значения после преобразования больше не могут быть представлены как unsigned char или EOF. Например, подписанный char значение -126 эквивалентно unsigned char значение 130, но подписанное int значение -126 не может быть представлено как unsigned char или EOF.

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

Перед преобразованием в более широкий целочисленный тип данных приведите знак или простой char значение явно для unsigned char.

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

Примеры

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

#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

extern char parsed_token_buffer[20];

static int parser(char *buf)
{
    int c = EOF;
    if (buf && *buf) {
        c = *buf++;    
    }
    return c;
}

void func()
{
    if (parser(parsed_token_buffer) == EOF) { 
        /* Handle error */
        fatal_error();
    }
}

В этом примере функция parser может проходить строковый ввод buf. Если символ в строке имеет значение -1, он может представлять EOF или допустимое значение символа '\377' (соответствует unsigned char эквивалент 255). При преобразовании в int переменная c, его значение становится целым значением -1, которое всегда EOF. Более позднее сравнение с EOF не обнаружит, возвращено ли значение из parser фактически является EOF.

Коррекция - Приведение к unsigned char Перед преобразованием

Одной из возможных корректировок является отливка простой char значение для unsigned char перед преобразованием в более широкий int тип. Только тогда можно проверить, является ли возвращаемое значение parser действительно является EOF.

#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

extern char parsed_token_buffer[20];

static int parser(char *buf)
{
    int c = EOF;
    if (buf && *buf) {
        c = (unsigned char)*buf++;    
    }
    return c;
}

void func()
{
    if (parser(parsed_token_buffer) == EOF) { 
        /* Handle error */
        fatal_error();
    }
}
#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define NUL '\0'
#define SOH 1    /* start of heading */
#define STX 2    /* start of text */
#define ETX 3    /* end of text */
#define EOT 4    /* end of transmission */
#define ENQ 5    /* enquiry */
#define ACK 6    /* acknowledge */

static const int ascii_table[UCHAR_MAX + 1] =
{
      [0]=NUL,[1]=SOH, [2]=STX, [3]=ETX, [4]=EOT, [5]=ENQ,[6]=ACK,
      /* ... */
      [126] = '~',
      /* ... */
      [130/*-126*/]='\202',
      /* ... */
      [255 /*-1*/]='\377'
};

int lookup_ascii_table(char c)
{
    int i;
    i = (c < 0 ? -c : c);
    return ascii_table[i];
}

В этом примере char переменная c преобразуется в int переменная i. Если c имеет отрицательные значения, они преобразуются в положительные значения перед присвоением i. Однако это преобразование может привести к непредвиденным значениям, когда i используется в качестве индекса массива. Например:

  • Если c имеет значение -1, представляющее недопустимый символ EOF, вы, вероятно, хотите рассматривать это значение отдельно. Однако в этом примере значение c равное -1, приводит к значению i равно 1. Функция lookup_ascii_table возвращает значение ascii_table[1] (или SOH) без недопустимого значения символа EOF учитывается.

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

  • Если c имеет отрицательное значение при назначении i, его знак разворачивается. Однако при доступе к элементам ascii_table через i, это изменение знака может привести к непредвиденному считыванию значений.

    Например, если c имеет значение -126, i имеет значение 126. Функция lookup_ascii_table возвращает значение ascii_table[126] (или '~') но вы, вероятно, ожидали значение ascii_table[130] (или '\202').

Коррекция - Приведение к unsigned char

Чтобы устранить неполадки, избегайте преобразования из char кому int. Сначала проверьте c для значения EOF. Затем приведите значение char переменная c кому unsigned char и использовать результат в качестве индекса массива.

#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define NUL '\0'
#define SOH 1    /* start of heading */
#define STX 2    /* start of text */
#define ETX 3    /* end of text */
#define EOT 4    /* end of transmission */
#define ENQ 5    /* enquiry */
#define ACK 6    /* acknowledge */

static const int ascii_table[UCHAR_MAX + 1] =
{
      [0]=NUL,[1]=SOH, [2]=STX, [3]=ETX, [4]=EOT, [5]=ENQ,[6]=ACK,
      /* ... */
      [126] = '~',
      /* ... */
      [130/*-126*/]='\202',
      /* ... */
      [255 /*-1*/]='\377'
};

int lookup_ascii_table(char c)
{
    int r = EOF;
    if (c != EOF) /* specific handling EOF, invalid character */
        r = ascii_table[(unsigned char)c]; /* cast to 'unsigned char' */
    return r;
}

Информация о результатах

Группа: Программирование
Язык: C | C++
По умолчанию: Вкл для рукописного кода, выкл для сгенерированного кода
Синтаксис командной строки: CHARACTER_MISUSE
Воздействие: среднее
CWE ID: 704
Представлен в R2017a