Почему мой код не будет segfault в Windows 7?

Это необычный вопрос, но вот:

В моем коде я где-то случайно разыменовываю NULL. Но вместо сбоя приложения из-за segfault оно, кажется, останавливает выполнение текущей функции и просто возвращает управление пользовательскому интерфейсу. Это затрудняет отладку, потому что обычно я хотел бы получать предупреждения о сбое, чтобы я мог подключить отладчик.

Что может быть причиной этого?

В частности, мой код представляет собой драйвер ODBC (т.е. DLL). Мое тестовое приложение — ODBC Test (odbct32w.exe), которое позволяет мне явно вызывать функции ODBC API в моей библиотеке DLL. Когда я вызываю одну из функций с известным segfault, вместо сбоя приложения ODBC Test просто возвращает управление пользовательскому интерфейсу, не печатая результат вызова функции. Затем я могу снова вызвать любую функцию в моем драйвере.

Я знаю, что технически приложение вызывает диспетчер драйверов ODBC, который загружает и вызывает функции в моем драйвере. Но это не имеет значения, так как мой segfault (или что-то еще) приводит к тому, что функция диспетчера драйверов также не возвращается (о чем свидетельствует приложение, не печатающее результат).

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


person Trevor    schedule 07.01.2011    source источник
comment
Мне нравится название этого вопроса... но я не могу возможно отправить его без ошибок!!   -  person    schedule 07.01.2011
comment
Вы, кажется, предоставили все, кроме кода. Возможно, компилятор оптимизирует код, который ничего не делает.   -  person Ian Boyd    schedule 07.01.2011
comment
Я полагаю, что исключение обрабатывается где-то?   -  person EboMike    schedule 07.01.2011
comment
Я с EboMike, что-то будет ловить AV   -  person David Heffernan    schedule 07.01.2011
comment
В зависимости от контекста — обычно любое такое действие приводит к ошибке ошибки страницы, поскольку вы не можете прочитать 0x00000000 (или около того). Этот бросок будет распространяться вверх по стеку к любому обработчику, который его примет. Нет обработчика -> обратно в ОС/систему выполнения, которая, вообще говоря, выгружает вызывающий нарушение .exe и завершает процесс. В этой цепочке масса возможностей перехватить такое поведение, не зная вашего контекста, не могу сказать, что именно может ходатайствовать от вашего имени.   -  person Mordachai    schedule 07.01.2011
comment
Ян: Я могу подключить отладчик и выполнить код до того места, где должен произойти segfault. Когда я перехожу к следующей строке, он просто возвращает управление обратно в приложение.   -  person Trevor    schedule 07.01.2011
comment
ЭбоМайк: Это C/C++. Сегментарную ошибку нельзя превращать в обрабатываемое исключение, кроме возбуждения сигнала. Насколько я могу судить, это приложение не обрабатывает сигналы. Как я уже сказал, одно и то же приложение на компьютере одного из моих коллег рухнет, как и ожидалось.   -  person Trevor    schedule 07.01.2011
comment
@Trevor: Возможно, вы захотите попробовать использовать windbg вместо отладчика Visual Studio. Не всегда легко увидеть исключения в отладчике Visual Studio.   -  person ReinstateMonica Larry Osterman    schedule 08.01.2011
comment
@ Тревор: это Windows. Нарушение прав доступа можно перехватить с помощью структурированной обработки исключений (SEH), механизма на уровне ОС.   -  person Seva Alekseyev    schedule 20.04.2011


Ответы (5)


Windows имеет непереносимые языковые расширения (известные как "SEH "), которые позволяют отлавливать ошибки страниц и нарушения сегментации как исключения.

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

Посмотрите этот пост в блоге, например: mode-callback-exceptions-in-x64/" rel="noreferrer">Случай исчезновения исключения OnLoad — исключения обратного вызова пользовательского режима в x64

Обновление:

