Я думаю, вы неправильно прочитали справочную страницу.Предполагая, что вы говорите о man-странице Linux, она утверждает (правильно), что explicit_bzero
и memset_explicit
и memset_s
более безопасны (для определенных целей), чем memset
и bzero
.Это не требует никакой разницы в безопасности между memset
и bzero
.Причина, по которой bzero
устарела, заключается в том, что это просто тривиальная оболочка вокруг memset
, и все реализации C имеют memset
, поэтому программисты могут также использовать memset
.
Разница между memset
/ *Варианты 1015 * и explicit_
/ _s
заключаются в том, что компиляторам запрещено оптимизировать явные варианты.Это делает явные варианты подходящими для очистки конфиденциальных данных.Например, рассмотрим следующий фрагмент программы:
bzero(password, password_length);
free(password);
Просто с bzero
или memset
многие современные компиляторы видят «о, вы пишете в память, а затем освобождаете ее.Нет возможности прочитать только что написанное bzero
, поэтому вызов bzero
равносилен бездействию.Ничего не делать быстрее, чем вызывать bzero
, поэтому я не буду генерировать код для вызова bzero
».
Недостаток в рассуждениях компилятора состоит в том, что причина обнуления памяти - не определенное поведениевашей программы, но что происходит в случае последующего неопределенного или неопределенного поведения.С точки зрения компилятора Си, неопределенное поведение означает, что все может произойти.С точки зрения инженера по безопасности, очень важно, что именно происходит с неопределенным поведением, таким как переполнение буфера или использование после освобождения.Точно так же, что именно считывается из неинициализированной памяти, не определено, но важно для инженера по безопасности.Инженеры по безопасности пытаются уменьшить влияние на безопасность такого неопределенного или неопределенного поведения.
Так что для инженера по безопасности оптимизация memset
является неудачной.Специалист по безопасности хочет гарантировать, что после освобождения памяти ее прежнее содержимое не будет вытекать, даже, скажем, из-за переполнения буфера .Следовательно, explicit_bzero
: компиляторы получают указание обрабатывать содержимое целевой памяти, когда эта функция возвращается как наблюдаемая, поэтому им не разрешается оптимизировать обратный вызов на том основании, что программа не читает с нее.Семантически, explicit_bzero(buffer, length)
эквивалентно
bzero(buffer, length);
for (size_t i = 0; i < length; i++) __observe__(buffer[i]);
, где __observe__
не имеет никакого эффекта, но, тем не менее, зависит от значения его аргумента.Поэтому компилятору не разрешается удалять вызов bzero
, потому что тогда __observe__
не будет считывать правильные значения.
Явное обнуление имеет ограничения.Страница man подчеркивает, что она не будет очищать копии переменных в регистрах, но обычно это не представляет большой проблемы, поскольку переполнение буфера или чтение из неинициализированной памяти редко приводит к утечке значений регистров.Самое большое ограничение на практике - realloc
.Когда вы используете динамически выделяемую память, realloc
может переместить ее, и нет никакого способа очистить старое значение.По этой причине, если содержимое буфера является чувствительным, вы не должны использовать на нем realloc
.
Другое ограничение явного обнуления состоит в том, что он применяется только на уровне программы, а не на уровне системы.Копии данных могут оставаться в кэш-памяти, в подкачке и т. Д. Цель обнуления памяти в вашей программе - защита от взлома безопасности в вашей программе.Он не защищает от больших системных компромиссов.
Обратите внимание, что написание собственного explicit_bzero
невозможно сделать переносимым .Лучшее, что вы можете сделать, - это заставить его работать с конечным набором версий конечного набора компиляторов, без гарантии того, что в следующей версии не будет более интересного оптимизатора, который проследит за вашей попыткой.Вот почему C11 добавил его в качестве стандартной функции с memset_s
.
¹ Почти.Технически автономные реализации не обязательно должны иметь memset
, но это такая простая и полезная функция, которая есть у большинства, и она обычно предоставляется компилятором, поэтому она доступна даже при сборке без обычной среды выполнения Си.