Re: X86_64 BUG: missing FS/GS LDT reload on fork()

From: H. Peter Anvin
Date: Fri Apr 23 2010 - 14:02:00 EST


On 04/23/2010 10:04 AM, Samuel Thibault wrote:
> Hello,
>
> I have an issue with FS/GS LDT reload in the child of fork(). The
> attached testcase fails quite often. It sets an LDT entry up, uses
> prctl to set gs's base to a 64bit value, then loads gs with the LDT
> entry. The LDT entry is now in effect. After a fork call, the LDT entry
> is not in effect any more, the 64bit base is back!
>

Okay... I have to say that I'm more than a bit confused why you're doing
this, but the __switch_no code in process_64.c has the following:

/*
* Check if the user used a selector != 0; if yes
* clear 64bit base, since overloaded base is always
* mapped to the Null selector
*/
if (fsindex)
prev->fs = 0;

[and the same for gs]

However, copy_thread() doesn't have the equivalent code, and __switch_to
clearly expects that to be maintained as an invariant -- it doesn't
check on entry, only on exit.

The following patch looks like it should address that.

-hpa
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index dc9690b..17cb329 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -276,12 +276,12 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,

set_tsk_thread_flag(p, TIF_FORK);

- p->thread.fs = me->thread.fs;
- p->thread.gs = me->thread.gs;
p->thread.io_bitmap_ptr = NULL;

savesegment(gs, p->thread.gsindex);
+ p->thread.gs = p->thread.gsindex ? 0 : me->thread.gs;
savesegment(fs, p->thread.fsindex);
+ p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs;
savesegment(es, p->thread.es);
savesegment(ds, p->thread.ds);