diff options
-rw-r--r-- | fs/proc/proc_sysctl.c | 2 | ||||
-rw-r--r-- | include/linux/bpf-cgroup.h | 9 | ||||
-rw-r--r-- | include/linux/filter.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/bpf.h | 3 | ||||
-rw-r--r-- | kernel/bpf/cgroup.c | 54 |
5 files changed, 63 insertions, 8 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 023101c6f0d7..2d61e5e8c863 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -590,7 +590,7 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, | |||
590 | goto out; | 590 | goto out; |
591 | 591 | ||
592 | error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, &count, | 592 | error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, &count, |
593 | &new_buf); | 593 | ppos, &new_buf); |
594 | if (error) | 594 | if (error) |
595 | goto out; | 595 | goto out; |
596 | 596 | ||
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 1e97271f9a10..cb3c6b3b89c8 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h | |||
@@ -114,7 +114,8 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, | |||
114 | int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, | 114 | int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, |
115 | struct ctl_table *table, int write, | 115 | struct ctl_table *table, int write, |
116 | void __user *buf, size_t *pcount, | 116 | void __user *buf, size_t *pcount, |
117 | void **new_buf, enum bpf_attach_type type); | 117 | loff_t *ppos, void **new_buf, |
118 | enum bpf_attach_type type); | ||
118 | 119 | ||
119 | static inline enum bpf_cgroup_storage_type cgroup_storage_type( | 120 | static inline enum bpf_cgroup_storage_type cgroup_storage_type( |
120 | struct bpf_map *map) | 121 | struct bpf_map *map) |
@@ -262,12 +263,12 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, | |||
262 | }) | 263 | }) |
263 | 264 | ||
264 | 265 | ||
265 | #define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, count, nbuf) \ | 266 | #define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, count, pos, nbuf) \ |
266 | ({ \ | 267 | ({ \ |
267 | int __ret = 0; \ | 268 | int __ret = 0; \ |
268 | if (cgroup_bpf_enabled) \ | 269 | if (cgroup_bpf_enabled) \ |
269 | __ret = __cgroup_bpf_run_filter_sysctl(head, table, write, \ | 270 | __ret = __cgroup_bpf_run_filter_sysctl(head, table, write, \ |
270 | buf, count, nbuf, \ | 271 | buf, count, pos, nbuf, \ |
271 | BPF_CGROUP_SYSCTL); \ | 272 | BPF_CGROUP_SYSCTL); \ |
272 | __ret; \ | 273 | __ret; \ |
273 | }) | 274 | }) |
@@ -340,7 +341,7 @@ static inline int bpf_percpu_cgroup_storage_update(struct bpf_map *map, | |||
340 | #define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; }) | 341 | #define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; }) |
341 | #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) | 342 | #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) |
342 | #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) | 343 | #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) |
343 | #define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,nbuf) ({ 0; }) | 344 | #define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,pos,nbuf) ({ 0; }) |
344 | 345 | ||
345 | #define for_each_cgroup_storage_type(stype) for (; false; ) | 346 | #define for_each_cgroup_storage_type(stype) for (; false; ) |
346 | 347 | ||
diff --git a/include/linux/filter.h b/include/linux/filter.h index a23653f9460c..fb0edad75971 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h | |||
@@ -1188,6 +1188,9 @@ struct bpf_sysctl_kern { | |||
1188 | size_t new_len; | 1188 | size_t new_len; |
1189 | int new_updated; | 1189 | int new_updated; |
1190 | int write; | 1190 | int write; |
1191 | loff_t *ppos; | ||
1192 | /* Temporary "register" for indirect stores to ppos. */ | ||
1193 | u64 tmp_reg; | ||
1191 | }; | 1194 | }; |
1192 | 1195 | ||
1193 | #endif /* __LINUX_FILTER_H__ */ | 1196 | #endif /* __LINUX_FILTER_H__ */ |
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 547b8258d731..89976de909af 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
@@ -3391,6 +3391,9 @@ struct bpf_sysctl { | |||
3391 | __u32 write; /* Sysctl is being read (= 0) or written (= 1). | 3391 | __u32 write; /* Sysctl is being read (= 0) or written (= 1). |
3392 | * Allows 1,2,4-byte read, but no write. | 3392 | * Allows 1,2,4-byte read, but no write. |
3393 | */ | 3393 | */ |
3394 | __u32 file_pos; /* Sysctl file position to read from, write to. | ||
3395 | * Allows 1,2,4-byte read an 4-byte write. | ||
3396 | */ | ||
3394 | }; | 3397 | }; |
3395 | 3398 | ||
3396 | #endif /* _UAPI__LINUX_BPF_H__ */ | 3399 | #endif /* _UAPI__LINUX_BPF_H__ */ |
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index ba4e21986760..b2adf22139b3 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c | |||
@@ -782,6 +782,9 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = { | |||
782 | * @pcount: value-result argument: value is size of buffer pointed to by @buf, | 782 | * @pcount: value-result argument: value is size of buffer pointed to by @buf, |
783 | * result is size of @new_buf if program set new value, initial value | 783 | * result is size of @new_buf if program set new value, initial value |
784 | * otherwise | 784 | * otherwise |
785 | * @ppos: value-result argument: value is position at which read from or write | ||
786 | * to sysctl is happening, result is new position if program overrode it, | ||
787 | * initial value otherwise | ||
785 | * @new_buf: pointer to pointer to new buffer that will be allocated if program | 788 | * @new_buf: pointer to pointer to new buffer that will be allocated if program |
786 | * overrides new value provided by user space on sysctl write | 789 | * overrides new value provided by user space on sysctl write |
787 | * NOTE: it's caller responsibility to free *new_buf if it was set | 790 | * NOTE: it's caller responsibility to free *new_buf if it was set |
@@ -796,12 +799,14 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = { | |||
796 | int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, | 799 | int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, |
797 | struct ctl_table *table, int write, | 800 | struct ctl_table *table, int write, |
798 | void __user *buf, size_t *pcount, | 801 | void __user *buf, size_t *pcount, |
799 | void **new_buf, enum bpf_attach_type type) | 802 | loff_t *ppos, void **new_buf, |
803 | enum bpf_attach_type type) | ||
800 | { | 804 | { |
801 | struct bpf_sysctl_kern ctx = { | 805 | struct bpf_sysctl_kern ctx = { |
802 | .head = head, | 806 | .head = head, |
803 | .table = table, | 807 | .table = table, |
804 | .write = write, | 808 | .write = write, |
809 | .ppos = ppos, | ||
805 | .cur_val = NULL, | 810 | .cur_val = NULL, |
806 | .cur_len = PAGE_SIZE, | 811 | .cur_len = PAGE_SIZE, |
807 | .new_val = NULL, | 812 | .new_val = NULL, |
@@ -1030,14 +1035,22 @@ static bool sysctl_is_valid_access(int off, int size, enum bpf_access_type type, | |||
1030 | { | 1035 | { |
1031 | const int size_default = sizeof(__u32); | 1036 | const int size_default = sizeof(__u32); |
1032 | 1037 | ||
1033 | if (off < 0 || off + size > sizeof(struct bpf_sysctl) || | 1038 | if (off < 0 || off + size > sizeof(struct bpf_sysctl) || off % size) |
1034 | off % size || type != BPF_READ) | ||
1035 | return false; | 1039 | return false; |
1036 | 1040 | ||
1037 | switch (off) { | 1041 | switch (off) { |
1038 | case offsetof(struct bpf_sysctl, write): | 1042 | case offsetof(struct bpf_sysctl, write): |
1043 | if (type != BPF_READ) | ||
1044 | return false; | ||
1039 | bpf_ctx_record_field_size(info, size_default); | 1045 | bpf_ctx_record_field_size(info, size_default); |
1040 | return bpf_ctx_narrow_access_ok(off, size, size_default); | 1046 | return bpf_ctx_narrow_access_ok(off, size, size_default); |
1047 | case offsetof(struct bpf_sysctl, file_pos): | ||
1048 | if (type == BPF_READ) { | ||
1049 | bpf_ctx_record_field_size(info, size_default); | ||
1050 | return bpf_ctx_narrow_access_ok(off, size, size_default); | ||
1051 | } else { | ||
1052 | return size == size_default; | ||
1053 | } | ||
1041 | default: | 1054 | default: |
1042 | return false; | 1055 | return false; |
1043 | } | 1056 | } |
@@ -1059,6 +1072,41 @@ static u32 sysctl_convert_ctx_access(enum bpf_access_type type, | |||
1059 | write), | 1072 | write), |
1060 | target_size)); | 1073 | target_size)); |
1061 | break; | 1074 | break; |
1075 | case offsetof(struct bpf_sysctl, file_pos): | ||
1076 | /* ppos is a pointer so it should be accessed via indirect | ||
1077 | * loads and stores. Also for stores additional temporary | ||
1078 | * register is used since neither src_reg nor dst_reg can be | ||
1079 | * overridden. | ||
1080 | */ | ||
1081 | if (type == BPF_WRITE) { | ||
1082 | int treg = BPF_REG_9; | ||
1083 | |||
1084 | if (si->src_reg == treg || si->dst_reg == treg) | ||
1085 | --treg; | ||
1086 | if (si->src_reg == treg || si->dst_reg == treg) | ||
1087 | --treg; | ||
1088 | *insn++ = BPF_STX_MEM( | ||
1089 | BPF_DW, si->dst_reg, treg, | ||
1090 | offsetof(struct bpf_sysctl_kern, tmp_reg)); | ||
1091 | *insn++ = BPF_LDX_MEM( | ||
1092 | BPF_FIELD_SIZEOF(struct bpf_sysctl_kern, ppos), | ||
1093 | treg, si->dst_reg, | ||
1094 | offsetof(struct bpf_sysctl_kern, ppos)); | ||
1095 | *insn++ = BPF_STX_MEM( | ||
1096 | BPF_SIZEOF(u32), treg, si->src_reg, 0); | ||
1097 | *insn++ = BPF_LDX_MEM( | ||
1098 | BPF_DW, treg, si->dst_reg, | ||
1099 | offsetof(struct bpf_sysctl_kern, tmp_reg)); | ||
1100 | } else { | ||
1101 | *insn++ = BPF_LDX_MEM( | ||
1102 | BPF_FIELD_SIZEOF(struct bpf_sysctl_kern, ppos), | ||
1103 | si->dst_reg, si->src_reg, | ||
1104 | offsetof(struct bpf_sysctl_kern, ppos)); | ||
1105 | *insn++ = BPF_LDX_MEM( | ||
1106 | BPF_SIZE(si->code), si->dst_reg, si->dst_reg, 0); | ||
1107 | } | ||
1108 | *target_size = sizeof(u32); | ||
1109 | break; | ||
1062 | } | 1110 | } |
1063 | 1111 | ||
1064 | return insn - insn_buf; | 1112 | return insn - insn_buf; |