summaryrefslogtreecommitdiff
path: root/lib/resource.c
diff options
context:
space:
mode:
authorMaria Matejka <mq@ucw.cz>2023-04-21 15:26:06 +0200
committerMaria Matejka <mq@ucw.cz>2023-04-24 10:33:28 +0200
commit22f54eaee6c6dbe12ad7bb0ee1da09e3e026b970 (patch)
treeeab05c98833ba8b966005aca6c4dd237fb026ec2 /lib/resource.c
parent6230d87c74e3629e21f1e0fe22a874a58302a01e (diff)
Resource pools are now bound with domains.
Memory allocation is a fragile part of BIRD and we need checking that everybody is using the resource pools in an appropriate way. To assure this, all the resource pools are associated with locking domains and every resource manipulation is thoroughly checked whether the appropriate locking domain is locked. With transitive resource manipulation like resource dumping or mass free operations, domains are locked and unlocked on the go, thus we require pool domains to have higher order than their parent to allow for this transitive operations. Adding pool locking revealed some cases of insecure memory manipulation and this commit fixes that as well.
Diffstat (limited to 'lib/resource.c')
-rw-r--r--lib/resource.c139
1 files changed, 100 insertions, 39 deletions
diff --git a/lib/resource.c b/lib/resource.c
index b1b89bdf..2071a411 100644
--- a/lib/resource.c
+++ b/lib/resource.c
@@ -46,6 +46,16 @@ static struct resclass pool_class = {
pool root_pool;
+static void
+rp_init(pool *z, struct domain_generic *dom, const char *name)
+{
+ ASSERT_DIE(DG_IS_LOCKED(dom));
+
+ z->name = name;
+ z->domain = dom;
+ z->inside = (TLIST_LIST(resource)) {};
+}
+
/**
* rp_new - create a resource pool
* @p: parent pool
@@ -55,77 +65,106 @@ pool root_pool;
* parent pool.
*/
pool *
-rp_new(pool *p, const char *name)
+rp_new(pool *p, struct domain_generic *dom, const char *name)
{
pool *z = ralloc(p, &pool_class);
- z->name = name;
- init_list(&z->inside);
+
+ if (dg_order(p->domain) > dg_order(dom))
+ bug("Requested reverse order pool creation: %s (%d) can't be a parent of %s (%d)",
+ domain_name(p->domain), dg_order(p->domain),
+ domain_name(dom), dg_order(dom));
+
+ if ((dg_order(p->domain) == dg_order(dom)) && (p->domain != dom))
+ bug("Requested incomparable order pool creation: %s (%d) can't be a parent of %s (%d)",
+ domain_name(p->domain), dg_order(p->domain),
+ domain_name(dom), dg_order(dom));
+
+ rp_init(z, dom, name);
return z;
}
pool *
-rp_vnewf(pool *p, const char *fmt, va_list args)
+rp_vnewf(pool *p, struct domain_generic *dom, const char *fmt, va_list args)
{
- pool *z = rp_new(p, NULL);
+ pool *z = rp_new(p, dom, NULL);
z->name = mb_vsprintf(p, fmt, args);
return z;
}
pool *
-rp_newf(pool *p, const char *fmt, ...)
+rp_newf(pool *p, struct domain_generic *dom, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- pool *z = rp_vnewf(p, fmt, args);
+ pool *z = rp_vnewf(p, dom, fmt, args);
va_end(args);
return z;
}
+#define POOL_LOCK \
+ struct domain_generic *dom = p->domain; \
+ int locking = !DG_IS_LOCKED(dom); \
+ if (locking) \
+ DG_LOCK(dom); \
+
+#define POOL_UNLOCK if (locking) DG_UNLOCK(dom);\
+
+void rp_free(pool *p)
+{
+ ASSERT_DIE(DG_IS_LOCKED(p->domain));
+ rfree(p);
+}
static void
pool_free(resource *P)
{
pool *p = (pool *) P;
- resource *r, *rr;
- r = HEAD(p->inside);
- while (rr = (resource *) r->n.next)
+ POOL_LOCK;
+ WALK_TLIST_DELSAFE(resource, r, &p->inside)
{
r->class->free(r);
xfree(r);
- r = rr;
}
+ POOL_UNLOCK;
}
+
static void
pool_dump(resource *P, unsigned indent)
{
pool *p = (pool *) P;
- resource *r;
+
+ POOL_LOCK;
debug("%s\n", p->name);
- WALK_LIST(r, p->inside)
+ WALK_TLIST_DELSAFE(resource, r, &p->inside)
rdump(r, indent + 3);
+
+ POOL_UNLOCK;
}
static struct resmem
pool_memsize(resource *P)
{
pool *p = (pool *) P;
- resource *r;
struct resmem sum = {
.effective = 0,
.overhead = sizeof(pool) + ALLOC_OVERHEAD,
};
- WALK_LIST(r, p->inside)
+ POOL_LOCK;
+
+ WALK_TLIST(resource, r, &p->inside)
{
struct resmem add = rmemsize(r);
sum.effective += add.effective;
sum.overhead += add.overhead;
}
+ POOL_UNLOCK;
+
return sum;
}
@@ -133,14 +172,25 @@ static resource *
pool_lookup(resource *P, unsigned long a)
{
pool *p = (pool *) P;
- resource *r, *q;
+ resource *q = NULL;
+
+ POOL_LOCK;
- WALK_LIST(r, p->inside)
+ WALK_TLIST(resource, r, &p->inside)
if (r->class->lookup && (q = r->class->lookup(r, a)))
- return q;
- return NULL;
+ break;
+
+ POOL_UNLOCK;
+ return q;
+}
+
+static pool *
+resource_parent(resource *r)
+{
+ return SKIP_BACK(pool, inside, resource_enlisted(r));
}
+
/**
* rmove - move a resource
* @res: resource
@@ -152,13 +202,13 @@ pool_lookup(resource *P, unsigned long a)
void rmove(void *res, pool *p)
{
resource *r = res;
+ pool *orig = resource_parent(r);
- if (r)
- {
- if (r->n.next)
- rem_node(&r->n);
- add_tail(&p->inside, &r->n);
- }
+ ASSERT_DIE(DG_IS_LOCKED(orig->domain));
+ ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
+ resource_rem_node(&orig->inside, r);
+ resource_add_tail(&p->inside, r);
}
/**
@@ -179,8 +229,10 @@ rfree(void *res)
if (!r)
return;
- if (r->n.next)
- rem_node(&r->n);
+ pool *orig = resource_parent(r);
+ ASSERT_DIE(DG_IS_LOCKED(orig->domain));
+ resource_rem_node(&orig->inside, r);
+
r->class->free(r);
r->class = NULL;
xfree(r);
@@ -239,12 +291,14 @@ rmemsize(void *res)
void *
ralloc(pool *p, struct resclass *c)
{
+ ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
resource *r = xmalloc(c->size);
bzero(r, c->size);
r->class = c;
- if (p)
- add_tail(&p->inside, &r->n);
+ resource_add_tail(&p->inside, r);
+
return r;
}
@@ -284,28 +338,29 @@ resource_init(void)
rcu_init();
resource_sys_init();
- root_pool.r.class = &pool_class;
- root_pool.name = "Root";
- init_list(&root_pool.inside);
- tmp_init(&root_pool);
+ rp_init(&root_pool, the_bird_domain.the_bird, "Root");
+ tmp_init(&root_pool, the_bird_domain.the_bird);
}
_Thread_local struct tmp_resources tmp_res;
void
-tmp_init(pool *p)
+tmp_init(pool *p, struct domain_generic *dom)
{
tmp_res.lp = lp_new_default(p);
tmp_res.parent = p;
- tmp_res.pool = rp_new(p, "TMP");
+ tmp_res.pool = rp_new(p, dom, "TMP");
+ tmp_res.domain = dom;
}
void
tmp_flush(void)
{
+ ASSERT_DIE(DG_IS_LOCKED(tmp_res.domain));
+
lp_flush(tmp_linpool);
rp_free(tmp_res.pool);
- tmp_res.pool = rp_new(tmp_res.parent, "TMP");
+ tmp_res.pool = rp_new(tmp_res.parent, tmp_res.domain, "TMP");
}
@@ -385,11 +440,13 @@ static struct resclass mb_class = {
void *
mb_alloc(pool *p, unsigned size)
{
+ ASSERT_DIE(DG_IS_LOCKED(p->domain));
+
struct mblock *b = xmalloc(sizeof(struct mblock) + size);
b->r.class = &mb_class;
- b->r.n = (node) {};
- add_tail(&p->inside, &b->r.n);
+ b->r.n = (struct resource_node) {};
+ resource_add_tail(&p->inside, &b->r);
b->size = size;
return b->data;
}
@@ -434,10 +491,14 @@ void *
mb_realloc(void *m, unsigned size)
{
struct mblock *b = SKIP_BACK(struct mblock, data, m);
+ struct pool *p = resource_parent(&b->r);
+
+ ASSERT_DIE(DG_IS_LOCKED(p->domain));
b = xrealloc(b, sizeof(struct mblock) + size);
- update_node(&b->r.n);
b->size = size;
+
+ resource_update_node(&p->inside, &b->r);
return b->data;
}