หากพบข้อยกเว้นการละเมิดการเข้าถึง


89

ตัวอย่าง

int *ptr;
*ptr = 1000;

ฉันสามารถตรวจจับข้อยกเว้นการละเมิดการเข้าถึงหน่วยความจำโดยใช้ C ++ มาตรฐานโดยไม่ต้องใช้ไมโครซอฟท์โดยเฉพาะ

คำตอบ:


43

ไม่ C ++ ไม่ทำให้เกิดข้อยกเว้นเมื่อคุณทำสิ่งที่ไม่ดีซึ่งจะทำให้เกิดผลกระทบด้านประสิทธิภาพ สิ่งต่างๆเช่นการละเมิดการเข้าถึงหรือการหารด้วยข้อผิดพลาดเป็นศูนย์นั้นเหมือนกับข้อยกเว้น "เครื่อง" มากกว่าสิ่งที่เป็นระดับภาษาที่คุณสามารถจับได้


ฉันรู้ว่าเป็นข้อยกเว้นของ HW แต่มีคำหลักเฉพาะของ Microsoft ที่จัดการสิ่งนี้ (__ ลอง __except) หรือไม่
Ahmed Said

2
@ Ahmed: ใช่ แต่ถ้าคุณใช้มันสิ่งที่ 'เป็นไปไม่ได้' อาจเกิดขึ้น ตัวอย่างเช่นบางส่วนของคำสั่งหลังจากบรรทัดรหัส AV อาจดำเนินการไปแล้วหรือข้อความก่อนที่ AV ยังไม่ดำเนินการ
Aaron

ดูคำตอบของฉันด้านล่างวิธีเปิดใช้งานการจัดการข้อยกเว้นดังกล่าวโดยใช้การลองปกติ ... catch block ใน VC ++
Volodymyr Frytskyy

@Aaron คุณสามารถอธิบายส่วน "สิ่งที่เป็นไปไม่ได้เกิดขึ้น" ได้หรือไม่? เป็นเพราะคอมไพเลอร์และ / หรือคำสั่งการจัดลำดับใหม่ของ CPU หรือไม่?
Weipeng L

ระบบปฏิบัติการพื้นฐานมักจะจัดเตรียมกลไกในการตรวจจับปัญหาดังกล่าวและจะไม่มีค่าใช้จ่ายใด ๆ เนื่องจากข้อยกเว้นถูกสร้างขึ้นโดยสถาปัตยกรรมของ CPU นี่เป็นหลักฐานจากวิธีการที่ดีบักเกอร์สามารถดักจับข้อยกเว้นเพื่อให้คุณดีบักโดยไม่ทำให้การเรียกใช้โค้ดช้าลง
Dino Dini

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 ด้วย
Fabio Ceconello

16
สิ่งนี้ใช้ไม่ได้ใน gcc แต่ทำงานใน VC ++ แต่ในบิลด์ "Debug" เท่านั้น ยังคงโหวตให้โซลูชันที่น่าสนใจ ตัวจัดการสัญญาณจะถูกเรียก แต่ข้อยกเว้นจะไม่ถูกโยนทิ้ง
Natalie Adams

2
ไม่ทำงานแบบพกพา เมื่อตัวจัดการสัญญาณถูกเรียกใช้สแต็กเฟรมและการลงทะเบียน munging จะไม่เหมือนกับเมื่อสแต็กเฟรมของฟังก์ชันปกติ (อาจไม่ได้ใช้สแต็กเดียวกันในบางระบบ) สิ่งที่ดีที่สุดที่คุณทำได้คือตั้งค่าสถานะเพื่อระบุว่ามีการเปิดใช้งานเครื่องจัดการสัญญาณแล้ว จากนั้นในการทดสอบรหัสของคุณสำหรับธงนั้นและโยน
Martin York

2
ซึ่งมีโอกาสสูงที่จะแนะนำพฤติกรรมที่ไม่ได้กำหนด เพื่อให้สิ่งนี้ทำงานบน POSIX ต้องไม่มีการsigaltstackติดตั้งสแต็กสัญญาณอื่น ๆ ( ) (เว้นแต่ว่าข้อยกเว้น C ++ จะอนุญาตให้ใช้งานได้) และทุกฟังก์ชันรันไทม์ที่จัดการกลไกการคลายตัวเองควรมีความปลอดภัยของสัญญาณ
minmaxavg

