Re: [Gc] C optimizer hazards in practice

Richard Brooksby <[email protected]> Wed, 16 Jul 2014 16:04:06 +0100
Newsgroups gmane.comp.programming.garbage-collection.boehmgc
Message-ID <[email protected]>
On 2014-07-16, at 09:00, Richard Brooksby <[email protected]> wrote:

> 2. We believe we have a repro using pointer types. We're working on inducing a failure with it now.

The attached program links with BDWGC 7.4.0 to cause an access violation by reading a prematurely recycled object due to the last reference being optimized away.  This program uses pointers to structures as its object type, and is therefore not bending the rules of the C standard.  It's about 100 lines.  We registered a finalizer to force overwriting of recycled objects to demonstrate the problem.

Compile with Microsoft C 16.00.40219.01 for x64 (comes with Visual Studio 2010) like this:

    cl /Zi /I\path\to\gc-7.4.0\include /O2 update.c \path\to\gc-7.4.0\gc64_dll.lib user32.lib

I'd appreciate it if someone out there would check that they get a repro using this example.

_______________________________________________
bdwgc mailing list
[email protected]
https://lists.opendylan.org/mailman/listinfo/bdwgc
update.c (application/octet-stream, 2.2 KB)
/* update.c -- demonstrate update_tree underscanning with BDWGC
 *
 * See thread <https://lists.opendylan.org/pipermail/bdwgc/2014-July/005981.html>.
 *
 * $Id: //info.ravenbrook.com/user/dl/test/bugs/update-tree-underscanning/update.c#12 $
 */

#include <malloc.h>
#include <stdlib.h>
#include <memory.h>

#define BOEHM
#ifdef BOEHM
#include <gc.h>
#define malloc(x) doalloc(x)
void GC_CALLBACK trashobj(void *p, void *cd) {
  size_t s = (char*)cd - (char*)0;
  memset(p, 0xcc, s);
}
void *doalloc(size_t s) {
  void *p = GC_MALLOC(s);
  GC_REGISTER_FINALIZER(p, trashobj,(char*)0+s, NULL,NULL);
  return p;
}
#endif

double pupdate = 1;
int width = 2;
unsigned depth = 10;

typedef struct info_s *info_t;

typedef struct obj_s obj_s, *obj_t;

struct obj_s {
  info_t info;
  int good;
  obj_t v[1];
};


#define objNULL ((obj_t)0)

obj_t mkvector(info_t info, size_t n) {
  obj_t p = malloc(sizeof(obj_s) + (n-1)*sizeof (obj_t));
  int i;
  p->info = info;
  p->good = 0x900d;
  for(i = 0; i < n; i++)
    p->v[i] = 0;
  return p;
}

static obj_t aref(obj_t obj, size_t i) {
  return obj->v[i];
}

static void aset(obj_t obj, size_t i, obj_t val) {
  obj->v[i] = val;
}

static obj_t mktree(info_t info, unsigned d, obj_t leaf) {
  obj_t tree;
  size_t i;
  if (d <= 0) return leaf;
  tree = mkvector(info, width);
  for (i = 0; i < width; ++i) {
    aset(tree, i, mktree(info, d - 1, leaf));
  }
  return tree;
}

static obj_t update_tree(info_t info, obj_t oldtree, unsigned d) {
  obj_t tree;
  size_t i;
#ifdef KEEP_ROOT
  void * volatile root = oldtree;
#endif
  if (oldtree == objNULL || d == 0) {
    GC_gcollect_and_unmap();
    return oldtree;
  }
  if (info) {
    tree = mkvector(info, width);
    for (i = 0; i < width; ++i) {
      aset(tree, i, update_tree(info, aref(oldtree, i), d - 1));
    }
  } else {
    tree = oldtree;
  }
#ifdef KEEP_ROOT
  /* The following line prevents root from being overwritten keeping a
   * reference to oldtree alive. */
  root = root;
#endif

  return tree;
}

int main(void)
{
  info_t info;
  int i;
  obj_t tree;
  GC_INIT();
  info = malloc(1);
  tree = mktree(info, depth, objNULL);
  tree = update_tree(info, tree, depth);
  return tree == 0;
}