Выявление исключений нарушения прав доступа?

89

пример

int *ptr;
*ptr = 1000;

Могу ли я поймать исключение нарушения доступа к памяти с помощью стандартного C ++ без использования каких-либо конкретных Microsoft.

Ахмед Саид
источник

Ответы:

42

Неа. C ++ не генерирует исключение, когда вы делаете что-то плохое, что может снизить производительность. Такие вещи, как нарушения доступа или ошибки деления на ноль, больше похожи на "машинные" исключения, чем на вещи уровня языка, которые вы можете уловить.

размотать
источник
Я знаю, что это исключения HW, но есть определенные ключевые слова microsoft для обработки этого (__ try __except)?
Ахмед Саид
2
@ Ахмед: да, но если вы воспользуетесь ими, могут произойти «невозможные» вещи. Например, некоторые из операторов после AV-строки кода, возможно, уже были выполнены, или операторы до AV не были выполнены.
Аарон
См. Мой ответ ниже, как включить такую ​​обработку исключений с помощью обычного блока try ... catch в VC ++.
Владимир Фрицкий
@ Аарон, можешь подробнее рассказать о том, что происходит "невозможное"? это из-за инструкций по переупорядочению компилятора и / или процессора?
Weipeng L
Базовая операционная система часто предоставляет механизмы для обнаружения таких проблем, и они не требуют затрат, поскольку исключение генерируется архитектурой ЦП. Об этом свидетельствует то, что отладчики могут перехватывать исключение, чтобы вы могли выполнять отладку, не замедляя выполнение кода.
Дино Дини
108

Прочтите и плачьте!

Я понял. Если вы не выбрасываете из обработчика, обработчик просто продолжит работу, как и исключение.

Магия происходит, когда вы генерируете собственное исключение и обрабатываете его.

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

void SignalHandler(int signal)
{
    printf("Signal %d",signal);
    throw "!Access Violation!";
}

int main()
{
    typedef void (*SignalHandlerPointer)(int);

    SignalHandlerPointer previousHandler;
    previousHandler = signal(SIGSEGV , SignalHandler);
    try{
        *(int *) 0 = 0;// Baaaaaaad thing that should never be caught. You should write good code in the first place.
    }
    catch(char *e)
    {
        printf("Exception Caught: %s\n",e);
    }
    printf("Now we continue, unhindered, like the abomination never happened. (I am an EVIL genius)\n");
    printf("But please kids, DONT TRY THIS AT HOME ;)\n");

}
Бернхард Баркер
источник
Хороший совет, особенно потому, что __try / __ except также не поймает AV.
Фабио Чеконелло
16
Это НЕ работает в gcc, но работает в VC ++, но только в сборке «Отладка». Все еще поддерживаю интересное решение. Обработчик сигнала будет вызван, но исключение не будет создано.
Натали Адамс,
2
Это не работает портативно. Когда вызывается обработчик сигнала, переключение фрейма стека и регистров отличается от обычного фрейма стека функций (в некоторых системах он может даже не использовать тот же стек). Лучшее, что вы можете сделать, - это установить флаг, указывающий, что обработчик сигнала активирован. Затем в вашем коде проверьте этот флаг и бросьте.
Мартин Йорк
2
Это может привести к неопределенному поведению. Чтобы это работало в POSIX, не должно быть установленных альтернативных стеков сигналов ( sigaltstack) (если это не разрешено реализацией разматывания исключений C ++), и каждая функция времени выполнения, обрабатывающая сам механизм разматывания, должна быть безопасной для сигналов.
minmaxavg
1
Если вы хотите вернуть обработчик по умолчанию для сигнала (в данном случае SIGSEGV), используйте следующее:signal(SIGSEGV, SIG_DFL);
kocica
67

Существует очень простой способ перехвата любого типа исключения (деление на ноль, нарушение прав доступа и т. Д.) В Visual Studio с помощью блока try -> catch (...). Достаточно незначительной настройки параметров проекта. Просто включите опцию / EHa в настройках проекта. См. Свойства проекта -> C / C ++ -> Генерация кода -> Измените параметр Включить исключения C ++ на «Да с исключениями SEH» . Это оно!

Подробности см. Здесь: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx

Владимир Фрицкий
источник
В Visual Studio .NET 2003 такого значения настройки нет, есть только «Нет» и «Да (/ EHsc)». Не могли бы вы уточнить, какая минимальная версия Visual Studio вам нужна, чтобы включить этот параметр?
izogfif
Ссылка указывает "Visual Studio 2005"
Дрю Делано
2
а что если с gcc или MinGW?
user1024
10

По крайней мере, для меня signal(SIGSEGV ...)подход, упомянутый в другом ответе , не работал на Win32 с Visual C ++ 2015 . Что действительно работало для меня, так это использовать _set_se_translator()найденное в eh.h. Это работает так:

Шаг 1 ) Убедитесь, что вы включили Да с исключениями SEH (/ EHa) в свойствах проекта / C ++ / Генерация кода / Включить исключения C ++ , как указано в ответе Владимира Фрицкого .

Шаг 2 ) Вызов _set_se_translator(), передав указатель функции (или лямбда) для нового транслятора исключений . Он называется переводчиком, потому что он просто принимает низкоуровневое исключение и повторно генерирует его, как что-то более легкое для улова, например std::exception:

#include <string>
#include <eh.h>

