Введение
Функция C для форматирования и вывода текста
Функция `printf` является функцией стандартной библиотеки C, которая форматирует текст и записывает его в стандартный вывод. Название `printf` является сокращением от "print formatted", где "print" относится к выводу на принтер, хотя функции не ограничиваются выводом на принтер. Стандартная библиотека предоставляет множество других аналогичных функций, которые образуют семейство функций, подобных `printf`. Эти функции принимают параметр строки формата и переменное количество параметров значений, которые функция сериализует в соответствии со строкой формата и записывает в выходной поток или буфер строки. Строка формата кодируется как язык шаблонов, состоящий из литерального текста и спецификаторов формата, каждый из которых указывает, как сериализовать значение. По мере обработки строки формата слева направо, следующее значение используется для каждого найденного спецификатора формата. Спецификатор формата начинается с символа `%` и имеет один или несколько следующих символов, которые указывают, как сериализовать значение. Синтаксис и семантика строки формата одинаковы для всех функций в семействе, подобных `printf`. Несоответствие между спецификаторами формата и количеством и типом значений может привести к сбою или уязвимости. Строка формата `printf` дополняет строку формата `scanf`, которая обеспечивает форматированный ввод (лексический анализ, также известный как парсинг). Обе строки формата предоставляют относительно простую функциональность по сравнению с другими движками шаблонов, лексерами и парсерами. Механизм форматирования был скопирован в других языках программирования.
1970-е годы: C
В 1973 году был включен как подпрограмма на языке C в состав версии 4 Unix.
1990-е годы: командование Shell
В 1990 году команда оболочки впервые задокументирована в составе 4.3BSD Reno. Она создана по образцу стандартной функции библиотеки. В 1991 году команда была включена в состав GNU shellutils (в настоящее время часть GNU Core Utilities).
Поле точности
Поле точности обычно задает максимальное ограничение вывода, в зависимости от конкретного типа форматирования. Для чисел с плавающей точкой оно определяет количество знаков после десятичной точки, до которых вывод должен быть округлен. Для строкового типа оно ограничивает количество символов, которые должны быть выведены, после чего строка усекается. Поле точности может быть опущено, указано в виде целого числа или динамического значения, передаваемого в качестве отдельного аргумента, если обозначено звездочкой. Например, выводит .
Форматирование типа данных по умолчанию
Обычный способ обработки форматирования с пользовательским типом данных — это преобразование значения пользовательского типа данных в строку, а затем использование спецификатора для включения сериализованного значения в более крупное сообщение. Некоторые функции, подобные printf, позволяют расширять мини-язык на основе escape-символов, что позволяет программисту использовать определенную функцию форматирования для типов, не встроенных в систему. Одним из примеров является (в настоящее время устаревший) glibc's. Однако он редко используется из-за конфликта со статической проверкой строки формата. Другой вариант — пользовательские форматировщики Vstr, которые позволяют добавлять имена форматов, состоящие из нескольких символов. Некоторые приложения (например, Apache HTTP Server) включают собственную функцию, подобную printf, и встраивают в нее расширения. Однако все они сталкиваются с теми же проблемами, что и . Функция printk ядра Linux поддерживает несколько способов отображения структур ядра с использованием общей спецификации, путем добавления дополнительных символов формата. Например, печатает IPv4-адрес в десятично-точечном формате. Это позволяет выполнять статическую проверку строк формата (части) за счет полной совместимости с обычной функцией printf.
Семья
Варианты предоставляют функции форматирования, но с дополнительным или слегка отличающимся поведением. `outputs` выводит данные в объект системного файла, что позволяет перенаправлять вывод не только в стандартный поток вывода. `writes` записывает данные в строковый буфер вместо стандартного вывода. `provides` обеспечивает большую безопасность по сравнению с ``, поскольку вызывающая сторона предоставляет параметр длины (n), который указывает максимальное количество символов для записи в буфер. Для большинства функций семейства `printf` существует вариант, принимающий `` вместо списка параметров переменной длины. Например, существуют ``, ``, ``.
Формат атаки строки
Параметры с дополнительными значениями игнорируются, но если строка формата содержит больше спецификаторов формата, чем переданных параметров значений, поведение не определено. Для некоторых C-компиляторов лишний спецификатор формата приводит к использованию значения, даже если его не существует. Это может привести к уязвимости, связанной с использованием строки формата. Обычно в C аргументы передаются через стек. Если передано недостаточно аргументов, функция printf может прочитать данные за пределами стекового фрейма, что позволяет злоумышленнику прочитать содержимое стека. Некоторые компиляторы, такие как GNU Compiler Collection, статически проверяют строки формата функций, подобных printf, и предупреждают о проблемах (при использовании флагов или ). GCC также предупреждает о пользовательских функциях, реализованных в стиле printf, если к функции применяется нестандартный атрибут "format".
Неконтролируемый эксплойт строки формата
Строка формата часто является строковым литералом, что позволяет проводить статический анализ вызова функции. Однако строка формата может быть значением переменной, что допускает динамическое форматирование, но также создает уязвимость безопасности, известную как эксплуатация неконтролируемой строки формата.
Запись в память
Хотя функция вывода на поверхность позволяет записывать данные в область памяти, указанную аргументом, этот функционал иногда используется в более сложных атаках на строки формата. Эта функциональность также делает систему случайно полной по Тьюрингу, даже при использовании корректного набора аргументов. Игра в крестики-нолики, написанная на языке строк формата, победила в 27-м конкурсе IOCCC.