Menangkap pengecualian pelanggaran akses?


89

Contoh

int *ptr;
*ptr = 1000;

dapatkah saya menangkap pengecualian pelanggaran akses memori menggunakan standar C ++ tanpa menggunakan microsoft tertentu.

Jawaban:


43

Nggak. C ++ tidak mengeluarkan pengecualian saat Anda melakukan sesuatu yang buruk, yang akan mengakibatkan kinerja yang buruk. Hal-hal seperti pelanggaran akses atau kesalahan pembagian dengan nol lebih seperti pengecualian "mesin", daripada hal-hal tingkat bahasa yang dapat Anda tangkap.


Saya tahu itu adalah pengecualian HW, tetapi ada kata kunci khusus Microsoft yang menangani ini (__ coba __except)?
Ahmed Said

2
@Ahmed: ya, tetapi jika Anda menggunakannya, hal-hal 'tidak mungkin' dapat terjadi. Misalnya, beberapa pernyataan setelah baris kode AV mungkin sudah dijalankan, atau pernyataan sebelum AV belum dijalankan.
Aaron

Lihat jawaban saya di bawah ini bagaimana mengaktifkan penanganan pengecualian seperti itu menggunakan blok try ... catch biasa di VC ++.
Volodymyr Frytskyy

@Aaron Bisakah Anda menjelaskan bagian "hal yang tidak mungkin terjadi"? apakah karena instruksi penyusunan ulang compiler dan / atau CPU?
Weipeng L

Sistem operasi yang mendasarinya sering kali menyediakan mekanisme untuk mengatasi masalah tersebut, dan tidak dikenakan biaya, karena pengecualian dibuat oleh arsitektur CPU. Hal ini dibuktikan dengan cara debugger dapat menjebak pengecualian untuk memungkinkan Anda men-debug, tanpa memperlambat eksekusi kode.
Dino Dini

108

Bacalah dan menangislah!

Saya menemukan jawabannya. Jika anda tidak melempar dari pawang, pawang hanya akan melanjutkan dan begitu juga dengan pengecualiannya.

Keajaiban terjadi ketika Anda melemparkan pengecualian Anda sendiri dan menanganinya.

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

}

Tip yang bagus, terutama karena __try / __ kecuali tidak akan menangkap AV juga.
Fabio Ceconello

16
Ini TIDAK berfungsi di gcc tetapi berfungsi di VC ++ tetapi hanya di build "Debug". Masih upvoting untuk solusi yang menarik. Penangan sinyal akan dipanggil tetapi pengecualian tidak akan dilempar.
Natalie Adams

2
Itu tidak bekerja portabel. Ketika penangan sinyal dipanggil, frame stack dan register munging tidak sama dengan ketika frame stack fungsi normal (bahkan mungkin tidak menggunakan stack yang sama pada beberapa sistem). Hal terbaik yang dapat Anda lakukan adalah menyetel bendera untuk menunjukkan penangan sinyal telah diaktifkan. Kemudian dalam uji kode Anda untuk bendera dan lemparan itu.
Martin York

2
Ini memiliki peluang tinggi untuk memperkenalkan perilaku tidak terdefinisi. Agar ini bekerja pada POSIX, tidak boleh ada tumpukan sinyal alternatif ( sigaltstack) yang dipasang (kecuali implementasi pelepasan pengecualian C ++ memungkinkannya), dan setiap fungsi runtime yang menangani mekanisme pelepasan itu sendiri harus aman terhadap sinyal.
minmaxavg

1
Jika Anda ingin mengembalikan penangan default ke sinyal (SIGSEGV dalam kasus ini), gunakan yang berikut ini:signal(SIGSEGV, SIG_DFL);
kocica

67

Ada cara yang sangat mudah untuk menangkap segala jenis pengecualian (pembagian dengan nol, pelanggaran akses, dll.) Di Visual Studio menggunakan blok try -> catch (...). Pengaturan proyek kecil sudah cukup. Cukup aktifkan / EHa opsi di pengaturan proyek. Lihat Properti Proyek -> C / C ++ -> Pembuatan Kode -> Ubah Pengecualian Aktifkan C ++ menjadi "Ya Dengan Pengecualian SEH" . Itu dia!

Lihat detailnya di sini: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx


Tidak ada nilai pengaturan seperti itu di Visual Studio .NET 2003, hanya ada "Tidak" dan "Ya (/ EHsc)". Dapatkah Anda menjelaskan versi Visual Studio minimal apa yang Anda perlukan untuk dapat mengaktifkan pengaturan ini?
izogfif

Tautan muncul untuk menentukan "Visual Studio 2005"
Drew Delano

2
bagaimana jika dengan gcc atau MinGW?
pengguna1024

10

Setidaknya bagi saya, signal(SIGSEGV ...)pendekatan yang disebutkan dalam jawaban lain tidak berfungsi pada Win32 dengan Visual C ++ 2015 . Apa yang melakukan pekerjaan untuk saya adalah untuk menggunakan _set_se_translator()ditemukan di eh.h. Ini bekerja seperti ini:

