Merge branch 'linux-5.3' of git://github.com/skeggsb/linux into drm-fixes
[linux-2.6-microblaze.git] / tools / testing / selftests / ptrace / get_syscall_info.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
4  * All rights reserved.
5  *
6  * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
7  * matches userspace expectations.
8  */
9
10 #include "../kselftest_harness.h"
11 #include <err.h>
12 #include <signal.h>
13 #include <asm/unistd.h>
14 #include "linux/ptrace.h"
15
16 static int
17 kill_tracee(pid_t pid)
18 {
19         if (!pid)
20                 return 0;
21
22         int saved_errno = errno;
23
24         int rc = kill(pid, SIGKILL);
25
26         errno = saved_errno;
27         return rc;
28 }
29
30 static long
31 sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
32 {
33         return syscall(__NR_ptrace, request, pid, addr, data);
34 }
35
36 #define LOG_KILL_TRACEE(fmt, ...)                               \
37         do {                                                    \
38                 kill_tracee(pid);                               \
39                 TH_LOG("wait #%d: " fmt,                        \
40                        ptrace_stop, ##__VA_ARGS__);             \
41         } while (0)
42
43 TEST(get_syscall_info)
44 {
45         static const unsigned long args[][7] = {
46                 /* a sequence of architecture-agnostic syscalls */
47                 {
48                         __NR_chdir,
49                         (unsigned long) "",
50                         0xbad1fed1,
51                         0xbad2fed2,
52                         0xbad3fed3,
53                         0xbad4fed4,
54                         0xbad5fed5
55                 },
56                 {
57                         __NR_gettid,
58                         0xcaf0bea0,
59                         0xcaf1bea1,
60                         0xcaf2bea2,
61                         0xcaf3bea3,
62                         0xcaf4bea4,
63                         0xcaf5bea5
64                 },
65                 {
66                         __NR_exit_group,
67                         0,
68                         0xfac1c0d1,
69                         0xfac2c0d2,
70                         0xfac3c0d3,
71                         0xfac4c0d4,
72                         0xfac5c0d5
73                 }
74         };
75         const unsigned long *exp_args;
76
77         pid_t pid = fork();
78
79         ASSERT_LE(0, pid) {
80                 TH_LOG("fork: %m");
81         }
82
83         if (pid == 0) {
84                 /* get the pid before PTRACE_TRACEME */
85                 pid = getpid();
86                 ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
87                         TH_LOG("PTRACE_TRACEME: %m");
88                 }
89                 ASSERT_EQ(0, kill(pid, SIGSTOP)) {
90                         /* cannot happen */
91                         TH_LOG("kill SIGSTOP: %m");
92                 }
93                 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
94                         syscall(args[i][0],
95                                 args[i][1], args[i][2], args[i][3],
96                                 args[i][4], args[i][5], args[i][6]);
97                 }
98                 /* unreachable */
99                 _exit(1);
100         }
101
102         const struct {
103                 unsigned int is_error;
104                 int rval;
105         } *exp_param, exit_param[] = {
106                 { 1, -ENOENT }, /* chdir */
107                 { 0, pid }      /* gettid */
108         };
109
110         unsigned int ptrace_stop;
111
112         for (ptrace_stop = 0; ; ++ptrace_stop) {
113                 struct ptrace_syscall_info info = {
114                         .op = 0xff      /* invalid PTRACE_SYSCALL_INFO_* op */
115                 };
116                 const size_t size = sizeof(info);
117                 const int expected_none_size =
118                         (void *) &info.entry - (void *) &info;
119                 const int expected_entry_size =
120                         (void *) &info.entry.args[6] - (void *) &info;
121                 const int expected_exit_size =
122                         (void *) (&info.exit.is_error + 1) -
123                         (void *) &info;
124                 int status;
125                 long rc;
126
127                 ASSERT_EQ(pid, wait(&status)) {
128                         /* cannot happen */
129                         LOG_KILL_TRACEE("wait: %m");
130                 }
131                 if (WIFEXITED(status)) {
132                         pid = 0;        /* the tracee is no more */
133                         ASSERT_EQ(0, WEXITSTATUS(status));
134                         break;
135                 }
136                 ASSERT_FALSE(WIFSIGNALED(status)) {
137                         pid = 0;        /* the tracee is no more */
138                         LOG_KILL_TRACEE("unexpected signal %u",
139                                         WTERMSIG(status));
140                 }
141                 ASSERT_TRUE(WIFSTOPPED(status)) {
142                         /* cannot happen */
143                         LOG_KILL_TRACEE("unexpected wait status %#x", status);
144                 }
145
146                 switch (WSTOPSIG(status)) {
147                 case SIGSTOP:
148                         ASSERT_EQ(0, ptrace_stop) {
149                                 LOG_KILL_TRACEE("unexpected signal stop");
150                         }
151                         ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
152                                                 PTRACE_O_TRACESYSGOOD)) {
153                                 LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
154                         }
155                         ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
156                                                       pid, size,
157                                                       (unsigned long) &info))) {
158                                 LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
159                         }
160                         ASSERT_EQ(expected_none_size, rc) {
161                                 LOG_KILL_TRACEE("signal stop mismatch");
162                         }
163                         ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
164                                 LOG_KILL_TRACEE("signal stop mismatch");
165                         }
166                         ASSERT_TRUE(info.arch) {
167                                 LOG_KILL_TRACEE("signal stop mismatch");
168                         }
169                         ASSERT_TRUE(info.instruction_pointer) {
170                                 LOG_KILL_TRACEE("signal stop mismatch");
171                         }
172                         ASSERT_TRUE(info.stack_pointer) {
173                                 LOG_KILL_TRACEE("signal stop mismatch");
174                         }
175                         break;
176
177                 case SIGTRAP | 0x80:
178                         ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
179                                                       pid, size,
180                                                       (unsigned long) &info))) {
181                                 LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
182                         }
183                         switch (ptrace_stop) {
184                         case 1: /* entering chdir */
185                         case 3: /* entering gettid */
186                         case 5: /* entering exit_group */
187                                 exp_args = args[ptrace_stop / 2];
188                                 ASSERT_EQ(expected_entry_size, rc) {
189                                         LOG_KILL_TRACEE("entry stop mismatch");
190                                 }
191                                 ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
192                                         LOG_KILL_TRACEE("entry stop mismatch");
193                                 }
194                                 ASSERT_TRUE(info.arch) {
195                                         LOG_KILL_TRACEE("entry stop mismatch");
196                                 }
197                                 ASSERT_TRUE(info.instruction_pointer) {
198                                         LOG_KILL_TRACEE("entry stop mismatch");
199                                 }
200                                 ASSERT_TRUE(info.stack_pointer) {
201                                         LOG_KILL_TRACEE("entry stop mismatch");
202                                 }
203                                 ASSERT_EQ(exp_args[0], info.entry.nr) {
204                                         LOG_KILL_TRACEE("entry stop mismatch");
205                                 }
206                                 ASSERT_EQ(exp_args[1], info.entry.args[0]) {
207                                         LOG_KILL_TRACEE("entry stop mismatch");
208                                 }
209                                 ASSERT_EQ(exp_args[2], info.entry.args[1]) {
210                                         LOG_KILL_TRACEE("entry stop mismatch");
211                                 }
212                                 ASSERT_EQ(exp_args[3], info.entry.args[2]) {
213                                         LOG_KILL_TRACEE("entry stop mismatch");
214                                 }
215                                 ASSERT_EQ(exp_args[4], info.entry.args[3]) {
216                                         LOG_KILL_TRACEE("entry stop mismatch");
217                                 }
218                                 ASSERT_EQ(exp_args[5], info.entry.args[4]) {
219                                         LOG_KILL_TRACEE("entry stop mismatch");
220                                 }
221                                 ASSERT_EQ(exp_args[6], info.entry.args[5]) {
222                                         LOG_KILL_TRACEE("entry stop mismatch");
223                                 }
224                                 break;
225                         case 2: /* exiting chdir */
226                         case 4: /* exiting gettid */
227                                 exp_param = &exit_param[ptrace_stop / 2 - 1];
228                                 ASSERT_EQ(expected_exit_size, rc) {
229                                         LOG_KILL_TRACEE("exit stop mismatch");
230                                 }
231                                 ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
232                                         LOG_KILL_TRACEE("exit stop mismatch");
233                                 }
234                                 ASSERT_TRUE(info.arch) {
235                                         LOG_KILL_TRACEE("exit stop mismatch");
236                                 }
237                                 ASSERT_TRUE(info.instruction_pointer) {
238                                         LOG_KILL_TRACEE("exit stop mismatch");
239                                 }
240                                 ASSERT_TRUE(info.stack_pointer) {
241                                         LOG_KILL_TRACEE("exit stop mismatch");
242                                 }
243                                 ASSERT_EQ(exp_param->is_error,
244                                           info.exit.is_error) {
245                                         LOG_KILL_TRACEE("exit stop mismatch");
246                                 }
247                                 ASSERT_EQ(exp_param->rval, info.exit.rval) {
248                                         LOG_KILL_TRACEE("exit stop mismatch");
249                                 }
250                                 break;
251                         default:
252                                 LOG_KILL_TRACEE("unexpected syscall stop");
253                                 abort();
254                         }
255                         break;
256
257                 default:
258                         LOG_KILL_TRACEE("unexpected stop signal %#x",
259                                         WSTOPSIG(status));
260                         abort();
261                 }
262
263                 ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
264                         LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
265                 }
266         }
267
268         ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
269 }
270
271 TEST_HARNESS_MAIN