156f668dfSMartin KaFai Lau /* Copyright (c) 2017 Facebook 256f668dfSMartin KaFai Lau * 356f668dfSMartin KaFai Lau * This program is free software; you can redistribute it and/or 456f668dfSMartin KaFai Lau * modify it under the terms of version 2 of the GNU General Public 556f668dfSMartin KaFai Lau * License as published by the Free Software Foundation. 656f668dfSMartin KaFai Lau */ 756f668dfSMartin KaFai Lau #include <linux/slab.h> 856f668dfSMartin KaFai Lau #include <linux/bpf.h> 956f668dfSMartin KaFai Lau 1056f668dfSMartin KaFai Lau #include "map_in_map.h" 1156f668dfSMartin KaFai Lau 1256f668dfSMartin KaFai Lau struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) 1356f668dfSMartin KaFai Lau { 1456f668dfSMartin KaFai Lau struct bpf_map *inner_map, *inner_map_meta; 159d5564ddSDaniel Borkmann u32 inner_map_meta_size; 1656f668dfSMartin KaFai Lau struct fd f; 1756f668dfSMartin KaFai Lau 1856f668dfSMartin KaFai Lau f = fdget(inner_map_ufd); 1956f668dfSMartin KaFai Lau inner_map = __bpf_map_get(f); 2056f668dfSMartin KaFai Lau if (IS_ERR(inner_map)) 2156f668dfSMartin KaFai Lau return inner_map; 2256f668dfSMartin KaFai Lau 2356f668dfSMartin KaFai Lau /* prog_array->owner_prog_type and owner_jited 2456f668dfSMartin KaFai Lau * is a runtime binding. Doing static check alone 2556f668dfSMartin KaFai Lau * in the verifier is not enough. 2656f668dfSMartin KaFai Lau */ 277b5dd2bdSRoman Gushchin if (inner_map->map_type == BPF_MAP_TYPE_PROG_ARRAY || 28c6fdcd6eSRoman Gushchin inner_map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE || 29c6fdcd6eSRoman Gushchin inner_map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { 3056f668dfSMartin KaFai Lau fdput(f); 3156f668dfSMartin KaFai Lau return ERR_PTR(-ENOTSUPP); 3256f668dfSMartin KaFai Lau } 3356f668dfSMartin KaFai Lau 3456f668dfSMartin KaFai Lau /* Does not support >1 level map-in-map */ 3556f668dfSMartin KaFai Lau if (inner_map->inner_map_meta) { 3656f668dfSMartin KaFai Lau fdput(f); 3756f668dfSMartin KaFai Lau return ERR_PTR(-EINVAL); 3856f668dfSMartin KaFai Lau } 3956f668dfSMartin KaFai Lau 40*d83525caSAlexei Starovoitov if (map_value_has_spin_lock(inner_map)) { 41*d83525caSAlexei Starovoitov fdput(f); 42*d83525caSAlexei Starovoitov return ERR_PTR(-ENOTSUPP); 43*d83525caSAlexei Starovoitov } 44*d83525caSAlexei Starovoitov 459d5564ddSDaniel Borkmann inner_map_meta_size = sizeof(*inner_map_meta); 469d5564ddSDaniel Borkmann /* In some cases verifier needs to access beyond just base map. */ 479d5564ddSDaniel Borkmann if (inner_map->ops == &array_map_ops) 489d5564ddSDaniel Borkmann inner_map_meta_size = sizeof(struct bpf_array); 499d5564ddSDaniel Borkmann 509d5564ddSDaniel Borkmann inner_map_meta = kzalloc(inner_map_meta_size, GFP_USER); 5156f668dfSMartin KaFai Lau if (!inner_map_meta) { 5256f668dfSMartin KaFai Lau fdput(f); 5356f668dfSMartin KaFai Lau return ERR_PTR(-ENOMEM); 5456f668dfSMartin KaFai Lau } 5556f668dfSMartin KaFai Lau 5656f668dfSMartin KaFai Lau inner_map_meta->map_type = inner_map->map_type; 5756f668dfSMartin KaFai Lau inner_map_meta->key_size = inner_map->key_size; 5856f668dfSMartin KaFai Lau inner_map_meta->value_size = inner_map->value_size; 5956f668dfSMartin KaFai Lau inner_map_meta->map_flags = inner_map->map_flags; 6056f668dfSMartin KaFai Lau inner_map_meta->max_entries = inner_map->max_entries; 6156f668dfSMartin KaFai Lau 629d5564ddSDaniel Borkmann /* Misc members not needed in bpf_map_meta_equal() check. */ 639d5564ddSDaniel Borkmann inner_map_meta->ops = inner_map->ops; 649d5564ddSDaniel Borkmann if (inner_map->ops == &array_map_ops) { 659d5564ddSDaniel Borkmann inner_map_meta->unpriv_array = inner_map->unpriv_array; 669d5564ddSDaniel Borkmann container_of(inner_map_meta, struct bpf_array, map)->index_mask = 679d5564ddSDaniel Borkmann container_of(inner_map, struct bpf_array, map)->index_mask; 689d5564ddSDaniel Borkmann } 699d5564ddSDaniel Borkmann 7056f668dfSMartin KaFai Lau fdput(f); 7156f668dfSMartin KaFai Lau return inner_map_meta; 7256f668dfSMartin KaFai Lau } 7356f668dfSMartin KaFai Lau 7456f668dfSMartin KaFai Lau void bpf_map_meta_free(struct bpf_map *map_meta) 7556f668dfSMartin KaFai Lau { 7656f668dfSMartin KaFai Lau kfree(map_meta); 7756f668dfSMartin KaFai Lau } 7856f668dfSMartin KaFai Lau 7956f668dfSMartin KaFai Lau bool bpf_map_meta_equal(const struct bpf_map *meta0, 8056f668dfSMartin KaFai Lau const struct bpf_map *meta1) 8156f668dfSMartin KaFai Lau { 8256f668dfSMartin KaFai Lau /* No need to compare ops because it is covered by map_type */ 8356f668dfSMartin KaFai Lau return meta0->map_type == meta1->map_type && 8456f668dfSMartin KaFai Lau meta0->key_size == meta1->key_size && 8556f668dfSMartin KaFai Lau meta0->value_size == meta1->value_size && 8656f668dfSMartin KaFai Lau meta0->map_flags == meta1->map_flags && 8756f668dfSMartin KaFai Lau meta0->max_entries == meta1->max_entries; 8856f668dfSMartin KaFai Lau } 8956f668dfSMartin KaFai Lau 9056f668dfSMartin KaFai Lau void *bpf_map_fd_get_ptr(struct bpf_map *map, 9156f668dfSMartin KaFai Lau struct file *map_file /* not used */, 9256f668dfSMartin KaFai Lau int ufd) 9356f668dfSMartin KaFai Lau { 9456f668dfSMartin KaFai Lau struct bpf_map *inner_map; 9556f668dfSMartin KaFai Lau struct fd f; 9656f668dfSMartin KaFai Lau 9756f668dfSMartin KaFai Lau f = fdget(ufd); 9856f668dfSMartin KaFai Lau inner_map = __bpf_map_get(f); 9956f668dfSMartin KaFai Lau if (IS_ERR(inner_map)) 10056f668dfSMartin KaFai Lau return inner_map; 10156f668dfSMartin KaFai Lau 10256f668dfSMartin KaFai Lau if (bpf_map_meta_equal(map->inner_map_meta, inner_map)) 10356f668dfSMartin KaFai Lau inner_map = bpf_map_inc(inner_map, false); 10456f668dfSMartin KaFai Lau else 10556f668dfSMartin KaFai Lau inner_map = ERR_PTR(-EINVAL); 10656f668dfSMartin KaFai Lau 10756f668dfSMartin KaFai Lau fdput(f); 10856f668dfSMartin KaFai Lau return inner_map; 10956f668dfSMartin KaFai Lau } 11056f668dfSMartin KaFai Lau 11156f668dfSMartin KaFai Lau void bpf_map_fd_put_ptr(void *ptr) 11256f668dfSMartin KaFai Lau { 11356f668dfSMartin KaFai Lau /* ptr->ops->map_free() has to go through one 11456f668dfSMartin KaFai Lau * rcu grace period by itself. 11556f668dfSMartin KaFai Lau */ 11656f668dfSMartin KaFai Lau bpf_map_put(ptr); 11756f668dfSMartin KaFai Lau } 11814dc6f04SMartin KaFai Lau 11914dc6f04SMartin KaFai Lau u32 bpf_map_fd_sys_lookup_elem(void *ptr) 12014dc6f04SMartin KaFai Lau { 12114dc6f04SMartin KaFai Lau return ((struct bpf_map *)ptr)->id; 12214dc6f04SMartin KaFai Lau } 123