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.
This commit is contained in:
parent
2f732e9a85
commit
1335be8276
2 changed files with 37 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue