Skip to content

Commit f96c391

Browse files
authored
Merge pull request #171 from f9micro/memory-leak
Use address space reference counting to fix leak
2 parents ce791fa + b56724b commit f96c391

File tree

6 files changed

+113
-19
lines changed

6 files changed

+113
-19
lines changed

include/memory.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,20 @@ typedef struct {
2323

2424
struct fpage *mpu_first; /*! head of MPU fpage list */
2525
struct fpage *mpu_stack_first; /*! head of MPU stack fpage list */
26-
uint32_t shared; /*! shared user number */
26+
uint32_t shared; /*! Reference count: number of threads using this AS */
2727
} as_t;
2828

29+
/*
30+
* Address space reference counting.
31+
* as_get(): Increment reference count (thread sharing this AS)
32+
* as_put(): Decrement reference count; frees AS when it reaches 0
33+
*
34+
* Thread must be unlinked from AS before calling as_put().
35+
* IRQ protection ensures atomicity of refcount operations.
36+
*/
37+
void as_get(as_t *as);
38+
void as_put(as_t *as);
39+
2940
/**
3041
* Memory pool represents area of physical address space
3142
* We set flags to it (kernel & user permissions),

include/platform/irq.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ static inline void irq_enable(void)
2525
__asm__ __volatile__ ("cpsie i" ::: "memory");
2626
}
2727

28+
/*
29+
* Save and restore interrupt state (PRIMASK).
30+
* Used for critical sections that may be nested or called from
31+
* contexts where IRQ state is unknown.
32+
*/
33+
static inline uint32_t irq_save_flags(void)
34+
{
35+
uint32_t flags;
36+
__asm__ __volatile__ (
37+
"mrs %0, primask\n\t"
38+
"cpsid i"
39+
: "=r" (flags) :: "memory");
40+
return flags;
41+
}
42+
43+
static inline void irq_restore_flags(uint32_t flags)
44+
{
45+
__asm__ __volatile__ (
46+
"msr primask, %0"
47+
:: "r" (flags) : "memory");
48+
}
49+
2850
static inline void irq_svc(void)
2951
{
3052
__asm__ __volatile__ ("svc #0");

include/thread.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ tcb_t *thread_init(l4_thread_t globalid, utcb_t *utcb);
133133
tcb_t *thread_create(l4_thread_t globalid, utcb_t *utcb);
134134
void thread_destroy(tcb_t *thr);
135135
void thread_space(tcb_t *thr, l4_thread_t spaceid, utcb_t *utcb);
136-
void thread_free_space(tcb_t *thr);
137136
void thread_init_ctx(void *sp, void *pc, void *rx, tcb_t *thr);
138137
void thread_init_kernel_ctx(void *sp, tcb_t *thr);
139138
void thread_switch(tcb_t *thr);

kernel/memory.c

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <init_hook.h>
1313
#include <kip.h>
1414
#include <platform/cortex_m.h>
15+
#include <platform/irq.h>
1516

1617
/**
1718
* @file memory.c
@@ -378,31 +379,79 @@ as_t *as_create(uint32_t as_spaceid)
378379

379380
as->as_spaceid = as_spaceid;
380381
as->first = NULL;
381-
as->shared = 0;
382+
as->mpu_first = NULL;
383+
as->mpu_stack_first = NULL;
384+
as->shared = 1; /* Creator holds initial reference */
382385

383386
return as;
384387
}
385388

