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

Основной причиной этой проблемы является расширение знака во время преобразования в более широкий тип. Большинство архитектур используют представление дополнения двух для хранения значений. В этом представлении самый значащий бит указывает на знак значения. При преобразовании в более широкий тип преобразование осуществляется путем копирования этого бита знака во все начальные биты более широкого типа, так что знак сохраняется. Для образца, 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. The 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++
По умолчанию: On для рукописного кода, off для сгенерированного кода
Синтаксис командной строки: CHARACTER_MISUSE
Влияние: Средний
ИДЕНТИФИКАТОР CWE: 704
Введенный в R2017a