aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/proc/proc_sysctl.c2
-rw-r--r--include/linux/bpf-cgroup.h9
-rw-r--r--include/linux/filter.h3
-rw-r--r--include/uapi/linux/bpf.h3
-rw-r--r--kernel/bpf/cgroup.c54
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,
114int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, 114int __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
119static inline enum bpf_cgroup_storage_type cgroup_storage_type( 120static 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 = {
796int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, 799int __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;