V1010. Unchecked tainted data is used in expression.
Анализатор обнаружил использование данных, полученных извне, без предварительной проверки. Такой сценарий излишнего доверия может привести к различным негативным последствиям, в том числе - стать причиной уязвимостей.
На данный момент диагностическое правило V1010 выявляет ошибки по нескольким паттернам:
- Использование недостоверных данных в выражении, используемом как индекс.
- Использование недостоверных данных в качестве аргумента функции, который должен содержать проверенные данные.
- Порча указателя за счёт изменения его значения с использованием недостоверных данных.
- Деление на недостоверные данные.
Примечание. Начиная с версии 7.32, пользователь может самостоятельно разметить необходимые функции как источники и приёмники недостоверных данных. Примеры аннотаций можно увидеть здесь.
Рассмотрим все паттерны более подробно.
Пример подозрительного кода при использовании в индексе недостоверных данных.
size_t index = 0;
....
if (scanf("%zu", &index) == 1)
{
....
DoSomething(arr[index]); // <=
}
Данный код может привести к доступу за границу массива 'arr' в случае, если пользователем будет введено значение отрицательное или превышающее максимально допустимый индекс массива 'arr'.
Безопасный код проверяет полученное значение:
if (index < ArraySize)
DoSomething(arr[index]);
Пример подозрительного кода при использовании недостоверных данных в аргументе функции.
char buf[1024];
char username [256];
....
if (scanf("%255s", username) == 1)
{
if (snprintf(buf, sizeof(buf) - 1, commandFormat, username) > 0)
{
int exitCode = system(buf); // <=
....
}
....
}
Этот код является уязвимым, т.к. пользовательский ввод передаётся командному интерпретатору без проверки полученных данных. Например, введя "&cmd", на Windows можно получить доступ к командному интерпретатору.
Правильный код должен осуществлять дополнительную проверку считанных данных:
if (IsValid(username))
{
if (snprintf(buf, sizeof(buf) - 1, commandFormat, username) > 0)
{
int exitCode = system(buf);
....
}
....
}
else
{
printf("Invalid username: %s", username);
....
}
Пример подозрительного кода, связанного с порчей указателя.
size_t offset = 0;
int *pArr = arr;
....
if (scanf("%zu", &offset) == 1)
{
pArr += offset; // <=
....
DoSomething(pArr);
}
В данном случае портится значение указателя 'pArr', т.к. в результате прибавления непроверенного значения 'offset' указатель может начать ссылаться за пределы массива. В результате можно испортить какие-то данные (на которые будет ссылаться 'pArr') с непредсказуемыми последствиями.
Правильный код проверяет допустимое смещение:
if (offset <= allowableOffset)
{
pArr += offset;
....
DoSomething(pArr);
}
Пример подозрительного кода с делением на недостоверные данные:
if (fscanf(stdin, "%zu", &denominator) == 1)
{
targetVal /= denominator;
}
Этот код может привести к делению на 0, если соответствующее значение будет введено пользователем.
Корректный код выполняет проверку допустимости значений:
if (fscanf(stdin, "%zu", &denominator) == 1)
{
if (denominator > MinDenominator && denominator < MaxDenominator)
{
targetVal /= denominator;
}
}
Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.). |
Данная диагностика классифицируется как:
Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1010. |