Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Changed NCCE 2

...

Code Block
bgColor#FFCCCC
jmp_buf buf;
unsigned void f(voidchar b[] = {0xe5, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};

int main() {
  gsetup();
  hdo_stuff();
  return 0;
}

void gsetup(void) {
  volatile int i = 10;
  f();
}

void f(void) {
  g();
}

void g(void) {
  if (setjmp(buf) == 0) {
    printf("i = %dsetjmp() invoked\n", i);
  } else {
    printf("longjmp: i = %d() invoked\n", i);
    exit(0);
  }
  return;
}

void hdo_stuff(void) {
  char ba[168];
  memsetmemcpy(ba, 0b, 168);
  /* ... stuff ... */
  longjmp(buf, 1);
}

void bad(void) {
  printf("Should not be called!\n");
  exit(1);
}

Implementation Details

When compiled for i386 x86-64 using GCC v4.1.2, the above example outputs the following when run:

Code Block
isetjmp() =invoked
10
longjmp:() iinvoked
= 0Should not be called!

Because g() has finished executing at the time longjmp() is called, it is no longer on the stack. When hdo_stuff() is invoked, its stackframe overwrites occupies the same memory as the old stackframe of g(). In this case i a was located in the same location as the end of array breturn address of function g(). The call to memsetmemcpy() sets the four bytes that i occupied in g() to 0 overwrites the return address, so when longjmp() sends control back to function g(), it prints out a value of 0the function returns to the wrong address (in this case to function bad()).
If the array b were user-specified, they would be able to set the return address of function g() to any location.

Compliant Solution

The longjmp() function should only be used when the function containing the corresponding setjmp() is guaranteed not to have completed execution, as in the following example.

Code Block
bgColor#ccccff
jmp_buf buf;
unsigned void f(void) {
  volatile int i = 10;
  char b[] = {0xe5, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};

int main() {
  if (setjmp(buf) == 0) {
    printf("i = %dsetjmp() invoked\n", i);
  } else {
    printf("longjmp: i = %d() invoked\n", i);
    exit(0);
  }
  hdo_stuff();
  return 0;
}

void hdo_stuff(void) {
  char ba[168];
  memsetmemcpy(ba, 0b, 168);
  /* ... stuff ... */
  longjmp(buf, 1);
}

void bad(void) {
  printf("Should not be called!\n");
  exit(1);
}

In this example there is no risk of overwriting i a return address because the stackframe of fmain() (the function that invoked setjmp()) is still on the stack, so when h do_stuff() is invoked, the two stackframes will not overlap.

...