diff options
| author | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-10-15 14:04:30 +0200 |
|---|---|---|
| committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-10-15 14:04:30 +0200 |
| commit | 1335be82768204325323bc9b34601b79c7b47aaf (patch) | |
| tree | 08c6fbf30c877e70c0881b6ba46141bd93c3ed66 | |
| parent | 2f732e9a850f5549b391072cd6dd13ea4b0a8185 (diff) | |
| download | plan9front-1335be82768204325323bc9b34601b79c7b47aaf.tar.xz | |
swap: track swap pages with > 255 references, setswapchan() swapimage.c
swaped pages use a 8bit refcount where as the Page uses a 16bit one.
this might be exploited with having a process having a single page
swaped out and then forking 255 times to make the swap map refcount
overflow and panic the kernel.
this condition is probably very rare. so instead of doubling the
size of the swap map, we add a single 32bit refcount swapalloc.xref
which will keep the combined refcount of all swap map entries who
exceeded 255 references.
zero swapimage.c in setswapchan() after closing it as the stat() call
below might error leaving a dangeling pointer.
| -rw-r--r-- | sys/src/9/port/portdat.h | 1 | ||||
| -rw-r--r-- | sys/src/9/port/swap.c | 46 |
2 files changed, 37 insertions, 10 deletions
diff --git a/sys/src/9/port/portdat.h b/sys/src/9/port/portdat.h index ce3470e74..92e5af666 100644 --- a/sys/src/9/port/portdat.h +++ b/sys/src/9/port/portdat.h @@ -338,6 +338,7 @@ struct Swapalloc Rendez r; /* Pager kproc idle sleep */ ulong highwater; /* Pager start threshold */ ulong headroom; /* Space pager frees under highwater */ + ulong xref; /* Ref count for all map refs >= 255 */ }swapalloc; struct Image diff --git a/sys/src/9/port/swap.c b/sys/src/9/port/swap.c index 49bf67de9..5c1d449bb 100644 --- a/sys/src/9/port/swap.c +++ b/sys/src/9/port/swap.c @@ -40,6 +40,8 @@ swapinit(void) swapalloc.alloc = swapalloc.swmap; swapalloc.last = swapalloc.swmap; swapalloc.free = conf.nswap; + swapalloc.xref = 0; + iolist = xalloc(conf.nswppo*sizeof(Page*)); if(swapalloc.swmap == 0 || iolist == 0) panic("swapinit: not enough memory"); @@ -53,8 +55,7 @@ newswap(void) uchar *look; lock(&swapalloc); - - if(swapalloc.free == 0){ + if(swapalloc.free == 0) { unlock(&swapalloc); return ~0; } @@ -77,22 +78,46 @@ putswap(Page *p) lock(&swapalloc); idx = &swapalloc.swmap[((ulong)p)/BY2PG]; - if(--(*idx) == 0) { - swapalloc.free++; - if(idx < swapalloc.last) - swapalloc.last = idx; + if(*idx == 0) + panic("putswap %#p ref == 0", p); + + if(*idx == 255) { + if(swapalloc.xref == 0) + panic("putswap %#p xref == 0", p); + + if(--swapalloc.xref == 0) { + for(idx = swapalloc.swmap; idx < swapalloc.top; idx++) { + if(*idx == 255) { + *idx = 0; + swapalloc.free++; + if(idx < swapalloc.last) + swapalloc.last = idx; + } + } + } + } else { + if(--(*idx) == 0) { + swapalloc.free++; + if(idx < swapalloc.last) + swapalloc.last = idx; + } } - if(*idx >= 254) - panic("putswap %#p == %ud", p, *idx); unlock(&swapalloc); } void dupswap(Page *p) { + uchar *idx; + lock(&swapalloc); - if(++swapalloc.swmap[((ulong)p)/BY2PG] == 0) - panic("dupswap"); + idx = &swapalloc.swmap[((ulong)p)/BY2PG]; + if(*idx == 255) + swapalloc.xref++; + else { + if(++(*idx) == 255) + swapalloc.xref += 255; + } unlock(&swapalloc); } @@ -413,6 +438,7 @@ setswapchan(Chan *c) error(Einuse); } cclose(swapimage.c); + swapimage.c = nil; } /* |
