diff options
author | Tycho Andersen <tycho@docker.com> | 2017-10-11 11:39:21 -0400 |
---|---|---|
committer | Kees Cook <keescook@chromium.org> | 2017-11-28 18:41:01 -0500 |
commit | 26500475ac1b499d8636ff281311d633909f5d20 (patch) | |
tree | e5b9fbb6661848e22324fc70678bbd432bae2688 | |
parent | f06eae831f0c1fc5b982ea200daf552810e1dd55 (diff) |
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.
Signed-off-by: Tycho Andersen <tycho@docker.com>
CC: Kees Cook <keescook@chromium.org>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
-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 | 33 |
4 files changed, 51 insertions, 0 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 99bddaf79076..61bd9dc260c8 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -1061,6 +1061,39 @@ out: | |||
1061 | __put_seccomp_filter(filter); | 1061 | __put_seccomp_filter(filter); |
1062 | return ret; | 1062 | return ret; |
1063 | } | 1063 | } |
1064 | |||
1065 | long seccomp_get_metadata(struct task_struct *task, | ||
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); | ||
1095 | return ret; | ||
1096 | } | ||
1064 | #endif | 1097 | #endif |
1065 | 1098 | ||
1066 | #ifdef CONFIG_SYSCTL | 1099 | #ifdef CONFIG_SYSCTL |