Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: only add BPF_F_MMAPABLE flag for data maps with global vars

Teach libbpf to not add BPF_F_MMAPABLE flag unnecessarily for ARRAY maps
that are backing data sections, if such data sections don't expose any
variables to user-space. Exposed variables are those that have
STB_GLOBAL or STB_WEAK ELF binding and correspond to BTF VAR's
BTF_VAR_GLOBAL_ALLOCATED linkage.

The overall idea is that if some data section doesn't have any variable that
is exposed through BPF skeleton, then there is no reason to make such
BPF array mmapable. Making BPF array mmapable is not a free no-op
action, because BPF verifier doesn't allow users to put special objects
(such as BPF spin locks, RB tree nodes, linked list nodes, kptrs, etc;
anything that has a sensitive internal state that should not be modified
arbitrarily from user space) into mmapable arrays, as there is no way to
prevent user space from corrupting such sensitive state through direct
memory access through memory-mapped region.

By making sure that libbpf doesn't add BPF_F_MMAPABLE flag to BPF array
maps corresponding to data sections that only have static variables
(which are not supposed to be visible to user space according to libbpf
and BPF skeleton rules), users now can have spinlocks, kptrs, etc in
either default .bss/.data sections or custom .data.* sections (assuming
there are no global variables in such sections).

The only possible hiccup with this approach is the need to use global
variables during BPF static linking, even if it's not intended to be
shared with user space through BPF skeleton. To allow such scenarios,
extend libbpf's STV_HIDDEN ELF visibility attribute handling to
variables. Libbpf is already treating global hidden BPF subprograms as
static subprograms and adjusts BTF accordingly to make BPF verifier
verify such subprograms as static subprograms with preserving entire BPF
verifier state between subprog calls. This patch teaches libbpf to treat
global hidden variables as static ones and adjust BTF information
accordingly as well. This allows to share variables between multiple
object files during static linking, but still keep them internal to BPF
program and not get them exposed through BPF skeleton.

Note, that if the user has some advanced scenario where they absolutely
need BPF_F_MMAPABLE flag on .data/.bss/.rodata BPF array map despite
only having static variables, they still can achieve this by forcing it
through explicit bpf_map__set_map_flags() API.

Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Dave Marchevsky <davemarchevsky@fb.com>
Link: https://lore.kernel.org/r/20221019002816.359650-3-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
4fcac46c f33f742d

