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
This commit is contained in:
parent
463b7715e7
commit
3f67987cdc
|
@ -73,6 +73,10 @@ thread-wide, without the need to invoke the kernel directly. selector
|
||||||
can be set to SYSCALL_DISPATCH_FILTER_ALLOW or SYSCALL_DISPATCH_FILTER_BLOCK.
|
can be set to SYSCALL_DISPATCH_FILTER_ALLOW or SYSCALL_DISPATCH_FILTER_BLOCK.
|
||||||
Any other value should terminate the program with a SIGSYS.
|
Any other value should terminate the program with a SIGSYS.
|
||||||
|
|
||||||
|
Additionally, a tasks syscall user dispatch configuration can be peeked
|
||||||
|
and poked via the PTRACE_(GET|SET)_SYSCALL_USER_DISPATCH_CONFIG ptrace
|
||||||
|
requests. This is useful for checkpoint/restart software.
|
||||||
|
|
||||||
Security Notes
|
Security Notes
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,12 @@ int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
|
||||||
#define clear_syscall_work_syscall_user_dispatch(tsk) \
|
#define clear_syscall_work_syscall_user_dispatch(tsk) \
|
||||||
clear_task_syscall_work(tsk, SYSCALL_USER_DISPATCH)
|
clear_task_syscall_work(tsk, SYSCALL_USER_DISPATCH)
|
||||||
|
|
||||||
|
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
|
||||||
|
void __user *data);
|
||||||
|
|
||||||
|
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
|
||||||
|
void __user *data);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
struct syscall_user_dispatch {};
|
struct syscall_user_dispatch {};
|
||||||
|
|
||||||
|
@ -35,6 +41,18 @@ static inline void clear_syscall_work_syscall_user_dispatch(struct task_struct *
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int syscall_user_dispatch_get_config(struct task_struct *task,
|
||||||
|
unsigned long size, void __user *data)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int syscall_user_dispatch_set_config(struct task_struct *task,
|
||||||
|
unsigned long size, void __user *data)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_GENERIC_ENTRY */
|
#endif /* CONFIG_GENERIC_ENTRY */
|
||||||
|
|
||||||
#endif /* _SYSCALL_USER_DISPATCH_H */
|
#endif /* _SYSCALL_USER_DISPATCH_H */
|
||||||
|
|
|
@ -112,6 +112,36 @@ struct ptrace_rseq_configuration {
|
||||||
__u32 pad;
|
__u32 pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG 0x4210
|
||||||
|
#define PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG 0x4211
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct ptrace_sud_config - Per-task configuration for Syscall User Dispatch
|
||||||
|
* @mode: One of PR_SYS_DISPATCH_ON or PR_SYS_DISPATCH_OFF
|
||||||
|
* @selector: Tracees user virtual address of SUD selector
|
||||||
|
* @offset: SUD exclusion area (virtual address)
|
||||||
|
* @len: Length of SUD exclusion area
|
||||||
|
*
|
||||||
|
* Used to get/set the syscall user dispatch configuration for a tracee.
|
||||||
|
* Selector is optional (may be NULL), and if invalid will produce
|
||||||
|
* a SIGSEGV in the tracee upon first access.
|
||||||
|
*
|
||||||
|
* If mode is PR_SYS_DISPATCH_ON, syscall dispatch will be enabled. If
|
||||||
|
* PR_SYS_DISPATCH_OFF, syscall dispatch will be disabled and all other
|
||||||
|
* parameters must be 0. The value in *selector (if not null), also determines
|
||||||
|
* whether syscall dispatch will occur.
|
||||||
|
*
|
||||||
|
* The Syscall User Dispatch Exclusion area described by offset/len is the
|
||||||
|
* virtual address space from which syscalls will not produce a user
|
||||||
|
* dispatch.
|
||||||
|
*/
|
||||||
|
struct ptrace_sud_config {
|
||||||
|
__u64 mode;
|
||||||
|
__u64 selector;
|
||||||
|
__u64 offset;
|
||||||
|
__u64 len;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These values are stored in task->ptrace_message
|
* These values are stored in task->ptrace_message
|
||||||
* by ptrace_stop to describe the current syscall-stop.
|
* by ptrace_stop to describe the current syscall-stop.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/prctl.h>
|
#include <linux/prctl.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
#include <linux/syscall_user_dispatch.h>
|
#include <linux/syscall_user_dispatch.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
|
@ -122,3 +123,42 @@ int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
|
||||||
{
|
{
|
||||||
return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
|
return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
|
||||||
|
void __user *data)
|
||||||
|
{
|
||||||
|
struct syscall_user_dispatch *sd = &task->syscall_dispatch;
|
||||||
|
struct ptrace_sud_config cfg;
|
||||||
|
|
||||||
|
if (size != sizeof(cfg))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
|
||||||
|
cfg.mode = PR_SYS_DISPATCH_ON;
|
||||||
|
else
|
||||||
|
cfg.mode = PR_SYS_DISPATCH_OFF;
|
||||||
|
|
||||||
|
cfg.offset = sd->offset;
|
||||||
|
cfg.len = sd->len;
|
||||||
|
cfg.selector = (__u64)(uintptr_t)sd->selector;
|
||||||
|
|
||||||
|
if (copy_to_user(data, &cfg, sizeof(cfg)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
|
||||||
|
void __user *data)
|
||||||
|
{
|
||||||
|
struct ptrace_sud_config cfg;
|
||||||
|
|
||||||
|
if (size != sizeof(cfg))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(&cfg, data, sizeof(cfg)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
|
||||||
|
(char __user *)(uintptr_t)cfg.selector);
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
#include <linux/sched/signal.h>
|
#include <linux/sched/signal.h>
|
||||||
#include <linux/minmax.h>
|
#include <linux/minmax.h>
|
||||||
|
#include <linux/syscall_user_dispatch.h>
|
||||||
|
|
||||||
#include <asm/syscall.h> /* for syscall_get_* */
|
#include <asm/syscall.h> /* for syscall_get_* */
|
||||||
|
|
||||||
|
@ -1259,6 +1260,14 @@ int ptrace_request(struct task_struct *child, long request,
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG:
|
||||||
|
ret = syscall_user_dispatch_set_config(child, addr, datavp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG:
|
||||||
|
ret = syscall_user_dispatch_get_config(child, addr, datavp);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue