Rilevamento delle eccezioni di violazione di accesso?


89

Esempio

int *ptr;
*ptr = 1000;

posso rilevare l'eccezione di violazione dell'accesso alla memoria utilizzando lo standard C ++ senza utilizzare alcuno specifico di Microsoft.

Risposte:


42

No. C ++ non genera un'eccezione quando si fa qualcosa di sbagliato, che incorrerebbe in un calo delle prestazioni. Cose come le violazioni di accesso o la divisione per zero errori sono più simili a eccezioni "macchina", piuttosto che cose a livello di linguaggio che puoi individuare.


So che sono eccezioni HW, ma ci sono parole chiave specifiche di Microsoft che gestiscono questo (__ try __except)?
Ahmed ha detto il

2
@ Ahmed: sì, ma se li usi, possono accadere cose "impossibili". Ad esempio, alcune delle istruzioni dopo la riga di codice AV potrebbero essere già state eseguite o le istruzioni prima dell'AV potrebbero non essere state eseguite.
Aaron

Vedi la mia risposta di seguito su come abilitare la gestione di tali eccezioni utilizzando il normale blocco try ... catch in VC ++.
Volodymyr Frytskyy

@Aaron puoi approfondire la parte "cose ​​impossibili che accadono"? è a causa delle istruzioni di riordino del compilatore e / o della CPU?
Weipeng L

Il sistema operativo sottostante fornirà spesso meccanismi per rilevare tali problemi e non comporteranno alcun costo, poiché l'eccezione è generata dall'architettura della CPU. Ciò è evidenziato dal modo in cui i debugger sono in grado di intercettare le eccezioni per consentire il debug, senza rallentare l'esecuzione del codice.
Dino Dini

108

Leggilo e piangi!

L'avevo capito. Se non lanci dal gestore, il gestore continuerà e così farà l'eccezione.

La magia accade quando lanci la tua eccezione e gestisci quella.

#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");

}

Bel suggerimento, soprattutto perché __try / __ eccetto non cattura nemmeno AV.
Fabio Ceconello

16
NON funziona in gcc ma funziona in VC ++ ma solo nella build "Debug". Sto ancora votando per una soluzione interessante. Il gestore del segnale verrebbe chiamato ma l'eccezione non verrà lanciata.
Natalie Adams,

2
Non funziona in modo portatile. Quando un gestore di segnale viene invocato, lo stack frame e il munging del registro non sono gli stessi di quando si utilizza uno stack frame normale (potrebbe non utilizzare lo stesso stack su alcuni sistemi). Il meglio che puoi fare è impostare un flag per indicare che il gestore del segnale è stato attivato. Quindi nel tuo codice prova quel flag e lancia.
Martin York

2
Ciò ha un'alta probabilità di introdurre comportamenti indefiniti. Affinché questo funzioni su POSIX, non devono essere sigaltstackinstallati stack di segnali alternativi ( ) (a meno che l'implementazione di svolgimento dell'eccezione C ++ non lo consenta) e ogni funzione di runtime che gestisce il meccanismo di svolgimento stesso dovrebbe essere a prova di segnale.
minmaxavg

1
Se vuoi restituire il gestore predefinito al segnale (SIGSEGV in questo caso), usa il seguente:signal(SIGSEGV, SIG_DFL);
kocica

67

Esiste un modo molto semplice per rilevare qualsiasi tipo di eccezione (divisione per zero, violazione di accesso, ecc.) In Visual Studio utilizzando try -> catch (...) block. È sufficiente un piccolo ritocco delle impostazioni del progetto. Abilita semplicemente l'opzione / EHa nelle impostazioni del progetto. Vedere Proprietà progetto -> C / C ++ -> Generazione codice -> Modifica Abilita eccezioni C ++ su "Sì con eccezioni SEH" . Questo è tutto!

Vedi i dettagli qui: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx


Non esiste tale valore di impostazione in Visual Studio .NET 2003, sono presenti solo "No" e "Sì (/ EHsc)". Puoi chiarire quale versione minima di Visual Studio è necessaria per poter abilitare questa impostazione?
izogfif

Il collegamento sembra specificare "Visual Studio 2005"
Drew Delano

2
cosa succede se con gcc o MinGW?
user1024

10

Almeno per me, l' signal(SIGSEGV ...)approccio menzionato in un'altra risposta non ha funzionato su Win32 con Visual C ++ 2015 . Quello che ha funzionato per me è stato usare _set_se_translator()trovato in eh.h. Funziona così:

Passaggio 1 ) Assicurati di abilitare Sì con eccezioni SEH (/ EHa) in Proprietà progetto / C ++ / Generazione codice / Abilita eccezioni C ++ , come menzionato nella risposta di Volodymyr Frytskyy .

