[LTP] [PATCH] lapi/tls: reserve pre-TCB space to avoid undefined behavior in clone10.c
Changwei Zou
changwei.zou@canonical.com
Mon Feb 9 12:20:03 CET 2026
Hi Petr,
With the original upstream LTP,
I ran clone10 -i 1000 on three machines (including AArch64 and AMD64),
and it failed on all of them.
This suggests there may be another bug that we still need to identify.
Thank you very much for your invaluable information.
Kind regards,
Changwei
*1. On an AArch64 cloud instance*
```sh
azure@clone10-aarch64-kcp:~/orig/ltp$
./testcases/kernel/syscalls/clone/clone10-i1000
clone10.c:68: TPASS:Parent(PID: 106163,TID:106163): TLS value correct: 100
clone10.c:48: TINFO:Child(PID: 106163,TID:106200): TLS value set to: 101
tst_test.c:1953: TBROK:TestkilledbySIGBUS!
Summary:
passed 36
failed 0
broken 1
skipped 0
warnings 0
```
*2. On an AMD64 machine*
```sh
ubuntu@ZBook:~/orig/ltp$ ./testcases/kernel/syscalls/clone/clone10-i1000
clone10.c:48: TINFO:Child(PID: 125560,TID:125870): TLS value set to: 101
clone10.c:68: TPASS:Parent(PID: 125560,TID:125560): TLS value correct: 100
double freeorcorruption(out)
clone10.c:48: TINFO:Child(PID: 125560,TID:125871): TLS value set to: 101
clone10.c:68: TPASS:Parent(PID: 125560,TID:125560): TLS value correct: 100
tst_test.c:1953: TBROK:TestkilledbySIGIOT/SIGABRT!
Summary:
passed 311
failed 0
broken 1
skipped 0
warnings 0
```
*3. On an AArch64 machine*
```sh
ubuntu@asus-pe100a:~/orig/ltp$
./testcases/kernel/syscalls/clone/clone10-i1000
clone10.c:68: TPASS:Parent(PID: 158953,TID:158953): TLS value correct: 100
clone10.c:48: TINFO:Child(PID: 158953,TID:159029): TLS value set to: 101
tst_test.c:1953: TBROK:TestkilledbySIGSEGV!
Summary:
passed 75
failed 0
broken 1
skipped 0
warnings 0
```
On 2/9/26 18:51, Petr Vorel wrote:
> Hi Changwei,
>
>> Allocate extra space before the TLS area to hold a struct pthread, ensuring
>> THREAD_SELF->cancelhandling is initialized to 0. This prevents undefined
>> behavior in __pthread_disable_asynccancel(), which is called at thread
>> cancellation points such as write().
>> Without this, touch_tls_in_child() could get stuck in tst_res().
> LGTM, but I'd prefer others had a look on it.
> Acked-by: Petr Vorel<pvorel@suse.cz>
>
> BTW clone10.c segfaults w/a the patch when run with more iterations:
>
> ./clone10 -i200
> clone10.c:48: TINFO: Child (PID: 4271, TID: 4285): TLS value set to: 101
> clone10.c:68: TPASS: Parent (PID: 4271, TID: 4271): TLS value correct: 100
> clone10.c:48: TINFO: Child (PID: 4271, TID: 4286): TLS value set to: 101
> clone10.c:68: TPASS: Parent (PID: 4271, TID: 4271): TLS value correct: 100
> tst_test.c:1953: TBROK: Test killed by SIGSEGV!
>
> Summary:
> passed 15
> failed 0
> broken 1
> skipped 0
> warnings 0
>
> Kind regards,
> Petr
>
>> (gdb) bt
>> 0 futex_wait () at ../sysdeps/nptl/futex-internal.h:141
>> 1 futex_wait_simple () at ../sysdeps/nptl/futex-internal.h:172
>> 2 __libc_disable_asynccancel () at ../nptl/cancellation.c:100
>> 3 __GI___libc_write () at ../sysdeps/unix/sysv/linux/write.c:26
>> 4 __GI___libc_write () at ../sysdeps/unix/sysv/linux/write.c:24
>> 5 print_result () at tst_test.c:387
>> 6 tst_vres_ () at tst_test.c:401
>> 7 tst_res_ () at tst_test.c:512
>> 8 touch_tls_in_child (arg=<optimized out>) atclone10.c:48
>> 9 thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78
>> Signed-off-by: Changwei Zou<changwei.zou@canonical.com>
>> ---
>> include/lapi/tls.h | 16 +++++++++++++---
>> 1 file changed, 13 insertions(+), 3 deletions(-)
>> diff --git a/include/lapi/tls.h b/include/lapi/tls.h
>> index 468fe3086..7f2fa18a1 100644
>> --- a/include/lapi/tls.h
>> +++ b/include/lapi/tls.h
>> @@ -22,6 +22,15 @@
>> #define TLS_SIZE 4096
>> #define TLS_ALIGN 16
>> +/*
>> + * Space allocated large enough to hold a struct pthread.
>> + *
>> + * Zero-initialized to ensure THREAD_SELF->cancelhandling starts at 0,
>> + * avoiding undefined behavior (e.g., in clone10.c) in __pthread_disable_asynccancel(),
>> + * which is called at thread cancellation points such as write().
>> + */
>> +#define TLS_PRE_TCB_SIZE (TLS_ALIGN * 256)
>> +
>> #if defined(__x86_64__)
>> typedef struct {
>> void *tcb;
>> @@ -36,10 +45,11 @@ extern void *tls_ptr;
>> static inline void *allocate_tls_area(void)
>> {
>> - void *tls_area = aligned_alloc(TLS_ALIGN, TLS_SIZE);
>> + char *tls_area = aligned_alloc(TLS_ALIGN, TLS_PRE_TCB_SIZE + TLS_SIZE);
>> if (!tls_area)
>> tst_brk(TBROK | TERRNO, "aligned_alloc failed");
>> - memset(tls_area, 0, TLS_SIZE);
>> + memset(tls_area, 0, TLS_PRE_TCB_SIZE + TLS_SIZE);
>> + tls_area += TLS_PRE_TCB_SIZE;
>> #if defined(__x86_64__)
>> tcb_t *tcb = (tcb_t *)tls_area;
>> @@ -59,7 +69,7 @@ static inline void free_tls(void)
>> {
>> usleep(10000);
>> if (tls_ptr) {
>> - free(tls_ptr);
>> + free(((char *)tls_ptr) - TLS_PRE_TCB_SIZE);
>> tls_ptr = NULL;
>> }
>> }
More information about the ltp
mailing list