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
95 return;95 return;
96}96}
97#endif /* CONFIG_SECCOMP_FILTER */97#endif /* CONFIG_SECCOMP_FILTER */
98
99#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
100extern long seccomp_get_filter(struct task_struct *task,
101 unsigned long filter_off, void __user *data);
102#else
103static 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 */
98#endif /* _LINUX_SECCOMP_H */109#endif /* _LINUX_SECCOMP_H */
include/uapi/linux/ptrace.h+2−0
@@ -64,6 +64,8 @@ struct ptrace_peeksiginfo_args { · 63 unmodified lines
64#define PTRACE_GETSIGMASK 0x420a64#define PTRACE_GETSIGMASK 0x420a
65#define PTRACE_SETSIGMASK 0x420b65#define PTRACE_SETSIGMASK 0x420b
66 66
67#define PTRACE_SECCOMP_GET_FILTER 0x420c
68
67/* Read signals from a shared (process wide) queue */69/* Read signals from a shared (process wide) queue */
68#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)70#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
69 71
kernel/ptrace.c+5−0
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request, · 1015 unmodified lines
1016 break;1016 break;
1017 }1017 }
1018#endif1018#endif
1019
1020 case PTRACE_SECCOMP_GET_FILTER:
1021 ret = seccomp_get_filter(child, addr, datavp);
1022 break;
1023
1019 default:1024 default:
1020 break;1025 break;
1021 }1026 }
kernel/seccomp.c+75−1
@@ -347,6 +347,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) · 346 unmodified lines
347{347{
348 struct seccomp_filter *sfilter;348 struct seccomp_filter *sfilter;
349 int ret;349 int ret;
350 const bool save_orig = config_enabled(CONFIG_CHECKPOINT_RESTORE);
350 351
351 if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)352 if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
352 return ERR_PTR(-EINVAL);353 return ERR_PTR(-EINVAL);
@@ -370,7 +371,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) · 17 unmodified lines
370 return ERR_PTR(-ENOMEM);371 return ERR_PTR(-ENOMEM);
371 372
372 ret = bpf_prog_create_from_user(&sfilter->prog, fprog,373 ret = bpf_prog_create_from_user(&sfilter->prog, fprog,
373 seccomp_check_filter, false);374 seccomp_check_filter, save_orig);
374 if (ret < 0) {375 if (ret < 0) {
375 kfree(sfilter);376 kfree(sfilter);
376 return ERR_PTR(ret);377 return ERR_PTR(ret);
@@ -867,3 +868,76 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) · 490 unmodified lines
867 /* prctl interface doesn't have flags, so they are always zero. */868 /* prctl interface doesn't have flags, so they are always zero. */
868 return do_seccomp(op, 0, uargs);869 return do_seccomp(op, 0, uargs);
869}870}
871
872#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
873long 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
939out:
940 spin_unlock_irq(&task->sighand->siglock);
941 return ret;
942}
943#endif