Passaggio 2 ) Chiama _set_se_translator(), passando un puntatore a funzione (o lambda) per il nuovo traduttore di eccezioni . Si chiama traduttore perché fondamentalmente prende solo l'eccezione di basso livello e la rilancia come qualcosa di più facile da catturare, come ad esempio 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());
});

Passaggio 3 ) Cattura l'eccezione come faresti normalmente:

try{
    MakeAnException();
}
catch(std::exception ex){
    HandleIt();
};

1
Questo sito contiene semplice paio di esempio a _set_se_translator () metodi e funziona per me, msdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitra Dash

8

Questo tipo di situazione dipende dall'implementazione e di conseguenza richiederà un meccanismo specifico del fornitore per eseguire il trap. Con Microsoft questo coinvolgerà SEH e * nix implicherà un segnale

In generale, comunque l'intercettazione di un'eccezione di violazione di accesso è una molto cattiva idea. Non c'è quasi modo di recuperare da un'eccezione AV e il tentativo di farlo risulterà solo più difficile trovare bug nel tuo programma.


1
Quindi il tuo consiglio è di sapere qual è la causa dell'eccezione AV, non è vero?
Ahmed ha detto il

4
Assolutamente. Gli AV sono rappresentativi di un bug nel codice e catturare l'eccezione nasconderà semplicemente il problema.
JaredPar

1
Per chiarire, lo standard C ++ fa una distinzione tra non definito, non specificato e implementazione definita. Implementazione definita significa che l'implementazione deve specificare cosa avviene. Il codice nella domanda non è definito, il che significa che può succedere di tutto e ogni volta può essere diverso.
KeithB

15
La cattura della violazione di accesso non è una cattiva idea: è positiva per l'esperienza utente. Tuttavia, l'unica cosa significativa che faccio in questo caso è: generare un altro processo con la GUI di segnalazione bug e provare a creare un dump del processo corrente. Generare un processo è sempre un'operazione di successo. Quindi, eseguo TerminateProcess () per auto-uccidermi.
Петър Петров

12
È una cattiva idea catturare un'eccezione e ignorarla silenziosamente. È un'ottima idea, quando possibile, rilevare un'eccezione e registrare le informazioni sullo stato dell'applicazione per scopi diagnostici. Una volta ho scritto un'interfaccia utente per una libreria grafica di backend che necessitava di alcuni debug. Ogni volta che si bloccava, le persone venivano da me perché sapevano che avevo scritto l'interfaccia utente. Ho messo una trappola del segnale intorno al backend che ha fatto apparire un avviso che ha detto all'utente che la libreria si è bloccata. La gente ha iniziato a rivolgersi all'autore della biblioteca.
Kent

8

Come affermato, non esiste un modo non Microsoft / fornitore di compilatori per farlo sulla piattaforma Windows. Tuttavia, è ovviamente utile rilevare questi tipi di eccezioni nel normale modo try {} catch (exception ex) {} per la segnalazione degli errori e più un'uscita graziosa della tua app (come dice JaredPar, l'app ora è probabilmente nei guai) . Usiamo _se_translator_function in un semplice class wrapper che ci permette di catturare le seguenti eccezioni in un gestore try:

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)

La classe originale proviene da questo articolo molto utile:

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


8
Vedo che l'utilizzo di un compilatore Microsoft viene considerato come un'istruzione illegale o una violazione dell'accesso. Interessante.
David Thornley,

3

Non il meccanismo di gestione delle eccezioni, ma puoi usare il meccanismo signal () fornito dal C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Scrivere su un puntatore NULL probabilmente causerà un segnale SIGSEGV


@maidamai signal()fa parte dello standard posix. Windows implementa lo standard posix (così come Linux e unix)
Martin York

-1

Una violazione del genere significa che c'è qualcosa di gravemente sbagliato nel codice ed è inaffidabile. Vedo che un programma potrebbe voler provare a salvare i dati dell'utente in un modo che si spera non sovrascriverà i dati precedenti, nella speranza che i dati dell'utente non siano già corrotti, ma per definizione non esiste un metodo standard di affrontare un comportamento indefinito.


6
Potrebbe essere possibile il ripristino dalla violazione di accesso. Il ripristino dalla voilation di EIP Jump non è mai possibile a meno che non si sia inaffidabili e si mantengano i puntatori delle istruzioni a livello di assembly. Tuttavia, rilevare la violazione di accesso è utile per generare un altro processo per la funzionalità GUI di segnalazione bug.
Петър Петров
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.