logo

mstrace

Small Linux strace(1) implementationgit clone https://anongit.hacktivis.me/git/mstrace.git/

mstrace.c (4114B)


  1. // SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+mstrace@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _DEFAULT_SOURCE
  4. #include "mstrace.h"
  5. #include <errno.h>
  6. #include <inttypes.h> // PRIu64
  7. #include <linux/ptrace.h> // ptrace_syscall_info, __u64
  8. #include <stdbool.h>
  9. #include <stdio.h> // fprintf
  10. #include <string.h> // strerror
  11. #include <sys/ptrace.h> // ptrace()
  12. #include <sys/syscall.h> // SYS_*
  13. #include <sys/wait.h>
  14. #include <unistd.h> // getpid, fork, execvp
  15. extern void print_syscall_entry(__u64 nr, __u64 args[6]);
  16. extern void print_syscall_exit(__u64 nr, __u64 args[6]);
  17. pid_t child = -1;
  18. FILE *out;
  19. static int
  20. wait_for_syscall(int *status)
  21. {
  22. while(1)
  23. {
  24. ptrace(PTRACE_SYSCALL, child, 0, 0);
  25. waitpid(child, status, 0);
  26. if(WIFSTOPPED(*status) && WSTOPSIG(*status) & 0x80) return 0;
  27. if(WIFEXITED(*status)) return 1;
  28. }
  29. }
  30. static void
  31. usage()
  32. {
  33. fputs("Usage: mstrace [-o output] <command> [args...]\n", stderr);
  34. }
  35. int
  36. main(int argc, char *argv[])
  37. {
  38. char *outname = NULL;
  39. out = stderr;
  40. for(int c = -1; (c = getopt(argc, argv, ":o:")) != -1;)
  41. {
  42. switch(c)
  43. {
  44. case 'o':
  45. if(outname)
  46. {
  47. fprintf(stderr, "mstrace: error: Multiple -o options unsupported\n");
  48. usage();
  49. return 1;
  50. }
  51. outname = optarg;
  52. break;
  53. case ':':
  54. fprintf(stderr, "mstrace: error: Missing operand for option: '-%c'\n", optopt);
  55. usage();
  56. return 1;
  57. case '?':
  58. fprintf(stderr, "mstrace: error: Unknown option '-%c'\n", optopt);
  59. usage();
  60. return 1;
  61. }
  62. }
  63. argc -= optind;
  64. argv += optind;
  65. if(argc < 1)
  66. {
  67. usage();
  68. return 1;
  69. }
  70. if(outname)
  71. {
  72. out = fopen(outname, "wb");
  73. if(!out)
  74. {
  75. fprintf(stderr, "mstrace: error: Failed opening file \"%s\": %s\n", outname, strerror(errno));
  76. return 1;
  77. }
  78. }
  79. child = fork();
  80. if(child < 0)
  81. {
  82. fprintf(stderr, "mstrace: error: Failed creating child process: %s\n", strerror(errno));
  83. return 1;
  84. }
  85. else if(child == 0)
  86. {
  87. errno = 0;
  88. int ret = ptrace(PTRACE_TRACEME);
  89. if(ret < 0 && errno != 0)
  90. {
  91. fprintf(stderr, "mstrace: error: Failed ptrace(PTRACE_TRACEME): %s\n", strerror(errno));
  92. return 1;
  93. }
  94. if(kill(getpid(), SIGSTOP) < 0)
  95. {
  96. fprintf(stderr, "mstrace: error: Failed stopping parent: %s\n", strerror(errno));
  97. return 1;
  98. }
  99. execvp(argv[0], argv);
  100. return 1;
  101. }
  102. struct
  103. {
  104. __u64 nr;
  105. __u64 args[6];
  106. } entry;
  107. entry.nr = 0;
  108. int status;
  109. waitpid(child, &status, 0);
  110. ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
  111. while(1)
  112. {
  113. if(wait_for_syscall(&status) != 0) break;
  114. struct ptrace_syscall_info syscall_info;
  115. long ret =
  116. ptrace(PTRACE_GET_SYSCALL_INFO, child, sizeof(struct ptrace_syscall_info), &syscall_info);
  117. if(ret < 0)
  118. {
  119. fprintf(
  120. stderr,
  121. "mstrace: error: Failed getting syscall information via PTRACE_GET_SYSCALL_INFO: %s\n",
  122. strerror(errno));
  123. continue;
  124. }
  125. switch(syscall_info.op)
  126. {
  127. case PTRACE_SYSCALL_INFO_ENTRY:
  128. entry.nr = syscall_info.entry.nr;
  129. for(int i = 0; i < 6; i++)
  130. entry.args[i] = syscall_info.entry.args[i];
  131. print_syscall_entry(entry.nr, entry.args);
  132. break;
  133. case PTRACE_SYSCALL_INFO_EXIT:
  134. if(entry.nr != SYS_execve) print_syscall_exit(entry.nr, entry.args);
  135. if(syscall_info.exit.is_error)
  136. {
  137. fprintf(out,
  138. " = (errno: %" PRIi64 ") %s\n",
  139. (int64_t)(-syscall_info.exit.rval),
  140. strerror(-syscall_info.exit.rval));
  141. }
  142. else
  143. {
  144. fprintf(out, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval);
  145. }
  146. entry.nr = 0;
  147. break;
  148. case PTRACE_SYSCALL_INFO_SECCOMP:
  149. print_syscall_exit(syscall_info.seccomp.nr, syscall_info.seccomp.args);
  150. // TODO: Check how strace manages this
  151. fprintf(out, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data);
  152. break;
  153. case PTRACE_SYSCALL_INFO_NONE:
  154. fprintf(out, "[syscall-info] none\n");
  155. break;
  156. }
  157. }
  158. if(entry.nr != 0) print_syscall_exit(entry.nr, entry.args);
  159. if(WIFEXITED(status))
  160. {
  161. fprintf(out, "+++ exited with %d +++\n", WEXITSTATUS(status));
  162. return WEXITSTATUS(status);
  163. }
  164. fflush(out);
  165. return 0;
  166. }