~lbnz/xr0

19703647bba9df28d1168857a1fda40347419882 — Amisi Kiarie 7 months ago 681e762 fix/leak-cycle v0.15.2
fix: runaway recursion with pointer cycle

resolves: https://github.com/xr0-org/xr0/issues/46
M include/object.h => include/object.h +5 -2
@@ 39,10 39,13 @@ object_isdeallocand(struct object *, struct state *);
struct location;

bool
object_references(struct object *, struct location *, struct state *);
object_references(struct object *, struct location *, struct state *,
		struct circuitbreaker *);

struct circuitbreaker;

bool
object_referencesheap(struct object *, struct state *);
object_referencesheap(struct object *, struct state *, struct circuitbreaker *);

bool
object_hasvalue(struct object *);

M include/state.h => include/state.h +8 -2
@@ 147,10 147,16 @@ char *
location_str(struct location *);

bool
location_references(struct location *l1, struct location *l2, struct state *);
location_references(struct location *l1, struct location *l2, struct state *,
		struct circuitbreaker *cb);

struct circuitbreaker;

struct circuitbreaker;

bool
location_referencesheap(struct location *, struct state *);
location_referencesheap(struct location *, struct state *,
		struct circuitbreaker *);

struct value *
location_transfigure(struct location *, struct state *compare);

M include/util.h => include/util.h +15 -0
@@ 115,4 115,19 @@ error_get_undecideable_cond(struct error *);
char *
error_str(struct error *);


struct circuitbreaker;

struct circuitbreaker *
circuitbreaker_create();

struct circuitbreaker *
circuitbreaker_copy(struct circuitbreaker *);

void
circuitbreaker_destroy(struct circuitbreaker *);

bool
circuitbreaker_append(struct circuitbreaker *, void *);

#endif

M include/value.h => include/value.h +5 -2
@@ 86,8 86,10 @@ value_islocation(struct value *);
struct location *
value_as_location(struct value *);

struct circuitbreaker;

bool
value_referencesheap(struct value *, struct state *);
value_referencesheap(struct value *, struct state *, struct circuitbreaker *);

bool
value_isconstant(struct value *v);


@@ 111,7 113,8 @@ struct ast_expr *
value_as_literal(struct value *v);

bool
value_references(struct value *, struct location *, struct state *);
value_references(struct value *, struct location *, struct state *,
		struct circuitbreaker *);

enum ast_binary_operator;


M src/object/object.c => src/object/object.c +26 -8
@@ 28,7 28,8 @@ bool
range_isdeallocand(struct range *, struct state *);

bool
range_references(struct range *, struct location *, struct state *);
range_references(struct range *, struct location *, struct state *,
		struct circuitbreaker *cb);


struct object {


@@ 152,13 153,21 @@ inner_str(struct object *obj)
}

bool
object_referencesheap(struct object *obj, struct state *s)
object_referencesheap(struct object *obj, struct state *s,
		struct circuitbreaker *cb)
{
	if (!circuitbreaker_append(cb, obj)) {
		/* already analysed */
		return false;
	}
	if (!object_isvalue(obj)) {
		/* TODO: do we need to exclude the case of ranges on stack? */
		return true;
	}
	return obj->value && value_referencesheap(obj->value, s);
	struct circuitbreaker *copy = circuitbreaker_copy(cb);
	bool ans = obj->value && value_referencesheap(obj->value, s, copy);
	circuitbreaker_destroy(copy);
	return ans;
}

bool


@@ 198,16 207,24 @@ object_isdeallocand(struct object *obj, struct state *s)
}

bool
object_references(struct object *obj, struct location *loc, struct state *s)
object_references(struct object *obj, struct location *loc, struct state *s,
		struct circuitbreaker *cb)
{
	if (!circuitbreaker_append(cb, obj)) {
		/* already handled */
		return false;
	}
	if (obj->type == OBJECT_DEALLOCAND_RANGE) {
		return range_references(obj->range, loc, s);
		return range_references(obj->range, loc, s, cb);
	}

	assert(obj->type == OBJECT_VALUE);

	struct circuitbreaker *copy = circuitbreaker_copy(cb);
	struct value *v = object_as_value(obj);
	return v ? value_references(v, loc, s) : false;
	bool ans = v ? value_references(v, loc, s, copy) : false;
	circuitbreaker_destroy(copy);
	return ans;
}

struct error *


@@ 588,9 605,10 @@ range_isdeallocand(struct range *r, struct state *s)
}

bool
range_references(struct range *r, struct location *loc, struct state *s)
range_references(struct range *r, struct location *loc, struct state *s,
		struct circuitbreaker *cb)
{
	return location_references(r->loc, loc, s);
	return location_references(r->loc, loc, s, cb);
}



M src/state/block.c => src/state/block.c +6 -3
@@ 116,12 116,13 @@ block_observe(struct block *b, struct ast_expr *offset, struct state *s,
}

