aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/bpf/cgroup.c
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 /kernel/bpf/cgroup.c
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>
Diffstat (limited to 'kernel/bpf/cgroup.c')
-rw-r--r--kernel/bpf/cgroup.c54
1 files changed, 51 insertions, 3 deletions
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;