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 | BPF_WRITE = 2 | 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 | struct bpf_prog; | 139 | struct bpf_prog; |
| 115 | 140 | ||
| 116 | struct bpf_verifier_ops { | 141 | struct bpf_verifier_ops { |
| @@ -120,7 +145,8 @@ struct bpf_verifier_ops { · 3 unmodified lines | |||
| 120 | /* return true if 'size' wide access at offset 'off' within bpf_context | 145 | /* return true if 'size' wide access at offset 'off' within bpf_context |
| 121 | * with 'type' (read or write) is allowed | 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 | u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg, | 151 | u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg, |
| 126 | int src_reg, int ctx_off, | 152 | int src_reg, int ctx_off, |
| @@ -126,31 +126,6 @@ · 125 unmodified lines | |||
| 126 | * are set to NOT_INIT to indicate that they are no longer readable. | 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 | struct reg_state { | 129 | struct reg_state { |
| 155 | enum bpf_reg_type type; | 130 | enum bpf_reg_type type; |
| 156 | union { | 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 | /* check access to 'struct bpf_context' fields */ | 671 | /* check access to 'struct bpf_context' fields */ |
| 697 | static int check_ctx_access(struct verifier_env *env, int off, int size, | 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 | if (env->prog->aux->ops->is_valid_access && | 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 | /* remember the offset of last byte accessed in ctx */ | 677 | /* remember the offset of last byte accessed in ctx */ |
| 703 | if (env->prog->aux->max_ctx_offset < off + size) | 678 | if (env->prog->aux->max_ctx_offset < off + size) |
| 704 | env->prog->aux->max_ctx_offset = off + size; | 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 | mark_reg_unknown_value(state->regs, value_regno); | 773 | mark_reg_unknown_value(state->regs, value_regno); |
| 799 | 774 | ||
| 800 | } else if (reg->type == PTR_TO_CTX) { | 775 | } else if (reg->type == PTR_TO_CTX) { |
| 776 | enum bpf_reg_type reg_type = UNKNOWN_VALUE; | ||
| 777 | |||
| 801 | if (t == BPF_WRITE && value_regno >= 0 && | 778 | if (t == BPF_WRITE && value_regno >= 0 && |
| 802 | is_pointer_value(env, value_regno)) { | 779 | is_pointer_value(env, value_regno)) { |
| 803 | verbose("R%d leaks addr into ctx\n", value_regno); | 780 | verbose("R%d leaks addr into ctx\n", value_regno); |
| 804 | return -EACCES; | 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 | if (!err && t == BPF_READ && value_regno >= 0) { | 784 | if (!err && t == BPF_READ && value_regno >= 0) { |
| 808 | mark_reg_unknown_value(state->regs, value_regno); | 785 | mark_reg_unknown_value(state->regs, value_regno); |
| 809 | if (off == offsetof(struct __sk_buff, data) && | 786 | if (env->allow_ptr_leaks) |
| 810 | env->allow_ptr_leaks) | ||
| 811 | /* note that reg.[id|off|range] == 0 */ | 787 | /* note that reg.[id|off|range] == 0 */ |
| 812 | state->regs[value_regno].type = PTR_TO_PACKET; | 788 | state->regs[value_regno].type = reg_type; |
| 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; | ||
| 816 | } | 789 | } |
| 817 | 790 | ||
| 818 | } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) { | 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 | /* bpf+kprobe programs can access fields of 'struct pt_regs' */ | 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 | /* check bounds */ | 355 | /* check bounds */ |
| 355 | if (off < 0 || off >= sizeof(struct pt_regs)) | 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 | if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE) | 434 | if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE) |
| 433 | return false; | 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 | static bool sk_filter_is_valid_access(int off, int size, | 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 | switch (off) { | 2091 | switch (off) { |
| 2091 | case offsetof(struct __sk_buff, tc_classid): | 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 | static bool tc_cls_act_is_valid_access(int off, int size, | 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 | if (type == BPF_WRITE) { | 2115 | if (type == BPF_WRITE) { |
| 2114 | switch (off) { | 2116 | switch (off) { |
| @@ -2123,6 +2125,16 @@ static bool tc_cls_act_is_valid_access(int off, int size, · 8 unmodified lines | |||
| 2123 | return false; | 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 | return __is_valid_access(off, size, type); | 2138 | return __is_valid_access(off, size, type); |
| 2127 | } | 2139 | } |
| 2128 | 2140 | ||