aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Morris <james.l.morris@oracle.com>2017-11-28 20:48:48 -0500
committerJames Morris <james.l.morris@oracle.com>2017-11-28 20:48:48 -0500
commit26025bf58b9fe3806ce4f92600e96a2c214c0d9c (patch)
treee5b9fbb6661848e22324fc70678bbd432bae2688
parentcf40a76e7d5874bb25f4404eecc58a2e033af885 (diff)
parent26500475ac1b499d8636ff281311d633909f5d20 (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.h8
-rw-r--r--include/uapi/linux/ptrace.h6
-rw-r--r--kernel/ptrace.c4
-rw-r--r--kernel/seccomp.c106
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)
96extern long seccomp_get_filter(struct task_struct *task, 96extern long seccomp_get_filter(struct task_struct *task,
97 unsigned long filter_off, void __user *data); 97 unsigned long filter_off, void __user *data);
98extern long seccomp_get_metadata(struct task_struct *task,
99 unsigned long filter_off, void __user *data);
98#else 100#else
99static inline long seccomp_get_filter(struct task_struct *task, 101static 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}
106static 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
71struct 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)
981long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, 981static 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
1022out:
1023 __put_seccomp_filter(orig);
1024 return filter;
1025}
1026
1027long 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
1060out:
1044 __put_seccomp_filter(filter); 1061 __put_seccomp_filter(filter);
1045 return ret; 1062 return ret;
1063}
1046 1064
1047out: 1065long 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