Недавно разбирались в причинах нестабильной работы модуля логгирования. Модуль, в общем-то, довольно простой. На вход подается строка, содержащая текст сообщения, а модуль занимается тем, что записывает это сообщение в журнал. Каждый из компонентов системы должен пользоваться этим модулем, для журналирования своих сообщений.
И вот, модуль логгирования вдруг стал время от времени "падать" в совершенно непредсказуемые моменты. Оказалось, что виноват в этом один из компонентов, который вместо осмысленного текста посылает в модуль логгирования мусор. У компонента проблема, но почему модуль-то падает?..
Ошибка стара, как Си :) Разработчик модуля логгирования реализовал функцию примерно так:
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.
Технологии мчатся вперед, а ошибки не меняются :)