aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/bpf/cgroup.c
diff options
context:
space:
mode:
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;