History

seccomp, ptrace: add support for dumping seccomp filters

This patch adds support for dumping a process' (classic BPF) seccomp filters via ptrace. PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF seccomp filters. addr should be an integer which represents the ith seccomp filter (0 is the most recently installed filter). data should be a struct sock_filter * with enough room for the ith filter, or NULL, in which case the filter is not saved. The return value for this command is the number of BPF instructions the program represents, or negative in the case of errors. Command specific errors are ENOENT: which indicates that there is no ith filter in this seccomp tree, and EMEDIUMTYPE, which indicates that the ith filter was not installed as a classic BPF filter. A caveat with this approach is that there is no way to get explicitly at the heirarchy of seccomp filters, and users need to memcmp() filters to decide which are inherited. This means that a task which installs two of the same filter can potentially confuse users of this interface. v2: * make save_orig const * check that the orig_prog exists (not necessary right now, but when grows eBPF support it will be) * s/n/filter_off and make it an unsigned long to match ptrace * count "down" the tree instead of "up" when passing a filter offset v3: * don't take the current task's lock for inspecting its seccomp mode * use a 0x42** constant for the ptrace command value v4: * don't copy to userspace while holding spinlocks v5: * add another condition to WARN_ON v6: * rebase on net-next

Tycho Andersen committed 11 years ago

Commit f8e529e1 parent 5b9e3bd4 files · +93 −1Browse files

Unified · Split

4 files
  1. include/linux/seccomp.h +11 −0
  2. include/uapi/linux/ptrace.h +2 −0
  3. kernel/ptrace.c +5 −0
  4. kernel/seccomp.c +75 −1
include/linux/seccomp.h+11−0
@@ -95,4 +95,15 @@ static inline void get_seccomp_filter(struct task_struct *tsk) · 94 unmodified lines
9595 return;
9696 }
9797 #endif /* CONFIG_SECCOMP_FILTER */
98+
99+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
100+extern long seccomp_get_filter(struct task_struct *task,
101+ unsigned long filter_off, void __user *data);
102+#else
103+static inline long seccomp_get_filter(struct task_struct *task,
104+ unsigned long n, void __user *data)
105+{
106+ return -EINVAL;
107+}
108+#endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */
98109 #endif /* _LINUX_SECCOMP_H */
include/uapi/linux/ptrace.h+2−0
@@ -64,6 +64,8 @@ struct ptrace_peeksiginfo_args { · 63 unmodified lines
6464 #define PTRACE_GETSIGMASK 0x420a
6565 #define PTRACE_SETSIGMASK 0x420b
6666
67+#define PTRACE_SECCOMP_GET_FILTER 0x420c
68+
6769 /* Read signals from a shared (process wide) queue */
6870 #define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
6971
kernel/ptrace.c+5−0
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request, · 1015 unmodified lines
10161016 break;
10171017 }
10181018 #endif
1019+
1020+ case PTRACE_SECCOMP_GET_FILTER:
1021+ ret = seccomp_get_filter(child, addr, datavp);
1022+ break;
1023+
10191024 default:
10201025 break;
10211026 }
kernel/seccomp.c+75−1
@@ -347,6 +347,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) · 346 unmodified lines
347347 {
348348 struct seccomp_filter *sfilter;
349349 int ret;
350+ const bool save_orig = config_enabled(CONFIG_CHECKPOINT_RESTORE);
350351
351352 if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
352353 return ERR_PTR(-EINVAL);
@@ -370,7 +371,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) · 17 unmodified lines
370371 return ERR_PTR(-ENOMEM);
371372
372373 ret = bpf_prog_create_from_user(&sfilter->prog, fprog,
373− seccomp_check_filter, false);
374+ seccomp_check_filter, save_orig);
374375 if (ret < 0) {
375376 kfree(sfilter);
376377 return ERR_PTR(ret);
@@ -867,3 +868,76 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) · 490 unmodified lines
867868 /* prctl interface doesn't have flags, so they are always zero. */
868869 return do_seccomp(op, 0, uargs);
869870 }
871+
872+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
873+long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
874+ void __user *data)
875+{
876+ struct seccomp_filter *filter;
877+ struct sock_fprog_kern *fprog;
878+ long ret;
879+ unsigned long count = 0;
880+
881+ if (!capable(CAP_SYS_ADMIN) ||
882+ current->seccomp.mode != SECCOMP_MODE_DISABLED) {
883+ return -EACCES;
884+ }
885+
886+ spin_lock_irq(&task->sighand->siglock);
887+ if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
888+ ret = -EINVAL;
889+ goto out;
890+ }
891+
892+ filter = task->seccomp.filter;
893+ while (filter) {
894+ filter = filter->prev;
895+ count++;
896+ }
897+
898+ if (filter_off >= count) {
899+ ret = -ENOENT;
900+ goto out;
901+ }
902+ count -= filter_off;
903+
904+ filter = task->seccomp.filter;
905+ while (filter && count > 1) {
906+ filter = filter->prev;
907+ count--;
908+ }
909+
910+ if (WARN_ON(count != 1 || !filter)) {
911+ /* The filter tree shouldn't shrink while we're using it. */
912+ ret = -ENOENT;
913+ goto out;
914+ }
915+
916+ fprog = filter->prog->orig_prog;
917+ if (!fprog) {
918+ /* This must be a new non-cBPF filter, since we save every
919+ * every cBPF filter's orig_prog above when
920+ * CONFIG_CHECKPOINT_RESTORE is enabled.
921+ */
922+ ret = -EMEDIUMTYPE;
923+ goto out;
924+ }
925+
926+ ret = fprog->len;
927+ if (!data)
928+ goto out;
929+
930+ get_seccomp_filter(task);
931+ spin_unlock_irq(&task->sighand->siglock);
932+
933+ if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
934+ ret = -EFAULT;
935+
936+ put_seccomp_filter(task);
937+ return ret;
938+
939+out:
940+ spin_unlock_irq(&task->sighand->siglock);
941+ return ret;
942+}
943+#endif