[PATCH v3 2/2] aarch64: Tests for locking GCS

Yury Khrustalev yury.khrustalev@arm.com
Mon Feb 16 13:49:26 GMT 2026


On Thu, Feb 12, 2026 at 01:58:11PM -0300, Adhemerval Zanella Netto wrote:
> 
> 
> On 09/02/26 08:58, Yury Khrustalev wrote:
> > Check that GCS is locked properly based on the value of the
> > glibc.cpu.aarch64_gcs tunable.
> 
> Maybe add some descriptions of the added test (what they test
> and why).

Makes sense, will do, thanks

> > ...
> >
> > +
> > +#include "tst-gcs-helper.h"
> > +
> > +#include <sys/prctl.h>
> > +#include <unistd.h>
> > +#include <errno.h>
> > +
> > +#ifndef PR_SET_SHADOW_STACK_STATUS
> > +# define PR_SET_SHADOW_STACK_STATUS 75
> > +#endif
> 
> Maybe move the PR_SET_SHADOW_STACK_* definition to tst-gcs-helper.h
> (they are replicate in multiple files).

Good point.

> 
> > +
> > +static int
> > +do_test_argv (int argc, char **argv)
> > +{
> > +  /* Check if GCS could possible by enabled.  */
> > +  if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
> > +    FAIL_UNSUPPORTED ("kernel or CPU does not support GCS");
> 
> Is checking HWCAP_GCS suffice to avoid false positive while checking GCS locking?
> I recall that there was some execve issue fixed only on recent kernels, but I
> don't recall whether this gates HWCAP_GCS advertise by the kernel.

It was fixed in all stable kernels AFAIK. Mark replied in [1].

[1]: https://inbox.sourceware.org/libc-alpha/bcd7a83c-cfba-41bf-b045-c6d528356af3@sirena.org.uk/

> > ...
> >
> > +  /* GCS should be enabled for this test at the start.  */
> > +  TEST_VERIFY (__check_gcs_status ());
> > +
> > +  if (argc == 1)
> > +    {
> > +      printf ("in parent: %u\n", getpid ());
> > +      /* Spawn child process.  */
> > +      char *args[] = { argv[0], (char *)"child", NULL };
> > +      execv (args[0], args);
> 
> This fails without --enable-hardcoded-path-in-tests, usually the tests that
> required re-spawning themselves add an extra argument (though
> TEST-ARGS = -- $(host-test-program-cmd) make rule) and handle the arguments
> resconstruction (like tst-spawn*.c ones).
> 
> You can also use support_spawn_wrap instead, it calls posix_spawn that will
> call execve in the end.

Thanks, I'll fix that.

> > ...
> >
> > +static int
> > +do_test (void)
> > +{
> > +  /* Check if GCS could possible by enabled.  */
> > +  if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
> > +    FAIL_UNSUPPORTED ("kernel or CPU does not support GCS");
> > +
> > +  /* GCS should be enabled for this test at the start.  */
> > +  TEST_VERIFY (__check_gcs_status ());
> > +
> > +  pid_t pid = fork ();
> 
> Use xfork here.

OK

> > ...
> >
> > +  if (pid)
> > +    {
> > +      int status;
> > +      waitpid(pid, &status, 0);
> 
> Use xwaitpid here.

OK.

> > ...
> >
> > +#ifndef NT_ARM_GCS
> > +#define NT_ARM_GCS 0x410
> > +#endif
> 
> This should be always define by elf.h.

OK

> > +
> > +/* Uapi struct for PTRACE_GETREGSET with NT_ARM_GCS.  */
> > +struct user_gcs
> > +{
> > +  uint64_t enabled;
> > +  uint64_t locked;
> > +  uint64_t gcspr_el0;
> > +};
> 
> Maybe move these to tst-gcs-helper.h?

It is only used in this one test, so I don't think moving it to the
header is helpful.

> > +
> > +static int
> > +target (void)
> > +{
> > +  /* This signal is raised after the process has started
> > +     and has been initialised so we can ptrace it at this
> > +     point and obtain GCS locked features.  */
> > +  raise (SIGUSR1);
> 
> Use xraise.

OK

> > +static void
> > +fork_target (char *argv[], uint64_t aarch64_gcs)
> > +{
> > +  /* Currently kernel returns only lower 32 bits of locked
> > +     features so we only compare them.  */
> > +  bool lock_gcs = aarch64_gcs != 0 && aarch64_gcs != 2;
> > +  uint64_t expected_locked = lock_gcs ? 0xfffffffful : 0ul;
> > +  pid_t pid = fork ();
> 
> Use xfork here.

OK

> > +  if (pid == 0)
> > +    {
> > +      char *args[] = { argv[0], (char *)"target", NULL };
> > +      char tunables[90];
> > +      snprintf (tunables, sizeof (tunables), "GLIBC_TUNABLES="
> > +		"glibc.cpu.aarch64_gcs=0x%016lx", aarch64_gcs);
> > +      char *envp[] = { tunables, NULL };
> > +      /* We need to ptrace child process to use PTRACE_GETREGSET
> > +	 with NT_ARM_GCS after it has started.  */
> > +      ptrace (PTRACE_TRACEME, 0, NULL, NULL);
> 
> I think we should also check for the ptrace return code.

OK

> > +      execve (args[0], args, envp);
> 
> Same as the tst-gcs-execv, this fails without --enable-hardcoded-path-in-tests
> (which is the dafault).

Will fix

> > +      FAIL_EXIT1 ("execve: %m");
> > +    }
> > +  while (true)
> > +    {
> > +      int status;
> > +      waitpid (pid, &status, 0);
> 
> Use xwaitpid.

OK

> > ...
> >
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-gcs-lock-static.c b/sysdeps/unix/sysv/linux/aarch64/tst-gcs-lock-static.c
> > new file mode 100644
> > index 0000000000..b80e2f70e8
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/aarch64/tst-gcs-lock-static.c
> >
> > ...
> >
> > +static int
> > +do_test (void)
> > +{
> > +  /* Check if GCS could possible by enabled.  */
> > +  if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
> > +    FAIL_UNSUPPORTED ("kernel or CPU does not support GCS");
> > +
> > +  TEST_VERIFY (__check_gcs_status ());
> > +
> > +  /* Try disabling GCS.  */
> > +  int res = prctl (PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
> > +  if (res)
> > +    {
> > +      TEST_COMPARE (errno, EBUSY);
> > +      perror ("prctl");
> 
> I think printing an error for the success case is confusing (at least it confuses
> me while testing, seeing a prctl printing the errno for a success testcase).

OK, makes sense

Thanks,
Yury



More information about the Libc-alpha mailing list