Re: Copyovering on SIG{SEGV,BUS,..}

From: James Turner (turnerjh@XTN.NET)
Date: 05/01/98


George <greerga@CIRCLEMUD.ORG> writes:

> If you look inside the glibc2 malloc library, there are functions called
> 'malloc_check,' 'free_check,' etc which would help in the case of
> corrupting the malloc structures.  Unfortunately, they don't exist in
> glibc2 by default.  You have to recompile the malloc library and link it
> with your program.  You may be able to use the malloc 'hooks' feature but I
> haven't tried that.

For now I've decided to handle it through a pair of guard functions
and ignore arbitrary free()ing.  It adds 2*getpagesize() to every
malloc, so it's not very efficient and shouldn't be used for anything
frequently allocated.  Right now I'm using it for buf, buf1, buf2, and
arg.  It has proven quite effective (in test cases... my mud currently
doesn't have any overruns because I'm using rather large buffers at
the moment).  It works well on linux, and I expect it would work on
any posix compliant system (though getpagesize() is a BSD and svr4
command according to man.. I believe there are appropriate constants
defined in the standard header limits.h.).

On a buffer overrun or underrun (that is smaller than 4k... which I
imagine is just about all of them), a segment violation will be
generated.  My next step will be to add semi-automatic stack
protection (via a pair of macros).  Something like

#define STACK_GUARD() char stack_guard[4096]; \
  mprotect(stack_guard, 4096, PROT_READ)

#define STACK_UNGUARD() \
  mprotect(stack_guard, 4096, PROT_READ|PROT_WRITE|PROT_EXEC)

#define STACK_RETURN(n) \
  mprotect(stack_guard, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);\
  return (n)

Then it would be called like

int myfunc()
{
  int x;
  char buf[256];
  STACK_GUARD();

  /* stuff happens */
  if (blah < 0)
    STACK_RETURN(-1);
  else
    /* more stuff */

  /* yet more stuff */

  STACK_UNGUARD();
}

Below are the guarded malloc/free functions.

void *
mud_malloc_guarded(size_t nbytes)
{
  void *p;
  int pagesize;

  assert((nbytes > 0) && (nbytes < 5*1024*1024));

  pagesize = getpagesize();

  nbytes = ((nbytes / pagesize) + 1) * pagesize;

  p = valloc(nbytes + 2 * pagesize);

  if (!p) {
    sprintf(errbuf, "mud_malloc_guarded failure for %d bytes", nbytes);
    perror(errbuf);
    abort();
  }

  bzero(p, nbytes + 2 * pagesize);
  if (mprotect(p, pagesize, PROT_READ) < 0)
    slog("SYSERR: mprotect, %s", strerror(errno));

  if (mprotect((char *)p + pagesize + nbytes, pagesize, PROT_READ) < 0)
    slog("SYSERR: mprotect, %s", strerror(errno));

  num_allocs++;
  bytes_alloc+=nbytes;
  return (void *)(((char *)p)+pagesize);
}

void
mud_free_guarded(void *ptr)
{
  num_frees++;
  free((char *)ptr - getpagesize());
  return;
}


--
James Turner               turnerjh@xtn.net
                           http://www.vuse.vanderbilt.edu/~turnerjh/


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     | http://democracy.queensu.ca/~fletcher/Circle/list-faq.html |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 12/15/00 PST