bpf: Introduce MEM_RDONLY flag
authorHao Luo <haoluo@google.com>
Thu, 28 Apr 2022 23:57:46 +0000 (16:57 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 1 May 2022 15:22:24 +0000 (17:22 +0200)
commit 20b2aff4bc15bda809f994761d5719827d66c0b4 upstream.

This patch introduce a flag MEM_RDONLY to tag a reg value
pointing to read-only memory. It makes the following changes:

1. PTR_TO_RDWR_BUF -> PTR_TO_BUF
2. PTR_TO_RDONLY_BUF -> PTR_TO_BUF | MEM_RDONLY

Signed-off-by: Hao Luo <haoluo@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211217003152.48334-6-haoluo@google.com
Cc: stable@vger.kernel.org # 5.15.x
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/map_iter.c
kernel/bpf/verifier.c
net/core/bpf_sk_storage.c
net/core/sock_map.c

index 7249f5e..83c28c6 100644 (file)
@@ -307,7 +307,10 @@ enum bpf_type_flag {
        /* PTR may be NULL. */
        PTR_MAYBE_NULL          = BIT(0 + BPF_BASE_TYPE_BITS),
 
-       __BPF_TYPE_LAST_FLAG    = PTR_MAYBE_NULL,
+       /* MEM is read-only. */
+       MEM_RDONLY              = BIT(1 + BPF_BASE_TYPE_BITS),
+
+       __BPF_TYPE_LAST_FLAG    = MEM_RDONLY,
 };
 
 /* Max number of base types. */
@@ -488,8 +491,7 @@ enum bpf_reg_type {
         * an explicit null check is required for this struct.
         */
        PTR_TO_MEM,              /* reg points to valid memory region */
-       PTR_TO_RDONLY_BUF,       /* reg points to a readonly buffer */
-       PTR_TO_RDWR_BUF,         /* reg points to a read/write buffer */
+       PTR_TO_BUF,              /* reg points to a read/write buffer */
        PTR_TO_PERCPU_BTF_ID,    /* reg points to a percpu kernel variable */
        PTR_TO_FUNC,             /* reg points to a bpf program function */
        __BPF_REG_TYPE_MAX,
index 1872b3e..9247dfc 100644 (file)
@@ -4804,8 +4804,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
 
                type = base_type(ctx_arg_info->reg_type);
                flag = type_flag(ctx_arg_info->reg_type);
-               if (ctx_arg_info->offset == off &&
-                   (type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) &&
+               if (ctx_arg_info->offset == off && type == PTR_TO_BUF &&
                    (flag & PTR_MAYBE_NULL)) {
                        info->reg_type = ctx_arg_info->reg_type;
                        return true;
index 631f0e4..b0fa190 100644 (file)
@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
        .ctx_arg_info_size      = 2,
        .ctx_arg_info           = {
                { offsetof(struct bpf_iter__bpf_map_elem, key),
-                 PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL },
+                 PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
                { offsetof(struct bpf_iter__bpf_map_elem, value),
-                 PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
+                 PTR_TO_BUF | PTR_MAYBE_NULL },
        },
 };
 
index 24e9955..0de4a94 100644 (file)
@@ -458,6 +458,11 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
                base_type(type) == PTR_TO_MEM;
 }
 
+static bool type_is_rdonly_mem(u32 type)
+{
+       return type & MEM_RDONLY;
+}
+
 static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
 {
        return type == ARG_PTR_TO_SOCK_COMMON;
@@ -533,7 +538,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
 static const char *reg_type_str(struct bpf_verifier_env *env,
                                enum bpf_reg_type type)
 {
-       char postfix[16] = {0};
+       char postfix[16] = {0}, prefix[16] = {0};
        static const char * const str[] = {
                [NOT_INIT]              = "?",
                [SCALAR_VALUE]          = "inv",
@@ -553,8 +558,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
                [PTR_TO_BTF_ID]         = "ptr_",
                [PTR_TO_PERCPU_BTF_ID]  = "percpu_ptr_",
                [PTR_TO_MEM]            = "mem",
-               [PTR_TO_RDONLY_BUF]     = "rdonly_buf",
-               [PTR_TO_RDWR_BUF]       = "rdwr_buf",
+               [PTR_TO_BUF]            = "buf",
                [PTR_TO_FUNC]           = "func",
                [PTR_TO_MAP_KEY]        = "map_key",
        };
@@ -567,8 +571,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
                        strncpy(postfix, "_or_null", 16);
        }
 
-       snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s",
-                str[base_type(type)], postfix);
+       if (type & MEM_RDONLY)
+               strncpy(prefix, "rdonly_", 16);
+
+       snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
+                prefix, str[base_type(type)], postfix);
        return env->type_str_buf;
 }
 
@@ -2546,8 +2553,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
        case PTR_TO_TCP_SOCK:
        case PTR_TO_XDP_SOCK:
        case PTR_TO_BTF_ID:
-       case PTR_TO_RDONLY_BUF:
-       case PTR_TO_RDWR_BUF:
+       case PTR_TO_BUF:
        case PTR_TO_PERCPU_BTF_ID:
        case PTR_TO_MEM:
        case PTR_TO_FUNC:
