Subject: Re: sanity checking arguments to library functions...
To: Simon Burge <simonb@netbsd.org>
From: Brian C. Grayson <bgrayson@orac.ece.utexas.edu>
List: tech-userlevel
Date: 04/20/1999 18:25:44
--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii

  Enclosed (as a MIME attachment) is a simple C program that
tries to detect whether the builtin-method will work for a
given machine.  I've tested it out on x86, which is the only
NetBSD arch I have access to.  This is a quick hack, so no
guarantees.....  :)

  1.  It should compile without errors, under -g -Werror.  If
    not, it probably means the method won't work on your arch, or
    that I've goofed.

  2.  It checks for several things:
    a.  Roughly valid return addresses and frame pointer
      addresses -- these should be in descending/ascending order
      respectively on most archs, if not all.
    b.  Makes certain that the offset of the saved previous frame
      pointer, within the current frame, remains constant for all
      frames.
    c.  Makes same constancy check for the return address offset.
    d.  And, each return address better be within the text segment.

  3.  For yucks, if you compiled with -g, take the return address
    output, and run "addr2line -e <executablename> -f <addr1>
    <addr1-1> <addr2> ..."  This is the kind of output I'd like
    to eventually provide, all without forking off a process.

  The program may have bugs, since I only have access to one test
platform.  In particular, I'm not up on 64-bit machines, so the
pointer arith is probably broken for Alpha and UltraSparc.  If
so, send me mail, and I'll try to correct them when I have a bit
more time.

  Anyway, if someone from each arch could try this out and give
me a Yea or Nay (private E-mail) on whether or not the program
is happy, I'd appreciate it.  Regardless, I'll try to write up
some code that does what I'd like, for at least the platforms
with sane frame formats....  :)

  I'll summarize the info if/when I get all/enough responses.

  Many thanks!

  Brian

--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="simpletest.c"

#include <stdio.h>

void FramesOkay();
void RetAddrOkay();

void a(), b(), c(), d(), e(), f(), g();

int
main (int argc, char **argv) {
  a();
}
void a() {b();}
void b() {c();}
void c() {d();}
void d() {e();}
void e() {f();}
void f() {
  g();
}
void g() {
  FramesOkay();
  RetAddrOkay();
}

int FrameOffsetInBytes(int framep_passed, int searchval) {
  int i;
  /*  Are pointers 32-bits or 64-bits on Alpha?  If 64, this is
   *  probably broken.  */
  int **framep = (int**) framep_passed;
  for (i=0; (*framep != (int*)searchval) && (i<100);
      i++, framep++) /*  Spin.  */ ;
  if (i >= 100) errx(-1, "Error:  did not find stored value %#10x within 100 words of frame beginning at %#10x!\n", searchval, framep_passed);
  return i*4;
}

int frameaddr[8];

void FramesOkay() {
  int i, frameoff;
  frameaddr[0]=(int)__builtin_frame_address(0);
  frameaddr[1]=(int)__builtin_frame_address(1);
  frameaddr[2]=(int)__builtin_frame_address(2);
  frameaddr[3]=(int)__builtin_frame_address(3);
  frameaddr[4]=(int)__builtin_frame_address(4);
  frameaddr[5]=(int)__builtin_frame_address(5);
  frameaddr[6]=(int)__builtin_frame_address(6);

  printf ("Last 7 frame addresses:\n");
  for (i=0; i<7; i++)
    printf("%#10x\n", frameaddr[i]);
  for (i=0; i<6; i++) {
    /*  Check for sanity -- they should be in increasing order from
     *  0 to 7, or decreasing in the call order.  */
    if (frameaddr[i] > frameaddr[i+1]) {
      errx(-1, "Error:  frame %d is at higher address than frame %d!\n",
	  i, i+1);
    }
    /*  Also check that the difference between any two frames is
     *  fairly small -- certainly less than a few pages!  */
    if (frameaddr[i+1] - frameaddr[i] > 16384) {
      errx(-1, "Error:  suspiciously large gap between frames %d and %d!\n",
	  i, i+1);
    }
  }
  /*  Also verify that the first few frame pointers are at a
   *  fixed offset from the beginning of a frame.  */
#define FRAME_OFF(i) (FrameOffsetInBytes(frameaddr[i], frameaddr[i+1]))
  frameoff=FRAME_OFF(0);
  printf("Frame offset appears to be %d\n", frameoff);
#define FRAME_OFF_CHECK(i) \
  if (FRAME_OFF(i) != frameoff) { errx(-1, \
      "Frame offset for frame %d is %d, not %d!\n", \
      i, FRAME_OFF(i), frameoff); }
  FRAME_OFF_CHECK(1);
  FRAME_OFF_CHECK(2);
  FRAME_OFF_CHECK(3);
  FRAME_OFF_CHECK(4);
  FRAME_OFF_CHECK(5);
}

void RetAddrOkay() {
  int returnaddr[8], i, retoffset;
  extern char *etext;
  int textEnd = (int) &etext;

  returnaddr[0]=(int)__builtin_return_address(0);
  returnaddr[1]=(int)__builtin_return_address(1);
  returnaddr[2]=(int)__builtin_return_address(2);
  returnaddr[3]=(int)__builtin_return_address(3);
  returnaddr[4]=(int)__builtin_return_address(4);
  returnaddr[5]=(int)__builtin_return_address(5);
  returnaddr[6]=(int)__builtin_return_address(6);
  printf ("Last 7 return addresses:\n");
  for (i=0; i<7; i++)
    printf("%#10x\n", returnaddr[i]);

  /*  Figure out the frame offset of the return address.  */
  retoffset = FrameOffsetInBytes(frameaddr[0], returnaddr[0]);
  printf("Return address frame offset appears to be %d\n", retoffset);

  for (i=0; i<7; i++) {
    /*  Make sure all return addresses are within text segment.  */
    if (returnaddr[i] > textEnd)
      err(-1, "Error:  return address %d of %#10x is outside "
	  "text segment, which ends at %#10x???\n", i,
	  returnaddr[i], textEnd);
    /*  Make sure all return addresses are in descending order,
     *  from 0 to 6.  */
    if (i<6 && returnaddr[i] < returnaddr[i+1])
      err(-1, "Error:  I expected the return addresses to be in "
	  "descending order!\n");
    if (FrameOffsetInBytes(frameaddr[i], returnaddr[i]) != retoffset) {
      err(-1, "Error:  For return address %d, I expected return address "
	  "offset of %d bytes, but saw %d bytes instead!\n", 
	  i, retoffset, FrameOffsetInBytes(frameaddr[i], returnaddr[i]));
    }
  }
}

--Kj7319i9nmIyA2yE--