diff options
author | James Morris <james.l.morris@oracle.com> | 2017-11-28 20:48:48 -0500 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2017-11-28 20:48:48 -0500 |
commit | 26025bf58b9fe3806ce4f92600e96a2c214c0d9c (patch) | |
tree | e5b9fbb6661848e22324fc70678bbd432bae2688 | |
parent | cf40a76e7d5874bb25f4404eecc58a2e033af885 (diff) | |
parent | 26500475ac1b499d8636ff281311d633909f5d20 (diff) |
Merge tag 'seccomp-next' of https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux into next-seccomp
add support for retrieving seccomp filter metadata (Tycho Andersen)
-rw-r--r-- | include/linux/seccomp.h | 8 | ||||
-rw-r--r-- | include/uapi/linux/ptrace.h | 6 | ||||
-rw-r--r-- | kernel/ptrace.c | 4 | ||||
-rw-r--r-- | kernel/seccomp.c | 106 |
4 files changed, 94 insertions, 30 deletions
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 10f25f7e4304..c723a5c4e3ff 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h | |||
@@ -95,11 +95,19 @@ static inline void get_seccomp_filter(struct task_struct *tsk) | |||
95 | #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) | 95 | #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) |
96 | extern long seccomp_get_filter(struct task_struct *task, | 96 | extern long seccomp_get_filter(struct task_struct *task, |
97 | unsigned long filter_off, void __user *data); | 97 | unsigned long filter_off, void __user *data); |
98 | extern long seccomp_get_metadata(struct task_struct *task, | ||
99 | unsigned long filter_off, void __user *data); | ||
98 | #else | 100 | #else |
99 | static inline long seccomp_get_filter(struct task_struct *task, | 101 | static inline long seccomp_get_filter(struct task_struct *task, |
100 | unsigned long n, void __user *data) | 102 | unsigned long n, void __user *data) |
101 | { | 103 | { |
102 | return -EINVAL; | 104 | return -EINVAL; |
103 | } | 105 | } |
106 | static inline long seccomp_get_metadata(struct task_struct *task, | ||
107 | unsigned long filter_off, | ||
108 | void __user *data) | ||
109 | { | ||
110 | return -EINVAL; | ||
111 | } | ||
104 | #endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */ | 112 | #endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */ |
105 | #endif /* _LINUX_SECCOMP_H */ | 113 | #endif /* _LINUX_SECCOMP_H */ |
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index e3939e00980b..e46d82b91166 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h | |||
@@ -66,6 +66,12 @@ struct ptrace_peeksiginfo_args { | |||
66 | #define PTRACE_SETSIGMASK 0x420b | 66 | #define PTRACE_SETSIGMASK 0x420b |
67 | 67 | ||
68 | #define PTRACE_SECCOMP_GET_FILTER 0x420c | 68 | #define PTRACE_SECCOMP_GET_FILTER 0x420c |
69 | #define PTRACE_SECCOMP_GET_METADATA 0x420d | ||
70 | |||
71 | struct seccomp_metadata { | ||
72 | unsigned long filter_off; /* Input: which filter */ | ||
73 | unsigned int flags; /* Output: filter's flags */ | ||
74 | }; | ||
69 | 75 | ||
70 | /* Read signals from a shared (process wide) queue */ | 76 | /* Read signals from a shared (process wide) queue */ |
71 | #define PTRACE_PEEKSIGINFO_SHARED (1 << 0) | 77 | #define PTRACE_PEEKSIGINFO_SHARED (1 << 0) |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 84b1367935e4..58291e9f3276 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -1092,6 +1092,10 @@ int ptrace_request(struct task_struct *child, long request, | |||
1092 | ret = seccomp_get_filter(child, addr, datavp); | 1092 | ret = seccomp_get_filter(child, addr, datavp); |
1093 | break; | 1093 | break; |
1094 | 1094 | ||
1095 | case PTRACE_SECCOMP_GET_METADATA: | ||
1096 | ret = seccomp_get_metadata(child, addr, datavp); | ||
1097 | break; | ||
1098 | |||
1095 | default: | 1099 | default: |
1096 | break; | 1100 | break; |
1097 | } | 1101 | } |
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 5f0dfb2abb8d..61bd9dc260c8 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -978,49 +978,68 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) | |||
978 | } | 978 | } |
979 | 979 | ||
980 | #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) | 980 | #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) |
981 | long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, | 981 | static struct seccomp_filter *get_nth_filter(struct task_struct *task, |
982 | void __user *data) | 982 | unsigned long filter_off) |
983 | { | 983 | { |
984 | struct seccomp_filter *filter; | 984 | struct seccomp_filter *orig, *filter; |
985 | struct sock_fprog_kern *fprog; | 985 | unsigned long count; |
986 | long ret; | ||
987 | unsigned long count = 0; | ||
988 | |||
989 | if (!capable(CAP_SYS_ADMIN) || | ||
990 | current->seccomp.mode != SECCOMP_MODE_DISABLED) { | ||
991 | return -EACCES; | ||
992 | } | ||
993 | 986 | ||
987 | /* | ||
988 | * Note: this is only correct because the caller should be the (ptrace) | ||
989 | * tracer of the task, otherwise lock_task_sighand is needed. | ||
990 | */ | ||
994 | spin_lock_irq(&task->sighand->siglock); | 991 | spin_lock_irq(&task->sighand->siglock); |
992 | |||
995 | if (task->seccomp.mode != SECCOMP_MODE_FILTER) { | 993 | if (task->seccomp.mode != SECCOMP_MODE_FILTER) { |
996 | ret = -EINVAL; | 994 | spin_unlock_irq(&task->sighand->siglock); |
997 | goto out; | 995 | return ERR_PTR(-EINVAL); |
998 | } | 996 | } |
999 | 997 | ||
1000 | filter = task->seccomp.filter; | 998 | orig = task->seccomp.filter; |
1001 | while (filter) { | 999 | __get_seccomp_filter(orig); |
1002 | filter = filter->prev; | 1000 | spin_unlock_irq(&task->sighand->siglock); |
1001 | |||
1002 | count = 0; | ||
1003 | for (filter = orig; filter; filter = filter->prev) | ||
1003 | count++; | 1004 | count++; |
1004 | } | ||
1005 | 1005 | ||
1006 | if (filter_off >= count) { | 1006 | if (filter_off >= count) { |
1007 | ret = -ENOENT; | 1007 | filter = ERR_PTR(-ENOENT); |
1008 | goto out; | 1008 | goto out; |
1009 | } | 1009 | } |
1010 | count -= filter_off; | ||
1011 | 1010 | ||
1012 | filter = task->seccomp.filter; | 1011 | count -= filter_off; |
1013 | while (filter && count > 1) { | 1012 | for (filter = orig; filter && count > 1; filter = filter->prev) |
1014 | filter = filter->prev; | ||
1015 | count--; | 1013 | count--; |
1016 | } | ||
1017 | 1014 | ||
1018 | if (WARN_ON(count != 1 || !filter)) { | 1015 | if (WARN_ON(count != 1 || !filter)) { |
1019 | /* The filter tree shouldn't shrink while we're using it. */ | 1016 | filter = ERR_PTR(-ENOENT); |
1020 | ret = -ENOENT; | ||
1021 | goto out; | 1017 | goto out; |
1022 | } | 1018 | } |
1023 | 1019 | ||
1020 | __get_seccomp_filter(filter); | ||
1021 | |||
1022 | out: | ||
1023 | __put_seccomp_filter(orig); | ||
1024 | return filter; | ||
1025 | } | ||
1026 | |||
1027 | long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, | ||
1028 | void __user *data) | ||
1029 | { | ||
1030 | struct seccomp_filter *filter; | ||
1031 | struct sock_fprog_kern *fprog; | ||
1032 | long ret; | ||
1033 | |||
1034 | if (!capable(CAP_SYS_ADMIN) || | ||
1035 | current->seccomp.mode != SECCOMP_MODE_DISABLED) { | ||
1036 | return -EACCES; | ||
1037 | } | ||
1038 | |||
1039 | filter = get_nth_filter(task, filter_off); | ||
1040 | if (IS_ERR(filter)) | ||
1041 | return PTR_ERR(filter); | ||
1042 | |||
1024 | fprog = filter->prog->orig_prog; | 1043 | fprog = filter->prog->orig_prog; |
1025 | if (!fprog) { | 1044 | if (!fprog) { |
1026 | /* This must be a new non-cBPF filter, since we save | 1045 | /* This must be a new non-cBPF filter, since we save |
@@ -1035,17 +1054,44 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, | |||
1035 | if (!data) | 1054 | if (!data) |
1036 | goto out; | 1055 | goto out; |
1037 | 1056 | ||
1038 | __get_seccomp_filter(filter); | ||
1039 | spin_unlock_irq(&task->sighand->siglock); | ||
1040 | |||
1041 | if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog))) | 1057 | if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog))) |
1042 | ret = -EFAULT; | 1058 | ret = -EFAULT; |
1043 | 1059 | ||
1060 | out: | ||
1044 | __put_seccomp_filter(filter); | 1061 | __put_seccomp_filter(filter); |
1045 | return ret; | 1062 | return ret; |
1063 | } | ||
1046 | 1064 | ||
1047 | out: | 1065 | long seccomp_get_metadata(struct task_struct *task, |
1048 | spin_unlock_irq(&task->sighand->siglock); | 1066 | unsigned long size, void __user *data) |
1067 | { | ||
1068 | long ret; | ||
1069 | struct seccomp_filter *filter; | ||
1070 | struct seccomp_metadata kmd = {}; | ||
1071 | |||
1072 | if (!capable(CAP_SYS_ADMIN) || | ||
1073 | current->seccomp.mode != SECCOMP_MODE_DISABLED) { | ||
1074 | return -EACCES; | ||
1075 | } | ||
1076 | |||
1077 | size = min_t(unsigned long, size, sizeof(kmd)); | ||
1078 | |||
1079 | if (copy_from_user(&kmd, data, size)) | ||
1080 | return -EFAULT; | ||
1081 | |||
1082 | filter = get_nth_filter(task, kmd.filter_off); | ||
1083 | if (IS_ERR(filter)) | ||
1084 | return PTR_ERR(filter); | ||
1085 | |||
1086 | memset(&kmd, 0, sizeof(kmd)); | ||
1087 | if (filter->log) | ||
1088 | kmd.flags |= SECCOMP_FILTER_FLAG_LOG; | ||
1089 | |||
1090 | ret = size; | ||
1091 | if (copy_to_user(data, &kmd, size)) | ||
1092 | ret = -EFAULT; | ||
1093 | |||
1094 | __put_seccomp_filter(filter); | ||
1049 | return ret; | 1095 | return ret; |
1050 | } | 1096 | } |
1051 | #endif | 1097 | #endif |