@@ -4275,22 +4281,28 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
        } else if (reg->type == CONST_PTR_TO_MAP) {
                err = check_ptr_to_map_access(env, regs, regno, off, size, t,
                                              value_regno);
-       } else if (reg->type == PTR_TO_RDONLY_BUF) {
-               if (t == BPF_WRITE) {
-                       verbose(env, "R%d cannot write into %s\n",
-                               regno, reg_type_str(env, reg->type));
-                       return -EACCES;
+       } else if (base_type(reg->type) == PTR_TO_BUF) {
+               bool rdonly_mem = type_is_rdonly_mem(reg->type);
+               const char *buf_info;
+               u32 *max_access;
+
+               if (rdonly_mem) {
+                       if (t == BPF_WRITE) {
+                               verbose(env, "R%d cannot write into %s\n",
+                                       regno, reg_type_str(env, reg->type));
+                               return -EACCES;
+                       }
+                       buf_info = "rdonly";
+                       max_access = &env->prog->aux->max_rdonly_access;
+               } else {
+                       buf_info = "rdwr";
+                       max_access = &env->prog->aux->max_rdwr_access;
                }
+
                err = check_buffer_access(env, reg, regno, off, size, false,
-                                         "rdonly",
-                                         &env->prog->aux->max_rdonly_access);
-               if (!err && value_regno >= 0)
-                       mark_reg_unknown(env, regs, value_regno);
-       } else if (reg->type == PTR_TO_RDWR_BUF) {
-               err = check_buffer_access(env, reg, regno, off, size, false,
-                                         "rdwr",
-                                         &env->prog->aux->max_rdwr_access);
-               if (!err && t == BPF_READ && value_regno >= 0)
+                                         buf_info, max_access);
+
+               if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
                        mark_reg_unknown(env, regs, value_regno);
        } else {
                verbose(env, "R%d invalid mem access '%s'\n", regno,
@@ -4551,8 +4563,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
                                   struct bpf_call_arg_meta *meta)
 {
        struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
+       const char *buf_info;
+       u32 *max_access;
 
-       switch (reg->type) {
+       switch (base_type(reg->type)) {
        case PTR_TO_PACKET:
        case PTR_TO_PACKET_META:
                return check_packet_access(env, regno, reg->off, access_size,
@@ -4571,18 +4585,20 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
                return check_mem_region_access(env, regno, reg->off,
                                               access_size, reg->mem_size,
                                               zero_size_allowed);
-       case PTR_TO_RDONLY_BUF:
-               if (meta && meta->raw_mode)
-                       return -EACCES;
-               return check_buffer_access(env, reg, regno, reg->off,
-                                          access_size, zero_size_allowed,
-                                          "rdonly",
-                                          &env->prog->aux->max_rdonly_access);
-       case PTR_TO_RDWR_BUF:
+       case PTR_TO_BUF:
+               if (type_is_rdonly_mem(reg->type)) {
+                       if (meta && meta->raw_mode)
+                               return -EACCES;
+
+                       buf_info = "rdonly";
+                       max_access = &env->prog->aux->max_rdonly_access;
+               } else {
+                       buf_info = "rdwr";
+                       max_access = &env->prog->aux->max_rdwr_access;
+               }
                return check_buffer_access(env, reg, regno, reg->off,
                                           access_size, zero_size_allowed,
-                                          "rdwr",
-                                          &env->prog->aux->max_rdwr_access);
+                                          buf_info, max_access);
        case PTR_TO_STACK:
                return check_stack_range_initialized(
                                env,
@@ -4858,8 +4874,8 @@ static const struct bpf_reg_types mem_types = {
                PTR_TO_MAP_KEY,
                PTR_TO_MAP_VALUE,
                PTR_TO_MEM,
-               PTR_TO_RDONLY_BUF,
-               PTR_TO_RDWR_BUF,
+               PTR_TO_BUF,
+               PTR_TO_BUF | MEM_RDONLY,
        },
 };
 
index 4cb5ef8..ea61dfe 100644 (file)
@@ -929,7 +929,7 @@ static struct bpf_iter_reg bpf_sk_storage_map_reg_info = {
                { offsetof(struct bpf_iter__bpf_sk_storage_map, sk),
                  PTR_TO_BTF_ID_OR_NULL },
                { offsetof(struct bpf_iter__bpf_sk_storage_map, value),
-                 PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
+                 PTR_TO_BUF | PTR_MAYBE_NULL },
        },
        .seq_info               = &iter_seq_info,
 };
index 5a8f3b5..6351b6a 100644 (file)
@@ -1575,7 +1575,7 @@ static struct bpf_iter_reg sock_map_iter_reg = {
        .ctx_arg_info_size      = 2,
        .ctx_arg_info           = {
                { offsetof(struct bpf_iter__sockmap, key),
-                 PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL },
+                 PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
                { offsetof(struct bpf_iter__sockmap, sk),
                  PTR_TO_BTF_ID_OR_NULL },
        },