什么是最简单的方式使一个C++程序崩溃?

What is the easiest way to make a C++ program crash?

我正在尝试创建一个与不同崩溃进程(这是我无法控制的)交互的Python程序。不幸的是,我正在连接的程序甚至不能可靠地崩溃!所以我想做一个快速的C++程序,它是故意崩溃的,但是我不知道最好的最短的方法,有人知道我应该把什么放在我的:

1
2
3
int main() {
    crashyCodeGoesHere();
}

让我的C++程序可靠崩溃


abort()函数可能是您最好的选择。它是C标准库的一部分,定义为"导致异常程序终止"(例如致命错误或崩溃)。


尝试:

1
2
raise(SIGSEGV);  // simulates a standard crash when access invalid memory
                 // ie anything that can go wrong with pointers.

发现于:

1
#include <signal.h>


除以零将使应用程序崩溃:

1
2
3
4
int main()
{
    return 1 / 0;
}


1
*((unsigned int*)0) = 0xDEAD;


好吧,我们是不是在stackoverflow上?

1
for (long long int i = 0; ++i; (&i)[i] = i);

(不保证按任何标准崩溃,但也没有任何建议的答案,包括自SIGABRT被抓获以来接受的答案)。实际上,这会在任何地方崩溃。)


1
 throw 42;

只是答案…:)


assert(false);也不错。

根据ISO/IEC 9899:1999,当未定义ndebug时,保证发生碰撞:

If NDEBUG is defined [...] the assert macro is defined simply as

1
#define assert(ignore) ((void)0)

The assert macro is redefined according to the current state of NDEBUG each time that is included.

[...]

The assert macro puts diagnostic tests into programs; [...] if expression (which shall have a scalar type) is false [...]. It
then calls the abort function.


由于崩溃是调用未定义行为的一种症状,并且由于调用未定义行为可能导致任何事情,包括崩溃,我认为您不希望真的使您的程序崩溃,只需要将它放到调试器中即可。最便携的方法可能是abort()

虽然raise(SIGABRT)具有相同的效果,但它肯定更适合写作。但是,两种方法都可以通过为SIGABRT安装信号处理程序来截获。因此,根据您的情况,您可能需要/需要发出另一个信号。可能是SIGFPESIGILLSIGINTSIGTERMSIGSEGV等,但都可以拦截。

当您不可移植时,您的选择可能会更广泛,比如在Linux上使用SIGBUS


我只有abort()函数:

它以异常的程序终止中止进程。它生成sigabrt信号,默认情况下,该信号使程序终止,并向主机环境返回一个不成功的终止错误代码。程序终止时,不会对自动或静态存储持续时间的对象执行析构函数,也不会调用任何atexit(在程序终止之前由exit()调用)函数。它永远不会回到它的呼叫者那里。


答案是平台特定的,取决于您的目标。但这里是mozilla javascript崩溃函数,我认为它说明了实现此功能的许多挑战:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
}


C++可以通过在处理另一个异常时抛出异常来确定地崩溃!标准规定,绝不从析构函数中抛出任何异常,也绝不在可能抛出异常的析构函数中使用任何函数。

我们必须做一个函数,所以让我们离开析构函数等。

ISO/IEC 14882第15.1-7节中的示例。应该是按C++标准崩溃的。这里有一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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,导致隐式调用中止:

If no exception is presently being handled, executing a
throw-expression with no operand calls std::terminate()

其他包括:自毁器抛掷:ISO/IEC 14882第15.2/3节


这个不见了:

1
int main = 42;


1
*( ( char* ) NULL ) = 0;

这将产生分段错误。


死循环递归方法调用的堆栈溢出怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <windows.h>
#include <stdio.h>

void main()
{
    StackOverflow(0);
}

void StackOverflow(int depth)
{
    char blockdata[10000];
    printf("Overflow: %d
"
, depth);
    StackOverflow(depth+1);
}

查看Microsoft知识库上的原始示例


这是上面答案中给出的中止的一个更可靠的版本。它可以处理SIGABRT被阻止的情况。您可以使用任何具有程序崩溃默认操作的信号而不是中止。

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    sigset_t act;
    sigemptyset(&act);
    sigfillset(&act);
    sigprocmask(SIG_UNBLOCK,&act,NULL);
    abort();
}

这在我的Linux系统上崩溃,因为字符串文本存储在只读内存中:

1
0[""]--;

顺便说一下,G++拒绝编译这个。编译器越来越智能化:)


1
int i = 1 / 0;

您的编译器可能会警告您这一点,但它在GCC4.4.3下编译得很好。这可能会导致SIGFPE(浮点异常),这在实际应用程序中可能不像其他答案造成的SIGSEGV(内存分段冲突)那样,但它仍然是崩溃。在我看来,这更具可读性。

另一种方法,如果我们打算欺骗和使用signal.h,是:

1
2
3
4
#include <signal.h>
int main() {
    raise(SIGKILL);
}

与sigsegv相比,这保证会杀死子进程。


1
2
int* p=0;
*p=0;

这也应该崩溃。在Windows上,它会因访问冲突而崩溃,我猜它应该在所有操作系统上都这样做。


这是Google在BreakPad中提供的代码片段。

1
2
  volatile int* a = reinterpret_cast<volatile int*>(NULL);
  *a = 1;


尽管这个问题已经得到了公认的答案…

1
2
3
void main(){
    throw 1;
}

或者…void main(){throw 1;}


1
2
3
4
5
int main(int argc, char *argv[])
{
    char *buf=NULL;buf[0]=0;
    return 0;
}

除非系统不支持只读内存块,否则写入只读内存将导致分段错误。

1
2
3
int main() {
    (int&)main = 0;
}

我在Windows7上用mingggw5.3.0测试过,在linux mint上用gcc测试过。我认为其他编译器和系统也会产生类似的效果。


或者另一种方式,因为我们在乐队。

一段可爱的无限递归。保证会把你的烟囱炸开。

1
2
3
4
int main(int argv, char* argc)
{
   return main(argv, argc)
}

打印输出:

Segmentation fault (core dumped)


1
2
3
4
5
6
7
8
9
10
11
12
void main()
{

  int *aNumber = (int*) malloc(sizeof(int));
  int j = 10;
  for(int i = 2; i <= j; ++i)
  {
      aNumber = (int*) realloc(aNumber, sizeof(int) * i);
      j += 10;
  }

}

希望这次崩溃。干杯。


尚未提及的:

1
((void(*)())0)();

这将把空指针视为函数指针,然后调用它。与大多数方法一样,这并不能保证会使程序崩溃,但是操作系统不检查和程序返回的可能性是微乎其微的。


这样做的一种时尚方式是纯虚拟函数调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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编译,打印:

pure virtual method called

terminate called without an active exception

Aborted (core dumped)


1
2
3
4
5
6
7
8
9
int main()
{
    int *p=3;
    int s;
    while(1) {
        s=*p;
        p++;
    }
}


1
2
char*freeThis;
free(freeThis);

释放未初始化的指针是未定义的行为。在许多平台/编译器上,freeThis将具有随机值(以前在该内存位置的任何值)。释放它会要求系统释放该地址的内存,这通常会导致分段错误并导致程序崩溃。