Я нахожу странными те идеи, которые мне приписывают в комментариях. Для записи:

  • Я не утверждал, что SEH плохой.

    Я сказал, что он "непереносимый", и это правда. Я также утверждал, что использование SEH для игнорирования STATUS_ACCESS_VIOLATION в коде пользовательского режима «печально». Я придерживаюсь этого. Я должен надеяться, что у меня хватило наглости сделать это в новом коде, а вы, просматривая мой код, накричали бы на меня, как если бы я написал catch (...) { /* Ignore this! */ }. Это плохая идея. Это особенно плохо для нарушения доступа, потому что получение AV обычно означает, что ваш процесс находится в плохом состоянии, и вам не следует продолжать выполнение.

  • Я не утверждал, что существование SEH означает, что вы должны проглатывать все ошибки.

    Конечно, SEH — это общий механизм, и его нельзя винить в каждом идиотском его использовании. Я сказал, что некоторые двоичные файлы Windows поглощают STATUS_ACCESS_VIOLATION при вызове указателя на функцию, что является истинным и наблюдаемым фактом, и что это далеко не красиво. Обратите внимание, что у них могут быть исторические причины или смягчающие обстоятельства, оправдывающие это. Отсюда «грустно, но факт».

  • Я не вводил здесь какую-либо риторику «Windows против Unix». Плохая идея — это плохая идея на любой платформе. Попытка восстановиться с SIGSEGV на ОС типа Unix была бы столь же схематичной.

