Skip to content

Commit 3f67987

Browse files
Gregory PriceKAGA-KOKO
Gregory Price
authored andcommitted
ptrace: Provide set/get interface for syscall user dispatch
The syscall user dispatch configuration can only be set by the task itself, but lacks a ptrace set/get interface which makes it impossible to implement checkpoint/restore for it. Add the required ptrace requests and the get/set functions in the syscall user dispatch code to make that possible. Signed-off-by: Gregory Price <gregory.price@memverge.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Link: https://lore.kernel.org/r/20230407171834.3558-4-gregory.price@memverge.com
1 parent 463b771 commit 3f67987

File tree

5 files changed

+101
-0
lines changed

5 files changed

+101
-0
lines changed

Documentation/admin-guide/syscall-user-dispatch.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ thread-wide, without the need to invoke the kernel directly. selector
7373
can be set to SYSCALL_DISPATCH_FILTER_ALLOW or SYSCALL_DISPATCH_FILTER_BLOCK.
7474
Any other value should terminate the program with a SIGSYS.
7575

76+
Additionally, a tasks syscall user dispatch configuration can be peeked
77+
and poked via the PTRACE_(GET|SET)_SYSCALL_USER_DISPATCH_CONFIG ptrace
78+
requests. This is useful for checkpoint/restart software.
79+
7680
Security Notes
7781
--------------
7882

include/linux/syscall_user_dispatch.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
2222
#define clear_syscall_work_syscall_user_dispatch(tsk) \
2323
clear_task_syscall_work(tsk, SYSCALL_USER_DISPATCH)
2424

25+
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
26+
void __user *data);
27+
28+
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
29+
void __user *data);
30+
2531
#else
2632
struct syscall_user_dispatch {};
2733

@@ -35,6 +41,18 @@ static inline void clear_syscall_work_syscall_user_dispatch(struct task_struct *
3541
{
3642
}
3743

44+
static inline int syscall_user_dispatch_get_config(struct task_struct *task,
45+
unsigned long size, void __user *data)
46+
{
47+
return -EINVAL;
48+
}
49+
50+
static inline int syscall_user_dispatch_set_config(struct task_struct *task,
51+
unsigned long size, void __user *data)
52+
{
53+
return -EINVAL;
54+
}
55+
3856
#endif /* CONFIG_GENERIC_ENTRY */
3957

4058
#endif /* _SYSCALL_USER_DISPATCH_H */

include/uapi/linux/ptrace.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,36 @@ struct ptrace_rseq_configuration {
112112
__u32 pad;
113113
};
114114

115+
#define PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG 0x4210
116+
#define PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG 0x4211
117+
118+
/*
119+
* struct ptrace_sud_config - Per-task configuration for Syscall User Dispatch
120+
* @mode: One of PR_SYS_DISPATCH_ON or PR_SYS_DISPATCH_OFF
121+
* @selector: Tracees user virtual address of SUD selector
122+
* @offset: SUD exclusion area (virtual address)
123+
* @len: Length of SUD exclusion area
124+
*
125+
* Used to get/set the syscall user dispatch configuration for a tracee.
126+
* Selector is optional (may be NULL), and if invalid will produce
127+
* a SIGSEGV in the tracee upon first access.
128+
*
129+
* If mode is PR_SYS_DISPATCH_ON, syscall dispatch will be enabled. If
130+
* PR_SYS_DISPATCH_OFF, syscall dispatch will be disabled and all other
131+
* parameters must be 0. The value in *selector (if not null), also determines
132+
* whether syscall dispatch will occur.
133+
*
134+
* The Syscall User Dispatch Exclusion area described by offset/len is the
135+
* virtual address space from which syscalls will not produce a user
136+
* dispatch.
137+
*/
138+
struct ptrace_sud_config {
139+
__u64 mode;
140+
__u64 selector;
141+
__u64 offset;
142+
__u64 len;
143+
};
144+
115145
/*
116146
* These values are stored in task->ptrace_message
117147
* by ptrace_stop to describe the current syscall-stop.

kernel/entry/syscall_user_dispatch.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
#include <linux/sched.h>
66
#include <linux/prctl.h>
7+
#include <linux/ptrace.h>
78
#include <linux/syscall_user_dispatch.h>
89
#include <linux/uaccess.h>
910
#include <linux/signal.h>
@@ -122,3 +123,42 @@ int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
122123
{
123124
return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
124125
}
126+
127+
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
128+
void __user *data)
129+
{
130+
struct syscall_user_dispatch *sd = &task->syscall_dispatch;
131+
struct ptrace_sud_config cfg;
132+
133+
if (size != sizeof(cfg))
134+
return -EINVAL;
135+
136+
if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
137+
cfg.mode = PR_SYS_DISPATCH_ON;
138+
else
139+
cfg.mode = PR_SYS_DISPATCH_OFF;
140+
141+
cfg.offset = sd->offset;
142+
cfg.len = sd->len;
143+
cfg.selector = (__u64)(uintptr_t)sd->selector;
144+
145+
if (copy_to_user(data, &cfg, sizeof(cfg)))
146+
return -EFAULT;
147+
148+
return 0;
149+
}
150+
151+
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
152+
void __user *data)
153+
{
154+
struct ptrace_sud_config cfg;
155+
156+
if (size != sizeof(cfg))
157+
return -EINVAL;
158+
159+
if (copy_from_user(&cfg, data, sizeof(cfg)))
160+
return -EFAULT;
161+
162+
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
163+
(char __user *)(uintptr_t)cfg.selector);
164+
}

kernel/ptrace.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <linux/compat.h>
3333
#include <linux/sched/signal.h>
3434
#include <linux/minmax.h>
35+
#include <linux/syscall_user_dispatch.h>
3536

3637
#include <asm/syscall.h> /* for syscall_get_* */
3738

@@ -1259,6 +1260,14 @@ int ptrace_request(struct task_struct *child, long request,
12591260
break;
12601261
#endif
12611262

1263+
case PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG:
1264+
ret = syscall_user_dispatch_set_config(child, addr, datavp);
1265+
break;
1266+
1267+
case PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG:
1268+
ret = syscall_user_dispatch_get_config(child, addr, datavp);
1269+
break;
1270+
12621271
default:
12631272
break;
12641273
}

0 commit comments

Comments
 (0)