Последняя сопоставленная страница

POSIX говорит "Система всегда заполняет нулями любую неполную страницу в конце объекта. Кроме того, система никогда не записывает какие-либо измененные части последней страницы объекта, которые выходят за его конец". , и документация Linux и FreeBSD имеет схожие формулировки на своих справочных страницах.
Это говорит о том, что, хотя чтение последних завершающих байтов не является строго законным (поскольку они находятся за пределами отображаемого диапазона), оно все же четко определено. и разработан таким образом, чтобы это могло работать без сбоев. Даже запись в эту область довольно четко определена.

Документация Windows, с другой стороны, ничего не говорит о конечных байтах в диапазоне меньше размера блока, и действительно предупреждает, что создание отображения большего размера, чем файл, увеличит размер файла и не обязательно обнулить данные.
Я склонен полагать, что это либо неверная информация, либо историческая (может быть, восходящая к Win95?). SetFileValidData требует нестандартных прав пользователя из соображений безопасности, поскольку это может сделать данные из ранее удаленного файла видимыми. Если бы разработчики ядра Windows позволили кому-то тривиально обойти это, сопоставив любой случайный файл, они должны были бы быть довольно глупыми.
Мое наблюдение в Windows XP заключается в том, что любые новые страницы, по-видимому, извлекаются из нулевого пула, а для пустой страницы обратная запись, либо файл автоматически разрежается, либо обратная запись выполняется очень и очень интеллектуальным способом (без заметной задержки в любое время, даже в диапазоне гигабайт).

Так о чем вопрос?

Мне нужно вычислить хэш-значения (возможно, тысяч) файлов, чтобы обнаружить подмножество файлов, которые были изменены. Можно предположить, что в качестве алгоритма используется SHA-256, хотя фактический алгоритм не имеет особого значения.
Что само по себе, конечно, не является большой проблемой, но, как и любое программное обеспечение, оно должно запускаться в кратчайшие сроки и не использовать память, и поэтому на. Обычные реалистичные ожидания, вы поняли :-)

Обычный способ вычисления такого хэша состоит в том, чтобы проверить, соответствует ли размер сообщения размеру блока хеш-функции (скажем, 64 байта), и заполнить последний неполный блок нулями, если это не так. Кроме того, хэш может иметь требования к выравниванию.
Обычно это означает, что вы должны либо сделать полную копию сообщения, либо написать специальный код, который хэширует все блоки, кроме одного, плюс копию последнего блока, дополненную нулями. Или что-то подобное. Алгоритм хеширования также часто молча делает такие вещи от своего имени. В любом случае это связано с перемещением большого количества данных и большей сложностью, чем можно было бы ожидать.

Теперь есть соблазн напрямую хешировать отображаемый в память файл и полагаться на тот факт, что отображение файла обязательно зависит от страниц памяти. Таким образом, и начальный адрес, и физически отображенная длина более или менее гарантированно будут кратны 4 КБ (64 КБ в некоторых системах). Что, конечно же, означает, что они также автоматически кратны 64, 128 или любому другому размеру блока, который может иметь хеш.
И по соображениям безопасности ни одна ОС не может позволить себе предоставить вам страницу, содержащую устаревшие данные.

Это означает, что вы можете просто наивно хэшировать весь файл, не беспокоясь о выравнивании, заполнении или чем-то еще, и избегая копирования данных. Он может прочитать несколько байтов после конца отображаемого диапазона, но обязательно останется на той же странице.

Я, конечно, знаю, что это технически незаконно. Чтение последних байтов за пределами отображаемого диапазона в некоторой степени сравнимо с утверждением, что malloc(5) в любом случае всегда возвращает 8-байтовый блок, поэтому безопасно использовать дополнительные 3 байта.

Хотя, помимо этой очевидной вещи, разумно ли мое предположение, что это будет «просто работать», или есть какая-то серьезная проблема, которую я не вижу ни на одной крупной платформе?