Langkah 1 ) Pastikan Anda mengaktifkan Ya dengan Pengecualian SEH (/ EHa) di Properti Proyek / C ++ / Pembuatan Kode / Aktifkan Pengecualian C ++ , seperti yang disebutkan dalam jawaban oleh Volodymyr Frytskyy .

Langkah 2 ) Panggil _set_se_translator(), berikan penunjuk fungsi (atau lambda) untuk penerjemah pengecualian baru . Ini disebut penerjemah karena pada dasarnya hanya mengambil pengecualian tingkat rendah dan melemparkannya kembali sebagai sesuatu yang lebih mudah ditangkap, seperti 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());
});

Langkah 3 ) Tangkap pengecualian seperti biasa:

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

1
Situs ini berisi beberapa contoh sederhana tentang metode _set_se_translator () dan berfungsi untuk saya, msdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitra Dash

8

Jenis situasi ini bergantung pada implementasi dan akibatnya akan membutuhkan mekanisme khusus vendor untuk menjebak. Dengan Microsoft ini akan melibatkan SEH, dan * nix akan melibatkan sinyal

Secara umum, meskipun menangkap pengecualian Pelanggaran Akses adalah ide yang sangat buruk. Hampir tidak ada cara untuk memulihkan dari pengecualian AV dan mencoba melakukannya hanya akan semakin sulit menemukan bug dalam program Anda.


1
Jadi saran Anda adalah mengetahui apa penyebab pengecualian AV bukan?
Ahmed Said

4
Benar. AV mewakili bug dalam kode Anda dan menangkap pengecualian hanya akan menyembunyikan masalah.
JaredPar

1
Untuk memperjelas, standar C ++ membuat perbedaan antara tidak ditentukan, tidak ditentukan, dan implementasi ditentukan. Implementasi yang didefinisikan berarti implementasi harus menentukan apa yang terjadi. Kode dalam pertanyaan tidak terdefinisi, yang berarti bahwa apapun bisa terjadi, dan berbeda setiap saat.
KeithB

15
Menangkap Pelanggaran Akses bukanlah ide yang buruk - ini bagus untuk pengalaman Pengguna. Namun, satu-satunya hal yang berarti yang saya lakukan dalam kasus ini adalah - menelurkan proses lain dengan GUI Pelaporan Bug dan mencoba membuat dump proses saat ini. Proses pemijahan selalu berhasil. Kemudian, saya melakukan TerminateProcess () untuk membunuh diri sendiri.
Петър Петров

12
Menangkap pengecualian dan mengabaikannya secara diam-diam adalah ide yang buruk. Ide yang sangat baik jika memungkinkan untuk menangkap pengecualian dan mencatat informasi tentang status aplikasi untuk tujuan diagnostik. Saya pernah menulis UI untuk pustaka grafis backend yang membutuhkan beberapa debugging. Setiap kali crash, orang-orang mendatangi saya karena mereka tahu saya yang menulis UI. Saya memasang jebakan sig di sekitar backend yang memunculkan peringatan yang memberi tahu pengguna bahwa perpustakaan macet. Orang-orang mulai pergi ke penulis perpustakaan.
Kent

8

Seperti yang dinyatakan, tidak ada cara vendor non Microsoft / compiler untuk melakukan ini di platform windows. Namun, jelas berguna untuk menangkap jenis pengecualian ini dengan cara try {} catch (exception ex) {} normal untuk pelaporan kesalahan dan lebih banyak lagi jalan keluar yang anggun dari aplikasi Anda (seperti yang dikatakan JaredPar, aplikasi sekarang mungkin dalam masalah) . Kami menggunakan _se_translator_function dalam pembungkus kelas sederhana yang memungkinkan kita untuk menangkap pengecualian berikut dalam penangan percobaan:

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)

Kelas asli berasal dari artikel yang sangat berguna ini:

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


8
Saya melihat bahwa menggunakan kompiler Microsoft diperlakukan sama sebagai instruksi ilegal atau pelanggaran akses. Menarik.
David Thornley

3

Bukan mekanisme penanganan pengecualian, tetapi Anda dapat menggunakan mekanisme signal () yang disediakan oleh C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Menulis ke pointer NULL mungkin akan menyebabkan sinyal SIGSEGV


@maidamai signal()adalah bagian dari standar posix . Windows mengimplementasikan standar posix (seperti halnya Linux dan unix)
Martin York

-1

Pelanggaran seperti itu berarti ada yang salah dengan kode, dan tidak dapat diandalkan. Saya dapat melihat bahwa sebuah program mungkin ingin mencoba untuk menyimpan data pengguna dengan cara yang diharapkan tidak akan menimpa data sebelumnya, dengan harapan bahwa data pengguna belum rusak, tetapi menurut definisi tidak ada metode standar menangani perilaku tidak terdefinisi.


7
Memulihkan dari pelanggaran akses dimungkinkan. Memulihkan dari voilation lompatan EIP tidak pernah mungkin kecuali Anda cerdik dan menyimpan petunjuk instruksi tingkat perakitan. Namun, menangkap pelanggaran Akses bagus untuk memicu proses lain untuk pelaporan bug fitur GUI.
Петър Петров
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.