]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/bpf/verifier.c
bpf: XDP_REDIRECT enable use of cpumap
[mirror_ubuntu-bionic-kernel.git] / kernel / bpf / verifier.c
index 274c6582ec398910754a6fe18936b628dea3f488..e4d5136725a2ff2fcdcfcba3f40f4be144dabe5e 100644 (file)
@@ -165,15 +165,26 @@ static __printf(2, 3) void verbose(struct bpf_verifier_env *env,
                                   const char *fmt, ...)
 {
        struct bpf_verifer_log *log = &env->log;
+       unsigned int n;
        va_list args;
 
-       if (!log->level || bpf_verifier_log_full(log))
+       if (!log->level || !log->ubuf || bpf_verifier_log_full(log))
                return;
 
        va_start(args, fmt);
-       log->len_used += vscnprintf(log->kbuf + log->len_used,
-                                   log->len_total - log->len_used, fmt, args);
+       n = vscnprintf(log->kbuf, BPF_VERIFIER_TMP_LOG_SIZE, fmt, args);
        va_end(args);
+
+       WARN_ONCE(n >= BPF_VERIFIER_TMP_LOG_SIZE - 1,
+                 "verifier log line truncated - local buffer too short\n");
+
+       n = min(log->len_total - log->len_used - 1, n);
+       log->kbuf[n] = '\0';
+
+       if (!copy_to_user(log->ubuf + log->len_used, log->kbuf, n + 1))
+               log->len_used += n;
+       else
+               log->ubuf = NULL;
 }
 
 static bool type_is_pkt_pointer(enum bpf_reg_type type)
@@ -802,6 +813,36 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
        return err;
 }
 
+static bool analyzer_is_valid_access(struct bpf_verifier_env *env, int off,
+                                    struct bpf_insn_access_aux *info)
+{
+       switch (env->prog->type) {
+       case BPF_PROG_TYPE_XDP:
+               switch (off) {
+               case offsetof(struct xdp_buff, data):
+                       info->reg_type = PTR_TO_PACKET;
+                       return true;
+               case offsetof(struct xdp_buff, data_end):
+                       info->reg_type = PTR_TO_PACKET_END;
+                       return true;
+               }
+               return false;
+       case BPF_PROG_TYPE_SCHED_CLS:
+               switch (off) {
+               case offsetof(struct sk_buff, data):
+                       info->reg_type = PTR_TO_PACKET;
+                       return true;
+               case offsetof(struct sk_buff, cb) +
+                    offsetof(struct bpf_skb_data_end, data_end):
+                       info->reg_type = PTR_TO_PACKET_END;
+                       return true;
+               }
+               return false;
+       default:
+               return false;
+       }
+}
+
 /* check access to 'struct bpf_context' fields.  Supports fixed offsets only */
 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)
@@ -810,12 +851,13 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
                .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, &info)) {
+       if (env->analyzer_ops) {
+               if (analyzer_is_valid_access(env, off, &info)) {
+                       *reg_type = info.reg_type;
+                       return 0;
+               }
+       } else if (env->prog->aux->ops->is_valid_access &&
+                  env->prog->aux->ops->is_valid_access(off, size, t, &info)) {
                /* A non zero info.ctx_field_size indicates that this field is a
                 * candidate for later verifier transformation to load the whole
                 * field and then apply a mask when accessed with a narrower
@@ -1402,6 +1444,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                if (func_id != BPF_FUNC_redirect_map)
                        goto error;
                break;
+       /* Restrict bpf side of cpumap, open when use-cases appear */
+       case BPF_MAP_TYPE_CPUMAP:
+               if (func_id != BPF_FUNC_redirect_map)
+                       goto error;
+               break;
        case BPF_MAP_TYPE_ARRAY_OF_MAPS:
        case BPF_MAP_TYPE_HASH_OF_MAPS:
                if (func_id != BPF_FUNC_map_lookup_elem)
@@ -1439,7 +1486,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                        goto error;
                break;
        case BPF_FUNC_redirect_map:
-               if (map->map_type != BPF_MAP_TYPE_DEVMAP)
+               if (map->map_type != BPF_MAP_TYPE_DEVMAP &&
+                   map->map_type != BPF_MAP_TYPE_CPUMAP)
                        goto error;
                break;
        case BPF_FUNC_sk_redirect_map:
@@ -4263,11 +4311,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
                if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 ||
                    !log->level || !log->ubuf)
                        goto err_unlock;
-
-               ret = -ENOMEM;
-               log->kbuf = vmalloc(log->len_total);
-               if (!log->kbuf)
-                       goto err_unlock;
        }
 
        env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT);
@@ -4304,18 +4347,11 @@ skip_full_check:
        if (ret == 0)
                ret = fixup_bpf_calls(env);
 
-       if (log->level && bpf_verifier_log_full(log)) {
-               BUG_ON(log->len_used >= log->len_total);
-               /* verifier log exceeded user supplied buffer */
+       if (log->level && bpf_verifier_log_full(log))
                ret = -ENOSPC;
-               /* fall through to return what was recorded */
-       }
-
-       /* copy verifier log back to user space including trailing zero */
-       if (log->level && copy_to_user(log->ubuf, log->kbuf,
-                                      log->len_used + 1) != 0) {
+       if (log->level && !log->ubuf) {
                ret = -EFAULT;
-               goto free_log_buf;
+               goto err_release_maps;
        }
 
        if (ret == 0 && env->used_map_cnt) {
@@ -4326,7 +4362,7 @@ skip_full_check:
 
                if (!env->prog->aux->used_maps) {
                        ret = -ENOMEM;
-                       goto free_log_buf;
+                       goto err_release_maps;
                }
 
                memcpy(env->prog->aux->used_maps, env->used_maps,
@@ -4339,9 +4375,7 @@ skip_full_check:
                convert_pseudo_ld_imm64(env);
        }
 
-free_log_buf:
-       if (log->level)
-               vfree(log->kbuf);
+err_release_maps:
        if (!env->prog->aux->used_maps)
                /* if we didn't copy map pointers into bpf_prog_info, release
                 * them now. Otherwise free_bpf_prog_info() will release them.