1
หากคุณต้องการคืนค่าตัวจัดการเริ่มต้นเป็นสัญญาณ (SIGSEGV ในกรณีนี้) ให้ใช้สิ่งต่อไปนี้:signal(SIGSEGV, SIG_DFL);
kocica

67

มีวิธีที่ง่ายมากในการตรวจจับข้อยกเว้นใด ๆ (การหารด้วยศูนย์การละเมิดการเข้าถึง ฯลฯ ) ในVisual Studioโดยใช้บล็อก try -> catch (... ) การปรับแต่งการตั้งค่าโครงการเล็กน้อยก็เพียงพอแล้ว เพียงแค่เปิดใช้งาน / EHa ตัวเลือกในการตั้งค่าโครงการ ดูคุณสมบัติโครงการ -> C / C ++ -> การสร้างโค้ด -> แก้ไขการเปิดใช้งานข้อยกเว้น C ++ เป็น "ใช่ด้วยข้อยกเว้น SEH"SEH" แค่นั้นแหละ!

ดูรายละเอียดที่นี่: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx


ไม่มีค่าการตั้งค่าดังกล่าวใน Visual Studio .NET 2003 มีเพียง "ไม่ใช่" และ "ใช่ (/ EHsc)" คุณช่วยอธิบายได้ไหมว่า Visual Studio เวอร์ชันขั้นต่ำที่คุณต้องสามารถเปิดใช้งานการตั้งค่านี้ได้
izogfif

ดูเหมือนว่าลิงก์จะระบุ "Visual Studio 2005"
Drew Delano

2
จะเป็นอย่างไรถ้าใช้ gcc หรือ MinGW
user1024

10

อย่างน้อยสำหรับฉันที่signal(SIGSEGV ...)วิธีการที่กล่าวถึงในคำตอบอื่นไม่ได้ทำงานใน Win32 ด้วย Visual C ++ 2015 สิ่งที่ได้ผลสำหรับฉันคือการใช้ที่_set_se_translator()พบในeh.h . มันทำงานดังนี้:

ขั้นตอนที่ 1 ) ตรวจสอบให้แน่ใจว่าคุณเปิดใช้งานใช่ด้วยข้อยกเว้น SEH (/ EHa)ในคุณสมบัติโครงการ / C ++ / การสร้างรหัส / เปิดใช้งานข้อยกเว้น C ++ดังที่กล่าวไว้ในคำตอบของVolodymyr FrytskyyFrytskyy

ขั้นตอนที่ 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
Pabitra Dash

8

สถานการณ์ประเภทนี้ขึ้นอยู่กับการนำไปใช้งานและด้วยเหตุนี้จึงต้องใช้กลไกเฉพาะของผู้ขายเพื่อดักจับ สำหรับ Microsoft สิ่งนี้จะเกี่ยวข้องกับ SEH และ * nix จะเกี่ยวข้องกับสัญญาณ

โดยทั่วไปแม้ว่าการตรวจจับข้อยกเว้นการละเมิดการเข้าถึงจะเป็นไฟล์ ความคิดที่แย่มาก แทบไม่มีวิธีใดเลยที่จะกู้คืนจากข้อยกเว้น AV และการพยายามทำเช่นนั้นจะทำให้พบจุดบกพร่องในโปรแกรมของคุณได้ยากขึ้น


1
ดังนั้นคำแนะนำของคุณคือการรู้ว่าอะไรคือสาเหตุของข้อยกเว้น AV ใช่หรือไม่?
Ahmed Said

4
อย่างแน่นอน AV เป็นตัวแทนของจุดบกพร่องในโค้ดของคุณและการจับข้อยกเว้นจะเป็นการซ่อนปัญหา
JaredPar

1
เพื่อความชัดเจนมาตรฐาน C ++ สร้างความแตกต่างระหว่างไม่ได้กำหนดไม่ระบุและการนำไปใช้งานที่กำหนดไว้ การกำหนดการนำไปใช้งานหมายความว่าการนำไปใช้งานต้องระบุสิ่งที่เกิดขึ้น รหัสในคำถามไม่ได้กำหนดไว้ซึ่งหมายความว่าทุกอย่างสามารถเกิดขึ้นได้และจะแตกต่างกันในแต่ละครั้ง
KeithB