На самом деле меня не слишком интересуют теоретические или исторические операционные системы, но я хотел бы оставаться несколько портативным. То есть я хотел бы быть уверен, что он надежно работает на всем, с чем вы, вероятно, столкнетесь на настольном компьютере или на «типичном хостинг-сервере» (то есть в основном на Windows, Linux, BSD, OSX).
Если существует операционная система 1985 года, которая помечает последнюю страницу как нечитаемую и применяет строгие диапазоны байтов в своем обработчике ошибок, я согласен с этим. Вы не можете (и не должны) сделать всех счастливыми.


person Damon    schedule 14.07.2011    source источник


Ответы (1)


Обычный способ вычисления такого хэша состоит в том, чтобы проверить, соответствует ли размер сообщения размеру блока хеш-функции (скажем, 64 байта), и заполнить последний неполный блок нулями, если это не так.

Не совсем. Таким образом, вы не могли узнать длину последнего блока (был ли там ноль или он исходит из заполнения). Заполнение работает немного по-другому: в одной схеме вы всегда добавляете один 1, а затем 0s до конца блока.

Если ваши данные заканчиваются на границе блока, это означает, что нужен еще один блок. Этот лишний блок может попасть на лишнюю страницу. Так что я не думаю, что это может работать так, как вы описали.

Он может считывать несколько байтов после конца отображаемого диапазона, но он обязательно все еще будет находиться на той же странице.

Я думаю, что это должно работать на Intel/AMD, поскольку никто ничего не может с этим поделать. ЦП i386+ имеют сегменты и страницы. Сегменты могут заканчиваться на любой границе байта, но, насколько мне известно, ни одна текущая ОС их не использует. Так что, пока вы остаетесь на своей странице, это все ваше.

Поэтому я думаю, что это могло бы работать так:

  • в случае, если самый последний блок не имеет полного размера, сделайте заполнение на месте
  • в противном случае запустите последний раунд на подготовленном константном блоке, таком как 1000000000000000
person maaartinus    schedule 30.08.2012
comment
Добавление одной единицы с последующим заполнением нулями не имеет преимуществ перед заполнением нулями. Кстати, это условность одного конкретного хэша, но не более того. Это также не позволяет вам определить длину, если только 1 не является недопустимым символом. Длина должна храниться отдельно в любом случае. Предположение о том, что все, что есть на моей странице, принадлежит мне, вероятно (как указано в вопросе), но не обязательно верно. Вот в чем собственно вопрос. Тем не менее, я могу тем временем подтвердить, что он надежно работает как минимум в 3 версиях Windows (и в любом случае гарантируется POSIX). - person Damon; 30.08.2012
comment
Это не помогает определить длину, но вам и не нужно ее определять. Вам нужно предотвратить атаки, и для этого заполнения может быть или не быть достаточно, в зависимости от хэша (например, для CubeHash это так). Но моя точка зрения заключалась в том, что вам нужно больше места, чем просто отверстие в конце блока. - person maaartinus; 30.08.2012
comment
Я повторяю, что если ОС не использует сегментные регистры, она не имеет права вмешиваться до того, как вы пересечете границу страницы. Насколько я знаю, ни одна современная ОС не использует регистры сегментов, но это легко проверить. - person maaartinus; 30.08.2012
comment
Предположения, которые вы делаете, неверны, извините. Предотвращение атак не является приоритетом, приоритетом является обнаружение модификаций файлов. Впрочем, если что, то сохранение явной длины делает хэш более защищенным от коллизионных атак, но и так не представляет интереса (все равно длину знать надо). Добавление любого количества известного открытого текста ничего не дает для предотвращения атак. Причина заполнения известным значением (включая все нули) заключается в том, что хеширование одного и того же сообщения, не являющегося кратным блоку, всегда дает одно и то же значение хеш-функции. - person Damon; 30.08.2012
comment
О том, что ОС не может вмешиваться, это можно сделать, например (теоретически, как указано в вопросе), пометив последнюю страницу только для чтения и выполнив это в обработчике ошибок, так же, как каждая современная ОС фиксирует стеки потоков. Это довольно маловероятно, потому что он сжигает ЦП из-за незначительной выгоды, насколько я могу судить о x86. В то время меня интересовало, существуют ли какие-либо настоящие архитектуры, которые работают в режиме паранойи, где это, возможно, не так (настоящие архитектуры, такие как неиспользование перфокарт и 7-битных байтов). - person Damon; 30.08.2012