Недавно разбирались в причинах нестабильной работы модуля логгирования. Модуль, в общем-то, довольно простой. На вход подается строка, содержащая текст сообщения, а модуль занимается тем, что записывает это сообщение в журнал. Каждый из компонентов системы должен пользоваться этим модулем, для журналирования своих сообщений.
И вот, модуль логгирования вдруг стал время от времени "падать" в совершенно непредсказуемые моменты. Оказалось, что виноват в этом один из компонентов, который вместо осмысленного текста посылает в модуль логгирования мусор. У компонента проблема, но почему модуль-то падает?..
Ошибка стара, как Си :) Разработчик модуля логгирования реализовал функцию примерно так:
void logMessage (const char *str)
{
printf(str);
}
Видно ошибку? Проблема тут в том, что текст сообщения напрямую подается в функцию
printf
, минуя использование форматных строк типа "%s\n"
. Если подавать на вход функции осмысленный текст, то все будет хорошо, до тех пор пока в тексте сообщения не встретится "%s"
. В этом случае функция printf
попытается сформировать новую строку, предполагая, что в стеке находятся дополнителльные аргументы. А аргументов-то там и нет. Вот тут-то printf
и упадет с большой долей вероятности (скорее всего это произойдет, когда printf
попытается вызвать strlen
для значения, которое достанет из стека).В нашем случае на вход функции подавался случайный мусор, поэтому время от времени в нем встречалась последовательность
"%s"
, и модуль вываливался с ошибкой Segmentation fault.Вот правильное использование
printf
для нашей функции логгирования:
void logMessage (const char *str)
{
printf("%s", str);
}
Вызов
printf
и ей подобных должен выглядеть именно так и никак иначе.Читать об уязвимостях printf.
Технологии мчатся вперед, а ошибки не меняются :)