Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет yigal_s ([info]yigal_s)
@ 2007-08-08 18:56:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
хардкор-программизм: как хороши, как свежи были розы...
Функция IsBadReadPtr и прочие из той же группы очень давно мне не нравились. Ну хотя бы тем, что в данный момент такая функция может выдать тебе, что по данному адресу можно что-то читать, а уже через долю секунды другой тред как-нибудь перераспредилит память, и читать уже не получится. То есть, думал я, не лучше ли сразу читать проблемную память из под try-except блока, не заморачиваясь предварительными проверками?

Реальность, однако, оказалась куда как хуже. Даже в чем-то безнадежной она оказалась.

Выяснилось, что:

если доступ к памяти "наугад", будь он сделан через IsBadReadPtr или же простое чтение памяти из-под try-except блока, нечаянно попадет на guard-страницу стека чужого треда,

то guard-аттрибут этой страницы будет снесён,

что впоследствии, когда стек второго треда подрастет за пределы той бывшей guard-страницы, приведет к "исчерпанию" стека этого треда и катастрофическому и молчаливому завершению всей аппликации.

Конечно, при касании guard-страницы, кидается STATUS_GUARD_PAGE_VIOLATION исключение, и можно было бы попытаться восстановить аттрибут guard-страницы. Можно было бы, если бы не очевидный race-condition: пока мы будем восстанавливаеть аттрибут, второй тред уже может навернуться. Функция же IsBadReadPtr попросту ловит все исключения, и обрабатывает их единообразно, и не пытаясь восстанавливать какие-то там аттрибуты.

И если бы только это...

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

Всё это, кстати, уже документировано на сайте msdn.


(Добавить комментарий)


[info]spamsink@lj
2007-08-08 14:04 (ссылка)
Конечно, при касании guard-страницы, кидается STATUS_GUARD_PAGE_VIOLATION исключение, и можно было бы попытаться восстановить аттрибут guard-страницы.

А как это работает в штатном режиме, когда честное обращение к стеку касается guard-страницы?

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 14:15 (ссылка)
Когда тред касается гард-страницы своего стека, то система, по идее, сама двигает guard-page, при этом исключение STATUS_GUARD_PAGE_VIOLATION вообще не появляется в user-mode.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]spamsink@lj
2007-08-08 14:29 (ссылка)
Система проверяет значение указателя стека, чтобы выяснить, каким способом коснулись guard-page? А если я на одно слово под стек полезу другим регистром, то что будет?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 14:30 (ссылка)
> проверяет значение указателя стека

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]spamsink@lj
2007-08-08 14:42 (ссылка)
Понятно. Тогда с какой, вообще, целью был сделан IsBadReadPtr? Я не могу придумать ни одного разумного примера его использования.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 14:50 (ссылка)
Просто дизайнеры ошиблись.

И не только в этом. К примеру, функцию PulseEvent тоже лучше не пытаться использовать. "This function is unreliable and should not be used" - цитата из нынешнего MSDN.

(Ответить) (Уровень выше)


[info]juan_gandhi@lj
2007-08-08 15:17 (ссылка)
Дебилизм какой. Нельзя что ли CAS использовать? Они там в Редмонде Хора не читали, наверное, про философов с вилками.

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 17:13 (ссылка)
Да черт их знает. Может, погнались за перформансом однотредных аппликаций?
А потом, поняв, что весь этот IsBadPtr stuff - одна большая лажа, просто всё забросили...?

(Ответить) (Уровень выше)


[info]ygam@lj
2007-08-09 21:37 (ссылка)
Что-что использовать?

(Ответить) (Уровень выше)


[info]disbalanced@lj
2007-08-08 15:51 (ссылка)
Круто!

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 17:01 (ссылка)
это лишь пересказ прочитанного и услышанного

(Ответить) (Уровень выше)