person asveikau    schedule 07.01.2011
comment
Я бы не назвал SEH расширением языка, я думаю о нем больше как об услуге, предоставляемой ОС. Я не совсем уверен насчет обратных вызовов оконных сообщений - что это такое? Вы имеете в виду WNDPROCs? Хотя Windows отличается от UNIX, сам по себе этот факт не делает ее хуже. - person David Heffernan; 07.01.2011
comment
@David Heffernan - Да, я имею в виду WNDPROC, и я ничего не говорю о том, делает ли понятие WNDPROC что-то низшим или превосходящим, только то, что использовать SEH для поимки STATUS_ACCESS_VIOLATION - плохая идея. Ловить катастрофическое поведение, не убивать процесс и делать вид, что ничего не произошло — категорически зло. - person asveikau; 07.01.2011
comment
Это кажется разумным, но не объясняет, почему код давал сбой на одной машине, но скрывал сбой на другой. - person Trevor; 07.01.2011
comment
@Trevor - я мог бы предложить посмотреть программу в отладчике на каждой машине и проверить стек, чтобы убедиться, что это действительно то же самое. В Windbg (средства отладки для Windows, бесплатная загрузка с MSFT) вам, возможно, придется использовать команду sxe, которая прерывается при возникновении исключений, даже если они перехвачены. - person asveikau; 07.01.2011
comment
@asveikau SEH обычно используется поставщиками инструментов для реализации исключений C++ или других языков с исключениями. SEH не проглатывает исключения, он создает и транспортирует их. Если вы вызываете определенные функции API Win32, и SEH активен, и вы передаете указатели NULL, тогда вы можете поднять AV в Windows DLL, я иногда вижу, что это происходит в kernel32 (который на самом деле не является ядром, просто чтобы запутать дело!) Если эти исключения вернутся в мое приложение и останутся необработанными, тогда да, мое приложение завершится. - person David Heffernan; 07.01.2011
comment
@asveikau По сути, я хочу сказать, что в Windows с SEH совершенно нормально, когда нарушение прав доступа приводит к исключению, которое, если оно не будет обработано, завершит работу приложения. Если вы решите в своих приложениях молча проглатывать исключения SEH, то это ваша ошибка, а не недостаток дизайна в Windows. - person David Heffernan; 07.01.2011
comment
В SEH нет ничего плохого, кроме проблем с сигналами. Это просто механизм ОС для обработки исключений на уровне ОС. Они предшествуют C++ (Win32 изначально был написан на C, у которого нет исключений). Единственными языковыми расширениями являются то, что они позволяют приложению C/C++ подключаться к транспорту SEH на уровне ОС и перехватывать такие исключения так же, как исключение C++. Чрезвычайно полезно, ЕСЛИ вы знаете, что делаете. - person Mordachai; 08.01.2011
comment
@ Дэвид Хеффернан - я предлагаю вам перечитать мой пост, возможно, несколько раз. Я чувствую, что вы вкладываете слова в мои уста. Я отредактировал сообщение, чтобы уточнить, что, по моему мнению, вы неправильно прочитали. - person asveikau; 08.01.2011
comment
@asveikau Все в порядке, кажется, у меня получилось с первого раза! И я до сих пор не рассматриваю SEH как расширение языка. Какой язык расширяется? В прошлый раз, когда я проверял, ссылка на недопустимые указатели была UB. Или я ошибся? - person David Heffernan; 08.01.2011
comment
@David Heffernan - я бы назвал __try расширением в C. C не имеет исключений, AFAIK __try реализуется в компиляторе, а не с помощью магии макросов, и последнее, что я знал, GCC не поддерживает __try. - person asveikau; 08.01.2011
comment
@asveikau __try — это расширение языка, но это не то же самое, что SEH. - person David Heffernan; 08.01.2011
comment
@Mordachai: Теоретически вы правы, что с SEH все в порядке. Но на практике чрезвычайно трудно получить правильный SEH. Вот почему Microsoft запретила внутреннее использование SEH с некоторыми довольно ограниченными исключениями (код режима ядра, проверяющий адреса пользовательского режима, RPC/COM). - person ReinstateMonica Larry Osterman; 08.01.2011
comment
Я принимаю это как ответ. Я смог использовать отладчик, чтобы увидеть исключение SEH. Я до сих пор не могу понять, почему приложение скрывает исключение для меня, а не для моего коллеги, но я не могу исследовать это дальше, поскольку у меня нет источника для этого. - person Trevor; 09.01.2011
comment
@ Ларри - Интересно. У меня никогда не было проблем с тем, чтобы заставить SEH работать с нашими приложениями на C++. Но затем я использую его только для перехвата исключений за пределами пространства C++ (истинные SEH, выдаваемые аппаратным обеспечением ЦП, такие как ошибки с плавающей запятой, переполнение стека и т. д.). Опять же, зачем вам использовать его для чего-то еще? - person Mordachai; 10.01.2011
comment
@Mordachai: Проблема в том, что перехват исключений может привести либо к проблемам с надежностью (вы перехватываете исключение и превращаете сбой в повреждение памяти или взаимоблокировку), либо к дырам в безопасности. Почти единственное, что вы должны сделать в обработчике исключений, это завершить процесс, и, учитывая, что ОС сделает это за вас (и сохранит аварийный дамп, чтобы вы могли получить его позже), обычно лучше просто позволить ОС обработать их. - person ReinstateMonica Larry Osterman; 11.01.2011
comment
@ Ларри - я с уважением не согласен. Я, например, написал обработчик переполнения стека, который правильно перехватывает, раскручивает стек и восстанавливает стек, ничего не подвергая опасности. Это не тривиальный код, и он полностью зависит от ОС. Но это правильное использование SEH. Точно так же я написал общую ловушку SEH, которая создает мини-дамп состояния выполнения, прежде чем перейти к аварийному завершению работы ОС. Это также чрезвычайно полезно для получения диагностики серьезных ошибок из очень трудно воспроизводимых проблем в полевых условиях. - person Mordachai; 11.01.2011
comment
@Mordachai: Я разговаривал с парнями, которые в прошлом владели средствами диагностики ошибок Windows - правильная обработка исключений невероятно трудна. В частности, запуск кода в процессе, когда вы не можете делать никаких предположений о состоянии процесса, довольно сложен. И это состояние, в котором вы находитесь, когда имеете дело с исключением нарушения прав доступа. - person ReinstateMonica Larry Osterman; 19.01.2011
comment
@ Ларри: это может быть ошеломляюще сложно, но, если обстоятельства правильные, это выполнимо. Наше приложение поразительно сложно сломать, к большому удовольствию наших пользователей. Каждое действие, по сути, предпринимается, и если что-то идет не так, то действие отбрасывается, не влияя на остальную стабильность системы, по крайней мере, теоретически. Я не призываю среднестатистического программиста возиться с SEH, но для нашего продукта это сработало очень хорошо. - person Mordachai; 19.01.2011
comment
@Mordachai: Реально единственная безопасная вещь, которую можно сделать с помощью обработчика, - это заморозить все текущие потоки в процессе и вызвать внешний отладчик. Любые попытки проверить состояние текущего процесса с помощью фильтра исключений или обработчика, скорее всего, потерпят неудачу. Вот почему написание обработчика исключений в продукте MSFT автоматически гарантирует, что отправка вашего приложения будет заблокирована из-за обширных внешних проверок. - person ReinstateMonica Larry Osterman; 20.01.2011
comment
@ Ларри: фигня. Тот факт, что один поток сталкивается с бесконечным циклом из-за точных условий, с которыми столкнулись данные пользователя, не делает недействительным состояние других потоков (если только они не обслуживают этот). Даже в этом случае вполне возможно спроектировать ваше программное обеспечение таким образом, чтобы раскрутка одного потока завершала (чисто) подчиненные потоки. А поскольку C++ имеет отличные возможности раскручивания стека, вы можете без проблем вернуться к стабильной начальной точке и позволить пользователю изменить условия и повторить попытку оттуда. - person Mordachai; 20.01.2011
comment
Не забываем про FTH — Fault Tollerant Heap — см. мой пост ниже! Это ваша проблема в Windows 7! - person Петър Петров; 25.04.2012

