summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorYonghong Song <yhs@fb.com>2017-06-22 15:07:39 -0700
committerDavid S. Miller <davem@davemloft.net>2017-06-23 14:04:11 -0400
commit239946314e57711d7da546b67964d0b387a3ee42 (patch)
tree958d35fbbbc439b561832c75de22f5fdfa825f7c /net
parent72de46556f8a291b2c72ea1fa22275ffef85e4f9 (diff)
bpf: possibly avoid extra masking for narrower load in verifier
Commit 31fd85816dbe ("bpf: permits narrower load from bpf program context fields") permits narrower load for certain ctx fields. The commit however will already generate a masking even if the prog-specific ctx conversion produces the result with narrower size. For example, for __sk_buff->protocol, the ctx conversion loads the data into register with 2-byte load. A narrower 2-byte load should not generate masking. For __sk_buff->vlan_present, the conversion function set the result as either 0 or 1, essentially a byte. The narrower 2-byte or 1-byte load should not generate masking. To avoid unnecessary masking, prog-specific *_is_valid_access now passes converted_op_size back to verifier, which indicates the valid data width after perceived future conversion. Based on this information, verifier is able to avoid unnecessary marking. Since we want more information back from prog-specific *_is_valid_access checking, all of them are packed into one data structure for more clarity. Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/filter.c92
1 files changed, 53 insertions, 39 deletions
diff --git a/net/core/filter.c b/net/core/filter.c
index 60ed6f343a63..4b788007415f 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2856,8 +2856,37 @@ lwt_xmit_func_proto(enum bpf_func_id func_id)
}
}
+static void __set_access_aux_info(int off, struct bpf_insn_access_aux *info)
+{
+ info->ctx_field_size = 4;
+ switch (off) {
+ case offsetof(struct __sk_buff, pkt_type) ...
+ offsetof(struct __sk_buff, pkt_type) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, vlan_present) ...
+ offsetof(struct __sk_buff, vlan_present) + sizeof(__u32) - 1:
+ info->converted_op_size = 1;
+ break;
+ case offsetof(struct __sk_buff, queue_mapping) ...
+ offsetof(struct __sk_buff, queue_mapping) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, protocol) ...
+ offsetof(struct __sk_buff, protocol) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, vlan_tci) ...
+ offsetof(struct __sk_buff, vlan_tci) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, vlan_proto) ...
+ offsetof(struct __sk_buff, vlan_proto) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, tc_index) ...
+ offsetof(struct __sk_buff, tc_index) + sizeof(__u32) - 1:
+ case offsetof(struct __sk_buff, tc_classid) ...
+ offsetof(struct __sk_buff, tc_classid) + sizeof(__u32) - 1:
+ info->converted_op_size = 2;
+ break;
+ default:
+ info->converted_op_size = 4;
+ }
+}
+
static bool __is_valid_access(int off, int size, enum bpf_access_type type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
if (off < 0 || off >= sizeof(struct __sk_buff))
return false;
@@ -2875,24 +2904,32 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type,
break;
case offsetof(struct __sk_buff, data) ...
offsetof(struct __sk_buff, data) + sizeof(__u32) - 1:
+ if (size != sizeof(__u32))
+ return false;
+ info->reg_type = PTR_TO_PACKET;
+ break;
case offsetof(struct __sk_buff, data_end) ...
offsetof(struct __sk_buff, data_end) + sizeof(__u32) - 1:
if (size != sizeof(__u32))
return false;
+ info->reg_type = PTR_TO_PACKET_END;
break;
default:
- /* permit narrower load for not cb/data/data_end fields */
- *ctx_field_size = 4;
if (type == BPF_WRITE) {
if (size != sizeof(__u32))
return false;
} else {
- if (size != sizeof(__u32))
+ int allowed;
+
+ /* permit narrower load for not cb/data/data_end fields */
#ifdef __LITTLE_ENDIAN
- return (off & 0x3) == 0 && (size == 1 || size == 2);
+ allowed = (off & 0x3) == 0 && size <= 4 && (size & (size - 1)) == 0;
#else
- return (off & 0x3) + size == 4 && (size == 1 || size == 2);
+ allowed = (off & 0x3) + size == 4 && size <= 4 && (size & (size - 1)) == 0;
#endif
+ if (!allowed)
+ return false;
+ __set_access_aux_info(off, info);
}
}
@@ -2901,8 +2938,7 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type,
static bool sk_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
switch (off) {
case offsetof(struct __sk_buff, tc_classid) ...
@@ -2924,13 +2960,12 @@ static bool sk_filter_is_valid_access(int off, int size,
}
}
- return __is_valid_access(off, size, type, ctx_field_size);
+ return __is_valid_access(off, size, type, info);
}
static bool lwt_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
switch (off) {
case offsetof(struct __sk_buff, tc_classid) ...
@@ -2950,22 +2985,12 @@ static bool lwt_is_valid_access(int off, int size,
}
}
- switch (off) {
- case offsetof(struct __sk_buff, data):
- *reg_type = PTR_TO_PACKET;
- break;
- case offsetof(struct __sk_buff, data_end):
- *reg_type = PTR_TO_PACKET_END;
- break;
- }
-
- return __is_valid_access(off, size, type, ctx_field_size);
+ return __is_valid_access(off, size, type, info);
}
static bool sock_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3028,8 +3053,7 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write,
static bool tc_cls_act_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -3045,16 +3069,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
}
}
- switch (off) {
- case offsetof(struct __sk_buff, data):
- *reg_type = PTR_TO_PACKET;
- break;
- case offsetof(struct __sk_buff, data_end):
- *reg_type = PTR_TO_PACKET_END;
- break;
- }
-
- return __is_valid_access(off, size, type, ctx_field_size);
+ return __is_valid_access(off, size, type, info);
}
static bool __is_valid_xdp_access(int off, int size)
@@ -3071,18 +3086,17 @@ static bool __is_valid_xdp_access(int off, int size)
static bool xdp_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type,
- int *ctx_field_size)
+ struct bpf_insn_access_aux *info)
{
if (type == BPF_WRITE)
return false;
switch (off) {
case offsetof(struct xdp_md, data):
- *reg_type = PTR_TO_PACKET;
+ info->reg_type = PTR_TO_PACKET;
break;
case offsetof(struct xdp_md, data_end):
- *reg_type = PTR_TO_PACKET_END;
+ info->reg_type = PTR_TO_PACKET_END;
break;
}