Das ptrace
Der Systemaufruf ermöglicht es dem Elternprozess, den angehängten Kindprozess zu inspizieren. Zum Beispiel unter Linux, strace
(was mit implementiert wird ptrace
Systemaufruf) kann die vom untergeordneten Prozess aufgerufenen Systemaufrufe überprüfen.
Wenn der angehängte untergeordnete Prozess einen Systemaufruf aufruft, kann der ptracing-Elternprozess benachrichtigt werden. Aber wie genau passiert das? Ich möchte die technischen Details hinter diesem Mechanismus erfahren.
Danke im Voraus.
Wenn der angehängte untergeordnete Prozess einen Systemaufruf aufruft, kann der ptracing-Elternprozess benachrichtigt werden. Aber wie genau passiert das?
Übergeordnete Prozessaufrufe ptrace
mit PTRACE_ATTACH
und sein Kind ruft ptrace
mit PTRACE_TRACEME
Möglichkeit. Dieses Paar verbindet zwei Prozesse, indem es einige Felder darin ausfüllt task_struct
(Kernel/ptrace.c: sys_ptraceKind wird haben PT_PTRACED
Flagge ein ptrace
Bereich struct task_struct
und pid des Ptracer-Prozesses als Elternteil und in ptrace_entry
aufführen – __ptrace_link
; Der Elternteil zeichnet die PID des Kindes auf ptraced
aufführen).
Dann ruft Strace an ptrace
mit PTRACE_SYSCALL
Flag, um sich selbst als Syscall-Debugger zu registrieren, indem thread_flag gesetzt wird TIF_SYSCALL_TRACE
in untergeordneten Prozessen struct thread_info
(durch so etwas wie set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
). arch/x86/include/asm/thread_info.h
:
67 /*
68 * thread information flags
69 * - these are process state flags that various assembly files
70 * may need to access ...*/
75 #define TIF_SYSCALL_TRACE 0 /* syscall trace active */
99 #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
Bei jedem Syscall-Eintritt oder -Austritt prüft der architekturspezifische Syscall-Eintrittscode dies _TIF_SYSCALL_TRACE
Flagge (direkt in der Assembler-Implementierung von syscall, zum Beispiel x86 arch/x86/kernel/entry_32.S
: jnz syscall_trace_entry
in ENTRY(system_call)
und ähnlichen Code in syscall_exit_work
), und wenn es gesetzt ist, wird Ptracer mit einem Signal (SIGTRAP) benachrichtigt und das Kind wird vorübergehend gestoppt. Dies geschieht in der Regel in syscall_trace_enter
und syscall_trace_leave
:
1457 long syscall_trace_enter(struct pt_regs *regs)
1483 if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
1484 tracehook_report_syscall_entry(regs))
1485 ret = -1L;
1507 void syscall_trace_leave(struct pt_regs *regs)
1531 if (step || test_thread_flag(TIF_SYSCALL_TRACE))
1532 tracehook_report_syscall_exit(regs, step);
Das tracehook_report_syscall_*
Sind echte Arbeiter hier, werden sie anrufen ptrace_report_syscall
. include/linux/tracehook.h
:
80 /**
81 * tracehook_report_syscall_entry - task is about to attempt a system call
82 * @regs: user register state of current task
83 *
84 * This will be called if %TIF_SYSCALL_TRACE has been set, when the
85 * current task has just entered the kernel for a system call.
86 * Full user register state is available here. Changing the values
87 * in @regs can affect the system call number and arguments to be tried.
88 * It is safe to block here, preventing the system call from beginning.
89 *
90 * Returns zero normally, or nonzero if the calling arch code should abort
91 * the system call. That must prevent normal entry so no system call is
92 * made. If @task ever returns to user mode after this, its register state
93 * is unspecified, but should be something harmless like an %ENOSYS error
94 * return. It should preserve enough information so that syscall_rollback()
95 * can work (see asm-generic/syscall.h).
96 *
97 * Called without locks, just after entering kernel mode.
98 */
99 static inline __must_check int tracehook_report_syscall_entry(
100 struct pt_regs *regs)
101 {
102 return ptrace_report_syscall(regs);
103 }
104
105 /**
106 * tracehook_report_syscall_exit - task has just finished a system call
107 * @regs: user register state of current task
108 * @step: nonzero if simulating single-step or block-step
109 *
110 * This will be called if %TIF_SYSCALL_TRACE has been set, when the
111 * current task has just finished an attempted system call. Full
112 * user register state is available here. It is safe to block here,
113 * preventing signals from being processed.
114 *
115 * If @step is nonzero, this report is also in lieu of the normal
116 * trap that would follow the system call instruction because
117 * user_enable_block_step() or user_enable_single_step() was used.
118 * In this case, %TIF_SYSCALL_TRACE might not be set.
119 *
120 * Called without locks, just before checking for pending signals.
121 */
122 static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
123 {
...
130
131 ptrace_report_syscall(regs);
132 }
Und ptrace_report_syscall
generiert SIGTRAP für Debugger oder strace über ptrace_notify
/ptrace_do_notify
:
55 /*
56 * ptrace report for syscall entry and exit looks identical.
57 */
58 static inline int ptrace_report_syscall(struct pt_regs *regs)
59 {
60 int ptrace = current->ptrace;
61
62 if (!(ptrace & PT_PTRACED))
63 return 0;
64
65 ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
66
67 /*
68 * this isn't the same as continuing with a signal, but it will do
69 * for normal use. strace only continues with a signal if the
70 * stopping signal is not SIGTRAP. -brl
71 */
72 if (current->exit_code) {
73 send_sig(current->exit_code, current, 1);
74 current->exit_code = 0;
75 }
76
77 return fatal_signal_pending(current);
78 }
ptrace_notify
ist implementiert in kernel/signal.c
stoppt es das Kind und übergibt sig_info an ptracer:
1961 static void ptrace_do_notify(int signr, int exit_code, int why)
1962 {
1963 siginfo_t info;
1964
1965 memset(&info, 0, sizeof info);
1966 info.si_signo = signr;
1967 info.si_code = exit_code;
1968 info.si_pid = task_pid_vnr(current);
1969 info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
1970
1971 /* Let the debugger run. */
1972 ptrace_stop(exit_code, why, 1, &info);
1973 }
1974
1975 void ptrace_notify(int exit_code)
1976 {
1977 BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
1978 if (unlikely(current->task_works))
1979 task_work_run();
1980
1981 spin_lock_irq(¤t->sighand->siglock);
1982 ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
1983 spin_unlock_irq(¤t->sighand->siglock);
1984 }
ptrace_stop
ist im selben signal.c
Datei, Zeile 1839 für 3.13.
man7.org/linux/man-pages/man2/ptrace.2.html ist nützlich
– Tristan
29. Mai 2014 um 8:07 Uhr
@tristan: Soweit ich weiß, möchte das OP etwas über die Mechanismen wissen, die dies ermöglichen, nicht wirklich über die Verwendung.
– Blagovest Buyukliev
29. Mai 2014 um 8:09 Uhr
@Blagovest Buyukliev: danke. das ist genau das was ich will. Ich weiß, wie man Ptrace benutzt. Ich möchte sein internes Prinzip kennen
– Daehe
29. Mai 2014 um 8:23 Uhr