Hi,
while implementing TLS support for linuxemu in Plan9 pc kernel i
learned that loading segment registers referencing bogus descriptors
generates a GP fault... in the noted() function from pc/trap.c here
is a comment and code, that kills the program if a note-handler tries
to change segment registers to something other than the user data
segment.
found, that you can crash the kernel by writing bogus segment
registers via devproc regs file (/proc/$pid/regs). devproc overrides
the (saved) registers of a process by calling setregisters() in
pc/trap.c. this function takes care of not changing the segment
registers cs and ss for exact that reason, but on the other hand
allows modification of ds, es, fs and gs what can result in a
kernelpanic too when forkret() in pc/l.s tries to load/restore them.
is here a special reason for allowing change of that registers?
otherwhise maybe handle them like cs and ss?
void
setregisters(Ureg* ureg, char* pureg, char* uva, int n)
{
ulong flags;
ulong cs;
ulong ss;
+ ulong ds, es, fs, gs;
flags = ureg->flags;
cs = ureg->cs;
ss = ureg->ss;
+ ds = ureg->ds;
+ es = ureg->es;
+ fs = ureg->fs;
+ gs = ureg->gs;
memmove(pureg, uva, n);
ureg->flags = (ureg->flags & 0x00FF) | (flags & 0xFF00);
ureg->cs = cs;
ureg->ss = ss;
+ ureg->ds = ds;
+ ureg->es = es;
+ ureg->fs = fs;
+ ureg->gs = gs;
}
i attached a test program to this email to reproduce the thing.
cinap
#include <u.h>
#include <libc.h>
#include <ureg.h>
void main(int argc, char *argv[])
{
int fd;
char name[64];
struct Ureg reg;
snprint(name, sizeof(name), "/proc/%d/regs", getpid());
if((fd = open(name, ORDWR)) < 0){
fprint(2, "cant open my regs: %r\n");
exits("open");
}
if(read(fd, ®, sizeof(reg)) < 0){
fprint(2, "read of regs failed: %r\n");
exits("read");
}
reg.gs = 666; /* some shit */
if(write(fd, ®, sizeof(reg)) < 0){
fprint(2, "write of regs failed: %r\n");
exits("write");
}
fprint(2, "fixed!\n");
close(fd);
}