@@ -1415,6 +1415,19 @@ static inline u32 bpf_map_flags_to_cap(struct bpf_map *map)
return BPF_MAP_CAN_READ | BPF_MAP_CAN_WRITE;
}
+static inline u32 bpf_fd_modes_to_cap(fmode_t mode)
+{
+ u32 cap = 0;
+
+ if (mode & FMODE_CAN_READ)
+ cap |= BPF_MAP_CAN_READ;
+
+ if (mode & FMODE_CAN_WRITE)
+ cap |= BPF_MAP_CAN_WRITE;
+
+ return cap;
+}
+
static inline bool bpf_map_flags_access_ok(u32 access_flags)
{
return (access_flags & (BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG)) !=
@@ -501,6 +501,7 @@ struct bpf_verifier_env {
struct bpf_verifier_state_list **explored_states; /* search pruning optimization */
struct bpf_verifier_state_list *free_list;
struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */
+ u32 used_maps_caps[MAX_USED_MAPS]; /* array of map capabilities possessed by eBPF program */
struct btf_mod_pair used_btfs[MAX_USED_BTFS]; /* array of BTF's used by BPF program */
u32 used_map_cnt; /* number of used maps */
u32 used_btf_cnt; /* number of used BTF objects */
@@ -3531,6 +3531,14 @@ static int check_map_access_type(struct bpf_verifier_env *env, u32 regno,
struct bpf_reg_state *regs = cur_regs(env);
struct bpf_map *map = regs[regno].map_ptr;
u32 cap = bpf_map_flags_to_cap(map);
+ int i;
+
+ for (i = 0; i < env->used_map_cnt; i++) {
+ if (env->used_maps[i] == map) {
+ cap &= env->used_maps_caps[i];
+ break;
+ }
+ }
if (type == BPF_WRITE && !(cap & BPF_MAP_CAN_WRITE)) {
verbose(env, "write into map forbidden, value_size=%d off=%d size=%d\n",
@@ -7040,6 +7048,8 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
{
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
struct bpf_map *map = meta->map_ptr;
+ u32 cap;
+ int i;
if (func_id != BPF_FUNC_tail_call &&
func_id != BPF_FUNC_map_lookup_elem &&
@@ -7058,11 +7068,20 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
return -EINVAL;
}
+ cap = bpf_map_flags_to_cap(map);
+
+ for (i = 0; i < env->used_map_cnt; i++) {
+ if (env->used_maps[i] == map) {
+ cap &= env->used_maps_caps[i];
+ break;
+ }
+ }
+
/* In case of read-only, some additional restrictions
* need to be applied in order to prevent altering the
* state of the map from program side.
*/
- if ((map->map_flags & BPF_F_RDONLY_PROG) &&
+ if (!(cap & BPF_MAP_CAN_WRITE) &&
(func_id == BPF_FUNC_map_delete_elem ||
func_id == BPF_FUNC_map_update_elem ||
func_id == BPF_FUNC_map_push_elem ||
@@ -12870,6 +12889,7 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
/* check whether we recorded this map already */
for (j = 0; j < env->used_map_cnt; j++) {
if (env->used_maps[j] == map) {
+ env->used_maps_caps[j] |= bpf_fd_modes_to_cap(f.file->f_mode);
aux->map_index = j;
fdput(f);
goto next_insn;
@@ -12889,7 +12909,9 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
bpf_map_inc(map);
aux->map_index = env->used_map_cnt;
- env->used_maps[env->used_map_cnt++] = map;
+ env->used_maps[env->used_map_cnt] = map;
+ env->used_maps_caps[env->used_map_cnt] = bpf_fd_modes_to_cap(f.file->f_mode);
+ env->used_map_cnt++;
if (bpf_map_is_cgroup_storage(map) &&
bpf_cgroup_storage_assign(env->prog->aux, map)) {