bool
block_references(struct block *b, struct location *loc, struct state *s)
block_references(struct block *b, struct location *loc, struct state *s,
		struct circuitbreaker *cb)
{
	int n = object_arr_nobjects(b->arr);
	struct object **obj = object_arr_objects(b->arr);
	for (int i = 0; i < n; i++) {
		if (object_references(obj[i], loc, s)) {
		if (object_references(obj[i], loc, s, cb)) {
			return true;
		}
	}


@@ 273,9 274,11 @@ block_undeclare(struct block *b, struct state *s)
	struct object **object = object_arr_objects(b->arr);
	for (int i = 0; i < n; i++) {
		struct object *obj = object[i];
		if (object_referencesheap(obj, s)) {
		struct circuitbreaker *cb = circuitbreaker_create();
		if (object_referencesheap(obj, s, cb)) {
			object_arr_append(new, object_abstractcopy(obj, s));
		}
		circuitbreaker_destroy(cb);
	}
	object_arr_destroy(b->arr);


M src/state/block.h => src/state/block.h +4 -1
@@ 31,8 31,11 @@ block_observe(struct block *, struct ast_expr *offset, struct state *,

struct location;

struct circuitbreaker;

bool
block_references(struct block *, struct location *, struct state *);
block_references(struct block *, struct location *, struct state *,
		struct circuitbreaker *);

struct error *
block_range_alloc(struct block *b, struct ast_expr *lw, struct ast_expr *up,

M src/state/location.c => src/state/location.c +5 -4
@@ 251,14 251,15 @@ location_equal(struct location *l1, struct location *l2)
}

bool
location_references(struct location *l1, struct location *l2, struct state *s)
location_references(struct location *l1, struct location *l2, struct state *s,
		struct circuitbreaker *cb)
{
	if (location_equal(l1, l2)) {
		return true;
	}

	struct block *b = state_getblock(s, l1);
	return b && block_references(b, l2, s);
	return b && block_references(b, l2, s, cb);
}

bool


@@ 268,7 269,7 @@ location_isauto(struct location *loc)
}

bool
location_referencesheap(struct location *l, struct state *s)
location_referencesheap(struct location *l, struct state *s, struct circuitbreaker *cb)
{
	if (l->type == LOCATION_DYNAMIC) {
		if (heap_blockisfreed(state_getheap(s), l->block)) {


@@ 280,7 281,7 @@ location_referencesheap(struct location *l, struct state *s)
	if (res.err) {
		assert(false);
	}
	return res.obj && object_referencesheap(res.obj, s);
	return res.obj && object_referencesheap(res.obj, s, cb);
}

static struct block_res

M src/state/location.h => src/state/location.h +3 -2
@@ 72,7 72,7 @@ bool
location_toclump(struct location *, struct clump *);

bool
location_referencesheap(struct location *, struct state *);
location_referencesheap(struct location *, struct state *, struct circuitbreaker *);

enum location_type
location_type(struct location *loc);


@@ 91,7 91,8 @@ bool
location_equal(struct location *loc1, struct location *loc2);

bool
location_references(struct location *loc1, struct location *loc2, struct state *);
location_references(struct location *loc1, struct location *loc2, struct state *,
		struct circuitbreaker *cb);

struct static_memory;


M src/state/stack.c => src/state/stack.c +4 -1
@@ 403,7 403,10 @@ variable_references(struct variable *v, struct location *loc, struct state *s)
{
	assert(location_type(loc) != LOCATION_VCONST);

	return location_references(v->loc, loc, s);
	struct circuitbreaker *cb = circuitbreaker_create();
	bool ans = location_references(v->loc, loc, s, cb);
	circuitbreaker_destroy(cb);
	return ans;
}

bool

M src/util/util.c => src/util/util.c +43 -0
@@ 428,3 428,46 @@ error_str(struct error *err)
		assert(false);
	}
}

struct circuitbreaker {
	int n;
	void **obj;
};

struct circuitbreaker *
circuitbreaker_create()
{
	return calloc(1, sizeof(struct circuitbreaker));
}

struct circuitbreaker *
circuitbreaker_copy(struct circuitbreaker *old)
{
	struct circuitbreaker *new = malloc(sizeof(struct circuitbreaker));
	new->n = old->n;
	new->obj = malloc(sizeof(void *) * old->n);
	for (int i = 0; i < old->n; i++) {
		new->obj[i] = old->obj[i];
	}
	return new;
}

void
circuitbreaker_destroy(struct circuitbreaker *cb)
{
	free(cb);
}

bool
circuitbreaker_append(struct circuitbreaker *cb, void *obj)
{
	for (int i = 0; i < cb->n; i++) {
		if (cb->obj[i] == obj) {
			return false;
		}
	}
	cb->obj = realloc(cb->obj, sizeof(void *) * ++cb->n);
	assert(cb->obj);
	cb->obj[cb->n-1] = obj;
	return true;
}

M src/value/value.c => src/value/value.c +32 -16
@@ 57,10 57,10 @@ value_ptr_indefinite_create()
	return v;
}

static bool
ptr_referencesheap(struct value *v, struct state *s)
bool
ptr_referencesheap(struct value *v, struct state *s, struct circuitbreaker *cb)
{
	return !v->ptr.isindefinite && location_referencesheap(v->ptr.loc, s);
	return !v->ptr.isindefinite && location_referencesheap(v->ptr.loc, s, cb);
}

struct number *


@@ 423,13 423,13 @@ value_struct_member(struct value *v, char *member)
	return map_get(v->_struct.m, member);
}

static bool
struct_referencesheap(struct value *v, struct state *s)
bool
struct_referencesheap(struct value *v, struct state *s, struct circuitbreaker *cb)
{
	struct map *m = v->_struct.m;
	for (int i = 0; i < m->n; i++) {
		struct value *val = object_as_value((struct object *) m->entry[i].value);
		if (val && value_referencesheap(val, s)) {
		if (val && value_referencesheap(val, s, cb)) {
			return true;
		}
	}


@@ 490,10 490,14 @@ value_copy(struct value *v)
	}
}

static bool
referencesheap_withcb(struct value *, struct state *);


struct value *
value_abstractcopy(struct value *v, struct state *s)
{
	if (!value_referencesheap(v, s)) {
	if (!referencesheap_withcb(v, s)) {
		return NULL;
	}
	switch (v->type) {


@@ 506,6 510,15 @@ value_abstractcopy(struct value *v, struct state *s)
	}
}

static bool
referencesheap_withcb(struct value *v, struct state *s)
{
	struct circuitbreaker *cb = circuitbreaker_create();
	bool ans = value_referencesheap(v, s, cb);
	circuitbreaker_destroy(cb);
	return ans;
}

void
value_destroy(struct value *v)
{


@@ 580,13 593,13 @@ value_as_location(struct value *v)
}

bool
value_referencesheap(struct value *v, struct state *s)
value_referencesheap(struct value *v, struct state *s, struct circuitbreaker *cb)
{
	switch (v->type) {
	case VALUE_PTR:
		return ptr_referencesheap(v, s);
		return ptr_referencesheap(v, s, cb);
	case VALUE_STRUCT:
		return struct_referencesheap(v, s);
		return struct_referencesheap(v, s, cb);
	default:
		return false;
	}


@@ 684,16 697,18 @@ value_type(struct value *v)
}

static bool
struct_references(struct value *v, struct location *loc, struct state *s);
struct_references(struct value *v, struct location *loc, struct state *s,
		struct circuitbreaker *);

bool
value_references(struct value *v, struct location *loc, struct state *s)
value_references(struct value *v, struct location *loc, struct state *s,
		struct circuitbreaker *cb)
{
	switch (v->type) {
	case VALUE_PTR:
		return !v->ptr.isindefinite && location_references(v->ptr.loc, loc, s);
		return !v->ptr.isindefinite && location_references(v->ptr.loc, loc, s, cb);
	case VALUE_STRUCT:
		return struct_references(v, loc, s);
		return struct_references(v, loc, s, cb);
	default:
		/* other kinds of values cannot reference */
		return false;


@@ 701,14 716,15 @@ value_references(struct value *v, struct location *loc, struct state *s)
}

static bool
struct_references(struct value *v, struct location *loc, struct state *s)
struct_references(struct value *v, struct location *loc, struct state *s,
		struct circuitbreaker *cb)
{
	struct map *m = v->_struct.m;
	for (int i = 0; i < m->n; i++) {
		struct value *val = object_as_value(
			(struct object *) m->entry[i].value
		);
		if (val && value_references(val, loc, s)) {
		if (val && value_references(val, loc, s, cb)) {
			return true;
		}
	}

A tests/0-basic/300-FAIL-with-recursive-struct.x => tests/0-basic/300-FAIL-with-recursive-struct.x +26 -0
@@ 0,0 1,26 @@
/*
 * thanks to jorendorff
 * https://github.com/xr0-org/xr0/issues/46
 */

#include <stdlib.h>

struct node {
        struct node *next;
};

struct node *
f() ~ [
	struct node *one;

        one = malloc(sizeof(struct node));
        one->next = one;
        return one;
]{
	struct node *one;

        one = malloc(sizeof(struct node));
        one->next = one;
        malloc(1);
        return one;
}

A tests/0-basic/300-FAIL-with-recursive-struct.x.EXPECTED => tests/0-basic/300-FAIL-with-recursive-struct.x.EXPECTED +1 -0
@@ 0,0 1,1 @@
f: garbage on heap

A tests/0-basic/301-FAIL-with-recursive-struct.x => tests/0-basic/301-FAIL-with-recursive-struct.x +24 -0
@@ 0,0 1,24 @@
/* 
 * thanks to jorendorff
 * https://github.com/xr0-org/xr0/issues/46
 */

#include <stdlib.h>

struct node {
	struct node *next;
};

struct node *
f() ~ [
	struct node one;

	one.next = &one;
        return &one;
]{
	struct node one;

	one.next = &one;
	malloc(1);
        return &one;
}

A tests/0-basic/301-FAIL-with-recursive-struct.x.EXPECTED => tests/0-basic/301-FAIL-with-recursive-struct.x.EXPECTED +1 -0
@@ 0,0 1,1 @@
f: garbage on heap