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


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.