static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
                            enum bpf_access_type t, enum bpf_reg_type *reg_type)
 {
-       int ctx_field_size = 0;
+       struct bpf_insn_access_aux info = { .reg_type = *reg_type };
 
        /* for analyzer ctx accesses are already validated and converted */
        if (env->analyzer_ops)
                return 0;
 
        if (env->prog->aux->ops->is_valid_access &&
-           env->prog->aux->ops->is_valid_access(off, size, t, reg_type, &ctx_field_size)) {
-               /* a non zero ctx_field_size indicates:
+           env->prog->aux->ops->is_valid_access(off, size, t, &info)) {
+               /* a non zero info.ctx_field_size indicates:
                 * . For this field, the prog type specific ctx conversion algorithm
                 *   only supports whole field access.
                 * . This ctx access is a candiate for later verifier transformation
                 *   to load the whole field and then apply a mask to get correct result.
+                * a non zero info.converted_op_size indicates perceived actual converted
+                * value width in convert_ctx_access.
                 */
-               if (ctx_field_size)
-                       env->insn_aux_data[insn_idx].ctx_field_size = ctx_field_size;
+               if ((info.ctx_field_size && !info.converted_op_size) ||
+                   (!info.ctx_field_size &&  info.converted_op_size)) {
+                       verbose("verifier bug in is_valid_access prog type=%u off=%d size=%d\n",
+                               env->prog->type, off, size);
+                       return -EACCES;
+               }
+
+               if (info.ctx_field_size) {
+                       env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
+                       env->insn_aux_data[insn_idx].converted_op_size = info.converted_op_size;
+               }
+               *reg_type = info.reg_type;
 
                /* remember the offset of last byte accessed in ctx */
                if (env->prog->aux->max_ctx_offset < off + size)
        struct bpf_insn insn_buf[16], *insn;
        struct bpf_prog *new_prog;
        enum bpf_access_type type;
-       int i, cnt, off, size, ctx_field_size, is_narrower_load, delta = 0;
+       int i, cnt, off, size, ctx_field_size, converted_op_size, is_narrower_load, delta = 0;
 
        if (ops->gen_prologue) {
                cnt = ops->gen_prologue(insn_buf, env->seen_direct_write,
                off = insn->off;
                size = bpf_size_to_bytes(BPF_SIZE(insn->code));
                ctx_field_size = env->insn_aux_data[i + delta].ctx_field_size;
-               is_narrower_load = (type == BPF_READ && size < ctx_field_size);
+               converted_op_size = env->insn_aux_data[i + delta].converted_op_size;
+               is_narrower_load = type == BPF_READ && size < ctx_field_size;
 
                /* If the read access is a narrower load of the field,
                 * convert to a 4/8-byte load, to minimum program type specific
                        verbose("bpf verifier is misconfigured\n");
                        return -EINVAL;
                }
-               if (is_narrower_load) {
+               if (is_narrower_load && size < converted_op_size) {
                        if (ctx_field_size <= 4)
                                insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
                                                        (1 << size * 8) - 1);
 
 
 /* bpf+kprobe programs can access fields of 'struct pt_regs' */
 static bool kprobe_prog_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 (off < 0 || off >= sizeof(struct pt_regs))
                return false;
 }
 
 static bool tp_prog_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 (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
                return false;
 };
 
 static bool pe_prog_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)
 {
        int sample_period_off;
 
        /* permit 1, 2, 4 byte narrower and 8 normal read access to sample_period */
        sample_period_off = offsetof(struct bpf_perf_event_data, sample_period);
        if (off >= sample_period_off && off < sample_period_off + sizeof(__u64)) {
-               *ctx_field_size = 8;
+               int allowed;
+
 #ifdef __LITTLE_ENDIAN
-               return (off & 0x7) == 0 && size <= 8 && (size & (size - 1)) == 0;
+               allowed = (off & 0x7) == 0 && size <= 8 && (size & (size - 1)) == 0;
 #else
-               return ((off & 0x7) + size) == 8 && size <= 8 && (size & (size - 1)) == 0;
+               allowed = ((off & 0x7) + size) == 8 && size <= 8 && (size & (size - 1)) == 0;
 #endif
+               if (!allowed)
+                       return false;
+               info->ctx_field_size = 8;
+               info->converted_op_size = 8;
        } else {
                if (size != sizeof(long))
                        return false;
 
        }
 }
 
+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;
                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);
                }
        }
 
 
 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) ...
                }
        }
 
-       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) ...
                }
        }
 
-       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) {
 
 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) {
                }
        }
 
-       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)
 
 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;
        }