我正在尝试制作一个与其他崩溃进程连接的Python程序(这在我手中)。不幸的是,我正在连接的程序甚至无法可靠地崩溃!因此,我想制作一个有意崩溃的快速C ++程序,但实际上我不知道这样做的最好和最短的方法,有人知道我该怎么做吗?
int main() {
crashyCodeGoesHere();
}
使我的C ++程序可靠地崩溃
*((char*)-1) = 'x';
代码引发崩溃,以调试我的答复中的
我正在尝试制作一个与其他崩溃进程连接的Python程序(这在我手中)。不幸的是,我正在连接的程序甚至无法可靠地崩溃!因此,我想制作一个有意崩溃的快速C ++程序,但实际上我不知道这样做的最好和最短的方法,有人知道我该怎么做吗?
int main() {
crashyCodeGoesHere();
}
使我的C ++程序可靠地崩溃
*((char*)-1) = 'x';
代码引发崩溃,以调试我的答复中的
Answers:
该abort()
功能可能是您最好的选择。它是C标准库的一部分,被定义为“导致程序异常终止”(例如,致命错误或崩溃)。
abort()
不会调用任何析构函数或atexit
函数,尽管在这里这可能无关紧要。
atexit
s,那么现在不会崩溃了吗?
abort()
是正确的答案,那么'exit(-1);'应该可以接受吗?
尝试:
raise(SIGSEGV); // simulates a standard crash when access invalid memory
// ie anything that can go wrong with pointers.
在发现:
#include <signal.h>
signal()
。但是,大多数理智的应用程序却没有。
除以零将使应用程序崩溃:
int main()
{
return 1 / 0;
}
main
包括ret
指令在内的主体被完全删除。执行可能只属于以下功能。
*((unsigned int*)0) = 0xDEAD;
好吧,我们堆栈溢出了吗?
for (long long int i = 0; ++i; (&i)[i] = i);
(不保证按任何标准崩溃,但是无论如何都不会建议任何建议的答案,包括可接受的答案SIGABRT
。因为实际上可能会被捕获。)
(&i)[i] += !i
,但是我担心编译器可能足够聪明,并且希望对其进行优化。:-)
throw 42;
只是答案... :)
assert(false);
也很好
根据ISO / IEC 9899:1999,保证在未定义NDEBUG时崩溃:
如果定义了NDEBUG,则将assert宏定义为
#define assert(ignore) ((void)0)
每次包含时,将根据NDEBUG的当前状态重新定义断言宏。
[...]
assert宏将诊断测试放入程序中;[...]如果expression(应为标量类型)为假[...]。然后,它调用中止功能。
assert
等效((void)0)
。
由于崩溃是调用未定义行为的征兆,并且由于调用未定义行为可能导致任何事情,包括崩溃,因此我认为您并不想真正使程序崩溃,而只是将其放入调试器中。这样做最方便的方法是abort()
。
虽然raise(SIGABRT)
具有相同的效果,但肯定要写得多。但是,可以通过为安装安装信号处理程序来拦截两种方式SIGABRT
。因此,根据您的情况,您可能希望/需要提出另一个信号。SIGFPE
,SIGILL
,SIGINT
,SIGTERM
或者SIGSEGV
可能是要走的路,但他们都可以被截获。
当您无法携带时,您的选择可能会更广泛,例如SIGBUS
在linux上使用。
答案是特定于平台的,取决于您的目标。但是,这是Mozilla Javascript崩溃函数,我认为它说明了实现此功能的许多挑战:
static JS_NEVER_INLINE void
CrashInJS()
{
/*
* We write 123 here so that the machine code for this function is
* unique. Otherwise the linker, trying to be smart, might use the
* same code for CrashInJS and for some other function. That
* messes up the signature in minidumps.
*/
#if defined(WIN32)
/*
* We used to call DebugBreak() on Windows, but amazingly, it causes
* the MSVS 2010 debugger not to be able to recover a call stack.
*/
*((int *) NULL) = 123;
exit(3);
#elif defined(__APPLE__)
/*
* On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
* trapped.
*/
*((int *) NULL) = 123; /* To continue from here in GDB: "return" then "continue". */
raise(SIGABRT); /* In case above statement gets nixed by the optimizer. */
#else
raise(SIGABRT); /* To continue from here in GDB: "signal 0". */
#endif
}
我看到有很多答案可以运用于完成工作的幸运案例中,但是没有一个是100%确定性的崩溃。有些会在一种硬件和操作系统上崩溃,而其他则不会。但是,根据官方C ++标准,有一种标准方法可以使其崩溃。
引用C ++标准ISO / IEC 14882§15.1-7:
如果异常处理机制在完成异常对象的初始化之后但在激活异常处理程序之前调用了通过异常退出的函数,则将调用std :: terminate(15.5.1)。
struct C { C() { } C(const C&) { if (std::uncaught_exceptions()) { throw 0; // throw during copy to handler’s exception-declaration object (15.3) } } }; int main() { try { throw C(); // calls std::terminate() if construction of the handler’s // exception-declaration object is not elided (12.8) } catch(C) { } }
我已经编写了一个小代码来演示这一点,可以在此处在Ideone上找到它并进行尝试。
class MyClass{
public:
~MyClass() throw(int) { throw 0;}
};
int main() {
try {
MyClass myobj; // its destructor will cause an exception
// This is another exception along with exception due to destructor of myobj and will cause app to terminate
throw 1; // It could be some function call which can result in exception.
}
catch(...)
{
std::cout<<"Exception catched"<<endl;
}
return 0;
}
ISO / IEC 14882§15.1/ 9提到没有try块的throw导致隐式调用中止:
如果当前没有异常在处理,则执行不带操作数的throw-expression调用std :: terminate()
其他包括:从析构函数中抛出:ISO / IEC 14882§15.2/ 3
*( ( char* ) NULL ) = 0;
这将产生分段错误。
死循环递归方法调用会导致堆栈溢出怎么办?
#include <windows.h>
#include <stdio.h>
void main()
{
StackOverflow(0);
}
void StackOverflow(int depth)
{
char blockdata[10000];
printf("Overflow: %d\n", depth);
StackOverflow(depth+1);
}
这在我的Linux系统上崩溃,因为字符串文字存储在只读内存中:
0[""]--;
顺便说一句,g ++拒绝对此进行编译。编译器变得越来越聪明:)
int i = 1 / 0;
您的编译器可能会对此发出警告,但在GCC 4.4.3下它可以正常编译,这可能会导致SIGFPE(浮点异常),在实际应用程序中,它可能不如SIGSEGV(违反内存分段)其他答案导致,但这仍然是崩溃。我认为,这更具可读性。
如果我们要作弊和使用signal.h
,另一种方法是:
#include <signal.h>
int main() {
raise(SIGKILL);
}
与SIGSEGV相比,这可以确保杀死子进程。
这是Google在Breakpad中提供的代码段。
volatile int* a = reinterpret_cast<volatile int*>(NULL);
*a = 1;
或者说另一种方式,因为我们在乐队旅行中。
可爱的无限递归。保证会打击您的筹码。
int main(int argv, char* argc)
{
return main(argv, argc)
}
打印输出:
分段故障(核心已转储)
main
自己实际上是未定义的行为,以防万一您不知道:)此外,也不保证尾部递归会破坏堆栈。如果要“保证”,则必须在递归调用之后执行某些操作,否则编译器可以将递归优化为无限循环。
尚未提及的一个:
((void(*)())0)();
这会将空指针视为函数指针,然后调用它。像大多数方法一样,这不能保证程序会崩溃,但是操作系统允许对此进行检查的可能性以及程序返回的可能性都可以忽略不计。
int main()
{
int *p=3;
int s;
while(1) {
s=*p;
p++;
}
}
long long
或size_t
并从p
各自的最大值开始或接近最大值,以加快崩溃速度。尽管即使在这种情况下也无法保证会崩溃。
做到这一点的一种时髦方式是纯粹的虚函数调用:
class Base;
void func(Base*);
class Base
{
public:
virtual void f() = 0;
Base()
{
func(this);
}
};
class Derived : Base
{
virtual void f()
{
}
};
void func(Base* p)
{
p->f();
}
int main()
{
Derived d;
}
与gcc一起编译,输出:
纯虚拟方法称为
在没有活动异常的情况下终止调用
中止(核心已弃用)
asm { cli; };