seccomp: hoist out filter resolving logic
Subsystems (2)
PATCH v3 1/2 seccomp: hoist out filter resolving logic
Hoist out the nth filter resolving logic that ptrace uses into a new function. We'll use this in the next patch to implement the new PTRACE_SECCOMP_GET_FILTER_FLAGS command.
v3: * significantly revamp get_nth_filter logic (Oleg)
* rebase on 4.14-rc4, using the new __{get,put}_seccomp_filter primitives
--- kernel/seccomp.c | 77 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 32 deletions(-)
kernel/seccomp.c +45 -32
@@ -977,49 +977,68 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) }#if defined(CONFIG_SECCOMP_FILTER ) && defined(CONFIG_CHECKPOINT_RESTORE ) -long seccomp_get_filter (struct task_struct *task,unsigned long filter_off, -void __user *data) +static struct seccomp_filter *get_nth_filter (struct task_struct *task, +unsigned long filter_off) { -struct seccomp_filter *filter; -struct sock_fprog_kern *fprog; -long ret; -unsigned long count =0 ; - -if (!capable (CAP_SYS_ADMIN ) || - current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { -return -EACCES ; - } +struct seccomp_filter *orig, *filter; +unsigned long count; +/* +* Note: this is only correct because the caller should be the (ptrace) +* tracer of the task, otherwise lock_task_sighand is needed. +*/ spin_lock_irq (&task->sighand->siglock); +if (task->seccomp.mode !=SECCOMP_MODE_FILTER ) { - ret = -EINVAL ; - goto out; +spin_unlock_irq (&task->sighand->siglock); +return ERR_PTR (-EINVAL ); } - filter = task->seccomp.filter; -while (filter) { - filter = filter->prev; + orig = task->seccomp.filter; +__get_seccomp_filter (orig); +spin_unlock_irq (&task->sighand->siglock); + + count =0 ; +for (filter = orig; filter; filter = filter->prev) count++; - }if (filter_off >= count) { - ret = -ENOENT ; + filter =ERR_PTR (-ENOENT ); goto out; } - count -= filter_off; - filter = task->seccomp.filter; -while (filter && count >1 ) { - filter = filter->prev; + count -= filter_off; +for (filter = orig; filter && count >1 ; filter = filter->prev) count--; - }if (WARN_ON (count !=1 || !filter)) { -/* The filter tree shouldn't shrink while we're using it. */ - ret = -ENOENT ; + filter =ERR_PTR (-ENOENT ); goto out; } +__get_seccomp_filter (filter); + +out: +__put_seccomp_filter (orig); +return filter; +} + +long seccomp_get_filter (struct task_struct *task,unsigned long filter_off, +void __user *data) +{ +struct seccomp_filter *filter; +struct sock_fprog_kern *fprog; +long ret; + +if (!capable (CAP_SYS_ADMIN ) || + current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { +return -EACCES ; + } + + filter =get_nth_filter (task, filter_off); +if (IS_ERR (filter)) +return PTR_ERR (filter); + fprog = filter->prog->orig_prog;if (!fprog) {/* This must be a new non-cBPF filter, since we save
@@ -1034,17 +1053,11 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,if (!data) goto out; -__get_seccomp_filter (filter); -spin_unlock_irq (&task->sighand->siglock); -if (copy_to_user (data, fprog->filter,bpf_classic_proglen (fprog))) ret = -EFAULT ; -__put_seccomp_filter (filter); -return ret; - out: -spin_unlock_irq (&task->sighand->siglock); +__put_seccomp_filter (filter); return ret; }#endif
Signature · 1 line
2.11.0
PATCH v3 2/2 ptrace, seccomp: add support for retrieving seccomp metadata
With the new SECCOMP_FILTER_FLAG_LOG, we need to be able to extract these flags for checkpoint restore, since they describe the state of a filter.
So, let's add PTRACE_SECCOMP_GET_METADATA, similar to ..._GET_FILTER, which returns the metadata of the nth filter (right now, just the flags). Hopefully this will be future proof, and new per-filter metadata can be added to this struct.
v3: * use GET_METADATA instead of GET_FLAGS
--- include/linux/seccomp.h | 8 ++++++++ include/uapi/linux/ptrace.h | 6 ++++++ kernel/ptrace.c | 4 ++++ kernel/seccomp.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+)
include/linux/seccomp.h +8 -0
@@ -94,11 +94,19 @@ static inline void get_seccomp_filter(struct task_struct *tsk)#if defined(CONFIG_SECCOMP_FILTER ) && defined(CONFIG_CHECKPOINT_RESTORE )extern long seccomp_get_filter (struct task_struct *task,unsigned long filter_off,void __user *data); +extern long seccomp_get_metadata (struct task_struct *task, +unsigned long filter_off,void __user *data);#else static inline long seccomp_get_filter (struct task_struct *task,unsigned long n,void __user *data) {return -EINVAL ; } +static inline long seccomp_get_metadata (struct task_struct *task, +unsigned long filter_off, +void __user *data) +{ +return -EINVAL ; +}#endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */ #endif /* _LINUX_SECCOMP_H */
include/uapi/linux/ptrace.h +6 -0
@@ -65,6 +65,12 @@ struct ptrace_peeksiginfo_args {#define PTRACE_SETSIGMASK 0x420b#define PTRACE_SECCOMP_GET_FILTER 0x420c +#define PTRACE_SECCOMP_GET_METADATA 0x420d + +struct seccomp_metadata { +unsigned long filter_off;/* Input: which filter */ +unsigned int flags;/* Output: filter's flags */ +};/* Read signals from a shared (process wide) queue */ #define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
kernel/ptrace.c +4 -0
@@ -1092,6 +1092,10 @@ int ptrace_request(struct task_struct *child, long request, ret =seccomp_get_filter (child, addr, datavp);break ; +case PTRACE_SECCOMP_GET_METADATA : + ret =seccomp_get_metadata (child, addr, datavp); +break ; +default :break ; }
kernel/seccomp.c +34 -0
@@ -1060,6 +1060,40 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,__put_seccomp_filter (filter);return ret; } + +long seccomp_get_metadata (struct task_struct *task, +unsigned long size,void __user *data) +{ +long ret; +struct seccomp_filter *filter; +struct seccomp_metadata kmd = {}; + +if (!capable (CAP_SYS_ADMIN ) || + current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { +return -EACCES ; + } + + size = min_t(unsigned long, size, sizeof(kmd)); + +if (copy_from_user (&kmd, data, size)) +return -EFAULT ; + + filter =get_nth_filter (task, kmd.filter_off); +if (IS_ERR (filter)) +return PTR_ERR (filter); + +memset (&kmd,0 ,sizeof (kmd)); +if (filter->log) + kmd.flags |=SECCOMP_FILTER_FLAG_LOG ; + + ret = size; +if (copy_to_user (data, &kmd, size)) + ret = -EFAULT ; + +__put_seccomp_filter (filter); +return ret; + +}#endif #ifdef CONFIG_SYSCTL
Signature · 1 line
2.11.0
Re: [PATCH v3 2/2] ptrace, seccomp: add support for retrieving seccomp metadata
Quoted 8 lines · Tycho Andersen
With the new
SECCOMP_FILTER_FLAG_LOG, we need to be able to extract these flags for checkpoint restore, since they describe the state of a filter.So, let's add
PTRACE_SECCOMP_GET_METADATA, similar to ..._GET_FILTER, which returns the metadata of the nth filter (right now, just the flags). Hopefully this will be future proof, and new per-filter metadata can be added to this struct.
Quoted 6 lines
v3: * use
GET_METADATAinstead ofGET_FLAGSSigned-off-by Tycho Andersen <[email protected]>CC Kees Cook <[email protected]>CC Andy Lutomirski <[email protected]>CC Oleg Nesterov <[email protected]>
Applied for -next (with a minor whitespace fix).
Thanks!
-Kees
Quoted 111 lines
--- include/linux/seccomp.h | 8 ++++++++ include/uapi/linux/ptrace.h | 6 ++++++ kernel/ptrace.c | 4 ++++ kernel/seccomp.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+)include/linux/seccomp.h +8
-0@@ -94,11 +94,19 @@ static inline void get_seccomp_filter(struct task_struct *tsk)#if defined(CONFIG_SECCOMP_FILTER ) && defined(CONFIG_CHECKPOINT_RESTORE )extern long seccomp_get_filter (struct task_struct *task,unsigned long filter_off,void __user *data); +extern long seccomp_get_metadata (struct task_struct *task, +unsigned long filter_off,void __user *data);#else static inline long seccomp_get_filter (struct task_struct *task,unsigned long n,void __user *data) {return -EINVAL ; } +static inline long seccomp_get_metadata (struct task_struct *task, +unsigned long filter_off, +void __user *data) +{ +return -EINVAL ; +}#endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */ #endif /* _LINUX_SECCOMP_H */ include/uapi/linux/ptrace.h +6
-0@@ -65,6 +65,12 @@ struct ptrace_peeksiginfo_args {#define PTRACE_SETSIGMASK 0x420b#define PTRACE_SECCOMP_GET_FILTER 0x420c +#define PTRACE_SECCOMP_GET_METADATA 0x420d + +struct seccomp_metadata { +unsigned long filter_off;/* Input: which filter */ +unsigned int flags;/* Output: filter's flags */ +};/* Read signals from a shared (process wide) queue */ #define PTRACE_PEEKSIGINFO_SHARED (1 << 0)kernel/ptrace.c +4
-0@@ -1092,6 +1092,10 @@ int ptrace_request(struct task_struct *child, long request, ret =seccomp_get_filter (child, addr, datavp);break ; +case PTRACE_SECCOMP_GET_METADATA : + ret =seccomp_get_metadata (child, addr, datavp); +break ; +default :break ; }kernel/seccomp.c +34
-1@@ -1060,6 +1060,40 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,__put_seccomp_filter (filter);return ret; } + +long seccomp_get_metadata (struct task_struct *task, +unsigned long size,void __user *data) +{ +long ret; +struct seccomp_filter *filter; +struct seccomp_metadata kmd = {}; + +if (!capable (CAP_SYS_ADMIN ) || + current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { +return -EACCES ; + } + + size = min_t(unsigned long, size, sizeof(kmd)); + +if (copy_from_user (&kmd, data, size)) +return -EFAULT ; + + filter =get_nth_filter (task, kmd.filter_off); +if (IS_ERR (filter)) +return PTR_ERR (filter); + +memset (&kmd,0 ,sizeof (kmd)); +if (filter->log) + kmd.flags |=SECCOMP_FILTER_FLAG_LOG ; + + ret = size; +if (copy_to_user (data, &kmd, size)) + ret = -EFAULT ; + +__put_seccomp_filter (filter); +return ret; + +}#endif #ifdef CONFIG_SYSCTL --2.11.0
Signature · 2 lines
Kees Cook Pixel Security
Quoted 4 lines · Tycho Andersen
Hoist out the nth filter resolving logic that ptrace uses into a new function. We'll use this in the next patch to implement the new
PTRACE_SECCOMP_GET_FILTER_FLAGScommand.
Quoted 8 lines
v3: * significantly revamp
get_nth_filterlogic (Oleg)
* rebase on 4.14-rc4, using the new __{get,put}_seccomp_filter primitivesSigned-off-by Tycho Andersen <[email protected]>CC Kees Cook <[email protected]>CC Andy Lutomirski <[email protected]>CC Oleg Nesterov <[email protected]>
Sorry for the giant delay in reviewing these. This all looks good, thanks! Applying for -next.
-Kees
Quoted 124 lines
--- kernel/seccomp.c | 77 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 32 deletions(-)kernel/seccomp.c +45
-33@@ -977,49 +977,68 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) }#if defined(CONFIG_SECCOMP_FILTER ) && defined(CONFIG_CHECKPOINT_RESTORE ) -long seccomp_get_filter (struct task_struct *task,unsigned long filter_off, -void __user *data) +static struct seccomp_filter *get_nth_filter (struct task_struct *task, +unsigned long filter_off) { -struct seccomp_filter *filter; -struct sock_fprog_kern *fprog; -long ret; -unsigned long count =0 ; - -if (!capable (CAP_SYS_ADMIN ) || - current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { -return -EACCES ; - } +struct seccomp_filter *orig, *filter; +unsigned long count; +/* +* Note: this is only correct because the caller should be the (ptrace) +* tracer of the task, otherwise lock_task_sighand is needed. +*/ spin_lock_irq (&task->sighand->siglock); +if (task->seccomp.mode !=SECCOMP_MODE_FILTER ) { - ret = -EINVAL ; - goto out; +spin_unlock_irq (&task->sighand->siglock); +return ERR_PTR (-EINVAL ); } - filter = task->seccomp.filter; -while (filter) { - filter = filter->prev; + orig = task->seccomp.filter; +__get_seccomp_filter (orig); +spin_unlock_irq (&task->sighand->siglock); + + count =0 ; +for (filter = orig; filter; filter = filter->prev) count++; - }if (filter_off >= count) { - ret = -ENOENT ; + filter =ERR_PTR (-ENOENT ); goto out; } - count -= filter_off; - filter = task->seccomp.filter; -while (filter && count >1 ) { - filter = filter->prev; + count -= filter_off; +for (filter = orig; filter && count >1 ; filter = filter->prev) count--; - }if (WARN_ON (count !=1 || !filter)) { -/* The filter tree shouldn't shrink while we're using it. */ - ret = -ENOENT ; + filter =ERR_PTR (-ENOENT ); goto out; } +__get_seccomp_filter (filter); + +out: +__put_seccomp_filter (orig); +return filter; +} + +long seccomp_get_filter (struct task_struct *task,unsigned long filter_off, +void __user *data) +{ +struct seccomp_filter *filter; +struct sock_fprog_kern *fprog; +long ret; + +if (!capable (CAP_SYS_ADMIN ) || + current->seccomp.mode !=SECCOMP_MODE_DISABLED ) { +return -EACCES ; + } + + filter =get_nth_filter (task, filter_off); +if (IS_ERR (filter)) +return PTR_ERR (filter); + fprog = filter->prog->orig_prog;if (!fprog) {/* This must be a new non-cBPF filter, since we save @@ -1034,17 +1053,11 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,if (!data) goto out; -__get_seccomp_filter (filter); -spin_unlock_irq (&task->sighand->siglock); -if (copy_to_user (data, fprog->filter,bpf_classic_proglen (fprog))) ret = -EFAULT ; -__put_seccomp_filter (filter); -return ret; - out: -spin_unlock_irq (&task->sighand->siglock); +__put_seccomp_filter (filter); return ret; }#endif --2.11.0
Signature · 2 lines
Kees Cook Pixel Security