Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'next-seccomp' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull seccomp updates from James Morris:
"Add support for retrieving seccomp metadata"

* 'next-seccomp' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
ptrace, seccomp: add support for retrieving seccomp metadata
seccomp: hoist out filter resolving logic

+99 -35
+8
include/linux/seccomp.h
··· 95 95 #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) 96 96 extern long seccomp_get_filter(struct task_struct *task, 97 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 100 #else 99 101 static inline long seccomp_get_filter(struct task_struct *task, 100 102 unsigned long n, void __user *data) 103 + { 104 + return -EINVAL; 105 + } 106 + static inline long seccomp_get_metadata(struct task_struct *task, 107 + unsigned long filter_off, 108 + void __user *data) 101 109 { 102 110 return -EINVAL; 103 111 }
+6
include/uapi/linux/ptrace.h
··· 66 66 #define PTRACE_SETSIGMASK 0x420b 67 67 68 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 76 /* Read signals from a shared (process wide) queue */ 71 77 #define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
+4
kernel/ptrace.c
··· 1092 1092 ret = seccomp_get_filter(child, addr, datavp); 1093 1093 break; 1094 1094 1095 + case PTRACE_SECCOMP_GET_METADATA: 1096 + ret = seccomp_get_metadata(child, addr, datavp); 1097 + break; 1098 + 1095 1099 default: 1096 1100 break; 1097 1101 }
+81 -35
kernel/seccomp.c
··· 978 978 } 979 979 980 980 #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) 981 + static struct seccomp_filter *get_nth_filter(struct task_struct *task, 982 + unsigned long filter_off) 983 + { 984 + struct seccomp_filter *orig, *filter; 985 + unsigned long count; 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 + */ 991 + spin_lock_irq(&task->sighand->siglock); 992 + 993 + if (task->seccomp.mode != SECCOMP_MODE_FILTER) { 994 + spin_unlock_irq(&task->sighand->siglock); 995 + return ERR_PTR(-EINVAL); 996 + } 997 + 998 + orig = task->seccomp.filter; 999 + __get_seccomp_filter(orig); 1000 + spin_unlock_irq(&task->sighand->siglock); 1001 + 1002 + count = 0; 1003 + for (filter = orig; filter; filter = filter->prev) 1004 + count++; 1005 + 1006 + if (filter_off >= count) { 1007 + filter = ERR_PTR(-ENOENT); 1008 + goto out; 1009 + } 1010 + 1011 + count -= filter_off; 1012 + for (filter = orig; filter && count > 1; filter = filter->prev) 1013 + count--; 1014 + 1015 + if (WARN_ON(count != 1 || !filter)) { 1016 + filter = ERR_PTR(-ENOENT); 1017 + goto out; 1018 + } 1019 + 1020 + __get_seccomp_filter(filter); 1021 + 1022 + out: 1023 + __put_seccomp_filter(orig); 1024 + return filter; 1025 + } 1026 + 981 1027 long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, 982 1028 void __user *data) 983 1029 { 984 1030 struct seccomp_filter *filter; 985 1031 struct sock_fprog_kern *fprog; 986 1032 long ret; 987 - unsigned long count = 0; 988 1033 989 1034 if (!capable(CAP_SYS_ADMIN) || 990 1035 current->seccomp.mode != SECCOMP_MODE_DISABLED) { 991 1036 return -EACCES; 992 1037 } 993 1038 994 - spin_lock_irq(&task->sighand->siglock); 995 - if (task->seccomp.mode != SECCOMP_MODE_FILTER) { 996 - ret = -EINVAL; 997 - goto out; 998 - } 999 - 1000 - filter = task->seccomp.filter; 1001 - while (filter) { 1002 - filter = filter->prev; 1003 - count++; 1004 - } 1005 - 1006 - if (filter_off >= count) { 1007 - ret = -ENOENT; 1008 - goto out; 1009 - } 1010 - count -= filter_off; 1011 - 1012 - filter = task->seccomp.filter; 1013 - while (filter && count > 1) { 1014 - filter = filter->prev; 1015 - count--; 1016 - } 1017 - 1018 - if (WARN_ON(count != 1 || !filter)) { 1019 - /* The filter tree shouldn't shrink while we're using it. */ 1020 - ret = -ENOENT; 1021 - goto out; 1022 - } 1039 + filter = get_nth_filter(task, filter_off); 1040 + if (IS_ERR(filter)) 1041 + return PTR_ERR(filter); 1023 1042 1024 1043 fprog = filter->prog->orig_prog; 1025 1044 if (!fprog) { ··· 1054 1035 if (!data) 1055 1036 goto out; 1056 1037 1057 - __get_seccomp_filter(filter); 1058 - spin_unlock_irq(&task->sighand->siglock); 1059 - 1060 1038 if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog))) 1061 1039 ret = -EFAULT; 1062 1040 1041 + out: 1063 1042 __put_seccomp_filter(filter); 1064 1043 return ret; 1044 + } 1065 1045 1066 - out: 1067 - spin_unlock_irq(&task->sighand->siglock); 1046 + long seccomp_get_metadata(struct task_struct *task, 1047 + unsigned long size, void __user *data) 1048 + { 1049 + long ret; 1050 + struct seccomp_filter *filter; 1051 + struct seccomp_metadata kmd = {}; 1052 + 1053 + if (!capable(CAP_SYS_ADMIN) || 1054 + current->seccomp.mode != SECCOMP_MODE_DISABLED) { 1055 + return -EACCES; 1056 + } 1057 + 1058 + size = min_t(unsigned long, size, sizeof(kmd)); 1059 + 1060 + if (copy_from_user(&kmd, data, size)) 1061 + return -EFAULT; 1062 + 1063 + filter = get_nth_filter(task, kmd.filter_off); 1064 + if (IS_ERR(filter)) 1065 + return PTR_ERR(filter); 1066 + 1067 + memset(&kmd, 0, sizeof(kmd)); 1068 + if (filter->log) 1069 + kmd.flags |= SECCOMP_FILTER_FLAG_LOG; 1070 + 1071 + ret = size; 1072 + if (copy_to_user(data, &kmd, size)) 1073 + ret = -EFAULT; 1074 + 1075 + __put_seccomp_filter(filter); 1068 1076 return ret; 1069 1077 } 1070 1078 #endif