386-
void as_destroy(as_t *as)
389+
/*
390+
* Increment address space reference count.
391+
* Called when a thread shares an existing address space.
392+
*/
393+
void as_get(as_t *as)
387394
{
388-
fpage_t *fp, *fpnext;
389-
fp = as->first;
395+
uint32_t flags;
390396

391-
if (as->shared > 0) {
392-
--as->shared;
397+
if (!as)
393398
return;
399+
400+
flags = irq_save_flags();
401+
as->shared++;
402+
irq_restore_flags(flags);
403+
}
404+
405+
/*
406+
* Decrement address space reference count.
407+
* Frees the address space when refcount reaches 0.
408+
* IRQs remain disabled through as_destroy() to prevent reentrancy.
409+
*/
410+
void as_put(as_t *as)
411+
{
412+
uint32_t flags;
413+
414+
if (!as)
415+
return;
416+
417+
flags = irq_save_flags();
418+
419+
/* Guard against refcount underflow (double-put bug) */
420+
if (as->shared == 0) {
421+
irq_restore_flags(flags);
422+
dbg_printf(DL_KDB, "AS: refcount underflow for %p\n",
423+
as->as_spaceid);
424+
return;
425+
}
426+
427+
if (--as->shared == 0) {
428+
/* Keep IRQs disabled through destroy */
429+
as_destroy(as);
394430
}
431+
irq_restore_flags(flags);
432+
}
433+
434+
/*
435+
* Free all fpages and the address space structure.
436+
* Called by as_put() when refcount reaches 0.
437+
* Caller must hold IRQs disabled.
438+
*/
439+
void as_destroy(as_t *as)
440+
{
441+
fpage_t *fp, *fpnext;
442+
fp = as->first;
395443

396444
/*
397445
* FIXME: What if a CLONED fpage which is MAPPED is to be deleted
398446
*/
399447
while (fp) {
448+
fpnext = fp->as_next;
449+
400450
if (fp->fpage.flags & FPAGE_CLONE)
401451
unmap_fpage(as, fp);
402452
else
403453
destroy_fpage(fp);
404454

405-
fpnext = fp->as_next;
406455
fp = fpnext;
407456
}
408457

kernel/syscall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static void sys_thread_control(uint32_t *param1, uint32_t *param2)
8181
param1[REG_R0] = 0;
8282
return;
8383
}
84-
thread_free_space(thr);
84+
/* thread_destroy() handles AS refcount via as_put() */
8585
thread_destroy(thr);
8686
param1[REG_R0] = 1;
8787
}

kernel/thread.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ void thread_destroy(tcb_t *thr)
277277
caller->t_child = thr->t_child;
278278
}
279279

280+
/* Release address space reference.
281+
* This may free the AS if this was the last reference.
282+
*/
283+
if (thr->as)
284+
as_put(thr->as);
285+
280286
thread_deinit(thr);
281287
}
282288

@@ -310,10 +316,23 @@ void thread_space(tcb_t *thr, l4_thread_t spaceid, utcb_t *utcb)
310316
dbg_printf(DL_THREAD,
311317
"\tNew space: as: %p, utcb: %p \n", thr->as, utcb);
312318
} else {
313-
tcb_t *space = thread_by_globalid(spaceid);
319+
tcb_t *space;
320+
as_t *shared_as;
314321

315-
thr->as = space->as;
316-
++(space->as->shared);
322+
irq_disable();
323+
space = thread_by_globalid(spaceid);
324+
if (!space || !space->as) {
325+
irq_enable();
326+
dbg_printf(DL_KDB,
327+
"THREAD: space thread %t not found\n",
328+
spaceid);
329+
return;
330+
}
331+
shared_as = space->as;
332+
as_get(shared_as);
333+
irq_enable();
334+
335+
thr->as = shared_as;
317336
}
318337

319338
/* If no caller, than it is mapping from kernel to root thread
@@ -333,12 +352,6 @@ void thread_space(tcb_t *thr, l4_thread_t spaceid, utcb_t *utcb)
333352
utcb, thr->t_globalid);
334353
}
335354

336-
void thread_free_space(tcb_t *thr)
337-
{
338-
/* free address space */
339-
as_destroy(thr->as);
340-
}
341-
342355
void thread_init_ctx(void *sp, void *pc, void *regs, tcb_t *thr)
343356
{
344357
int i;

0 commit comments

Comments
 (0)