[LTP] clone10.c failed on 32bit compilation

Wei Gao wegao@suse.com
Wed Jan 21 05:09:58 CET 2026


On Tue, Jan 20, 2026 at 08:58:05PM +0800, Li Wang wrote:
> Wei Gao <wegao@suse.com> wrote:
> 
> > Try following patch firslty still got EINVAL since tls_desc->entry_number will be -1 still go same error.
> >
> > diff --git a/include/lapi/tls.h b/include/lapi/tls.h
> > index a067872e0..aedc907d9 100644
> > --- a/include/lapi/tls.h
> > +++ b/include/lapi/tls.h
> > @@ -73,6 +73,7 @@ static inline void init_tls(void)
> >         tls_desc->limit_in_pages = 0;
> >         tls_desc->seg_not_present = 0;
> >         tls_desc->useable = 1;
> > +       tls_ptr = tls_desc;
> >
> >  #else
> >         tst_brk(TCONF, "Unsupported architecture for TLS");
> >
> > so i try to change entry_number to correct one base kernel src logic.
> > diff --git a/include/lapi/tls.h b/include/lapi/tls.h
> > index a067872e0..9e69244da 100644
> > --- a/include/lapi/tls.h
> > +++ b/include/lapi/tls.h
> > @@ -64,7 +64,7 @@ static inline void init_tls(void)
> >         tls_ptr = allocate_tls_area();
> >         tls_desc = SAFE_MALLOC(sizeof(*tls_desc));
> >         memset(tls_desc, 0, sizeof(*tls_desc));
> > -       tls_desc->entry_number = -1;
> > +       tls_desc->entry_number = 13;
> >         tls_desc->base_addr = (unsigned long)tls_ptr;
> >         tls_desc->limit = TLS_SIZE;
> >         tls_desc->seg_32bit = 1;
> >
> > Now i get following output, no EINVAL now but it seems child and parent point to same tls area.
> > tst_tmpdir.c:316: TINFO: Using /tmp/LTP_cloa20awq as tmpdir (tmpfs filesystem)
> > tst_test.c:2025: TINFO: LTP version: 20250130-546-g13dbd838c
> > tst_test.c:2028: TINFO: Tested kernel: 6.19.0-rc5-gb71e635feefc #11 SMP PREEMPT_DYNAMIC Tue Jan 13 16:16:15 CST 2026 x86_64
> > tst_kconfig.c:71: TINFO: Couldn't locate kernel config!
> > tst_test.c:1846: TINFO: Overall timeout per run is 0h 00m 30s
> > clone10.c:48: TINFO: Child (PID: 5262, TID: 5263): TLS value set to: 101
> > clone10.c:72: TFAIL: Parent (PID: 5262, TID: 5262): TLS value mismatch: got 101, expected 100
> 
> Well, this indicates that the Child does not switch to itself TLS
> correctly, still use the parent, and so the '__thread tls_var' value
> gets modified.
> 
> With two days of research, I roughly drew the picture of the possible
> reason as below:
> 
> Using a naked clone() to verify that CLONE_SETTLS is unreliable
> when running 32-bit on x86_64, since i386 TLS requires two steps:
> writing the descriptor and switching the selector, but CLONE_SETTLS
> only overrides the former:
> 
> This is the simplified call chain:
> 
> kernel_clone()
>    copy_process()
>       copy_thread()
>           set_new_tls()
>              do_set_thread_area()
> 
> In copy_thread(), the child’s register frame is copied from the parent
> *childregs = *current_pt_regs(); and on the 32-bit side it also does
> savesegment(gs, p->thread.gs); saving the current %gs into thread_struct.
> 
> Together, this means that unless something explicitly overwrites it later,
> the child’s initial %gs selector is inherited from the parent.
> 
>   https://elixir.bootlin.com/linux/v6.18/source/arch/x86/kernel/process.c#L243
> 
> Then, in do_set_thread_area(), the kernel updates the TLS descriptor
> set_tls_desc(p, idx, &info, 1); However, when (p != current), the x86_32 path
> does not update or refresh any segment selector. So it updates the descriptor
> but does not switch the child’s %gs selector to the new modified_sel.
> 
>   https://elixir.bootlin.com/linux/v6.18/source/arch/x86/kernel/tls.c#L150
> 
> Therefore, relying on CLONE_SETTLS alone can leave the child executing
> with the parent’s %gs selector, so TLS accesses still resolve to the
> old TLS base.
> 
> In summary, if this analysis is make sense, we need to configure the TCONF
> test branch skip on i386.

Your analysis is correct when we use 13 as tls_desc->entry_number. If we
not change kernel logic (force switch child's %gs to poing gdt 13 entry then
we can not test this feature).

So correct solution is not create new GDT TLS entry but reuse exist
entry, by default parent will use GDT_ENTRY_TLS_MIN which number is 12.
So we clone with tls_desc->entry_number to 12 and ONLY change
tls_desc->base_addr, when switch to child %gs still same but GDT
number 12 entry already updated by new base_addr. But now i encounter strange 
SIGSEGV error when switch to child, no idea why.

BTW: if we use pure 32bit we should resuse entry_number to 6, so basicly
in code we should first use __NR_get_thread_area get currently using
entry_number.

I agree we can skip test on i386 firstly since we are currently still not support
in this test case.

> 
> -- 
> Regards,
> Li Wang
> 


More information about the ltp mailing list