Если кто‑то пытается хот‑патчить уже хот‑патченную функцию, как избежать конфликтов?

В последние несколько дней я провёл небольшой обзор механизмов горячего патчинга. Но проект горячего патча допускает только одного хот-патчера. Если кто-то пытается поставить хот-патч функции и обнаруживает, что она уже была хот-патчена, что произойдёт?

Если кто-то пытается поставить хот-патч функции и обнаруживает, что она уже была хот-патчена, значит что-то пошло не так.

Предполагаемая аудитория хот-патчинга — это Windows Update на системах, поддерживающих хот-патчинг (на момент написания этой статьи — Windows Server и, насколько мне известно, недавно добавленная Windows 11 Enterprise). Идея в том, что когда приходит обновление Windows, и администратор выбрал опцию хот-патчинга, и файл в обновлении помечен как «безопасный для хот-патчинга»,¹ то Windows Update использует пространство, зарезервированное для хот-патчинга, чтобы в реальном времени заменить затронутые функции.

Поскольку единственный код, уполномоченный использовать пространство хот-патча, — это Windows Update, система не должна обрабатывать случай, когда функцию уже хот-патчил кто-то другой. Нет другого кода, уполномоченного быть «кем-то другим»!

Но что если функция была перехвачена (detour) или как-то иначе пропатчена кем-то, кто не имеет на это прав?

Моё чтение кода хот-патчинга подсказывает, что если код хот-патча обнаруживает самовольное патчение, он объявляет файл непригодным для хот-патчинга, и системе придётся перезагрузиться. (Это обычно огорчает клиентов.)

Существует условие гонки: предварительное сканирование (prescan) может показать, что все функции безопасны для патчинга, но затем кто-то может пропатчить функцию после завершения prescan. В таком случае патчер выполнит работу наполовину и затем обнаружит самовольно пропатченную функцию, и теперь он как бы застрянет. Он не может продолжить, и он не может надёжно откатиться (потому что откат, вероятно, тоже провалится, поскольку патч был перекрыт). В памяти останется бинарник, наполовину пропатченный, и кто знает, что теперь произойдёт.

Приложение, которое использует пространство хот-патча, паркуется в зоне пожара. Пока пожарная машина не приехала, всё выглядит нормально, а потом чей-то дом сгорает дотла, потому что пожарная машина не может туда попасть.

Сопутствующее чтение: Слои совместимости приложений существуют для клиента, а не для программы.

¹ Не все изменения безопасны для хот-патчинга. Например, если изменение меняет компоновку или инварианты структуры данных, оно не подходит для хот-патчинга, потому что все экземпляры этой структуры, созданные до хот-патча, после хот-патча будут находиться в недопустимом состоянии.

Рэймонд участвует в эволюции Windows более 30 лет. В 2003 году он завёл веб-сайт под названием The Old New Thing, который стал гораздо популярнее всего, что он мог себе представить, — развитие, которое до сих пор заставляет его нервничать. Сайт породил книгу с тем же названием The Old New Thing (Addison Wesley, 2007). Он время от времени появляется в аккаунте Windows Dev Docs в Twitter, чтобы рассказывать истории, не несущие полезной информации.

Я полагаю, существует жёсткое требование, что каждая другая установка обновления требует перезагрузки. Что теперь приобретает смысл в данном контексте.

Что если «кем-то другим» был сам Windows Update некоторое время назад? Значит ли это, что Windows не будет хот-патчить два обновления в один и тот же файл?

Я достаточно уверен, что Windows Update справляется с этим нормально. Я могу представить два способа, как это делается.

1) Windows Update знает, что он сам является патчером, проверяя историю патчей, и обновляет целевую прыжковую таблицу (jump target).
2) Windows Update просто проверяет, имеет ли файл тот формат, который он использует (и при желании проверяет, является ли целевой адрес прыжка правдоподобной ссылкой в зарезервированной области патча), и обновляет целевой адрес прыжка.

Предсказываю — рано или поздно вы будете писать посты о отладке падений, вызванных применением приложениями хот-патчинга в корыстных целях, чтобы «добавить ценность» другим приложениям, внедряя себя в них.

Почему компилятор иногда использует ud2, а иногда int 3 для кода, который не должен выполняться?

Один из способов рассматривать спецификаторы доступа в типах реализации C++/WinRT…