Linux Overflow Signal Handler Example

From Wiki
Jump to: navigation, search

This code demonstrates how to set up a signal handler to catch a numeric overflow for signed integers.

According to the GCC info page: -ftrapv -- This option generates traps for signed overflow on addition, subtraction, multiplication operations. What they don't tell is what sort of trap it generates. On an x86 machine, this generates a SIGABRT, which kind of sucks, because now we can't tell the difference between an overflow and an assert() (unless we setup and tear down the signal handler as need it).

In the example below, we find that adding 2 to INT_MAX (+2,147,483,647) will generate an overflow in __addvsi3, which handles addition of two signed numbers. As to what *exactly* this does, I don't know. You'd think normally the compiler would have no need to call a subroutine to add two integers, so it must do this to perform the overflow checking.

What follows is the output of the stack backtrace, and a partial disassembly of main() where the call to __addvsi3 occurs:


Code: partial output of ./overflow
 
7 elements in backtrace
./a.out[0x8048c15]
[0xffffe40c]
/lib/libc.so.6(abort+0x188)[0xb7e49ed8]
./a.out[0x8048dc4]
./a.out[0x8048d57]
/lib/libc.so.6(__libc_start_main+0xe5)[0xb7e34635]
./a.out[0x8048821]
  


Code: overflow.lst
 
 8048d4c:       8b 45 f0                mov    -0x10(%ebp),%eax
 8048d4f:       89 04 24                mov    %eax,(%esp)
 8048d52:       e8 29 00 00 00          call   8048d80 <__addvsi3>
 8048d57:       89 45 f8                mov    %eax,-0x8(%ebp)
 8048d5a:       8b 45 f8                mov    -0x8(%ebp),%eax
  


Examining the backtrace, we see the address 0x8048d57, which we see is where the _addvsi3 subroutine would return to if it completed successfully.

Basically, it's a matter of learning how to read the disassembly and the backtrace. Often times the register value are useful to determine why something happened at the address where the error occurred.


Code: overflow.c
 
//
//  overflow.c -- demonstrates capturing the SIGABRT for a signed numeric overflow
//
//  Compile with 'gcc -W -Wall -ftrapv overflow.c -o overflow'
//  Create a list file with 'objdump -d overflow >overflow.lst'
//  Follow the stack backtrace to determine what caused the abort
//

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <execinfo.h>
#include <limits.h>
#include <ucontext.h>
#include <sys/types.h>

#define arrsizeof(x) (sizeof(x)/sizeof(x[0]))

//
//
//
char *getProcessName (void)
{
  char buffer [128];
  FILE *fp;

  sprintf (buffer, "/proc/%d/stat", getpid ());

  if ((fp = fopen (buffer, "r")))
  {
    if (fgets (buffer, sizeof (buffer), fp))
    {
      char *s;

      if ((s = index (buffer, ')')))
      {
        *s = '\0';

        if ((s = index (buffer, '(')))
          return ++s;
      }
    }
  }

  return NULL;
}

//
//
//
static void sighandlerPrint (FILE *fp, int signo, int code, ucontext_t *context, void *bt [], int bt_size)
{
  char *processName;
  time_t ttime = time (NULL);

  fprintf (fp, "%s", ctime (&ttime));
  fprintf (fp, "PID=%d (%s)\n", getpid (), (processName = getProcessName ()) ? processName : "unknown");
  fprintf (fp, "signo=%d/%s\n", signo, strsignal (signo));
  fprintf (fp, "code=%d (not always applicable)\n", code);
  fprintf (fp, "\nContext: 0x%08lx\n", (unsigned long) context);
  fprintf (fp, "    gs: 0x%08x   fs: 0x%08x   es: 0x%08x   ds: 0x%08x\n"
               "   edi: 0x%08x  esi: 0x%08x  ebp: 0x%08x  esp: 0x%08x\n"
               "   ebx: 0x%08x  edx: 0x%08x  ecx: 0x%08x  eax: 0x%08x\n"
               "  trap:   %8u  err: 0x%08x  eip: 0x%08x   cs: 0x%08x\n"
               "  flag: 0x%08x   sp: 0x%08x   ss: 0x%08x  cr2: 0x%08lx\n",
               context->uc_mcontext.gregs [REG_GS],     context->uc_mcontext.gregs [REG_FS],   context->uc_mcontext.gregs [REG_ES],  context->uc_mcontext.gregs [REG_DS],
               context->uc_mcontext.gregs [REG_EDI],    context->uc_mcontext.gregs [REG_ESI],  context->uc_mcontext.gregs [REG_EBP], context->uc_mcontext.gregs [REG_ESP],
               context->uc_mcontext.gregs [REG_EBX],    context->uc_mcontext.gregs [REG_EDX],  context->uc_mcontext.gregs [REG_ECX], context->uc_mcontext.gregs [REG_EAX],
               context->uc_mcontext.gregs [REG_TRAPNO], context->uc_mcontext.gregs [REG_ERR],  context->uc_mcontext.gregs [REG_EIP], context->uc_mcontext.gregs [REG_CS],
               context->uc_mcontext.gregs [REG_EFL],    context->uc_mcontext.gregs [REG_UESP], context->uc_mcontext.gregs [REG_SS],  context->uc_mcontext.cr2
  );

  fprintf (fp, "\n%d elements in backtrace\n", bt_size);
  fflush (fp);

  backtrace_symbols_fd (bt, bt_size, fileno (fp));
}

//
//
//
static void sighandlerABRT (int signo, struct siginfo *si, void *ctx)
{
  void *bt [128];
  int bt_size;

  bt_size = backtrace (bt, arrsizeof (bt));

  sighandlerPrint (stderr, signo, si->si_code, (ucontext_t *) ctx, bt, bt_size);

  exit (1);
}

//
//
//
int installSignalHandlers (void)
{
  struct sigaction sa;

  sigemptyset (&sa.sa_mask);
  sigaddset (&sa.sa_mask, SIGABRT);

  sa.sa_flags = SA_ONESHOT | SA_SIGINFO;
  sa.sa_sigaction = sighandlerABRT;

  if (sigaction (SIGABRT, &sa, NULL))
  {
    fprintf (stderr, "sigaction failed, line %d, %d/%s\n", __LINE__, errno, strerror (errno));
    exit (1);
  }

  return 1;
}

//
//
//
int main (void)
{
  int a = INT_MAX;
  int b = 2;
  int c = 0;

  installSignalHandlers ();

  c = a + b;

  printf ("c=%d\n", c);

  exit (0);
}