Misuse of sign-extended character value

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

Описание

Этот дефект происходит, когда вы преобразуете со знаком или плоскость 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 переменная, вы видите неожиданные результаты.

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

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

Риск

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

  • Если вы сравниваете значение переменных с EOF:

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

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

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

  • Если вы передаете значение переменных в качестве аргумента к обрабатывающей символ функции:

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

Фиксация

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

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

Примеры

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

#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
Удар: Средняя
ID CWE: 704
Введенный в R2017a