aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.arch/aarch64-gcs.c
blob: 9eb2e9eaa96b1f53675a67898717fd06db167b8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* This test program is part of GDB, the GNU debugger.

   Copyright 2025 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/auxv.h>
#include <sys/syscall.h>
#include <linux/prctl.h>

/* Feature check for Guarded Control Stack.  */
#ifndef HWCAP_GCS
#define HWCAP_GCS (1ULL << 32)
#endif

#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL << 0)
#endif

/* We need to use a macro to call prctl because after GCS is enabled, it's not
   possible to return from the function which enabled it.  This is because the
   return address of the calling function isn't on the GCS.  */
#define my_syscall2(num, arg1, arg2)					\
  ({									\
    register long _num __asm__("x8") = (num);				\
    register long _arg1 __asm__("x0") = (long)(arg1);			\
    register long _arg2 __asm__("x1") = (long)(arg2);			\
    register long _arg3 __asm__("x2") = 0;				\
    register long _arg4 __asm__("x3") = 0;				\
    register long _arg5 __asm__("x4") = 0;				\
									\
    __asm__ volatile ("svc #0\n"					\
		      : "=r"(_arg1)					\
		      : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),	\
			"r"(_arg5), "r"(_num)				\
		      : "memory", "cc");				\
    _arg1;								\
  })

#define get_gcspr(void)							\
  ({									\
    unsigned long *gcspr;						\
									\
    /* Get GCSPR_EL0.  */						\
    asm volatile ("mrs	%0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc");	\
									\
    gcspr;								\
  })

static unsigned long *handler_gcspr = 0;

static void
handler (int sig)
{
  handler_gcspr = get_gcspr ();
}

static int __attribute__ ((unused))
called_from_gdb (int val)
{
  return val + 1;
}

/* Corrupt the return address to see if GDB will report a SIGSEGV with the
   expected $_siginfo.si_code.  */
static void __attribute__ ((noinline))
normal_function2 (void)
{
  /* x30 holds the return address.  */
  register unsigned long x30 __asm__("x30") __attribute__ ((unused));

  /* Cause a GCS exception.  */
  x30 = 0xbadc0ffee;
  /* Use explicit ret so that we can verify that a SIGSEGV was generated
     exactly on the return instruction.  */
  __asm__ volatile ("ret\n");
}

static inline void __attribute__ ((__always_inline__))
inline_function2 (void)
{
  normal_function2 ();
}

static void __attribute__ ((noinline))
normal_function1 (void)
{
  inline_function2 ();
}

/* First in a sequence of inline and normal functions, to test GDB
   backtrace.  */
static inline void __attribute__ ((__always_inline__))
inline_function1 (void)
{
  normal_function1 ();
}

/* Trivial function, just so that GDB can test return with wrong GCSPR.  */
static void __attribute__ ((noinline))
normal_function0 (void)
{
  /* Use explicit ret so that we can verify that a SIGSEGV was generated
     exactly on the return instruction.  */
  __asm__ volatile ("ret\n");
}

int
main (void)
{
  if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
    {
      fprintf (stderr, "GCS support not found in AT_HWCAP\n");
      return EXIT_FAILURE;
    }

  /* Force shadow stacks on, our tests *should* be fine with or
     without libc support and with or without this having ended
     up tagged for GCS and enabled by the dynamic linker.  We
     can't use the libc prctl() function since we can't return
     from enabling the stack.  Also lock GCS if not already
     locked so we can test behaviour when it's locked.  */
  unsigned long gcs_mode;
  int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
  if (ret)
    {
      fprintf (stderr, "Failed to read GCS state: %d\n", ret);
      return EXIT_FAILURE;
    }

  if (!(gcs_mode & PR_SHADOW_STACK_ENABLE))
    {
      gcs_mode = PR_SHADOW_STACK_ENABLE;
      ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode);
      if (ret)
	{
	  fprintf (stderr, "Failed to configure GCS: %d\n", ret);
	  return EXIT_FAILURE;
	}
    }

  /* Regular function call.  */
  normal_function0 ();

  /* This is used by GDB.  */
  __attribute__((unused)) unsigned long *gcspr = get_gcspr ();

  struct sigaction act = { 0 };

  act.sa_handler = &handler;	/* Break here.  */
  if (sigaction (SIGUSR1, &act, NULL) == -1)
    {
      perror ("sigaction");
      exit (EXIT_FAILURE);
    }

  raise (SIGUSR1);

/* Call sequence of inline and normal functions, to test GDB backtrace.  */
  inline_function1 ();

  /* Avoid returning, in case libc doesn't understand GCS.  */
  exit (EXIT_SUCCESS);
}