[LTP] [PATCH v4] Add EPERM test for clone and clone3

Stephen Bertram sbertram@redhat.com
Fri Nov 14 19:36:43 CET 2025


Thanks Li.

It led to the clone tests at least which we also need :).

I have a better understanding after working with you on this and would like
to follow up next week to close out the remaining issues.

thanks,

stephen
He/His/Him


On Wed, Nov 12, 2025 at 10:51 PM Li Wang <liwang@redhat.com> wrote:

>
>
> On Thu, Nov 13, 2025 at 7:11 AM Stephen Bertram via ltp <
> ltp@lists.linux.it> wrote:
>
>> Confirming EPERM is returned when CAP_SYS_ADMIN is
>> removed from clone and clone3.
>> And for clone3 the set_tid_size is greater than 0.
>>
>> Signed-off-by: Stephen Bertram <sbertram@redhat.com>
>> ---
>>  runtest/syscalls                            |  2 +
>>  testcases/kernel/syscalls/clone/.gitignore  |  1 +
>>  testcases/kernel/syscalls/clone/clone11.c   | 74 +++++++++++++++++++++
>>  testcases/kernel/syscalls/clone3/.gitignore |  1 +
>>  testcases/kernel/syscalls/clone3/clone304.c | 65 ++++++++++++++++++
>>  5 files changed, 143 insertions(+)
>>  create mode 100644 testcases/kernel/syscalls/clone/clone11.c
>>  create mode 100644 testcases/kernel/syscalls/clone3/clone304.c
>>
>> diff --git a/runtest/syscalls b/runtest/syscalls
>> index 54d94c0ca..a1ef7548b 100644
>> --- a/runtest/syscalls
>> +++ b/runtest/syscalls
>> @@ -124,10 +124,12 @@ clone07 clone07
>>  clone08 clone08
>>  clone09 clone09
>>  clone10 clone10
>> +clone11 clone11
>>
>>  clone301 clone301
>>  clone302 clone302
>>  clone303 clone303
>> +clone304 clone304
>>
>>  close01 close01
>>  close02 close02
>> diff --git a/testcases/kernel/syscalls/clone/.gitignore
>> b/testcases/kernel/syscalls/clone/.gitignore
>> index adfb8257d..0edcfef5d 100644
>> --- a/testcases/kernel/syscalls/clone/.gitignore
>> +++ b/testcases/kernel/syscalls/clone/.gitignore
>> @@ -8,3 +8,4 @@
>>  /clone08
>>  /clone09
>>  /clone10
>> +/clone11
>> diff --git a/testcases/kernel/syscalls/clone/clone11.c
>> b/testcases/kernel/syscalls/clone/clone11.c
>> new file mode 100644
>> index 000000000..6fb9e5346
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/clone/clone11.c
>> @@ -0,0 +1,74 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (c) 2025 Stephen Bertram <sbertram@redhat.com>
>> + */
>> +
>> +/*\
>> + * This test verifies that :man2:`clone` fails with EPERM when
>> CAP_SYS_ADMIN
>> + * has been dropped.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#define DESC(x) .flags = x, .sflags = #x
>> +
>> +#include "tst_test.h"
>> +#include "clone_platform.h"
>> +#include "lapi/sched.h"
>> +
>> +static void *child_stack;
>> +static int *child_pid;
>> +
>> +static struct tcase {
>> +       uint64_t flags;
>> +       char *sflags;
>> +} tcases[] = {
>> +       { DESC(CLONE_NEWPID) },
>> +       { DESC(CLONE_NEWCGROUP) },
>> +       { DESC(CLONE_NEWIPC) },
>> +       { DESC(CLONE_NEWNET) },
>> +       { DESC(CLONE_NEWNS) },
>> +       { DESC(CLONE_NEWUTS) },
>> +};
>> +
>> +static int child_fn(void *arg LTP_ATTRIBUTE_UNUSED)
>> +{
>> +       *child_pid = getpid();
>> +       exit(0);
>> +}
>> +
>> +static void run(unsigned int n)
>> +{
>> +       struct tcase *tc = &tcases[n];
>> +
>> +       TST_EXP_FAIL(ltp_clone(tc->flags, child_fn, NULL,
>> CHILD_STACK_SIZE,
>> +                       child_stack), EPERM, "clone(%s) should fail with
>> EPERM"
>> +                       , tc->sflags);
>> +}
>> +
>> +static void setup(void)
>> +{
>> +       child_pid = SAFE_MMAP(NULL, sizeof(*child_pid), PROT_READ |
>> PROT_WRITE,
>> +               MAP_SHARED | MAP_ANONYMOUS, -1, 0);
>> +}
>> +
>> +static void cleanup(void)
>> +{
>> +       if (child_pid)
>> +               SAFE_MUNMAP(child_pid, sizeof(*child_pid));
>> +}
>> +
>> +static struct tst_test test = {
>> +       .tcnt = ARRAY_SIZE(tcases),
>> +       .setup = setup,
>> +       .test = run,
>> +       .cleanup = cleanup,
>> +       .needs_root = 1,
>> +       .caps = (struct tst_cap []) {
>> +                       TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
>> +                       {},
>> +       },
>> +       .bufs = (struct tst_buffers []) {
>> +                       {&child_stack, .size = CHILD_STACK_SIZE},
>> +                       {},
>> +       },
>> +};
>> diff --git a/testcases/kernel/syscalls/clone3/.gitignore
>> b/testcases/kernel/syscalls/clone3/.gitignore
>> index 10369954b..e9b5312f4 100644
>> --- a/testcases/kernel/syscalls/clone3/.gitignore
>> +++ b/testcases/kernel/syscalls/clone3/.gitignore
>> @@ -1,3 +1,4 @@
>>  clone301
>>  clone302
>>  clone303
>> +clone304
>> diff --git a/testcases/kernel/syscalls/clone3/clone304.c
>> b/testcases/kernel/syscalls/clone3/clone304.c
>> new file mode 100644
>> index 000000000..8e51a6ff7
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/clone3/clone304.c
>> @@ -0,0 +1,65 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (c) 2025 Stephen Bertram <sbertram@redhat.com>
>> + */
>> +
>> +/*\
>> + * This test verifies that :man2:`clone3` fails with EPERM when
>> CAP_SYS_ADMIN
>> + * has been dropped and ``clone_args.set_tid_size`` is greater than zero.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#define DESC(x) .flags = x, .sflags = #x
>> +
>> +#include "tst_test.h"
>> +#include "lapi/sched.h"
>> +
>> +static struct clone_args args = {0};
>> +static pid_t tid_array[4] = {0, 0, 0, 0};
>> +
>> +static struct tcase {
>> +       uint64_t flags;
>> +       char *sflags;
>> +} tcases[] = {
>> +       { DESC(CLONE_NEWPID) },
>> +       { DESC(CLONE_NEWCGROUP) },
>> +       { DESC(CLONE_NEWIPC) },
>> +       { DESC(CLONE_NEWNET) },
>> +       { DESC(CLONE_NEWNS) },
>> +       { DESC(CLONE_NEWUTS) },
>>
>
> Hi Stephen,
>
> You seem to have missed my point; maybe I did not describe it clearly.
>
>  - EPERM due to CLONE_NEW* without CAP_SYS_ADMIN is a
>     long-standing permission check for namespace creation.
>
>  - EPERM due to set_tid_size > 0 without CAP_SYS_ADMIN is a
>    clone3-specific path tied to PID namespace ownership and set_tid.
>
> However, combining both in a single call confirms the “both conditions
> present”
> case, but it can mask which check actually triggered EPERM if behavior
> changes or regresses.
>
> What you need to do is to separate the situations, eliminating orthogonal
> impacts,
> e.g.
>
>
>    - Unshares a new user namespace (and mount namespace to avoid
>    surprises) in setup().
>    - Drops CAP_SYS_ADMIN using tst_cap_action() (via the .caps field).
>    -
>    - Then runs three groups:
>    -
>    -     - set_tid_size > 0 only (no CLONE_NEW*): expect EPERM
>    -
>    -     - CLONE_NEW* only (set_tid_size = 0): expect EPERM
>    -
>    -     - Both set_tid_size > 0 and CLONE_NEW*: expect EPERM
>    -
>    - We can achieve all of these only in one clone304 testcase.
>
>
>
> +};
>> +
>> +static void run(unsigned int n)
>> +{
>> +       struct tcase *tc = &tcases[n];
>> +
>> +       args.flags = tc->flags;
>> +
>> +       TST_EXP_FAIL(clone3(&args, sizeof(args)), EPERM, "clone3(%s)
>> should fail with EPERM"
>> +                       , tc->sflags);
>> +}
>> +
>> +static void setup(void)
>> +{
>> +       clone3_supported_by_kernel();
>> +
>> +       memset(&args, 0, sizeof(args));
>> +       args.set_tid = (uintptr_t)tid_array;
>> +       /* set_tid_size greater than zero - requires CAP_SYS_ADMIN */
>> +       args.set_tid_size = 4;
>> +}
>> +
>> +static struct tst_test test = {
>> +       .tcnt = ARRAY_SIZE(tcases),
>> +       .setup = setup,
>> +       .test = run,
>> +       .needs_root = 1,
>> +       .caps = (struct tst_cap []) {
>> +                       TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
>> +                       {},
>> +       },
>> +       .bufs = (struct tst_buffers []) {
>> +                       {&args, .size = sizeof(struct clone_args)},
>> +                       {},
>> +       },
>> +};
>> --
>> 2.49.0
>>
>>
>> --
>> Mailing list info: https://lists.linux.it/listinfo/ltp
>>
>>
>
> --
> Regards,
> Li Wang
>


More information about the ltp mailing list