[info]dumalkin@lj
2007-08-08 16:28 (ссылка)
А можно идиотский вопрос ?
Зачем вам проверять "вслепую" можно ли читать/писать в случайный адрес ?
Я не в порядке критики, мне просто интересна сама идея когда такое решение имеет смысл.
А вообще это явный пример несогласованности двух груп в МС - системщики придумали красивое решение как наращивать стек, user mode API захотели помочь юзерам ... :) Получилось как всегда :)

П.С.
Если вам надо знать законен ли адрес, можно использовать свой heap manager - брать отдельные страницы, делать lock/commit и детали уже делать самим. Тогда по значению самого адреса можно будет понять выделен он или нет, да и ни с какими стеками это точно не пересечется - это типа если вам защита от bad guy нужна, который вам плохие адреса подсовывает :)

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-08 16:58 (ссылка)
> Зачем вам проверять "вслепую" можно ли читать/писать в случайный адрес ?

Например, чтобы написать библиотечную функцию, которая не падает при некорректных аргументах.

Или чтобы сделать стримминг аргументов функции (вот как DCOM делает когда маршалит вызов на другую машину).

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

Поэтому более актуальный пример - трассировка аргументов функции. При такой трассировке уже креш апликации из-за ошибки трассировщика полностью недопустим. Какими бы неверными не были бы аргументы функции с точки зрения этого самого трассировщика.

> Если вам надо знать законен ли адрес, можно использовать свой heap
manager

Ага. А если в аппликации модули двух производителей, каждый из которых захотел использоватьс "свой heap manager"? Кроме того, на С++, коим я занимаюсь, память, актуальная для доступа, может быть не только в хипе, но и в других местах. в том же стеке, кстати.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dumalkin@lj
2007-08-08 17:13 (ссылка)
Что происходит с DCOM когда ему подсовывают бред - я думаю вы знаете.
.Нет в подобной ситуации тихо исчезает :)

Трассировщик - дело хорошее (я так и подозревал что задача инфраструктурная, а не аппликативная), но вы пытаетесь решить проблему, которую большинство известных мне коммерческих пакетов не решили.
Последний раз, когда искали нечто вроде Purify, не смогли найти ни один, способный справится со всем проектом - при том что мусора в аргументах не было :)

Кстати, универсальный трассировщик, работающий на принципе "инструментации" всего когда (замена пролога/эпилога и т.д.), как Purify, очень хорошо запутывается при работе с пакетами, использующими нетривиальные способы работы с памятью - при отсутствии каких бы то ни было багов.

В одном проекте использовался буфер, сидящий в карточке (железо), спроецированный в адресное пространство процесса. Причем не постоянно - была аппликативная логика когда можно читать, когда нельзя - изза ограничений железа. Не помню какой отладчик (user mode) они использовали, но постоянно получали "голубой экран" - как оказалось дебаггер в "неурочное время" пытался зачем-то прочитать адрес с шины, которая была "у железки" :) Если я правильно помню, они с этим возились где-то месяц пока я их не осенил :) Самое смешное, что впоследствии оказалось что та проблема которую они хотели решить с помощью отладчика не существовала вовсе, и вся система работает - только не надо ее трогать :)

(Ответить) (Уровень выше)


[info]aamonster@lj
2007-08-09 07:03 (ссылка)
Ну, если на то пошло, в нормальной работе эта функция вызываться там, где результат не предопределен, не должна. Грубо говоря, ее место - в assert'ах.

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-09 07:21 (ссылка)
а её из какого места не вызывай, всё равно последствия могут быть фатальными

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]aamonster@lj
2007-08-09 07:29 (ссылка)
Это ту, что Write? Да. Поразительно, на самом деле - неужели нельзя было ее реализовать так, чтобы на это время остальные треды замораживались? Если нельзя - то в морг.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2007-08-09 09:48 (ссылка)
> Это ту, что Write

любую

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]aamonster@lj
2007-08-09 10:08 (ссылка)
Ах да - прочитал невнимательно...
Изумительная реализация, ничего не скажешь.

(Ответить) (Уровень выше)