Bắt các ngoại lệ vi phạm quyền truy cập?


89

Thí dụ

int *ptr;
*ptr = 1000;

Tôi có thể bắt ngoại lệ vi phạm quyền truy cập bộ nhớ bằng cách sử dụng C ++ tiêu chuẩn mà không sử dụng bất kỳ phần mềm microsoft cụ thể nào không.

Câu trả lời:


42

Không. C ++ không đưa ra một ngoại lệ khi bạn làm điều gì đó tồi tệ, điều đó sẽ dẫn đến một cú đánh hiệu suất. Những thứ như vi phạm quyền truy cập hoặc lỗi chia cho 0 giống như ngoại lệ "máy" hơn là những thứ ở cấp độ ngôn ngữ mà bạn có thể bắt gặp.


Tôi biết đó là ngoại lệ HW, nhưng có những từ khóa cụ thể của microsoft xử lý điều này (__ thử __except)?
Ahmed nói

2
@Ahmed: có, nhưng nếu bạn sử dụng chúng, những điều 'không thể' có thể xảy ra. Ví dụ, một số câu lệnh sau dòng mã AV có thể đã được thực thi hoặc các câu lệnh trước AV chưa thực thi.
Aaron

Hãy xem câu trả lời của tôi dưới đây về cách bật xử lý ngoại lệ như vậy bằng cách sử dụng khối try ... catch thông thường trong VC ++.
Volodymyr Frytskyy

@Aaron bạn có thể nói rõ hơn về phần "những điều không thể xảy ra" được không? đó là do trình biên dịch và / hoặc hướng dẫn sắp xếp lại CPU?
Weipeng L

Hệ điều hành cơ bản thường sẽ cung cấp các cơ chế để bắt các vấn đề như vậy và chúng sẽ không phải trả phí, vì ngoại lệ được tạo ra bởi kiến ​​trúc CPU. Điều này được chứng minh bằng cách mà trình gỡ lỗi có thể bẫy ngoại lệ để cho phép bạn gỡ lỗi mà không làm chậm quá trình thực thi mã.
Dino Dini,

108

Đọc nó và khóc!

Tôi đã hiểu rồi. Nếu bạn không ném từ trình xử lý, trình xử lý sẽ tiếp tục và ngoại lệ cũng vậy.

Điều kỳ diệu xảy ra khi bạn ném ngoại lệ của mình và xử lý điều đó.

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

}

Mẹo hay, đặc biệt là vì __try / __ ngoại trừ việc cũng sẽ không bắt được AV.
Fabio Ceconello

16
Điều này KHÔNG hoạt động trong gcc nhưng hoạt động trong VC ++ nhưng chỉ trong bản dựng "Gỡ lỗi". Vẫn ủng hộ cho một giải pháp thú vị. Bộ xử lý tín hiệu sẽ được gọi nhưng ngoại lệ sẽ không được ném ra.
Natalie Adams

2
Điều đó không hoạt động di động. Khi một trình xử lý tín hiệu được gọi ra, khung ngăn xếp và đăng ký munging không giống như khi một khung ngăn xếp chức năng bình thường (thậm chí có thể không sử dụng cùng một ngăn xếp trên một số hệ thống). Điều tốt nhất bạn có thể làm là đặt một lá cờ để cho biết bộ xử lý tín hiệu đã được kích hoạt. Sau đó, trong bài kiểm tra mã của bạn cho cờ đó và ném.
Martin York,

2
Điều này có nhiều khả năng đưa ra hành vi không xác định. Để tính năng này hoạt động trên POSIX, không được sigaltstackcài đặt bất kỳ ngăn xếp tín hiệu thay thế nào ( ) (trừ khi việc triển khai xử lý ngoại lệ C ++ cho phép nó) và mọi hàm thời gian chạy xử lý chính cơ chế tháo cuộn phải an toàn về tín hiệu.
minmaxavg 25/11/17

1
Nếu bạn muốn quay trở lại xử lý mặc định để tín hiệu (SIGSEGV trong trường hợp này), JUSE sử dụng như sau:signal(SIGSEGV, SIG_DFL);
kocica

67

Có một cách rất dễ dàng để bắt bất kỳ loại ngoại lệ nào (chia cho không, vi phạm quyền truy cập, v.v.) trong Visual Studio bằng cách sử dụng khối try -> catch (...). Một điều chỉnh cài đặt dự án nhỏ là đủ. Chỉ cần bật tùy chọn / EHa trong cài đặt dự án. Xem Thuộc tính dự án -> C / C ++ -> Tạo mã -> Sửa đổi Kích hoạt ngoại lệ C ++ thành "Có với ngoại lệ SEH" . Đó là nó!

Xem chi tiết tại đây: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx


Không có giá trị thiết lập như vậy trong Visual Studio .NET 2003, chỉ có "Không" và "Có (/ EHsc)". Bạn có thể làm rõ bạn cần phiên bản Visual Studio tối thiểu nào để có thể bật cài đặt này không?
izogfif

Các liên kết xuất hiện để xác định "Visual Studio 2005"
Drew Delano

2
điều gì xảy ra nếu với gcc hoặc MinGW?
user1024,

10

Ít nhất đối với tôi, signal(SIGSEGV ...)cách tiếp cận được đề cập trong một câu trả lời khác không hoạt động trên Win32 với Visual C ++ 2015 . Những gì đã làm việc cho tôi là sử dụng _set_se_translator()tìm thấy trong eh.h. Nó hoạt động như thế này:

Bước 1 ) Đảm bảo bạn bật Có với Ngoại lệ SEH (/ EHa) trong Thuộc tính dự án / C ++ / Tạo mã / Kích hoạt ngoại lệ C ++ , như đã đề cập trong câu trả lời của Volodymyr Frytskyy .