15
การจับการละเมิดการเข้าถึงไม่ใช่ความคิดที่เลว แต่เป็นสิ่งที่ดีสำหรับประสบการณ์ของผู้ใช้ อย่างไรก็ตามสิ่งเดียวที่มีความหมายที่ฉันทำในกรณีนี้คือ - สร้างกระบวนการอื่นด้วย Bug Reporting GUI และพยายามสร้างการถ่ายโอนข้อมูลกระบวนการปัจจุบัน กระบวนการวางไข่เป็นการดำเนินการที่ประสบความสำเร็จเสมอ จากนั้นฉันทำ TerminateProcess () เพื่อฆ่าตัวเอง
ПетърПетров

12
เป็นความคิดที่ดีที่จะจับข้อยกเว้นและเพิกเฉยต่อสิ่งนั้นอย่างเงียบ ๆ เป็นความคิดที่ดีมากเมื่อเป็นไปได้ที่จะจับข้อยกเว้นและบันทึกข้อมูลเกี่ยวกับสถานะของแอปพลิเคชันเพื่อวัตถุประสงค์ในการวินิจฉัย ครั้งหนึ่งฉันเคยเขียน UI สำหรับไลบรารีกราฟิกแบ็กเอนด์ที่จำเป็นต้องมีการดีบัก ทุกครั้งที่เกิดปัญหาผู้คนมาหาฉันเพราะพวกเขารู้ว่าฉันเขียน UI ฉันวางกับดัก sig ไว้รอบ ๆ แบ็กเอนด์ที่ปรากฏการแจ้งเตือนที่แจ้งให้ผู้ใช้ทราบว่าห้องสมุดขัดข้อง ผู้คนเริ่มไปหาผู้เขียนห้องสมุด
Kent

8

ตามที่ระบุไว้ไม่มีวิธีของผู้จำหน่ายที่ไม่ใช่ Microsoft / คอมไพเลอร์ในการดำเนินการนี้บนแพลตฟอร์ม windows อย่างไรก็ตามเห็นได้ชัดว่ามีประโยชน์ในการตรวจจับข้อยกเว้นประเภทนี้ในวิธีการลองใช้ {} catch (ข้อยกเว้น ex) {} ปกติสำหรับการรายงานข้อผิดพลาดและวิธีการออกจากแอปของคุณอย่างสง่างาม (ดังที่ JaredPar กล่าวว่าแอปอาจมีปัญหา) . เราใช้ _se_translator_function ใน class wrapper ที่ช่วยให้เราสามารถจับข้อยกเว้นต่อไปนี้ในตัวจัดการลอง:

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 ถือว่าเหมือนกับคำสั่งที่ผิดกฎหมายหรือการละเมิดการเข้าถึง น่าสนใจ.
David Thornley

3

ไม่ใช่กลไกการจัดการข้อยกเว้น แต่คุณสามารถใช้กลไกสัญญาณ () ที่จัดทำโดย C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

การเขียนไปยังตัวชี้ NULL อาจทำให้เกิดสัญญาณ SIGSEGV


@maidamai signal()เป็นส่วนหนึ่งของมาตรฐาน posix Windows ใช้มาตรฐาน posix (เช่นเดียวกับ Linux และ unix)
Martin York

-1

การละเมิดดังกล่าวหมายความว่ามีบางอย่างผิดปกติอย่างร้ายแรงกับรหัสและไม่น่าเชื่อถือ ฉันเห็นว่าโปรแกรมอาจต้องการพยายามบันทึกข้อมูลของผู้ใช้ในลักษณะที่หวังว่าจะไม่เขียนทับข้อมูลก่อนหน้านี้โดยหวังว่าข้อมูลของผู้ใช้จะไม่เสียหาย แต่ไม่มีวิธีการมาตรฐานตามคำจำกัดความ ของการจัดการกับพฤติกรรมที่ไม่ได้กำหนด


7
การกู้คืนจากการละเมิดการเข้าถึงอาจทำได้ การกู้คืนจากการขยายเสียงกระโดด EIP จะไม่สามารถทำได้เว้นแต่คุณจะหลบหลีกและรักษาคำแนะนำระดับการประกอบ อย่างไรก็ตามการตรวจจับการละเมิดการเข้าถึงเป็นสิ่งที่ดีสำหรับการสร้างกระบวนการอื่นสำหรับคุณลักษณะ GUI รายงานข้อบกพร่อง
ПетърПетров
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.