/LARGEADDRESSAWARE может испортить вам весь день.
Только что потратил кучу времени на разборки с /LARGEADDRESSAWARE. Этот ключ используется для указания сборщику, что данный модуль (.exe или .dll) способен корректно обрабатывать адреса больше 2GB. Модули, помеченные подобным образом, могут быть загружены выше 2GB при условии, что система поддерживает увеличенное пользовательское адресное пространство. На данный момент это либо серверные версии Windows, сконфигурированные ключом /3GB в boot.ini, либо 64-х битные версии системы.
Про причины того, почему расширенное адресное пространство не включается для всех программ, хорошо написал Raymond Chen: Myth: The /3GB switch expands the user-mode address space of all programs. Дело как всегда в том, что слишком много приложений не будут работать в такой конфигурации - начинает буксовать арифметика указателей. Сегодняшний баг занёс ещё одну операцию в “черный” список.
Итак, какое значение будет присвоено переменной wide_ptr после инициализации (код компилируется для x86)?
PVOID ptr = (PVOID)0x87654321;
ULONGLONG wide_ptr = (ULONGLONG)ptr;
Ответ - 0xffffffff87654321! Очевидно, что проблема в расширении знака, однако это совсем не очевидно из данного отрывка кода. Чтобы результат получился верным, код нужно переписать вот так:
PVOID ptr = (PVOID)0x87654321;
ULONGLONG wide_ptr = (ULONGLONG)(ULONG_PTR)ptr;
Я думаю, расстрелять надо того @удака, который н-дцать лет назад сделал указатели signed.
Если б каждого такого расстреливать - сколько бы народу полегло!
А заодно еще и того, кто придумал иметь общий стек на адреса возврата и временные данные (и тем самым породил проблемы с переполнением буфера).
Кстати, совершенно не понимаю, почему современные компилерам не делают раздельные стеки. Тем самым проблема переполнения решается на 95% простой перекомпиляцией программы.
Подозреваю, что из-за проблем с совместимостью и производительностью:
У всех свои недостатки (с) Во-первых, как-то смешно говорить о десятке лишних инструкций на системный вызов при процессорах о трех гигагерцах, а во-вторых, раз программу все равно перекомпилировать, то вполне можно иметь #define SAFE_STACK, по которому системные вызовы и будут все понимать.
По-моему, все же лучше слегка пожертвовать производительностью, чем потом закрывать туеву хучу дырок постфактум. Проверка длины буфера - это, похоже, sacred knowledge, известное в основном только нашим программерам.
P.S. У твоего блога сносит крышу, если коммент содержит кавычки или апострофы. Которые еще и эскейпятся криво. Похоже, вы там чего-то намудрили с защитой от sql injection.
Не на системный вызов. На вызов любой функции. Если функция обращается к стеку данных, значит понадобится дополнительный код, обслуживающий обращения к стеку данных.
Скорее всего производительность не будет основной проблемой. Вот совместимость - это да. Она будет такой проблемой, что мало не покажеться. Хотя, если подумать, .NET (или Java), по сути, и является решением, которое как раз и родилось в результате попыток сделать компилируемый код безопаснее. Заодно и проблему совместимости обошли.
Значит пришла пора перейти на версию посвежее: http://trac.wordpress.org/ticket/2761.