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;

void f(voidunsigned char 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
i = 10
longjmp: i = 0setjmp() invoked
longjmp() invoked
Should 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(), the function returns to the wrong address (in this case to function bad()), it prints out a value of 0.
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;

void f(void) {
  volatile int i = 10;unsigned 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.

...