Bước 2 ) Gọi _set_se_translator(), truyền con trỏ hàm (hoặc lambda) cho trình dịch ngoại lệ mới . Nó được gọi là trình dịch vì về cơ bản nó chỉ lấy ngoại lệ cấp thấp và ném lại nó như một thứ dễ bắt hơn, chẳng hạn như 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());
});

Bước 3 ) Bắt ngoại lệ như bạn thường làm:

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

1
Trang web này chứa một vài ví dụ đơn giản về các phương thức _set_se_translator () và hoạt động đối với tôi, msdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitra Dash

8

Loại tình huống này phụ thuộc vào việc triển khai và do đó nó sẽ yêu cầu một cơ chế cụ thể của nhà cung cấp để mắc bẫy. Với Microsoft, điều này sẽ liên quan đến SEH và * nix sẽ liên quan đến một tín hiệu

Nói chung, mặc dù bắt một ngoại lệ Vi phạm quyền truy cập là một ý tưởng rất tồi. Hầu như không có cách nào để khôi phục từ một ngoại lệ AV và cố gắng làm như vậy sẽ chỉ dẫn đến việc khó tìm thấy lỗi hơn trong chương trình của bạn.


1
Vì vậy, lời khuyên cho bạn là để biết nguyên nhân của AV ngoại lệ là gì, phải không?
Ahmed nói

4
Chắc chắn rồi. AV là đại diện cho một lỗi trong mã của bạn và bắt ngoại lệ sẽ chỉ ẩn vấn đề.
JaredPar

1
Để làm rõ, tiêu chuẩn C ++ phân biệt giữa không xác định, không xác định và triển khai được xác định. Việc thực hiện được xác định nghĩa là việc thực hiện phải chỉ rõ những gì diễn ra. Mã trong câu hỏi không được xác định, có nghĩa là bất cứ điều gì có thể xảy ra và mỗi lần khác nhau.
KeithB

15
Bắt vi phạm quyền truy cập không phải là ý tưởng tồi - nó tốt cho trải nghiệm người dùng. Tuy nhiên, điều có ý nghĩa duy nhất tôi làm trong trường hợp này là - tạo ra một quy trình khác với GUI báo cáo lỗi và cố gắng tạo một kết xuất quy trình hiện tại. Quá trình sinh sản luôn là một hoạt động thành công. Sau đó, tôi thực hiện TerminaProcess () để tự giết.
Петър Петров

12
Đó là một ý tưởng tồi nếu bắt một ngoại lệ và im lặng bỏ qua nó. Đó là một ý tưởng rất hay khi có thể bắt được một ngoại lệ và ghi lại thông tin về trạng thái của ứng dụng cho mục đích chẩn đoán. Tôi đã từng viết một giao diện người dùng cho một thư viện đồ họa phụ trợ cần một số gỡ lỗi. Mỗi khi nó gặp sự cố, mọi người lại tìm đến tôi vì họ biết tôi đã viết UI. Tôi đặt một cái bẫy sig xung quanh phần phụ trợ bật lên một cảnh báo cho người dùng biết rằng thư viện đã bị sập. Mọi người bắt đầu tìm đến tác giả của thư viện.
Kent

8

Như đã nêu, không có cách nào không phải của Microsoft / nhà cung cấp trình biên dịch để thực hiện việc này trên nền tảng windows. Tuy nhiên, rõ ràng là hữu ích khi bắt các loại ngoại lệ này theo cách thông thường, hãy thử {} catch (exception ex) {} để báo lỗi và hơn thế nữa là thoát ứng dụng của bạn một cách dễ dàng (như JaredPar nói, ứng dụng hiện có thể đang gặp sự cố) . Chúng tôi sử dụng _se_translator_ Chức năng trong một trình bao bọc lớp đơn giản cho phép chúng tôi nắm bắt các ngoại lệ sau trong trình xử lý thử aa:

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)

Lớp học ban đầu đến từ bài viết rất hữu ích này:

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


8
Tôi thấy rằng việc sử dụng trình biên dịch của Microsoft được coi giống như một hướng dẫn bất hợp pháp hoặc vi phạm quyền truy cập. Hấp dẫn.
David Thornley

3

Không phải là cơ chế xử lý ngoại lệ, nhưng bạn có thể sử dụng cơ chế signal () được cung cấp bởi C.

> man signal

     11    SIGSEGV      create core image    segmentation violation

Ghi vào con trỏ NULL có thể gây ra tín hiệu SIGSEGV


@maidamai signal()là một phần của tiêu chuẩn posix. Cửa sổ cụ tiêu chuẩn POSIX (cũng như Linux và Unix)
Martin York

-1

Một vi phạm như vậy có nghĩa là có điều gì đó sai nghiêm trọng với mã và nó không đáng tin cậy. Tôi có thể thấy rằng một chương trình có thể muốn cố gắng lưu dữ liệu của người dùng theo cách mà người ta hy vọng sẽ không ghi đè lên dữ liệu trước đó, với hy vọng rằng dữ liệu của người dùng chưa bị hỏng, nhưng theo định nghĩa thì không có phương pháp chuẩn đối phó với hành vi không xác định.


6
Có thể khôi phục sau khi vi phạm quyền truy cập. Không bao giờ có thể khôi phục từ thông báo nhảy EIP trừ khi bạn tinh ý và giữ các con trỏ hướng dẫn cấp lắp ráp. Tuy nhiên, việc phát hiện vi phạm Access sẽ tốt cho việc tạo ra một quy trình khác cho tính năng GUI báo cáo lỗi.
Петър Петров
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.