1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
6 * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
7 * matches userspace expectations.
10 #include "../kselftest_harness.h"
13 #include <asm/unistd.h>
14 #include "linux/ptrace.h"
17 kill_tracee(pid_t pid
)
22 int saved_errno
= errno
;
24 int rc
= kill(pid
, SIGKILL
);
31 sys_ptrace(int request
, pid_t pid
, unsigned long addr
, unsigned long data
)
33 return syscall(__NR_ptrace
, request
, pid
, addr
, data
);
36 #define LOG_KILL_TRACEE(fmt, ...) \
39 TH_LOG("wait #%d: " fmt, \
40 ptrace_stop, ##__VA_ARGS__); \
43 TEST(get_syscall_info
)
45 static const unsigned long args
[][7] = {
46 /* a sequence of architecture-agnostic syscalls */
75 const unsigned long *exp_args
;
84 /* get the pid before PTRACE_TRACEME */
86 ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME
, 0, 0, 0)) {
87 TH_LOG("PTRACE_TRACEME: %m");
89 ASSERT_EQ(0, kill(pid
, SIGSTOP
)) {
91 TH_LOG("kill SIGSTOP: %m");
93 for (unsigned int i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
95 args
[i
][1], args
[i
][2], args
[i
][3],
96 args
[i
][4], args
[i
][5], args
[i
][6]);
103 unsigned int is_error
;
105 } *exp_param
, exit_param
[] = {
106 { 1, -ENOENT
}, /* chdir */
107 { 0, pid
} /* gettid */
110 unsigned int ptrace_stop
;
112 for (ptrace_stop
= 0; ; ++ptrace_stop
) {
113 struct ptrace_syscall_info info
= {
114 .op
= 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
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) -
127 ASSERT_EQ(pid
, wait(&status
)) {
129 LOG_KILL_TRACEE("wait: %m");
131 if (WIFEXITED(status
)) {
132 pid
= 0; /* the tracee is no more */
133 ASSERT_EQ(0, WEXITSTATUS(status
));
136 ASSERT_FALSE(WIFSIGNALED(status
)) {
137 pid
= 0; /* the tracee is no more */
138 LOG_KILL_TRACEE("unexpected signal %u",
141 ASSERT_TRUE(WIFSTOPPED(status
)) {
143 LOG_KILL_TRACEE("unexpected wait status %#x", status
);
146 switch (WSTOPSIG(status
)) {
148 ASSERT_EQ(0, ptrace_stop
) {
149 LOG_KILL_TRACEE("unexpected signal stop");
151 ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS
, pid
, 0,
152 PTRACE_O_TRACESYSGOOD
)) {
153 LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
155 ASSERT_LT(0, (rc
= sys_ptrace(PTRACE_GET_SYSCALL_INFO
,
157 (unsigned long) &info
))) {
158 LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
160 ASSERT_EQ(expected_none_size
, rc
) {
161 LOG_KILL_TRACEE("signal stop mismatch");
163 ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE
, info
.op
) {
164 LOG_KILL_TRACEE("signal stop mismatch");
166 ASSERT_TRUE(info
.arch
) {
167 LOG_KILL_TRACEE("signal stop mismatch");
169 ASSERT_TRUE(info
.instruction_pointer
) {
170 LOG_KILL_TRACEE("signal stop mismatch");
172 ASSERT_TRUE(info
.stack_pointer
) {
173 LOG_KILL_TRACEE("signal stop mismatch");
178 ASSERT_LT(0, (rc
= sys_ptrace(PTRACE_GET_SYSCALL_INFO
,
180 (unsigned long) &info
))) {
181 LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
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");
191 ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY
, info
.op
) {
192 LOG_KILL_TRACEE("entry stop mismatch");
194 ASSERT_TRUE(info
.arch
) {
195 LOG_KILL_TRACEE("entry stop mismatch");
197 ASSERT_TRUE(info
.instruction_pointer
) {
198 LOG_KILL_TRACEE("entry stop mismatch");
200 ASSERT_TRUE(info
.stack_pointer
) {
201 LOG_KILL_TRACEE("entry stop mismatch");
203 ASSERT_EQ(exp_args
[0], info
.entry
.nr
) {
204 LOG_KILL_TRACEE("entry stop mismatch");
206 ASSERT_EQ(exp_args
[1], info
.entry
.args
[0]) {
207 LOG_KILL_TRACEE("entry stop mismatch");
209 ASSERT_EQ(exp_args
[2], info
.entry
.args
[1]) {
210 LOG_KILL_TRACEE("entry stop mismatch");
212 ASSERT_EQ(exp_args
[3], info
.entry
.args
[2]) {
213 LOG_KILL_TRACEE("entry stop mismatch");
215 ASSERT_EQ(exp_args
[4], info
.entry
.args
[3]) {
216 LOG_KILL_TRACEE("entry stop mismatch");
218 ASSERT_EQ(exp_args
[5], info
.entry
.args
[4]) {
219 LOG_KILL_TRACEE("entry stop mismatch");
221 ASSERT_EQ(exp_args
[6], info
.entry
.args
[5]) {
222 LOG_KILL_TRACEE("entry stop mismatch");
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");
231 ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT
, info
.op
) {
232 LOG_KILL_TRACEE("exit stop mismatch");
234 ASSERT_TRUE(info
.arch
) {
235 LOG_KILL_TRACEE("exit stop mismatch");
237 ASSERT_TRUE(info
.instruction_pointer
) {
238 LOG_KILL_TRACEE("exit stop mismatch");
240 ASSERT_TRUE(info
.stack_pointer
) {
241 LOG_KILL_TRACEE("exit stop mismatch");
243 ASSERT_EQ(exp_param
->is_error
,
244 info
.exit
.is_error
) {
245 LOG_KILL_TRACEE("exit stop mismatch");
247 ASSERT_EQ(exp_param
->rval
, info
.exit
.rval
) {
248 LOG_KILL_TRACEE("exit stop mismatch");
252 LOG_KILL_TRACEE("unexpected syscall stop");
258 LOG_KILL_TRACEE("unexpected stop signal %#x",
263 ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL
, pid
, 0, 0)) {
264 LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
268 ASSERT_EQ(ARRAY_SIZE(args
) * 2, ptrace_stop
);