6.58 Встроенные функции проверки размера объектов
GCC реализует ограниченный механизм защиты от переполнения буфера, который может предотвратить некоторые атаки переполнения буфера, определяя размеры объектов, в которые собираются записывать данные, и предотвращая запись, когда размер недостаточен. Встроенные функции, описанные ниже, дают наилучшие результаты, когда используются вместе и при включенном оптимизации. Например, для обнаружения размеров объектов через границы функций или для отслеживания присваивания указателей через нетривиальные потоки управления они полагаются на различные проходы оптимизации, включенные с -O2. Однако в определенной степени они могут использоваться и без оптимизации.
- Встроенная функция: size_t __builtin_object_size (const void * ptr, int type)
-
является встроенным конструктом, который возвращает постоянное число байтов от ptr до конца объекта, на который указывает указатель ptr (если известно на этапе компиляции). Для определения размеров динамически выделенных объектов функция полагается на функции выделения, вызываемые для получения памяти, которая должна быть объявлена с атрибутом
alloc_size(см. Общие атрибуты функций).__builtin_object_sizeникогда не вычисляет свои аргументы на побочные эффекты. Если в них есть побочные эффекты, она возвращает(size_t) -1для type 0 или 1 и(size_t) 0для type 2 или 3. Если существует несколько объектов, на которые может указывать ptr, и все они известны на этапе компиляции, возвращаемое число является максимальным из оставшихся число байтов в этих объектах, если type & 2 равно 0, и минимальным, если оно не равно нулю. Если на этапе компиляции невозможно определить, на какие объекты указывает ptr,__builtin_object_sizeдолжно вернуть(size_t) -1для type 0 или 1 и(size_t) 0для type 2 или 3.type — целая константа от 0 до 3. Если младший бит сброшен, объекты являются целыми переменными, если он установлен, ближайшее окружающее подобъект считается объектом, на который указывает указатель. Второй бит определяет, вычисляется ли максимальное или минимальное количество оставшихся байтов.
struct V { char buf1[10]; int b; char buf2[10]; } var; char *p = &var.buf1[1], *q = &var.b; /* Here the object p points to is var. */ assert (__builtin_object_size (p, 0) == sizeof (var) - 1); /* The subobject p points to is var.buf1. */ assert (__builtin_object_size (p, 1) == sizeof (var.buf1) - 1); /* The object q points to is var. */ assert (__builtin_object_size (q, 0) == (char *) (&var + 1) - (char *) &var.b); /* The subobject q points to is var.b. */ assert (__builtin_object_size (q, 1) == sizeof (var.b));
Для многих общих функций работы со строками добавлены встроенные функции, например, для memcpy __builtin___memcpy_chk предоставлена встроенная функция. Эта встроенная функция имеет дополнительный последний аргумент, который является числом байтов, оставшихся в объекте, на который указывает аргумент dest, или (size_t) -1 если размер неизвестен.
Встроенные функции оптимизируются до обычных функций работы со строками, таких как memcpy если последний аргумент (size_t) -1 или если на этапе компиляции известно, что объект назначения не будет переполнен. Если компилятор может определить на этапе компиляции, что объект всегда будет переполнен, он выдает предупреждение.
Предполагаемое использование может быть, например,
#undef memcpy #define bos0(dest) __builtin_object_size (dest, 0) #define memcpy(dest, src, n) \ __builtin___memcpy_chk (dest, src, n, bos0 (dest)) char *volatile p; char buf[10]; /* It is unknown what object p points to, so this is optimized into plain memcpy - no checking is possible. */ memcpy (p, "abcde", n); /* Destination is known and length too. It is known at compile time there will be no overflow. */ memcpy (&buf[5], "abcde", 5); /* Destination is known, but the length is not known at compile time. This will result in __memcpy_chk call that can check for overflow at run time. */ memcpy (&buf[5], "abcde", n); /* Destination is known and it is known at compile time there will be overflow. There will be a warning and __memcpy_chk call that will abort the program at run time. */ memcpy (&buf[6], "abcde", 5);
Такие встроенные функции предоставляются для memcpy, mempcpy, memmove, memset, strcpy, stpcpy, strncpy, strcat и strncat.
Также существуют встроенные функции проверки для функций форматированного вывода.
int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...);
int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os,
const char *fmt, ...);
int __builtin___vsprintf_chk (char *s, int flag, size_t os, const char *fmt,
va_list ap);
int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os,
const char *fmt, va_list ap);
Добавленный аргумент flag передается без изменений в функции __sprintf_chk и т. д. и может содержать специфичные для реализации флаги о дополнительных мерах безопасности, которые может предпринять функция проверки, например, обработку %n по-разному.
Аргумент os — это размер объекта s, на который указывает, как и в других встроенных функциях. Однако есть небольшая разница в поведении, если os является (size_t) -1, встроенные функции оптимизируются до функций без проверки только если flag равен 0, в противном случае функция проверки вызывается с аргументом os, установленным в (size_t) -1.
В дополнение к этому существуют встроенные функции проверки __builtin___printf_chk, __builtin___vprintf_chk, __builtin___fprintf_chk и __builtin___vfprintf_chk. У них есть только один дополнительный аргумент flag перед строкой формата fmt. Если компилятор может оптимизировать их до функций fputc и т. д., он это делает, в противном случае вызывается функция проверки, и аргумент flag передаётся ей.
Далее: Другие встроенные функции, Предыдущее: Расширения модели памяти x86, специфичные для транзакционной памяти, Вверх: Расширения C [Содержание][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-9.5.0/gcc/Object-Size-Checking.html