2016-05-25 17 views
8

Nel codice mostrato di seguito ho utilizzato tutti i modi documentati per rilevare un'eccezione e produrre una diagnostica. Utilizza le parole chiave try/catch di C++, rileva un'eccezione SEH con le parole chiave dell'estensione __try/__catch, utilizza le funzioni winapi di AddVectoredExceptionHandler() e SetUnhandledExceptionFilter() per installare i filtri VEH/SEH.Come segnalare un sovraccarico del buffer dello stack su Windows?

Esecuzione con Visual C++ 2003:
/GS: output "ciao, mondo!" e termina con il codice di uscita 0.
/GS-: uscite "ciao, mondo!" e termina con il codice di uscita 0.

L'esecuzione di questo con Visual C++ 2013:
/GS: nessuna uscita, termina con codice di uscita -1.073,740791 millions
/GS-: "Ciao, mondo" uscite e termina con exit con 0.

Come si produce una diagnostica in un programma compilato VS2013 con/GS in vigore?

#include "stdafx.h" 
#include <Windows.h> 

#define CALL_FIRST 1 
#define CALL_LAST 0 

LONG WINAPI MyVectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) 
{ 
    UNREFERENCED_PARAMETER(ExceptionInfo); 

    printf("MyVectoredHandler\n"); 
    return EXCEPTION_CONTINUE_SEARCH; 
} 

LONG WINAPI MyUnhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo) 
{ 
    printf("SetUnhandledExceptionFilter\n"); 

    return EXCEPTION_CONTINUE_SEARCH; 
} 

void f() 
{ 
    __try 
    { 
     char p[20] = "hello,world!"; 
     p[24] = '!'; 
     printf("%s\n", p); 
    } 
    __except (EXCEPTION_EXECUTE_HANDLER) 
    { 
     printf("f() exception\n"); 
    } 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    AddVectoredExceptionHandler(CALL_FIRST, MyVectoredHandler); 
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); 

    try{ 
     f(); 
    } 
    catch (...){ 
     printf("catched f exception\n"); 
    } 
    return 0; 
} 
+0

Il codice causa un comportamento non definito. L'unico modo affidabile per "rilevare" è controllare i limiti prima di eseguire l'accesso fuori limite –

+1

... come quello che verrebbe fatto se si utilizza un tipo di contenitore che esegue il rilevamento. per esempio. usando 'std :: string' per le stringhe e indicizzandolo con la funzione membro' at'. – Hurkyl

+0

I sovraccarichi dello stack sono difficili da rilevare. Si attivano solo quando si sovrascrive il frame dello stack. Prova a passare all'eccezione di runtime * Limiti di array superati *. Potrebbe funzionare. – cup

risposta

4

La funzione CRT che gestisce il rilevamento di sovraccarichi del buffer di stack, __report_gsfailure(), presuppone che il danneggiamento del frame dello stack sia stato causato da un attacco di malware. Tale malware ha tradizionalmente messo in disordine i filtri delle eccezioni [0] SEH (memorizzati nello stack frame) per ottenere un gestore di eccezioni che attivi il payload del malware. Uno dei modi per far diventare i dati trasformabili in codice eseguibile.

In modo che la funzione CRT non possa presumere che il lancio di un'eccezione è sicuro. E non fa più nella CRT inclusa con VS2013, torna a ~ VS2005. Se il sistema operativo lo supporta, fallirà nel tempo e, in caso contrario, garantisce che un gestore di eccezioni VEH/SEH registrato non possa vedere neanche l'eccezione. Kaboom, si blocca sul desktop senza diagnostica se non si ha un debugger collegato.

L'opzione/SAFESEH sconfigge questo tipo di attacco malware, quindi non è così grave come una volta. Se sei ancora nella fase in cui il tuo codice soffre di bug di corruzione dello stack e la tua app non è abbastanza popolare da diventare bersaglio di malware, la sostituzione della funzione CRT è qualcosa che potresti prendere in considerazione.

Parlate con il vostro supervisore, non volete mai essere personalmente responsabili di questo dato l'enorme responsabilità verso il vostro cliente. La storia raramente racconta la storia di quello che è successo all'unico programmatore il cui codice ha messo fuori causa un'intera azienda per un mese. Ma sicuramente non era niente di carino.

incolla questo codice da qualche parte vicino alla vostra funzione main():

__declspec(noreturn) extern "C" 
void __cdecl __report_gsfailure() { 
    RaiseException(STATUS_STACK_BUFFER_OVERRUN, EXCEPTION_NONCONTINUABLE, 0, nullptr); 
} 

e un piano per rimuoverlo di nuovo presto.

+0

Collegamento non riuscito quando si aggiunge __report_gsfailure personalizzato: errore LNK2005: ___report_gsfailure 已经 在 LIBCMT.lib (gs_report.obj) 中 定义 – liuaifu

+1

Hmm, questo è il codice testato, verificato che ha funzionato su VS2015. Non ho idea di cosa dice il testo cinese, odora di gs_report.obj è già stato caricato da un'altra libreria, quindi ora ce ne sono due. Utilizzare l'opzione del linker/VERBOSE per scoprire perché è stato utilizzato gs_report.obj. E fortemente, * fortemente * preferisci costruire con/MD invece di/MT. –

+0

Grazie per questa soluzione. Secondo le mie scoperte, msvcrtd inserisce anche un altro __report_gsfailure, quindi questo funziona solo nelle build Release. –

3

Non esiste una soluzione per la domanda come richiesto.

L'overrunning di un array causa un comportamento non definito nel C++ standard, quindi non è garantito alcun risultato particolare. Non fornire un risultato affidabile non è un problema con il compilatore, è un comportamento consentito.

Sono a conoscenza di nessuna implementazione che garantisce alcun comportamento specifico in risposta a un sovraccarico - VS certamente non lo è. Il che non sorprende in quanto i compilatori non sono tenuti a farlo (cioè, essenzialmente, il significato di comportamento non definito). Il motivo è che spesso è difficile rilevare in modo affidabile o coerente tali occorrenze.

Ciò significa che l'unico modo coerente per rilevare un sovraccarico dell'array è verificare che gli indici dell'array siano validi PRIMA di utilizzarli per accedere a un elemento dell'array e intraprendere le azioni appropriate (ad esempio lanciare un'eccezione che può essere rilevata invece di eseguire l'operazione errata). Il rovescio della medaglia è che non fornisce un modo semplice o affidabile per catturare errori nel codice arbitrario - a meno di modificare tutto il codice per eseguire i controlli richiesti.

Problemi correlati