Виловлювання винятків щодо порушення доступу?


89

Приклад

int *ptr;
*ptr = 1000;

Чи можу я виявити виняток щодо порушення доступу до пам'яті, використовуючи стандартний C ++, не використовуючи жодної специфіки Microsoft.

Відповіді:


42

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


Я знаю, що це винятки HW, але є спеціальні ключові слова Microsoft, які обробляють це (__ спробуйте __except)?
Ахмед Саїд

2
@Ahmed: так, але якщо ви ними користуєтесь, можуть статися "неможливі" речі. Наприклад, деякі оператори після AV-рядка коду, можливо, вже виконані, або оператори перед AV-кодом не виконані.
Аарон

Дивіться мою відповідь нижче про те, як увімкнути обробку таких винятків за допомогою звичайного блоку try ... catch у VC ++.
Володимир Фрицький

@Aaron, чи можете ви детальніше розказати про "неможливе, що відбувається"? це через компілятор та / або інструкції щодо переупорядкування ЦП?
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 / __, крім того, також не вловлює AV.
Фабіо Чеконелло,

16
Це НЕ працює у gcc, але працює у VC ++, але лише у збірці "Налагодження". Все ще голосую за цікаве рішення. Буде викликаний обробник сигналу, але виняток не буде видано.
Наталі Адамс

2
Це не працює портативно. Коли виклик обробника сигналу викликає кадр стека, а зміна реєстру - це не те саме, що коли звичайний кадр стека функцій (він може навіть не використовувати той самий стек у деяких системах). Найкраще, що ви можете зробити, це встановити прапор, який вказує, що обробник сигналу активований. Потім у вашому тесті коду на цей прапор і киньте.
Martin York

2
Це має високі шанси внести невизначену поведінку. Для того, щоб це працювало на POSIX, не повинно бути встановлених альтернативних стеків сигналів ( sigaltstack) (за винятком випадків, коли це дозволяє розгортання виключення C ++), і кожна функція виконання, яка обробляє сам механізм розмотування, повинна бути безпечною для сигналу.
minmaxavg

1
Якщо ви хочете повернути обробник за замовчуванням для сигналу (у цьому випадку SIGSEGV), використовуйте наступне:signal(SIGSEGV, SIG_DFL);
kocica

67

Існує дуже простий спосіб вловити будь-який виняток (ділення на нуль, порушення доступу тощо) у Visual Studio за допомогою блоку try -> catch (...). Досить незначного налаштування параметрів проекту. Просто увімкніть параметр / EHa у налаштуваннях проекту. Див. Властивості проекту -> C / C ++ -> Генерація коду -> Змінити параметр Enable C ++ Exceptions на "Так з винятками 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
Dash

8

Цей тип ситуації залежить від реалізації, і, отже, для того, щоб затримати механізм, йому знадобиться механізм, що відповідає постачальнику. З Microsoft це буде стосуватися SEH, а * nix - сигналу

Загалом, хоча вловлювати виняток щодо порушення доступу дуже погана ідея. Практично немає способу відновити виняток AV, а спроба зробити це призведе до ускладнення пошуку помилок у вашій програмі.


1
Тож ваша порада - знати, що є причиною винятку AV, чи не так?
Ахмед Саїд

4
Абсолютно. AV представляють помилку у вашому коді, і виявлення винятку просто приховає проблему.
JaredPar

1
Щоб пояснити, стандарт С ++ робить різницю між невизначеною, невизначеною та визначеною реалізацією. Визначення реалізації означає, що впровадження повинно визначати, що має місце. Код у запитанні невизначений, а це означає, що все може статися, і кожен раз може бути іншим.
KeithB

15
Ловити порушення доступу - це не погана ідея - це корисно для досвіду користувачів. Однак єдине значуще, що я роблю в цьому випадку, - це створити ще один процес із графічним інтерфейсом Reporting Bug і спробувати створити поточний дамп процесу. Нерест процесу - це завжди успішна операція. Потім я роблю TerminateProcess () для самовбивства.
Петър Петров

12
Невдало ловити виняток і мовчки ігнорувати його. Дуже гарна ідея, коли можливо виявити виняток і записати інформацію про стан програми для діагностичних цілей. Одного разу я написав інтерфейс для внутрішньої графічної бібліотеки, яка потребувала налагодження. Кожного разу, коли воно падало, люди приходили до мене, бо знали, що я написав інтерфейс. Я розмістив сиг-пастку навколо бекенда, що видало сповіщення, яке повідомляло користувачеві про те, що бібліотека вийшла з ладу. Люди почали ходити до автора бібліотеки.
Кент,

8

Як зазначалося, на платформі Windows не існує способу, який не відповідає Microsoft / компілятору. Однак, очевидно, корисно ловити такі типи винятків звичайним способом спроби {} catch (виняток ex) {} для звітування про помилки та більш витонченого виходу з вашого додатка (як каже JaredPar, програма зараз, мабуть, має проблеми) . Ми використовуємо функцію _se_translator_ у простій обгортці класу, яка дозволяє нам вловлювати такі винятки в обробнику спроб:

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 ніколи неможливе, якщо ви не хитра і не тримаєте вказівники інструкцій на рівні складання. Однак виявлення порушення доступу корисно для створення іншого процесу для графічного інтерфейсу повідомлення про помилки.
Петър Петров
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.