Messages in this thread Patch in this message | | | Date | Sun, 9 Feb 1997 15:04:44 +0100 (MET) | From | Hans Lermen <> | Subject | PATCH, FIX for: Reliable OOPS'es on 2.0.28/2.1.25 after modify_ldt |
| |
Hi Linus, Alan,
The below two patches fix a problem in ldt.c, that was reported by Ion Badulescu <ionut@moisil.wal.rhno.columbia.edu> and was traced down by Ramon <ramon@atlante.ieeesb.etsit.upm.es>:
Ramon <ramon@atlante.ieeesb.etsit.upm.es> wrote: > > Wine tells the operating system to free a selector that is being used > in the %fs register. The kernel does it, but when trying to restore > the registers to return from the system call, the processor generates > a expection since it does not like place an unused selector in the > %fs register. >
Well, of course Wine could fix the problem, but the kernel shouldn't oops anyway. The below patches were tested by Ion Badulescu and Wine seems happy with the -EBUSY.
The 2.1.x patch also is needed, because (though the kernel doesn't use %fs/%gs) the oops may happen on schedule.
Hans <lermen@fgan.de>
-------------------------------FOR 2.0.x !!! ---------------------------- --- linux-2.0.28/arch/i386/kernel/ldt.c Tue Jan 14 21:08:40 1997 +++ linux/arch/i386/kernel/ldt.c Sun Feb 9 14:28:50 1997 @@ -11,6 +11,7 @@ #include <asm/segment.h> #include <asm/system.h> #include <linux/ldt.h> +#include <asm/ptrace.h> static int read_ldt(void * ptr, unsigned long bytecount) { @@ -59,7 +60,7 @@ return (last >= first && last < TASK_SIZE); } -static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) +static int write_ldt(struct pt_regs * regs, void * ptr, unsigned long bytecount, int oldmode) { struct modify_ldt_ldt_s ldt_info; unsigned long *lp; @@ -101,6 +102,9 @@ && ldt_info.limit_in_pages == 0 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0 )) ) { + unsigned short sel =(ldt_info.entry_number <<3) | 7; + if (regs->fs == sel || regs->gs == sel) + return -EBUSY; *lp = 0; *(lp+1) = 0; return 0; @@ -125,8 +129,8 @@ if (func == 0) return read_ldt(ptr, bytecount); if (func == 1) - return write_ldt(ptr, bytecount, 1); + return write_ldt((struct pt_regs *) &func, ptr, bytecount, 1); if (func == 0x11) - return write_ldt(ptr, bytecount, 0); + return write_ldt((struct pt_regs *) &func, ptr, bytecount, 0); return -ENOSYS; } ------------------------------------------------------------------------- -------------------------------FOR 2.1.x !!! ---------------------------- --- linux-2.1.25/arch/i386/kernel/ldt.c Fri Jan 31 10:34:54 1997 +++ linux/arch/i386/kernel/ldt.c Fri Feb 7 12:29:40 1997 @@ -60,6 +60,7 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) { +#define SEGREG(x) ({ unsigned short ret;__asm__("movw %%" #x ",%0": "=r" (ret)); ret;}) struct modify_ldt_ldt_s ldt_info; unsigned long *lp; int error, i; @@ -98,6 +99,9 @@ && ldt_info.limit_in_pages == 0 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0 )) ) { + unsigned short seg = (ldt_info.entry_number << 3) | 7; + if (SEGREG(fs) == seg || SEGREG(gs) == seg) + return -EBUSY; *lp = 0; *(lp+1) = 0; return 0; -------------------------------------------------------------------------
| |