Difference between revisions of "Stubbing noreturn Functions"

From OC Systems Wiki!
Jump to: navigation, search
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
[[Category:Uals]]
 +
[[Category:Stubbing]]
 +
 +
__NOTOC__
 +
== NeedsWork ==
 +
In Aprobe versions 4.4.6b and 4.4.6c <code>coverage.ual</code> does its own probe on <code>abort()</code> to trigger a coverage snapshot, and deallocates some internal data structures at that point so that a core dump may occur on subsequent abort calls.  A fix is targeted for Aprobe version 4.4.6d.  It is also possible to rebuild the UAL locally to correct the problem. 
 +
 
== Background ==
 
== Background ==
 
The gcc compiler supports applying the "noreturn" attribute to functions, as described at https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gcc/Common-Function-Attributes.html#index-functions-that-never-return-3249.  This attribute enables the compiler to "optimize" callers of such functions by treating any statements following the call as unreachable code and eliminating them.
 
The gcc compiler supports applying the "noreturn" attribute to functions, as described at https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gcc/Common-Function-Attributes.html#index-functions-that-never-return-3249.  This attribute enables the compiler to "optimize" callers of such functions by treating any statements following the call as unreachable code and eliminating them.
  
Two examples of this attribute are <code>exit</code> and <code>abort</code>.
+
Two examples of functions carrying this attribute are <code>exit()</code> and <code>abort()</code>.
  
This optimization is so aggressive that even the return sequence for calling functions can be eliminated if the call is at the end of the routine.  Thus an attempt to stub such a routine with aprobe's <code>ap_StubRoute</code> will effectively result in a branch to random instructions, which in some cases may not have been intended as executable code at all.
+
This optimization is so aggressive that even the return sequence for calling functions can be eliminated if the call is at the end of the routine.  Thus an attempt to stub such a routine with aprobe's <code>ap_StubRoutine</code> will effectively result in a branch to random instructions, which in some cases may not have been intended as executable code at all.
  
 
Following are code for a simple program to demonstrate the issue, and a probe to allow the noreturn function to be stubbed.
 
Following are code for a simple program to demonstrate the issue, and a probe to allow the noreturn function to be stubbed.
Line 11: Line 18:
  
 
=== APC Source ===
 
=== APC Source ===
 +
<source lang="c">
 +
//
 +
// stub_abort.apc
 +
//
 +
probe thread {
 +
  probe "abort" in "libc.so" {
 +
    on_entry {
 +
      ap_ExecutionContextPtr->ReturnAddress = (ap_Uint32)(*(int *)($$EBP+4));
 +
      // Get abort's caller's return address from the stack and use
 +
      // it as the address this probe will return to.
 +
      $$EBP = *(int *)($$EBP);
 +
      // Pop the frame pointer so it's correct when we return to abort's
 +
      // caller's caller.
 +
      ap_StubRoutine;
 +
      // Perform the usual stubbing.
 +
    }
 +
  }
 +
}
 +
</source>
 +
 +
=== C Source ===
 
<source lang="c">
 
<source lang="c">
 
//
 
//
Line 21: Line 49:
 
{
 
{
 
   abort();
 
   abort();
   printf("Control returned from abort().");
+
   printf("I returned from abort()?\n");
 
}
 
}
  
Line 29: Line 57:
 
   printf("I will survive!\n");
 
   printf("I will survive!\n");
 
}
 
}
</source>
 
  
=== C Source ===
 
<source lang="c">
 
//
 
// stub_abort.apc
 
//
 
probe thread {
 
  probe "abort" in "libc.so" {
 
    on_entry {
 
      ap_ExecutionContextPtr->ReturnAddress = (ap_Uint32)(*(int *)($$EBP+4));
 
      // Get abort's caller's return address from the stack and use
 
      // it as the address this probe will return from.
 
      $$EBP = *(int *)($$EBP);
 
      // Pop the frame pointer so it's correct when we return to abort's
 
      // caller's caller.
 
      ap_StubRoutine;
 
      // Perform the usual stubbing.
 
    }
 
  }
 
}
 
 
</source>
 
</source>

Latest revision as of 18:03, 3 April 2017


NeedsWork

In Aprobe versions 4.4.6b and 4.4.6c coverage.ual does its own probe on abort() to trigger a coverage snapshot, and deallocates some internal data structures at that point so that a core dump may occur on subsequent abort calls. A fix is targeted for Aprobe version 4.4.6d. It is also possible to rebuild the UAL locally to correct the problem.

Background

The gcc compiler supports applying the "noreturn" attribute to functions, as described at https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gcc/Common-Function-Attributes.html#index-functions-that-never-return-3249. This attribute enables the compiler to "optimize" callers of such functions by treating any statements following the call as unreachable code and eliminating them.

Two examples of functions carrying this attribute are exit() and abort().

This optimization is so aggressive that even the return sequence for calling functions can be eliminated if the call is at the end of the routine. Thus an attempt to stub such a routine with aprobe's ap_StubRoutine will effectively result in a branch to random instructions, which in some cases may not have been intended as executable code at all.

Following are code for a simple program to demonstrate the issue, and a probe to allow the noreturn function to be stubbed.

Note that because the compiler has deprived the caller of abort (in this example, fatal()) of a return sequence, we manipulate the instruction and frame pointer registers to effect a return to the caller of fatal(), in this case main().

APC Source

//
// stub_abort.apc
//
probe thread {
  probe "abort" in "libc.so" {
    on_entry {
      ap_ExecutionContextPtr->ReturnAddress = (ap_Uint32)(*(int *)($$EBP+4));
      // Get abort's caller's return address from the stack and use
      // it as the address this probe will return to.
      $$EBP = *(int *)($$EBP);
      // Pop the frame pointer so it's correct when we return to abort's
      // caller's caller.
      ap_StubRoutine;
      // Perform the usual stubbing.
    }
  }
}

C Source

//
// stub_abort.c
//
#include <stdlib.h>
#include <stdio.h>

void fatal()
{
   abort();
   printf("I returned from abort()?\n");
}

int main ()
{
   fatal();
   printf("I will survive!\n");
}