set_syscall_info.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2018-2025 Dmitry V. Levin <ldv@strace.io>
  4. * All rights reserved.
  5. *
  6. * Check whether PTRACE_SET_SYSCALL_INFO semantics implemented in the kernel
  7. * matches userspace expectations.
  8. */
  9. #include "kselftest_harness.h"
  10. #include <err.h>
  11. #include <fcntl.h>
  12. #include <signal.h>
  13. #include <asm/unistd.h>
  14. #include <linux/types.h>
  15. #include <linux/ptrace.h>
  16. #if defined(_MIPS_SIM) && _MIPS_SIM == _MIPS_SIM_NABI32
  17. /*
  18. * MIPS N32 is the only architecture where __kernel_ulong_t
  19. * does not match the bitness of syscall arguments.
  20. */
  21. typedef unsigned long long kernel_ulong_t;
  22. #else
  23. typedef __kernel_ulong_t kernel_ulong_t;
  24. #endif
  25. struct si_entry {
  26. int nr;
  27. kernel_ulong_t args[6];
  28. };
  29. struct si_exit {
  30. unsigned int is_error;
  31. int rval;
  32. };
  33. static unsigned int ptrace_stop;
  34. static pid_t tracee_pid;
  35. static int
  36. kill_tracee(pid_t pid)
  37. {
  38. if (!pid)
  39. return 0;
  40. int saved_errno = errno;
  41. int rc = kill(pid, SIGKILL);
  42. errno = saved_errno;
  43. return rc;
  44. }
  45. static long
  46. sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
  47. {
  48. return syscall(__NR_ptrace, request, pid, addr, data);
  49. }
  50. #define LOG_KILL_TRACEE(fmt, ...) \
  51. do { \
  52. kill_tracee(tracee_pid); \
  53. TH_LOG("wait #%d: " fmt, \
  54. ptrace_stop, ##__VA_ARGS__); \
  55. } while (0)
  56. static void
  57. check_psi_entry(struct __test_metadata *_metadata,
  58. const struct ptrace_syscall_info *info,
  59. const struct si_entry *exp_entry,
  60. const char *text)
  61. {
  62. unsigned int i;
  63. int exp_nr = exp_entry->nr;
  64. #if defined __s390__ || defined __s390x__
  65. /* s390 is the only architecture that has 16-bit syscall numbers */
  66. exp_nr &= 0xffff;
  67. #endif
  68. ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info->op) {
  69. LOG_KILL_TRACEE("%s: entry stop mismatch", text);
  70. }
  71. ASSERT_TRUE(info->arch) {
  72. LOG_KILL_TRACEE("%s: entry stop mismatch", text);
  73. }
  74. ASSERT_TRUE(info->instruction_pointer) {
  75. LOG_KILL_TRACEE("%s: entry stop mismatch", text);
  76. }
  77. ASSERT_TRUE(info->stack_pointer) {
  78. LOG_KILL_TRACEE("%s: entry stop mismatch", text);
  79. }
  80. ASSERT_EQ(exp_nr, info->entry.nr) {
  81. LOG_KILL_TRACEE("%s: syscall nr mismatch", text);
  82. }
  83. for (i = 0; i < ARRAY_SIZE(exp_entry->args); ++i) {
  84. ASSERT_EQ(exp_entry->args[i], info->entry.args[i]) {
  85. LOG_KILL_TRACEE("%s: syscall arg #%u mismatch",
  86. text, i);
  87. }
  88. }
  89. }
  90. static void
  91. check_psi_exit(struct __test_metadata *_metadata,
  92. const struct ptrace_syscall_info *info,
  93. const struct si_exit *exp_exit,
  94. const char *text)
  95. {
  96. ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info->op) {
  97. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  98. }
  99. ASSERT_TRUE(info->arch) {
  100. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  101. }
  102. ASSERT_TRUE(info->instruction_pointer) {
  103. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  104. }
  105. ASSERT_TRUE(info->stack_pointer) {
  106. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  107. }
  108. ASSERT_EQ(exp_exit->is_error, info->exit.is_error) {
  109. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  110. }
  111. ASSERT_EQ(exp_exit->rval, info->exit.rval) {
  112. LOG_KILL_TRACEE("%s: exit stop mismatch", text);
  113. }
  114. }
  115. TEST(set_syscall_info)
  116. {
  117. const pid_t tracer_pid = getpid();
  118. const kernel_ulong_t dummy[] = {
  119. (kernel_ulong_t) 0xdad0bef0bad0fed0ULL,
  120. (kernel_ulong_t) 0xdad1bef1bad1fed1ULL,
  121. (kernel_ulong_t) 0xdad2bef2bad2fed2ULL,
  122. (kernel_ulong_t) 0xdad3bef3bad3fed3ULL,
  123. (kernel_ulong_t) 0xdad4bef4bad4fed4ULL,
  124. (kernel_ulong_t) 0xdad5bef5bad5fed5ULL,
  125. };
  126. int splice_in[2], splice_out[2];
  127. ASSERT_EQ(0, pipe(splice_in));
  128. ASSERT_EQ(0, pipe(splice_out));
  129. ASSERT_EQ(sizeof(dummy), write(splice_in[1], dummy, sizeof(dummy)));
  130. const struct {
  131. struct si_entry entry[2];
  132. struct si_exit exit[2];
  133. } si[] = {
  134. /* change scno, keep non-error rval */
  135. {
  136. {
  137. {
  138. __NR_gettid,
  139. {
  140. dummy[0], dummy[1], dummy[2],
  141. dummy[3], dummy[4], dummy[5]
  142. }
  143. }, {
  144. __NR_getppid,
  145. {
  146. dummy[0], dummy[1], dummy[2],
  147. dummy[3], dummy[4], dummy[5]
  148. }
  149. }
  150. }, {
  151. { 0, tracer_pid }, { 0, tracer_pid }
  152. }
  153. },
  154. /* set scno to -1, keep error rval */
  155. {
  156. {
  157. {
  158. __NR_chdir,
  159. {
  160. (uintptr_t) ".",
  161. dummy[1], dummy[2],
  162. dummy[3], dummy[4], dummy[5]
  163. }
  164. }, {
  165. -1,
  166. {
  167. (uintptr_t) ".",
  168. dummy[1], dummy[2],
  169. dummy[3], dummy[4], dummy[5]
  170. }
  171. }
  172. }, {
  173. { 1, -ENOSYS }, { 1, -ENOSYS }
  174. }
  175. },
  176. /* keep scno, change non-error rval */
  177. {
  178. {
  179. {
  180. __NR_getppid,
  181. {
  182. dummy[0], dummy[1], dummy[2],
  183. dummy[3], dummy[4], dummy[5]
  184. }
  185. }, {
  186. __NR_getppid,
  187. {
  188. dummy[0], dummy[1], dummy[2],
  189. dummy[3], dummy[4], dummy[5]
  190. }
  191. }
  192. }, {
  193. { 0, tracer_pid }, { 0, tracer_pid + 1 }
  194. }
  195. },
  196. /* change arg1, keep non-error rval */
  197. {
  198. {
  199. {
  200. __NR_chdir,
  201. {
  202. (uintptr_t) "",
  203. dummy[1], dummy[2],
  204. dummy[3], dummy[4], dummy[5]
  205. }
  206. }, {
  207. __NR_chdir,
  208. {
  209. (uintptr_t) ".",
  210. dummy[1], dummy[2],
  211. dummy[3], dummy[4], dummy[5]
  212. }
  213. }
  214. }, {
  215. { 0, 0 }, { 0, 0 }
  216. }
  217. },
  218. /* set scno to -1, change error rval to non-error */
  219. {
  220. {
  221. {
  222. __NR_gettid,
  223. {
  224. dummy[0], dummy[1], dummy[2],
  225. dummy[3], dummy[4], dummy[5]
  226. }
  227. }, {
  228. -1,
  229. {
  230. dummy[0], dummy[1], dummy[2],
  231. dummy[3], dummy[4], dummy[5]
  232. }
  233. }
  234. }, {
  235. { 1, -ENOSYS }, { 0, tracer_pid }
  236. }
  237. },
  238. /* change scno, change non-error rval to error */
  239. {
  240. {
  241. {
  242. __NR_chdir,
  243. {
  244. dummy[0], dummy[1], dummy[2],
  245. dummy[3], dummy[4], dummy[5]
  246. }
  247. }, {
  248. __NR_getppid,
  249. {
  250. dummy[0], dummy[1], dummy[2],
  251. dummy[3], dummy[4], dummy[5]
  252. }
  253. }
  254. }, {
  255. { 0, tracer_pid }, { 1, -EISDIR }
  256. }
  257. },
  258. /* change scno and all args, change non-error rval */
  259. {
  260. {
  261. {
  262. __NR_gettid,
  263. {
  264. dummy[0], dummy[1], dummy[2],
  265. dummy[3], dummy[4], dummy[5]
  266. }
  267. }, {
  268. __NR_splice,
  269. {
  270. splice_in[0], 0, splice_out[1], 0,
  271. sizeof(dummy), SPLICE_F_NONBLOCK
  272. }
  273. }
  274. }, {
  275. { 0, sizeof(dummy) }, { 0, sizeof(dummy) + 1 }
  276. }
  277. },
  278. /* change arg1, no exit stop */
  279. {
  280. {
  281. {
  282. __NR_exit_group,
  283. {
  284. dummy[0], dummy[1], dummy[2],
  285. dummy[3], dummy[4], dummy[5]
  286. }
  287. }, {
  288. __NR_exit_group,
  289. {
  290. 0, dummy[1], dummy[2],
  291. dummy[3], dummy[4], dummy[5]
  292. }
  293. }
  294. }, {
  295. { 0, 0 }, { 0, 0 }
  296. }
  297. },
  298. };
  299. long rc;
  300. unsigned int i;
  301. tracee_pid = fork();
  302. ASSERT_LE(0, tracee_pid) {
  303. TH_LOG("fork: %m");
  304. }
  305. if (tracee_pid == 0) {
  306. /* get the pid before PTRACE_TRACEME */
  307. tracee_pid = getpid();
  308. ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
  309. TH_LOG("PTRACE_TRACEME: %m");
  310. }
  311. ASSERT_EQ(0, kill(tracee_pid, SIGSTOP)) {
  312. /* cannot happen */
  313. TH_LOG("kill SIGSTOP: %m");
  314. }
  315. for (i = 0; i < ARRAY_SIZE(si); ++i) {
  316. rc = syscall(si[i].entry[0].nr,
  317. si[i].entry[0].args[0],
  318. si[i].entry[0].args[1],
  319. si[i].entry[0].args[2],
  320. si[i].entry[0].args[3],
  321. si[i].entry[0].args[4],
  322. si[i].entry[0].args[5]);
  323. if (si[i].exit[1].is_error) {
  324. if (rc != -1 || errno != -si[i].exit[1].rval)
  325. break;
  326. } else {
  327. if (rc != si[i].exit[1].rval)
  328. break;
  329. }
  330. }
  331. /*
  332. * Something went wrong, but in this state tracee
  333. * cannot reliably issue syscalls, so just crash.
  334. */
  335. *(volatile unsigned char *) (uintptr_t) i = 42;
  336. /* unreachable */
  337. _exit(i + 1);
  338. }
  339. for (ptrace_stop = 0; ; ++ptrace_stop) {
  340. struct ptrace_syscall_info info = {
  341. .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
  342. };
  343. const size_t size = sizeof(info);
  344. const int expected_entry_size =
  345. (void *) &info.entry.args[6] - (void *) &info;
  346. const int expected_exit_size =
  347. (void *) (&info.exit.is_error + 1) -
  348. (void *) &info;
  349. int status;
  350. ASSERT_EQ(tracee_pid, wait(&status)) {
  351. /* cannot happen */
  352. LOG_KILL_TRACEE("wait: %m");
  353. }
  354. if (WIFEXITED(status)) {
  355. tracee_pid = 0; /* the tracee is no more */
  356. ASSERT_EQ(0, WEXITSTATUS(status)) {
  357. LOG_KILL_TRACEE("unexpected exit status %u",
  358. WEXITSTATUS(status));
  359. }
  360. break;
  361. }
  362. ASSERT_FALSE(WIFSIGNALED(status)) {
  363. tracee_pid = 0; /* the tracee is no more */
  364. LOG_KILL_TRACEE("unexpected signal %u",
  365. WTERMSIG(status));
  366. }
  367. ASSERT_TRUE(WIFSTOPPED(status)) {
  368. /* cannot happen */
  369. LOG_KILL_TRACEE("unexpected wait status %#x", status);
  370. }
  371. ASSERT_LT(ptrace_stop, ARRAY_SIZE(si) * 2) {
  372. LOG_KILL_TRACEE("ptrace stop overflow");
  373. }
  374. switch (WSTOPSIG(status)) {
  375. case SIGSTOP:
  376. ASSERT_EQ(0, ptrace_stop) {
  377. LOG_KILL_TRACEE("unexpected signal stop");
  378. }
  379. ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, tracee_pid,
  380. 0, PTRACE_O_TRACESYSGOOD)) {
  381. LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
  382. }
  383. break;
  384. case SIGTRAP | 0x80:
  385. ASSERT_LT(0, ptrace_stop) {
  386. LOG_KILL_TRACEE("unexpected syscall stop");
  387. }
  388. ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
  389. tracee_pid, size,
  390. (uintptr_t) &info))) {
  391. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1: %m");
  392. }
  393. if (ptrace_stop & 1) {
  394. /* entering syscall */
  395. const struct si_entry *exp_entry =
  396. &si[ptrace_stop / 2].entry[0];
  397. const struct si_entry *set_entry =
  398. &si[ptrace_stop / 2].entry[1];
  399. /* check ptrace_syscall_info before the changes */
  400. ASSERT_EQ(expected_entry_size, rc) {
  401. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
  402. ": entry stop mismatch");
  403. }
  404. check_psi_entry(_metadata, &info, exp_entry,
  405. "PTRACE_GET_SYSCALL_INFO #1");
  406. /* apply the changes */
  407. info.entry.nr = set_entry->nr;
  408. for (i = 0; i < ARRAY_SIZE(set_entry->args); ++i)
  409. info.entry.args[i] = set_entry->args[i];
  410. ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
  411. tracee_pid, size,
  412. (uintptr_t) &info)) {
  413. LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
  414. }
  415. /* check ptrace_syscall_info after the changes */
  416. memset(&info, 0, sizeof(info));
  417. info.op = 0xff;
  418. ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
  419. tracee_pid, size,
  420. (uintptr_t) &info))) {
  421. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
  422. }
  423. ASSERT_EQ(expected_entry_size, rc) {
  424. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
  425. ": entry stop mismatch");
  426. }
  427. check_psi_entry(_metadata, &info, set_entry,
  428. "PTRACE_GET_SYSCALL_INFO #2");
  429. } else {
  430. /* exiting syscall */
  431. const struct si_exit *exp_exit =
  432. &si[ptrace_stop / 2 - 1].exit[0];
  433. const struct si_exit *set_exit =
  434. &si[ptrace_stop / 2 - 1].exit[1];
  435. /* check ptrace_syscall_info before the changes */
  436. ASSERT_EQ(expected_exit_size, rc) {
  437. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
  438. ": exit stop mismatch");
  439. }
  440. check_psi_exit(_metadata, &info, exp_exit,
  441. "PTRACE_GET_SYSCALL_INFO #1");
  442. /* apply the changes */
  443. info.exit.is_error = set_exit->is_error;
  444. info.exit.rval = set_exit->rval;
  445. ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
  446. tracee_pid, size,
  447. (uintptr_t) &info)) {
  448. LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
  449. }
  450. /* check ptrace_syscall_info after the changes */
  451. memset(&info, 0, sizeof(info));
  452. info.op = 0xff;
  453. ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
  454. tracee_pid, size,
  455. (uintptr_t) &info))) {
  456. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2: %m");
  457. }
  458. ASSERT_EQ(expected_exit_size, rc) {
  459. LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
  460. ": exit stop mismatch");
  461. }
  462. check_psi_exit(_metadata, &info, set_exit,
  463. "PTRACE_GET_SYSCALL_INFO #2");
  464. }
  465. break;
  466. default:
  467. LOG_KILL_TRACEE("unexpected stop signal %u",
  468. WSTOPSIG(status));
  469. abort();
  470. }
  471. ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, tracee_pid, 0, 0)) {
  472. LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
  473. }
  474. }
  475. ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2);
  476. }
  477. TEST_HARNESS_MAIN