Разыменование указателя NULL — это неопределенное поведение, которое может привести к чему угодно — seg.fault, письму в IRS или публикации в stackoverflow :)

person Gene Bushuyev    schedule 07.01.2011
comment
Это в порядке возрастания вредности? :) - person EboMike; 07.01.2011
comment
Это на 100% правильно, но на самом деле это не ответ на этот конкретный вопрос. - person Mordachai; 07.01.2011
comment
Это действительно так. Неопределенное поведение означает, что он может дать сбой на машине с операционной системой X, но не сбой на другой машине с операционной системой Y. Фактически, ЛЮБОЕ изменение (другой компилятор, другая платформа, другая конфигурация, другое время суток) может привести к другим результатам. . - person EboMike; 08.01.2011
comment
@EboMike или Безумие по убыванию - person Newtopian; 25.03.2015

В Windows 7 также есть Fault Tollerant Heap (FTH), который иногда делает такие вещи. В моем случае это тоже было NULL-разыменование. Если вы разрабатываете Windows 7, вы действительно хотите отключить ее!

Что такое отказоустойчивая куча в Windows 7?

http://msdn.microsoft.com/en-us/library/dd744764%28v=vs.85%29.aspx

person Петър Петров    schedule 20.04.2011

Прочтите о различных типах обработчиков исключений здесь. ловить одинаковые исключения.

person user541686    schedule 07.01.2011

Прикрепите свой отладчик ко всем приложениям, которые могут вызывать вашу dll, включите функцию прерывания при возникновении исключения, а не просто необработанного в меню [отладка]|[исключения].

ODBC — это большинство (если не все) COM, поскольку такие необработанные исключения будут вызывать проблемы, которые могут выглядеть как выход из функции ODBC странным образом или так плохо, как зависание и никогда не возвращаются.

person Greg Domjan    schedule 07.01.2011
comment
На 100% согласен - вероятно, происходит то, что какой-то код выше в стеке перехватывает исключение. Чтобы найти это, запустите ваше приложение под отладчиком (или подключите отладчик к вашему приложению) и включите перехват исключений первого шанса. Он должен попасть достаточно легко. - person ReinstateMonica Larry Osterman; 08.01.2011