[LTP] [PATCH v5] Add tls parameter and flag:CLONE_SETTLS cover for clone and clone3 syscall
Li Wang
liwang@redhat.com
Tue May 6 12:45:43 CEST 2025
Hi Chunfu,
On Tue, Apr 29, 2025 at 4:50 PM chunfuwen via ltp <ltp@lists.linux.it>
wrote:
> tls parameter and related flag:CLONE_SETTLS are missed in the testing,
> so add them into existed test case
>
> Signed-off-by: chunfuwen <chwen@redhat.com>
> ---
> Changes in v5:
> - wrap duplicate code into one single methold
> - remove duplicately malloc
>
> Changes in v4:
> - remove riscv and loongarch definition
>
> Changes in v3:
> - fix missing head file for x86
>
> Changes in v2:
> - create separate files for clone and clone3
>
> ---
> testcases/kernel/syscalls/clone/clone10.c | 172 ++++++++++++++++++++
> testcases/kernel/syscalls/clone3/clone304.c | 152 +++++++++++++++++
>
We need to update .gitignore and runtest file as long as we add a new test.
> 2 files changed, 324 insertions(+)
> create mode 100644 testcases/kernel/syscalls/clone/clone10.c
> create mode 100644 testcases/kernel/syscalls/clone3/clone304.c
>
> diff --git a/testcases/kernel/syscalls/clone/clone10.c
> b/testcases/kernel/syscalls/clone/clone10.c
> new file mode 100644
> index 000000000..475bb2ece
> --- /dev/null
> +++ b/testcases/kernel/syscalls/clone/clone10.c
> @@ -0,0 +1,172 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Red Hat Inc. All Rights Reserved.
> + * Author: Chunfu Wen <chwen@redhat.com>
> + */
> +
> +/*\
> + * Add tls parameter and flag:CLONE_SETTLS cover for clone
> + */
> +
> +#define _GNU_SOURCE
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <sched.h>
> +#include <sys/wait.h>
> +#if defined(__i386__)
> +#include <asm/ldt.h>
> +#endif
> +
> +#include "tst_test.h"
> +#include "clone_platform.h"
> +#include "lapi/syscalls.h"
> +#include "lapi/futex.h"
> +
> +#define TLS_SIZE 4096
> +#define TLS_ALIGN 16
> +
> +static pid_t ptid, ctid;
> +static void *tls_ptr;
> +static void *child_stack;
> +static struct user_desc *tls_desc;
> +
> +/* TLS variable to validate in child */
> +static __thread int tls_test_var = 12345;
> +
> +static int test_flags[] = {
> + CLONE_SETTLS,
> + CLONE_PARENT | CLONE_SETTLS,
> + CLONE_CHILD_SETTID | CLONE_SETTLS,
> + CLONE_PARENT_SETTID | CLONE_VM | CLONE_SETTLS,
> + CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_CLEARTID |
> CLONE_SETTLS | SIGCHLD,
> + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_VFORK | CLONE_SIGHAND |
> CLONE_SETTLS,
> +};
>
This does not look correct, from what I know, CLONE_SETTLS usually with
CLONE_VM, CONE_FS, CONE_FILES, CLONE_SIGHAND, CLONE_THREAD
in using, and it must have CLONE_THREAD | CLONE_VM | CLONE_SIGHAND
as the minimal combination.
> +
> +static void *allocate_tls_area(void)
> +{
> + void *tls_area = aligned_alloc(TLS_ALIGN, TLS_SIZE);
> + if (!tls_area) {
> + perror("aligned_alloc");
> + exit(EXIT_FAILURE);
> + }
> + memset(tls_area, 0, TLS_SIZE);
> + return tls_area;
> +}
> +
> +static void init_tls(void)
> +{
> +#if defined(__x86_64__)
> + /* x86-64: tls_ptr is the new %fs base address */
> + tls_ptr = allocate_tls_area();
> +
> +#elif defined(__i386__)
> + /* x86 32-bit: TLS is a struct user_desc */
> + 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->base_addr = (unsigned long)tls_ptr;
> + tls_desc->limit = TLS_SIZE;
> + tls_desc->seg_32bit = 1;
> + tls_desc->contents = 0;
> + tls_desc->read_exec_only = 0;
> + tls_desc->limit_in_pages = 0;
> + tls_desc->seg_not_present = 0;
> + tls_desc->useable = 1;
> +#elif defined(__aarch64__) || defined(__powerpc64__) || defined(__s390x__)
>
The code below is the same as x86_64, why not combine them?
> + /*
> + * Other architectures: detect if dedicated TLS register is
> available.
> + * You don't manually touch TPIDR_EL0.
> + * The kernel automatically writes it into the TPIDR_EL0 register
> for the new thread after clone() succeeds.
> + */
> + tls_ptr = allocate_tls_area();
> +#else
> + tst_brk(TCONF, "This architecture does not support TLS");
> +#endif
> +}
> +
> +static void free_tls(void)
> +{
> +#if defined(__x86_64__)
> + free(tls_ptr);
> +
> +#elif defined(__i386__)
> + if (tls_desc) {
> + free((void *)(uintptr_t)tls_desc->base_addr);
> + free(tls_desc);
> + }
> +
> +#elif defined(__aarch64__) || defined(__powerpc64__) || defined(__s390x__)
> + free(tls_ptr);
> +#endif
> +}
> +
> +static void setup(void)
> +{
> + child_stack = SAFE_MALLOC(CHILD_STACK_SIZE);
> + init_tls();
> +}
> +
> +static void cleanup(void)
> +{
> + free(child_stack);
> + free_tls();
> +}
> +
> +static int check_child(void *arg LTP_ATTRIBUTE_UNUSED)
>
Or, rename it to check_in_child sounds better?
> +{
> + if (tls_test_var == 12345)
> + tst_res(TPASS, "Child TLS variable has expected value");
> + else
> + tst_res(TFAIL, "Child TLS variable corrupted or not set");
> +
> + tst_syscall(__NR_exit, 0);
>
Why not exit(0) directly?
> + return 0;
>
It never had a chance to come here, isn't it?
> +}
> +
> +static long clone_child(int flags)
> +{
> + TEST(ltp_clone7(flags, check_child, NULL, CHILD_STACK_SIZE,
> + child_stack, &ptid, tls_ptr, &ctid));
> +
>
> + if (TST_RET == -1 && TTERRNO == ENOSYS)
> + tst_brk(TCONF, "clone parameters doesn't match");
>
The minimal supported kernel version is 4.4 for LTP, so we can drop those
two lines safely.
> +
> + if (TST_RET == -1)
> + tst_brk(TBROK | TTERRNO, "clone() failed");
> +
> + return TST_RET;
+}
> +
> +static void verify_tls(unsigned int i)
> +{
> + pid_t child;
> + ctid = -1;
> + struct timespec timeout = { 5 /* sec */, 0 };
> + child = SAFE_FORK();
> + if (!child) {
>
> + tst_syscall(__NR_getpid);
>
Useless code, plz remove it.
+ clone_child(test_flags[i]);
> + if (test_flags[i] & CLONE_THREAD) {
> + if (syscall(SYS_futex, &ctid, FUTEX_WAIT, -1,
> &timeout)) {
> + if (errno != EWOULDBLOCK || ctid == -1) {
> + tst_res(TFAIL | TERRNO,
> + "futex failed, ctid: %d",
> ctid);
> + exit(0);
> + }
> + }
> + }
>
Those nesting not elegant, we'd better avoid nesting more than 3 times.
> + exit(0);
> + }
>
> + sleep(1);
>
Any reason for sleeping 1 second here?
> + tst_reap_children();
>
We need to report test result in the main loop, otherwise:
# ./clone10
tst_test.c:1903: TINFO: LTP version: 20250130
tst_test.c:1907: TINFO: Tested kernel: 6.14.4-300.fc42.x86_64 #1 SMP
PREEMPT_DYNAMIC Fri Apr 25 15:43:38 UTC 2025 x86_64
tst_kconfig.c:88: TINFO: Parsing kernel config
'/lib/modules/6.14.4-300.fc42.x86_64/build/.config'
tst_test.c:1720: TINFO: Overall timeout per run is 0h 00m 30s
tst_test.c:1572: TBROK: Test 0 haven't reported results!
Summary:
passed 0
failed 0
broken 1
skipped 0
warnings 0
> +}
> +
> +static struct tst_test test = {
> + .tcnt = ARRAY_SIZE(test_flags),
> + .test = verify_tls,
> + .setup = setup,
> + .cleanup = cleanup,
> + .forks_child = 1
> +};
> diff --git a/testcases/kernel/syscalls/clone3/clone304.c
> b/testcases/kernel/syscalls/clone3/clone304.c
> new file mode 100644
> index 000000000..6d307843b
> --- /dev/null
> +++ b/testcases/kernel/syscalls/clone3/clone304.c
> @@ -0,0 +1,152 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Red Hat Inc. All Rights Reserved.
> + * Author: Chunfu Wen <chwen@redhat.com>
> + */
> +
> +/*\
> + * Add tls parameter and flag:CLONE_SETTLS cover for clone3
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <stdlib.h>
> +#include <sys/wait.h>
> +#if defined(__i386__)
> +#include <asm/ldt.h>
> +#endif
> +
> +#include "tst_test.h"
> +#include "lapi/sched.h"
> +#include "lapi/pidfd.h"
> +
> +#define TLS_SIZE 4096
> +#define TLS_ALIGN 16
> +
> +static int pidfd, child_tid, parent_tid;
> +static struct clone_args *args;
> +static void *tls_ptr;
> +static struct user_desc *tls_desc;
> +
> +/* TLS variable to validate in child */
> +static __thread int tls_test_var = 54321;
> +
> +static int test_flags[] = {
> + CLONE_FS | CLONE_SETTLS,
> + CLONE_NEWPID | CLONE_SETTLS,
> +};
> +
> +static void *allocate_tls_region(void)
> +{
> + void *tls_area = aligned_alloc(TLS_ALIGN, TLS_SIZE);
> + if (!tls_area) {
> + perror("aligned_alloc");
> + exit(EXIT_FAILURE);
> + }
> + memset(tls_area, 0, TLS_SIZE);
> + return tls_area;
> +}
> +
> +static void initialize_tls(void)
> +{
> +#if defined(__x86_64__)
> + /* x86-64: tls_ptr is the new %fs base address */
> + tls_ptr = allocate_tls_region();
> +
> +#elif defined(__i386__)
> + /* x86 32-bit: TLS is a struct user_desc */
> + tls_ptr = allocate_tls_region();
> + tls_desc = SAFE_MALLOC(sizeof(*tls_desc));
> + memset(tls_desc, 0, sizeof(*tls_desc));
> + tls_desc->entry_number = -1;
> + tls_desc->base_addr = (unsigned long)tls_ptr;
> + tls_desc->limit = TLS_SIZE;
> + tls_desc->seg_32bit = 1;
> + tls_desc->contents = 0;
> + tls_desc->read_exec_only = 0;
> + tls_desc->limit_in_pages = 0;
> + tls_desc->seg_not_present = 0;
> + tls_desc->useable = 1;
> +
> +#elif defined(__aarch64__) || defined(__powerpc64__) || defined(__s390x__)
> + /*
> + * Other architectures: detect if dedicated TLS register is
> available.
> + * You don't manually touch TPIDR_EL0.
> + * The kernel automatically writes it into the TPIDR_EL0 register
> for the new thread after clone() succeeds.
> + */
> + tls_ptr = allocate_tls_region();
> +#else
> + tst_brk(TCONF, "This architecture does not support TLS");
> +#endif
> +}
> +
> +static void free_tls(void)
> +{
> +#if defined(__x86_64__)
> + free(tls_ptr);
> +
> +#elif defined(__i386__)
> + if (tls_desc) {
> + free((void *)(uintptr_t)tls_desc->base_addr);
> + free(tls_desc);
> + }
> +
> +#elif defined(__aarch64__) || defined(__powerpc64__) || defined(__s390x__)
> + free(tls_ptr);
> +#endif
> +}
> +
> +static void do_child(void)
> +{
> + /* Validate TLS usage */
> + if (tls_test_var == 54321) {
> + tst_res(TPASS, "Child TLS variable has expected value");
> + } else {
> + tst_res(TFAIL, "Child TLS variable corrupted or not set");
> + }
> + exit(0);
> +}
> +
> +static void run(unsigned int n)
> +{
> + int status;
> + pid_t pid;
> +
> + args->flags = test_flags[n];
> + args->pidfd = (uint64_t)(&pidfd);
> + args->child_tid = (uint64_t)(&child_tid);
> + args->parent_tid = (uint64_t)(&parent_tid);
> + args->stack = 0;
> + args->stack_size = 0;
> + args->tls = (uint64_t)tls_ptr;
> +
> + TEST(pid = clone3(args, sizeof(*args)));
> + if (pid < 0) {
> + tst_res(TFAIL | TTERRNO, "clone3() failed (%d)", n);
> + return;
> + }
> +
> + if (!pid)
> + do_child();
> +
> + SAFE_WAITPID(pid, &status, __WALL);
> +}
> +
> +static void setup(void)
> +{
> + clone3_supported_by_kernel();
> + initialize_tls();
> +}
> +
> +static struct tst_test test = {
> + .tcnt = ARRAY_SIZE(test_flags),
> + .test = run,
> + .setup = setup,
> + .cleanup = free_tls,
> + .needs_root = 1,
> + .needs_checkpoints = 1,
> + .bufs = (struct tst_buffers []) {
> + {&args, .size = sizeof(*args)},
> + {},
> + }
> +};
> --
> 2.43.5
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
>
--
Regards,
Li Wang
More information about the ltp
mailing list