GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara menulis penangan sinyal untuk menangkap SIGSEGV?

Anda dapat memulihkan dari SIGSEGV di linux. Anda juga dapat memulihkan dari kesalahan segmentasi pada Windows (Anda akan melihat pengecualian terstruktur, bukan sinyal). Namun standar POSIX tidak menjamin pemulihan, jadi kode Anda akan sangat tidak portabel.

Lihatlah libsigsegv.


Ketika penangan sinyal Anda kembali (dengan asumsi itu tidak memanggil keluar atau longjmp atau sesuatu yang mencegahnya untuk benar-benar kembali), kode akan berlanjut pada titik sinyal terjadi, menjalankan kembali instruksi yang sama. Karena pada titik ini, perlindungan memori belum diubah, itu hanya akan membuang sinyal lagi, dan Anda akan kembali ke penangan sinyal dalam loop tak terbatas.

Jadi untuk membuatnya berfungsi, Anda harus memanggil mprotect di penangan sinyal. Sayangnya, seperti yang dicatat oleh Steven Schansker, mprotect tidak aman untuk async, jadi Anda tidak dapat memanggilnya dengan aman dari penangan sinyal. Jadi, sejauh menyangkut POSIX, Anda kacau.

Untungnya pada sebagian besar implementasi (semua varian UNIX dan Linux modern sejauh yang saya tahu), mprotect adalah panggilan sistem, jadi aman untuk dipanggil dari dalam penangan sinyal, sehingga Anda dapat melakukan sebagian besar yang Anda inginkan. Masalahnya adalah jika Anda ingin mengubah kembali perlindungan setelah membaca, Anda harus melakukannya di program utama setelah membaca.

Kemungkinan lain adalah melakukan sesuatu dengan argumen ketiga ke penangan sinyal, yang menunjuk ke OS dan struktur khusus arch yang berisi info tentang di mana sinyal terjadi. Di Linux, ini adalah ucontext structure, yang berisi info khusus mesin tentang alamat $PC dan konten register lain tempat terjadinya sinyal. Jika Anda memodifikasi ini, Anda mengubah ke mana penangan sinyal akan kembali, sehingga Anda dapat mengubah $PC menjadi tepat setelah instruksi yang salah sehingga tidak akan dijalankan kembali setelah penangan kembali. Ini sangat sulit dilakukan dengan benar (dan juga tidak portabel).

edit

ucontext struktur didefinisikan dalam <ucontext.h> . Di dalam ucontext bidang uc_mcontext berisi konteks mesin, dan di dalam itu , larik gregs berisi konteks register umum. Jadi di penangan sinyal Anda:

ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];

akan memberi Anda pc tempat pengecualian terjadi. Anda dapat membacanya untuk mengetahui instruksi apa yang salah, dan melakukan sesuatu yang berbeda.

Sejauh menyangkut portabilitas pemanggilan mprotect dalam penangan sinyal, sistem apa pun yang mengikuti spesifikasi SVID atau spesifikasi BSD4 harus aman -- mereka mengizinkan pemanggilan panggilan sistem apa pun (apa pun di bagian 2 manual) dalam sinyal penangan.


Anda telah jatuh ke dalam perangkap yang dilakukan semua orang saat pertama kali mencoba menangani sinyal. Jebakan? Berpikir bahwa Anda benar-benar dapat melakukan apa saja yang berguna dengan penangan sinyal. Dari penangan sinyal, Anda hanya diizinkan untuk memanggil panggilan pustaka asinkron dan aman untuk masuk kembali.

Lihat penasihat CERT ini tentang alasannya dan daftar fungsi POSIX yang aman.

Perhatikan bahwa printf(), yang sudah Anda panggil, tidak ada dalam daftar itu.

Juga tidak mprotect. Anda tidak diizinkan untuk memanggilnya dari penangan sinyal. Itu mungkin bekerja, tapi saya bisa berjanji Anda akan mengalami masalah di jalan. Berhati-hatilah dengan penangan sinyal, mereka sulit untuk melakukannya dengan benar!

EDIT

Karena saya sudah menjadi douchebag portabilitas saat ini, saya akan menunjukkan bahwa Anda juga tidak boleh menulis ke variabel bersama (yaitu global) tanpa mengambil tindakan pencegahan yang tepat.


Linux
  1. Cara menulis loop di Bash

  2. Cara Mengatasi:Tidak Dapat Menulis ke Partisi Ext3 atau Ext4

  3. Bagaimana cara menulis karakter non-ASCII menggunakan gema?

  1. Seberapa Besar Buffer Pipa?

  2. Bagaimana Cara Menulis Script Startup Untuk Systemd?

  3. Bagaimana Cara Menulis File Ke Yang Lain?

  1. Bagaimana cara bergabung dengan utas yang menggantung di pemblokiran IO?

  2. Bagaimana cara menulis file dengan C di Linux?

  3. Antrian sinyal di C