+78 -19
+78 -19
tools/lib/bpf/libbpf.c
··· 1577 1577 } 1578 1578 1579 1579 static int 1580 - bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map); 1580 + map_fill_btf_type_info(struct bpf_object *obj, struct bpf_map *map); 1581 + 1582 + /* Internal BPF map is mmap()'able only if at least one of corresponding 1583 + * DATASEC's VARs are to be exposed through BPF skeleton. I.e., it's a GLOBAL 1584 + * variable and it's not marked as __hidden (which turns it into, effectively, 1585 + * a STATIC variable). 1586 + */ 1587 + static bool map_is_mmapable(struct bpf_object *obj, struct bpf_map *map) 1588 + { 1589 + const struct btf_type *t, *vt; 1590 + struct btf_var_secinfo *vsi; 1591 + int i, n; 1592 + 1593 + if (!map->btf_value_type_id) 1594 + return false; 1595 + 1596 + t = btf__type_by_id(obj->btf, map->btf_value_type_id); 1597 + if (!btf_is_datasec(t)) 1598 + return false; 1599 + 1600 + vsi = btf_var_secinfos(t); 1601 + for (i = 0, n = btf_vlen(t); i < n; i++, vsi++) { 1602 + vt = btf__type_by_id(obj->btf, vsi->type); 1603 + if (!btf_is_var(vt)) 1604 + continue; 1605 + 1606 + if (btf_var(vt)->linkage != BTF_VAR_STATIC) 1607 + return true; 1608 + } 1609 + 1610 + return false; 1611 + } 1581 1612 1582 1613 static int 1583 1614 bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, ··· 1640 1609 def->max_entries = 1; 1641 1610 def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG 1642 1611 ? BPF_F_RDONLY_PROG : 0; 1643 - def->map_flags |= BPF_F_MMAPABLE; 1612 + 1613 + /* failures are fine because of maps like .rodata.str1.1 */ 1614 + (void) map_fill_btf_type_info(obj, map); 1615 + 1616 + if (map_is_mmapable(obj, map)) 1617 + def->map_flags |= BPF_F_MMAPABLE; 1644 1618 1645 1619 pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", 1646 1620 map->name, map->sec_idx, map->sec_offset, def->map_flags); ··· 1661 1625 zfree(&map->name); 1662 1626 return err; 1663 1627 } 1664 - 1665 - /* failures are fine because of maps like .rodata.str1.1 */ 1666 - (void) bpf_map_find_btf_info(obj, map); 1667 1628 1668 1629 if (data) 1669 1630 memcpy(map->mmaped, data, data_sz); ··· 2573 2540 fill_map_from_def(map->inner_map, &inner_def); 2574 2541 } 2575 2542 2576 - err = bpf_map_find_btf_info(obj, map); 2543 + err = map_fill_btf_type_info(obj, map); 2577 2544 if (err) 2578 2545 return err; 2579 2546 ··· 2881 2848 __u32 size = 0, i, vars = btf_vlen(t); 2882 2849 const char *sec_name = btf__name_by_offset(btf, t->name_off); 2883 2850 struct btf_var_secinfo *vsi; 2851 + bool fixup_offsets = false; 2884 2852 int err; 2885 2853 2886 2854 if (!sec_name) { ··· 2889 2855 return -ENOENT; 2890 2856 } 2891 2857 2892 - /* extern-backing datasecs (.ksyms, .kconfig) have their size and 2893 - * variable offsets set at the previous step, so we skip any fixups 2894 - * for such sections 2858 + /* Extern-backing datasecs (.ksyms, .kconfig) have their size and 2859 + * variable offsets set at the previous step. Further, not every 2860 + * extern BTF VAR has corresponding ELF symbol preserved, so we skip 2861 + * all fixups altogether for such sections and go straight to sorting 2862 + * VARs within their DATASEC. 2895 2863 */ 2896 - if (t->size) 2864 + if (strcmp(sec_name, KCONFIG_SEC) == 0 || strcmp(sec_name, KSYMS_SEC) == 0) 2897 2865 goto sort_vars; 2898 2866 2899 - err = find_elf_sec_sz(obj, sec_name, &size); 2900 - if (err || !size) { 2901 - pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", 2902 - sec_name, size, err); 2903 - return -ENOENT; 2904 - } 2867 + /* Clang leaves DATASEC size and VAR offsets as zeroes, so we need to 2868 + * fix this up. But BPF static linker already fixes this up and fills 2869 + * all the sizes and offsets during static linking. So this step has 2870 + * to be optional. But the STV_HIDDEN handling is non-optional for any 2871 + * non-extern DATASEC, so the variable fixup loop below handles both 2872 + * functions at the same time, paying the cost of BTF VAR <-> ELF 2873 + * symbol matching just once. 2874 + */ 2875 + if (t->size == 0) { 2876 + err = find_elf_sec_sz(obj, sec_name, &size); 2877 + if (err || !size) { 2878 + pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", 2879 + sec_name, size, err); 2880 + return -ENOENT; 2881 + } 2905 2882 2906 - t->size = size; 2883 + t->size = size; 2884 + fixup_offsets = true; 2885 + } 2907 2886 2908 2887 for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) { 2909 2888 const struct btf_type *t_var; ··· 2948 2901 return -ENOENT; 2949 2902 } 2950 2903 2951 - vsi->offset = sym->st_value; 2904 + if (fixup_offsets) 2905 + vsi->offset = sym->st_value; 2906 + 2907 + /* if variable is a global/weak symbol, but has restricted 2908 + * (STV_HIDDEN or STV_INTERNAL) visibility, mark its BTF VAR 2909 + * as static. This follows similar logic for functions (BPF 2910 + * subprogs) and influences libbpf's further decisions about 2911 + * whether to make global data BPF array maps as 2912 + * BPF_F_MMAPABLE. 2913 + */ 2914 + if (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN 2915 + || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL) 2916 + var->linkage = BTF_VAR_STATIC; 2952 2917 } 2953 2918 2954 2919 sort_vars: ··· 4282 4223 return 0; 4283 4224 } 4284 4225 4285 - static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map) 4226 + static int map_fill_btf_type_info(struct bpf_object *obj, struct bpf_map *map) 4286 4227 { 4287 4228 int id; 4288 4229