// Be sure to enable "Yes with SEH Exceptions (/EHa)" in C++ / Code Generation;
_set_se_translator([](unsigned int u, EXCEPTION_POINTERS *pExp) {
    std::string error = "SE Exception: ";
    switch (u) {
    case 0xC0000005:
        error += "Access Violation";
        break;
    default:
        char result[11];
        sprintf_s(result, 11, "0x%08X", u);
        error += result;
    };
    throw std::exception(error.c_str());
});

Шаг 3 ) Перехватите исключение, как обычно:

try{
    MakeAnException();
}
catch(std::exception ex){
    HandleIt();
};
Майкл
источник
1
Этот сайт содержит простую пару примеров методов _set_se_translator () и работает для меня, msdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitra Dash
8

Этот тип ситуации зависит от реализации и, следовательно, для перехвата требуется специальный механизм поставщика. В Microsoft это будет включать SEH, а * nix будет включать сигнал

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

ДжаредПар
источник
1
Итак, ваш совет - узнать, в чем причина исключения AV, не так ли?
Ахмед Саид
4
Абсолютно. AV представляют ошибку в вашем коде, и перехват исключения просто скроет проблему.
JaredPar
1
Чтобы уточнить, стандарт C ++ делает различие между неопределенным, неопределенным и определенным реализацией. Определенная реализация означает, что реализация должна указывать, что происходит. Код в вопросе не определен, что означает, что все может случиться и каждый раз будет отличаться.
KeithB
15
Выявление нарушения доступа - неплохая идея - это хорошо для пользователя. Однако единственная значимая вещь, которую я делаю в этом случае, - это запускать другой процесс с графическим интерфейсом отчетов об ошибках и пытаться создать дамп текущего процесса. Создание процесса - всегда успешная операция. Затем я выполняю TerminateProcess () для самоуничтожения.
Петър Петров
12
Это плохая идея - перехватывать исключение и молча игнорировать его. Это очень хорошая идея, когда возможно перехватить исключение и записать информацию о состоянии приложения для диагностических целей. Однажды я написал пользовательский интерфейс для серверной графической библиотеки, которая нуждалась в некоторой отладке. Каждый раз, когда он падал, люди приходили ко мне, потому что знали, что я написал пользовательский интерфейс. Я поставил сиг-ловушку вокруг бэкэнда, которая выскочила бы предупреждение, сообщающее пользователю, что библиотека потерпела крах. Люди стали ходить к автору библиотеки.
Kent
8

Как уже говорилось, на платформе Windows нет способа сделать это сторонним поставщиком / поставщиком компилятора. Однако, очевидно, полезно перехватывать эти типы исключений обычным способом try {} catch (exception ex) {} для сообщения об ошибках и, более того, для плавного выхода из вашего приложения (как говорит JaredPar, приложение, вероятно, сейчас в беде) . Мы используем _se_translator_function в простой оболочке класса, которая позволяет нам перехватывать следующие исключения в обработчике попытки:

DECLARE_EXCEPTION_CLASS(datatype_misalignment)
DECLARE_EXCEPTION_CLASS(breakpoint)
DECLARE_EXCEPTION_CLASS(single_step)
DECLARE_EXCEPTION_CLASS(array_bounds_exceeded)
DECLARE_EXCEPTION_CLASS(flt_denormal_operand)
DECLARE_EXCEPTION_CLASS(flt_divide_by_zero)
DECLARE_EXCEPTION_CLASS(flt_inexact_result)
DECLARE_EXCEPTION_CLASS(flt_invalid_operation)
DECLARE_EXCEPTION_CLASS(flt_overflow)
DECLARE_EXCEPTION_CLASS(flt_stack_check)
DECLARE_EXCEPTION_CLASS(flt_underflow)
DECLARE_EXCEPTION_CLASS(int_divide_by_zero)
DECLARE_EXCEPTION_CLASS(int_overflow)
DECLARE_EXCEPTION_CLASS(priv_instruction)
DECLARE_EXCEPTION_CLASS(in_page_error)
DECLARE_EXCEPTION_CLASS(illegal_instruction)
DECLARE_EXCEPTION_CLASS(noncontinuable_exception)
DECLARE_EXCEPTION_CLASS(stack_overflow)
DECLARE_EXCEPTION_CLASS(invalid_disposition)
DECLARE_EXCEPTION_CLASS(guard_page)
DECLARE_EXCEPTION_CLASS(invalid_handle)
DECLARE_EXCEPTION_CLASS(microsoft_cpp)

Исходный класс взят из этой очень полезной статьи:

http://www.codeproject.com/KB/cpp/exception.aspx

Дэмиен
источник
8
Я вижу, что использование компилятора Microsoft рассматривается как незаконная инструкция или нарушение прав доступа. Интересно.
Дэвид Торнли
3

Не механизм обработки исключений, но вы можете использовать механизм signal (), который предоставляется C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Запись в указатель NULL, вероятно, вызовет сигнал SIGSEGV

Мартин Йорк
источник
@maidamai signal()является частью стандарта posix. Windows реализует стандарт posix (как и Linux и unix)
Мартин Йорк,
-1

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

Дэвид Торнли
источник
6
Возможно восстановление после нарушения прав доступа. Восстановление после отмены перехода EIP невозможно, если вы не изворотливы и не храните указатели инструкций на уровне сборки. Однако обнаружение нарушения прав доступа полезно для запуска другого процесса для сообщения об ошибках в графическом интерфейсе.
Петър Петров