The ctx structure passed into bpf programs is different depending on bpf program type. The verifier incorrectly marked ctx->data and ctx->data_end access based on ctx offset only. That caused loads in tracing programs int bpf_prog(struct pt_regs *ctx) { .. ctx->ax .. } to be incorrectly marked as PTR_TO_PACKET which later caused verifier to reject the program that was actually valid in tracing context. Fix this by doing program type specific matching of ctx offsets.
Unified · Split
| @@ -111,6 +111,31 @@ enum bpf_access_type { · 110 unmodified lines | ||
| 111 | 111 | BPF_WRITE = 2 |
| 112 | 112 | }; |
| 113 | 113 | |
| 114 | +/* types of values stored in eBPF registers */ | |
| 115 | +enum bpf_reg_type { | |
| 116 | + NOT_INIT = 0, /* nothing was written into register */ | |
| 117 | + UNKNOWN_VALUE, /* reg doesn't contain a valid pointer */ | |
| 118 | + PTR_TO_CTX, /* reg points to bpf_context */ | |
| 119 | + CONST_PTR_TO_MAP, /* reg points to struct bpf_map */ | |
| 120 | + PTR_TO_MAP_VALUE, /* reg points to map element value */ | |
| 121 | + PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */ | |
| 122 | + FRAME_PTR, /* reg == frame_pointer */ | |
| 123 | + PTR_TO_STACK, /* reg == frame_pointer + imm */ | |
| 124 | + CONST_IMM, /* constant integer value */ | |
| 125 | + | |
| 126 | + /* PTR_TO_PACKET represents: | |
| 127 | + * skb->data | |
| 128 | + * skb->data + imm | |
| 129 | + * skb->data + (u16) var | |
| 130 | + * skb->data + (u16) var + imm | |
| 131 | + * if (range > 0) then [ptr, ptr + range - off) is safe to access | |
| 132 | + * if (id > 0) means that some 'var' was added | |
| 133 | + * if (off > 0) menas that 'imm' was added | |
| 134 | + */ | |
| 135 | + PTR_TO_PACKET, | |
| 136 | + PTR_TO_PACKET_END, /* skb->data + headlen */ | |
| 137 | +}; | |
| 138 | + | |
| 114 | 139 | struct bpf_prog; |
| 115 | 140 | |
| 116 | 141 | struct bpf_verifier_ops { |
| @@ -120,7 +145,8 @@ struct bpf_verifier_ops { · 3 unmodified lines | ||
| 120 | 145 | /* return true if 'size' wide access at offset 'off' within bpf_context |
| 121 | 146 | * with 'type' (read or write) is allowed |
| 122 | 147 | */ |
| 123 | − bool (*is_valid_access)(int off, int size, enum bpf_access_type type); | |
| 148 | + bool (*is_valid_access)(int off, int size, enum bpf_access_type type, | |
| 149 | + enum bpf_reg_type *reg_type); | |
| 124 | 150 | |
| 125 | 151 | u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg, |
| 126 | 152 | int src_reg, int ctx_off, |
| @@ -126,31 +126,6 @@ · 125 unmodified lines | ||
| 126 | 126 | * are set to NOT_INIT to indicate that they are no longer readable. |
| 127 | 127 | */ |
| 128 | 128 | |
| 129 | −/* types of values stored in eBPF registers */ | |
| 130 | −enum bpf_reg_type { | |
| 131 | − NOT_INIT = 0, /* nothing was written into register */ | |
| 132 | − UNKNOWN_VALUE, /* reg doesn't contain a valid pointer */ | |
| 133 | − PTR_TO_CTX, /* reg points to bpf_context */ | |
| 134 | − CONST_PTR_TO_MAP, /* reg points to struct bpf_map */ | |
| 135 | − PTR_TO_MAP_VALUE, /* reg points to map element value */ | |
| 136 | − PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */ | |
| 137 | − FRAME_PTR, /* reg == frame_pointer */ | |
| 138 | − PTR_TO_STACK, /* reg == frame_pointer + imm */ | |
| 139 | − CONST_IMM, /* constant integer value */ | |
| 140 | − | |
| 141 | − /* PTR_TO_PACKET represents: | |
| 142 | − * skb->data | |
| 143 | − * skb->data + imm | |
| 144 | − * skb->data + (u16) var | |
| 145 | − * skb->data + (u16) var + imm | |
| 146 | − * if (range > 0) then [ptr, ptr + range - off) is safe to access | |
| 147 | − * if (id > 0) means that some 'var' was added | |
| 148 | − * if (off > 0) menas that 'imm' was added | |
| 149 | − */ | |
| 150 | − PTR_TO_PACKET, | |
| 151 | − PTR_TO_PACKET_END, /* skb->data + headlen */ | |
| 152 | −}; | |
| 153 | − | |
| 154 | 129 | struct reg_state { |
| 155 | 130 | enum bpf_reg_type type; |
| 156 | 131 | union { |
| @@ -695,10 +670,10 @@ static int check_packet_access(struct verifier_env *env, u32 regno, int off, · 538 unmodified lines | ||
| 695 | 670 | |
| 696 | 671 | /* check access to 'struct bpf_context' fields */ |
| 697 | 672 | static int check_ctx_access(struct verifier_env *env, int off, int size, |
| 698 | − enum bpf_access_type t) | |
| 673 | + enum bpf_access_type t, enum bpf_reg_type *reg_type) | |
| 699 | 674 | { |
| 700 | 675 | if (env->prog->aux->ops->is_valid_access && |
| 701 | − env->prog->aux->ops->is_valid_access(off, size, t)) { | |
| 676 | + env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) { | |
| 702 | 677 | /* remember the offset of last byte accessed in ctx */ |
| 703 | 678 | if (env->prog->aux->max_ctx_offset < off + size) |
| 704 | 679 | env->prog->aux->max_ctx_offset = off + size; |
| @@ -798,21 +773,19 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off, · 93 unmodified lines | ||
| 798 | 773 | mark_reg_unknown_value(state->regs, value_regno); |
| 799 | 774 | |
| 800 | 775 | } else if (reg->type == PTR_TO_CTX) { |
| 776 | + enum bpf_reg_type reg_type = UNKNOWN_VALUE; | |
| 777 | + | |
| 801 | 778 | if (t == BPF_WRITE && value_regno >= 0 && |
| 802 | 779 | is_pointer_value(env, value_regno)) { |
| 803 | 780 | verbose("R%d leaks addr into ctx\n", value_regno); |
| 804 | 781 | return -EACCES; |
| 805 | 782 | } |
| 806 | − err = check_ctx_access(env, off, size, t); | |
| 783 | + err = check_ctx_access(env, off, size, t, ®_type); | |
| 807 | 784 | if (!err && t == BPF_READ && value_regno >= 0) { |
| 808 | 785 | mark_reg_unknown_value(state->regs, value_regno); |
| 809 | − if (off == offsetof(struct __sk_buff, data) && | |
| 810 | − env->allow_ptr_leaks) | |
| 786 | + if (env->allow_ptr_leaks) | |
| 811 | 787 | /* note that reg.[id|off|range] == 0 */ |
| 812 | − state->regs[value_regno].type = PTR_TO_PACKET; | |
| 813 | − else if (off == offsetof(struct __sk_buff, data_end) && | |
| 814 | − env->allow_ptr_leaks) | |
| 815 | − state->regs[value_regno].type = PTR_TO_PACKET_END; | |
| 788 | + state->regs[value_regno].type = reg_type; | |
| 816 | 789 | } |
| 817 | 790 | |
| 818 | 791 | } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) { |
| @@ -349,7 +349,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func · 348 unmodified lines | ||
| 349 | 349 | } |
| 350 | 350 | |
| 351 | 351 | /* bpf+kprobe programs can access fields of 'struct pt_regs' */ |
| 352 | −static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type) | |
| 352 | +static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type, | |
| 353 | + enum bpf_reg_type *reg_type) | |
| 353 | 354 | { |
| 354 | 355 | /* check bounds */ |
| 355 | 356 | if (off < 0 || off >= sizeof(struct pt_regs)) |
| @@ -427,7 +428,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) · 71 unmodified lines | ||
| 427 | 428 | } |
| 428 | 429 | } |
| 429 | 430 | |
| 430 | −static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type) | |
| 431 | +static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type, | |
| 432 | + enum bpf_reg_type *reg_type) | |
| 431 | 433 | { |
| 432 | 434 | if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE) |
| 433 | 435 | return false; |
| @@ -2085,7 +2085,8 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type) · 2084 unmodified lines | ||
| 2085 | 2085 | } |
| 2086 | 2086 | |
| 2087 | 2087 | static bool sk_filter_is_valid_access(int off, int size, |
| 2088 | − enum bpf_access_type type) | |
| 2088 | + enum bpf_access_type type, | |
| 2089 | + enum bpf_reg_type *reg_type) | |
| 2089 | 2090 | { |
| 2090 | 2091 | switch (off) { |
| 2091 | 2092 | case offsetof(struct __sk_buff, tc_classid): |
| @@ -2108,7 +2109,8 @@ static bool sk_filter_is_valid_access(int off, int size, · 16 unmodified lines | ||
| 2108 | 2109 | } |
| 2109 | 2110 | |
| 2110 | 2111 | static bool tc_cls_act_is_valid_access(int off, int size, |
| 2111 | − enum bpf_access_type type) | |
| 2112 | + enum bpf_access_type type, | |
| 2113 | + enum bpf_reg_type *reg_type) | |
| 2112 | 2114 | { |
| 2113 | 2115 | if (type == BPF_WRITE) { |
| 2114 | 2116 | switch (off) { |
| @@ -2123,6 +2125,16 @@ static bool tc_cls_act_is_valid_access(int off, int size, · 8 unmodified lines | ||
| 2123 | 2125 | return false; |
| 2124 | 2126 | } |
| 2125 | 2127 | } |
| 2128 | + | |
| 2129 | + switch (off) { | |
| 2130 | + case offsetof(struct __sk_buff, data): | |
| 2131 | + *reg_type = PTR_TO_PACKET; | |
| 2132 | + break; | |
| 2133 | + case offsetof(struct __sk_buff, data_end): | |
| 2134 | + *reg_type = PTR_TO_PACKET_END; | |
| 2135 | + break; | |
| 2136 | + } | |
| 2137 | + | |
| 2126 | 2138 | return __is_valid_access(off, size, type); |
| 2127 | 2139 | } |
| 2128 | 2140 | |