aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Ignatov <rdna@fb.com>2019-03-07 21:50:52 -0500
committerAlexei Starovoitov <ast@kernel.org>2019-04-12 16:54:58 -0400
commite1550bfe0de47e30484ba91de1e50a91ec1c31f5 (patch)
tree417b3aa59134dff973b0b59f0cfdc1460284aed9
parent4e63acdff864654cee0ac5aaeda3913798ee78f6 (diff)
bpf: Add file_pos field to bpf_sysctl ctx
Add file_pos field to bpf_sysctl context to read and write sysctl file position at which sysctl is being accessed (read or written). The field can be used to e.g. override whole sysctl value on write to sysctl even when sys_write is called by user space with file_pos > 0. Or BPF program may reject such accesses. Signed-off-by: Andrey Ignatov <rdna@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-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;