aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-06-14 05:20:15 -0400
committerOleg Nesterov <oleg@redhat.com>2011-06-16 15:41:53 -0400
commit3544d72a0e10d0aa1c1bd59ed77a53a59cdc12f7 (patch)
tree27eb767b5cb98f58cccb5b7053a58bccd105f9d0
parent73ddff2bee159ffb580bd24faf625cd5e628f5ec (diff)
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side effects on tracee signal and job control states. This patch implements a new ptrace request PTRACE_SEIZE which attaches a tracee without trapping it or affecting its signal and job control states. The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_* flags in @data. Currently, the only defined flag is PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE. PTRACE_SEIZE will change ptrace behaviors outside of attach itself. The changes will be implemented gradually and the DEVEL flag is to prevent programs which expect full SEIZE behavior from using it before all the behavior modifications are complete while allowing unit testing. The flag will be removed once SEIZE behaviors are completely implemented. * PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After attaching tracee continues to run unless a trap condition occurs. * PTRACE_SEIZE doesn't affect signal or group stop state. * If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of the stopping signals if group stop is in effect or SIGTRAP otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO instead of NULL. Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be used to determine whether new SEIZE behaviors should be enabled. Test program follows. #define PTRACE_SEIZE 0x4206 #define PTRACE_SEIZE_DEVEL 0x80000000 static const struct timespec ts100ms = { .tv_nsec = 100000000 }; static const struct timespec ts1s = { .tv_sec = 1 }; static const struct timespec ts3s = { .tv_sec = 3 }; int main(int argc, char **argv) { pid_t tracee; tracee = fork(); if (tracee == 0) { nanosleep(&ts100ms, NULL); while (1) { printf("tracee: alive\n"); nanosleep(&ts1s, NULL); } } if (argc > 1) kill(tracee, SIGSTOP); nanosleep(&ts100ms, NULL); ptrace(PTRACE_SEIZE, tracee, NULL, (void *)(unsigned long)PTRACE_SEIZE_DEVEL); if (argc > 1) { waitid(P_PID, tracee, NULL, WSTOPPED); ptrace(PTRACE_CONT, tracee, NULL, NULL); } nanosleep(&ts3s, NULL); printf("tracer: exiting\n"); return 0; } When the above program is called w/o argument, tracee is seized while running and remains running. When tracer exits, tracee continues to run and print out messages. # ./test-seize-simple tracee: alive tracee: alive tracee: alive tracer: exiting tracee: alive tracee: alive When called with an argument, tracee is seized from stopped state and continued, and returns to stopped state when tracer exits. # ./test-seize tracee: alive tracee: alive tracee: alive tracer: exiting # ps -el|grep test-seize 1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize -v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan suggested. -v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If group stop is in effect the stop signal number is returned as part of exit_code; otherwise, SIGTRAP. This was suggested by Denys and Oleg. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Jan Kratochvil <jan.kratochvil@redhat.com> Cc: Denys Vlasenko <vda.linux@googlemail.com> Cc: Oleg Nesterov <oleg@redhat.com>
-rw-r--r--include/linux/ptrace.h7
-rw-r--r--kernel/ptrace.c35
-rw-r--r--kernel/signal.c39
3 files changed, 66 insertions, 15 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index e93ef1a54fc7..67ad3f152329 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -47,6 +47,11 @@
47#define PTRACE_GETREGSET 0x4204 47#define PTRACE_GETREGSET 0x4204
48#define PTRACE_SETREGSET 0x4205 48#define PTRACE_SETREGSET 0x4205
49 49
50#define PTRACE_SEIZE 0x4206
51
52/* flags in @data for PTRACE_SEIZE */
53#define PTRACE_SEIZE_DEVEL 0x80000000 /* temp flag for development */
54
50/* options set using PTRACE_SETOPTIONS */ 55/* options set using PTRACE_SETOPTIONS */
51#define PTRACE_O_TRACESYSGOOD 0x00000001 56#define PTRACE_O_TRACESYSGOOD 0x00000001
52#define PTRACE_O_TRACEFORK 0x00000002 57#define PTRACE_O_TRACEFORK 0x00000002
@@ -65,6 +70,7 @@
65#define PTRACE_EVENT_EXEC 4 70#define PTRACE_EVENT_EXEC 4
66#define PTRACE_EVENT_VFORK_DONE 5 71#define PTRACE_EVENT_VFORK_DONE 5
67#define PTRACE_EVENT_EXIT 6 72#define PTRACE_EVENT_EXIT 6
73#define PTRACE_EVENT_STOP 7
68 74
69#include <asm/ptrace.h> 75#include <asm/ptrace.h>
70 76
@@ -77,6 +83,7 @@
77 * flags. When the a task is stopped the ptracer owns task->ptrace. 83 * flags. When the a task is stopped the ptracer owns task->ptrace.
78 */ 84 */
79 85
86#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */
80#define PT_PTRACED 0x00000001 87#define PT_PTRACED 0x00000001
81#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ 88#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
82#define PT_TRACESYSGOOD 0x00000004 89#define PT_TRACESYSGOOD 0x00000004
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 45a8a4c5d8b2..dcf9f974198c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -209,10 +209,28 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode)
209 return !err; 209 return !err;
210} 210}
211 211
212static int ptrace_attach(struct task_struct *task) 212static int ptrace_attach(struct task_struct *task, long request,
213 unsigned long flags)
213{ 214{
215 bool seize = (request == PTRACE_SEIZE);
214 int retval; 216 int retval;
215 217
218 /*
219 * SEIZE will enable new ptrace behaviors which will be implemented
220 * gradually. SEIZE_DEVEL is used to prevent applications
221 * expecting full SEIZE behaviors trapping on kernel commits which
222 * are still in the process of implementing them.
223 *
224 * Only test programs for new ptrace behaviors being implemented
225 * should set SEIZE_DEVEL. If unset, SEIZE will fail with -EIO.
226 *
227 * Once SEIZE behaviors are completely implemented, this flag and
228 * the following test will be removed.
229 */
230 retval = -EIO;
231 if (seize && !(flags & PTRACE_SEIZE_DEVEL))
232 goto out;
233
216 audit_ptrace(task); 234 audit_ptrace(task);
217 235
218 retval = -EPERM; 236 retval = -EPERM;
@@ -244,11 +262,16 @@ static int ptrace_attach(struct task_struct *task)
244 goto unlock_tasklist; 262 goto unlock_tasklist;
245 263
246 task->ptrace = PT_PTRACED; 264 task->ptrace = PT_PTRACED;
265 if (seize)
266 task->ptrace |= PT_SEIZED;
247 if (task_ns_capable(task, CAP_SYS_PTRACE)) 267 if (task_ns_capable(task, CAP_SYS_PTRACE))
248 task->ptrace |= PT_PTRACE_CAP; 268 task->ptrace |= PT_PTRACE_CAP;
249 269
250 __ptrace_link(task, current); 270 __ptrace_link(task, current);
251 send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); 271
272 /* SEIZE doesn't trap tracee on attach */
273 if (!seize)
274 send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
252 275
253 spin_lock(&task->sighand->siglock); 276 spin_lock(&task->sighand->siglock);
254 277
@@ -785,8 +808,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
785 goto out; 808 goto out;
786 } 809 }
787 810
788 if (request == PTRACE_ATTACH) { 811 if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
789 ret = ptrace_attach(child); 812 ret = ptrace_attach(child, request, data);
790 /* 813 /*
791 * Some architectures need to do book-keeping after 814 * Some architectures need to do book-keeping after
792 * a ptrace attach. 815 * a ptrace attach.
@@ -927,8 +950,8 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
927 goto out; 950 goto out;
928 } 951 }
929 952
930 if (request == PTRACE_ATTACH) { 953 if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
931 ret = ptrace_attach(child); 954 ret = ptrace_attach(child, request, data);
932 /* 955 /*
933 * Some architectures need to do book-keeping after 956 * Some architectures need to do book-keeping after
934 * a ptrace attach. 957 * a ptrace attach.
diff --git a/kernel/signal.c b/kernel/signal.c
index b5f55ca1f43f..589292f38530 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1873,21 +1873,26 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1873 recalc_sigpending_tsk(current); 1873 recalc_sigpending_tsk(current);
1874} 1874}
1875 1875
1876void ptrace_notify(int exit_code) 1876static void ptrace_do_notify(int signr, int exit_code, int why)
1877{ 1877{
1878 siginfo_t info; 1878 siginfo_t info;
1879 1879
1880 BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
1881
1882 memset(&info, 0, sizeof info); 1880 memset(&info, 0, sizeof info);
1883 info.si_signo = SIGTRAP; 1881 info.si_signo = signr;
1884 info.si_code = exit_code; 1882 info.si_code = exit_code;
1885 info.si_pid = task_pid_vnr(current); 1883 info.si_pid = task_pid_vnr(current);
1886 info.si_uid = current_uid(); 1884 info.si_uid = current_uid();
1887 1885
1888 /* Let the debugger run. */ 1886 /* Let the debugger run. */
1887 ptrace_stop(exit_code, why, 1, &info);
1888}
1889
1890void ptrace_notify(int exit_code)
1891{
1892 BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
1893
1889 spin_lock_irq(&current->sighand->siglock); 1894 spin_lock_irq(&current->sighand->siglock);
1890 ptrace_stop(exit_code, CLD_TRAPPED, 1, &info); 1895 ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
1891 spin_unlock_irq(&current->sighand->siglock); 1896 spin_unlock_irq(&current->sighand->siglock);
1892} 1897}
1893 1898
@@ -2017,7 +2022,13 @@ static bool do_signal_stop(int signr)
2017/** 2022/**
2018 * do_jobctl_trap - take care of ptrace jobctl traps 2023 * do_jobctl_trap - take care of ptrace jobctl traps
2019 * 2024 *
2020 * It is currently used only to trap for group stop while ptraced. 2025 * When PT_SEIZED, it's used for both group stop and explicit
2026 * SEIZE/INTERRUPT traps. Both generate PTRACE_EVENT_STOP trap with
2027 * accompanying siginfo. If stopped, lower eight bits of exit_code contain
2028 * the stop signal; otherwise, %SIGTRAP.
2029 *
2030 * When !PT_SEIZED, it's used only for group stop trap with stop signal
2031 * number as exit_code and no siginfo.
2021 * 2032 *
2022 * CONTEXT: 2033 * CONTEXT:
2023 * Must be called with @current->sighand->siglock held, which may be 2034 * Must be called with @current->sighand->siglock held, which may be
@@ -2025,11 +2036,21 @@ static bool do_signal_stop(int signr)
2025 */ 2036 */
2026static void do_jobctl_trap(void) 2037static void do_jobctl_trap(void)
2027{ 2038{
2039 struct signal_struct *signal = current->signal;
2028 int signr = current->jobctl & JOBCTL_STOP_SIGMASK; 2040 int signr = current->jobctl & JOBCTL_STOP_SIGMASK;
2029 2041
2030 WARN_ON_ONCE(!signr); 2042 if (current->ptrace & PT_SEIZED) {
2031 ptrace_stop(signr, CLD_STOPPED, 0, NULL); 2043 if (!signal->group_stop_count &&
2032 current->exit_code = 0; 2044 !(signal->flags & SIGNAL_STOP_STOPPED))
2045 signr = SIGTRAP;
2046 WARN_ON_ONCE(!signr);
2047 ptrace_do_notify(signr, signr | (PTRACE_EVENT_STOP << 8),
2048 CLD_STOPPED);
2049 } else {
2050 WARN_ON_ONCE(!signr);
2051 ptrace_stop(signr, CLD_STOPPED, 0, NULL);
2052 current->exit_code = 0;
2053 }
2033} 2054}
2034 2055
2035static int ptrace_signal(int signr, siginfo_t *info, 2056static int ptrace_signal(int signr, siginfo_t *info,