Skip to content

Commit a2c70db

Browse files
author
Alexei Starovoitov
committed
Merge branch 'libbpf: allow to opt-out from BPF map creation'
Andrii Nakryiko says: ==================== Add bpf_map__set_autocreate() API which is a BPF map counterpart of bpf_program__set_autoload() and serves similar goal of allowing to build more flexible CO-RE applications. See patch #3 for example scenarios in which the need for such API came up previously. Patch #1 is a follow-up patch to previous patch set adding verifier log fixup logic, making sure bpf_core_format_spec()'s return result is used for something useful. Patch #2 is a small refactoring to avoid unnecessary verbose memory management around obj->maps array. Patch #3 adds and API and corresponding BPF verifier log fix up logic to provide human-comprehensible error message with useful details. Patch #4 adds a simple selftest validating both the API itself and libbpf's log fixup logic for it. ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 32c03c4 + 68964e1 commit a2c70db

File tree

5 files changed

+209
-49
lines changed

5 files changed

+209
-49
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 122 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ enum libbpf_map_type {
357357
};
358358

359359
struct bpf_map {
360+
struct bpf_object *obj;
360361
char *name;
361362
/* real_name is defined for special internal maps (.rodata*,
362363
* .data*, .bss, .kconfig) and preserves their original ELF section
@@ -386,7 +387,7 @@ struct bpf_map {
386387
char *pin_path;
387388
bool pinned;
388389
bool reused;
389-
bool skipped;
390+
bool autocreate;
390391
__u64 map_extra;
391392
};
392393

@@ -1433,36 +1434,21 @@ static int find_elf_var_offset(const struct bpf_object *obj, const char *name, _
14331434

14341435
static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
14351436
{
1436-
struct bpf_map *new_maps;
1437-
size_t new_cap;
1438-
int i;
1439-
1440-
if (obj->nr_maps < obj->maps_cap)
1441-
return &obj->maps[obj->nr_maps++];
1442-
1443-
new_cap = max((size_t)4, obj->maps_cap * 3 / 2);
1444-
new_maps = libbpf_reallocarray(obj->maps, new_cap, sizeof(*obj->maps));
1445-
if (!new_maps) {
1446-
pr_warn("alloc maps for object failed\n");
1447-
return ERR_PTR(-ENOMEM);
1448-
}
1437+
struct bpf_map *map;
1438+
int err;
14491439

1450-
obj->maps_cap = new_cap;
1451-
obj->maps = new_maps;
1440+
err = libbpf_ensure_mem((void **)&obj->maps, &obj->maps_cap,
1441+
sizeof(*obj->maps), obj->nr_maps + 1);
1442+
if (err)
1443+
return ERR_PTR(err);
14521444

1453-
/* zero out new maps */
1454-
memset(obj->maps + obj->nr_maps, 0,
1455-
(obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
1456-
/*
1457-
* fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
1458-
* when failure (zclose won't close negative fd)).
1459-
*/
1460-
for (i = obj->nr_maps; i < obj->maps_cap; i++) {
1461-
obj->maps[i].fd = -1;
1462-
obj->maps[i].inner_map_fd = -1;
1463-
}
1445+
map = &obj->maps[obj->nr_maps++];
1446+
map->obj = obj;
1447+
map->fd = -1;
1448+
map->inner_map_fd = -1;
1449+
map->autocreate = true;
14641450

1465-
return &obj->maps[obj->nr_maps++];
1451+
return map;
14661452
}
14671453

14681454
static size_t bpf_map_mmap_sz(const struct bpf_map *map)
@@ -4324,6 +4310,20 @@ static int bpf_get_map_info_from_fdinfo(int fd, struct bpf_map_info *info)
43244310
return 0;
43254311
}
43264312

4313+
bool bpf_map__autocreate(const struct bpf_map *map)
4314+
{
4315+
return map->autocreate;
4316+
}
4317+
4318+
int bpf_map__set_autocreate(struct bpf_map *map, bool autocreate)
4319+
{
4320+
if (map->obj->loaded)
4321+
return libbpf_err(-EBUSY);
4322+
4323+
map->autocreate = autocreate;
4324+
return 0;
4325+
}
4326+
43274327
int bpf_map__reuse_fd(struct bpf_map *map, int fd)
43284328
{
43294329
struct bpf_map_info info = {};
@@ -5180,9 +5180,11 @@ bpf_object__create_maps(struct bpf_object *obj)
51805180
* bpf_object loading will succeed just fine even on old
51815181
* kernels.
51825182
*/
5183-
if (bpf_map__is_internal(map) &&
5184-
!kernel_supports(obj, FEAT_GLOBAL_DATA)) {
5185-
map->skipped = true;
5183+
if (bpf_map__is_internal(map) && !kernel_supports(obj, FEAT_GLOBAL_DATA))
5184+
map->autocreate = false;
5185+
5186+
if (!map->autocreate) {
5187+
pr_debug("map '%s': skipped auto-creating...\n", map->name);
51865188
continue;
51875189
}
51885190

@@ -5805,6 +5807,36 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
58055807
return err;
58065808
}
58075809

5810+
/* base map load ldimm64 special constant, used also for log fixup logic */
5811+
#define MAP_LDIMM64_POISON_BASE 2001000000
5812+
#define MAP_LDIMM64_POISON_PFX "200100"
5813+
5814+
static void poison_map_ldimm64(struct bpf_program *prog, int relo_idx,
5815+
int insn_idx, struct bpf_insn *insn,
5816+
int map_idx, const struct bpf_map *map)
5817+
{
5818+
int i;
5819+
5820+
pr_debug("prog '%s': relo #%d: poisoning insn #%d that loads map #%d '%s'\n",
5821+
prog->name, relo_idx, insn_idx, map_idx, map->name);
5822+
5823+
/* we turn single ldimm64 into two identical invalid calls */
5824+
for (i = 0; i < 2; i++) {
5825+
insn->code = BPF_JMP | BPF_CALL;
5826+
insn->dst_reg = 0;
5827+
insn->src_reg = 0;
5828+
insn->off = 0;
5829+
/* if this instruction is reachable (not a dead code),
5830+
* verifier will complain with something like:
5831+
* invalid func unknown#2001000123
5832+
* where lower 123 is map index into obj->maps[] array
5833+
*/
5834+
insn->imm = MAP_LDIMM64_POISON_BASE + map_idx;
5835+
5836+
insn++;
5837+
}
5838+
}
5839+
58085840
/* Relocate data references within program code:
58095841
* - map references;
58105842
* - global variable references;
@@ -5818,33 +5850,35 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
58185850
for (i = 0; i < prog->nr_reloc; i++) {
58195851
struct reloc_desc *relo = &prog->reloc_desc[i];
58205852
struct bpf_insn *insn = &prog->insns[relo->insn_idx];
5853+
const struct bpf_map *map;
58215854
struct extern_desc *ext;
58225855

58235856
switch (relo->type) {
58245857
case RELO_LD64:
5858+
map = &obj->maps[relo->map_idx];
58255859
if (obj->gen_loader) {
58265860
insn[0].src_reg = BPF_PSEUDO_MAP_IDX;
58275861
insn[0].imm = relo->map_idx;
5828-
} else {
5862+
} else if (map->autocreate) {
58295863
insn[0].src_reg = BPF_PSEUDO_MAP_FD;
5830-
insn[0].imm = obj->maps[relo->map_idx].fd;
5864+
insn[0].imm = map->fd;
5865+
} else {
5866+
poison_map_ldimm64(prog, i, relo->insn_idx, insn,
5867+
relo->map_idx, map);
58315868
}
58325869
break;
58335870
case RELO_DATA:
5871+
map = &obj->maps[relo->map_idx];
58345872
insn[1].imm = insn[0].imm + relo->sym_off;
58355873
if (obj->gen_loader) {
58365874
insn[0].src_reg = BPF_PSEUDO_MAP_IDX_VALUE;
58375875
insn[0].imm = relo->map_idx;
5838-
} else {
5839-
const struct bpf_map *map = &obj->maps[relo->map_idx];
5840-
5841-
if (map->skipped) {
5842-
pr_warn("prog '%s': relo #%d: kernel doesn't support global data\n",
5843-
prog->name, i);
5844-
return -ENOTSUP;
5845-
}
5876+
} else if (map->autocreate) {
58465877
insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
5847-
insn[0].imm = obj->maps[relo->map_idx].fd;
5878+
insn[0].imm = map->fd;
5879+
} else {
5880+
poison_map_ldimm64(prog, i, relo->insn_idx, insn,
5881+
relo->map_idx, map);
58485882
}
58495883
break;
58505884
case RELO_EXTERN_VAR:
@@ -6962,7 +6996,7 @@ static void fixup_log_failed_core_relo(struct bpf_program *prog,
69626996
const struct bpf_core_relo *relo;
69636997
struct bpf_core_spec spec;
69646998
char patch[512], spec_buf[256];
6965-
int insn_idx, err;
6999+
int insn_idx, err, spec_len;
69667000

69677001
if (sscanf(line1, "%d: (%*d) call unknown#195896080\n", &insn_idx) != 1)
69687002
return;
@@ -6975,11 +7009,44 @@ static void fixup_log_failed_core_relo(struct bpf_program *prog,
69757009
if (err)
69767010
return;
69777011

6978-
bpf_core_format_spec(spec_buf, sizeof(spec_buf), &spec);
7012+
spec_len = bpf_core_format_spec(spec_buf, sizeof(spec_buf), &spec);
69797013
snprintf(patch, sizeof(patch),
69807014
"%d: <invalid CO-RE relocation>\n"
6981-
"failed to resolve CO-RE relocation %s\n",
6982-
insn_idx, spec_buf);
7015+
"failed to resolve CO-RE relocation %s%s\n",
7016+
insn_idx, spec_buf, spec_len >= sizeof(spec_buf) ? "..." : "");
7017+
7018+
patch_log(buf, buf_sz, log_sz, line1, line3 - line1, patch);
7019+
}
7020+
7021+
static void fixup_log_missing_map_load(struct bpf_program *prog,
7022+
char *buf, size_t buf_sz, size_t log_sz,
7023+
char *line1, char *line2, char *line3)
7024+
{
7025+
/* Expected log for failed and not properly guarded CO-RE relocation:
7026+
* line1 -> 123: (85) call unknown#2001000345
7027+
* line2 -> invalid func unknown#2001000345
7028+
* line3 -> <anything else or end of buffer>
7029+
*
7030+
* "123" is the index of the instruction that was poisoned.
7031+
* "345" in "2001000345" are map index in obj->maps to fetch map name.
7032+
*/
7033+
struct bpf_object *obj = prog->obj;
7034+
const struct bpf_map *map;
7035+
int insn_idx, map_idx;
7036+
char patch[128];
7037+
7038+
if (sscanf(line1, "%d: (%*d) call unknown#%d\n", &insn_idx, &map_idx) != 2)
7039+
return;
7040+
7041+
map_idx -= MAP_LDIMM64_POISON_BASE;
7042+
if (map_idx < 0 || map_idx >= obj->nr_maps)
7043+
return;
7044+
map = &obj->maps[map_idx];
7045+
7046+
snprintf(patch, sizeof(patch),
7047+
"%d: <invalid BPF map reference>\n"
7048+
"BPF map '%s' is referenced but wasn't created\n",
7049+
insn_idx, map->name);
69837050

69847051
patch_log(buf, buf_sz, log_sz, line1, line3 - line1, patch);
69857052
}
@@ -7012,6 +7079,14 @@ static void fixup_verifier_log(struct bpf_program *prog, char *buf, size_t buf_s
70127079
fixup_log_failed_core_relo(prog, buf, buf_sz, log_sz,
70137080
prev_line, cur_line, next_line);
70147081
return;
7082+
} else if (str_has_pfx(cur_line, "invalid func unknown#"MAP_LDIMM64_POISON_PFX)) {
7083+
prev_line = find_prev_line(buf, cur_line);
7084+
if (!prev_line)
7085+
continue;
7086+
7087+
fixup_log_missing_map_load(prog, buf, buf_sz, log_sz,
7088+
prev_line, cur_line, next_line);
7089+
return;
70157090
}
70167091
}
70177092
}
@@ -8200,7 +8275,7 @@ int bpf_object__pin_maps(struct bpf_object *obj, const char *path)
82008275
char *pin_path = NULL;
82018276
char buf[PATH_MAX];
82028277

8203-
if (map->skipped)
8278+
if (!map->autocreate)
82048279
continue;
82058280

82068281
if (path) {

tools/lib/bpf/libbpf.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,28 @@ struct bpf_map *bpf_map__prev(const struct bpf_map *map, const struct bpf_object
866866
LIBBPF_API struct bpf_map *
867867
bpf_object__prev_map(const struct bpf_object *obj, const struct bpf_map *map);
868868

869+
/**
870+
* @brief **bpf_map__set_autocreate()** sets whether libbpf has to auto-create
871+
* BPF map during BPF object load phase.
872+
* @param map the BPF map instance
873+
* @param autocreate whether to create BPF map during BPF object load
874+
* @return 0 on success; -EBUSY if BPF object was already loaded
875+
*
876+
* **bpf_map__set_autocreate()** allows to opt-out from libbpf auto-creating
877+
* BPF map. By default, libbpf will attempt to create every single BPF map
878+
* defined in BPF object file using BPF_MAP_CREATE command of bpf() syscall
879+
* and fill in map FD in BPF instructions.
880+
*
881+
* This API allows to opt-out of this process for specific map instance. This
882+
* can be useful if host kernel doesn't support such BPF map type or used
883+
* combination of flags and user application wants to avoid creating such
884+
* a map in the first place. User is still responsible to make sure that their
885+
* BPF-side code that expects to use such missing BPF map is recognized by BPF
886+
* verifier as dead code, otherwise BPF verifier will reject such BPF program.
887+
*/
888+
LIBBPF_API int bpf_map__set_autocreate(struct bpf_map *map, bool autocreate);
889+
LIBBPF_API bool bpf_map__autocreate(const struct bpf_map *map);
890+
869891
/**
870892
* @brief **bpf_map__fd()** gets the file descriptor of the passed
871893
* BPF map

tools/lib/bpf/libbpf.map

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,12 @@ LIBBPF_0.7.0 {
442442

443443
LIBBPF_0.8.0 {
444444
global:
445+
bpf_map__autocreate;
446+
bpf_map__set_autocreate;
445447
bpf_object__destroy_subskeleton;
446448
bpf_object__open_subskeleton;
449+
bpf_program__attach_kprobe_multi_opts;
447450
bpf_program__attach_usdt;
448451
libbpf_register_prog_handler;
449452
libbpf_unregister_prog_handler;
450-
bpf_program__attach_kprobe_multi_opts;
451453
} LIBBPF_0.7.0;

tools/testing/selftests/bpf/prog_tests/log_fixup.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ static void bad_core_relo_subprog(void)
8585
if (!ASSERT_ERR(err, "load_fail"))
8686
goto cleanup;
8787

88-
/* there should be no prog loading log because we specified per-prog log buf */
8988
ASSERT_HAS_SUBSTR(log_buf,
9089
": <invalid CO-RE relocation>\n"
9190
"failed to resolve CO-RE relocation <byte_off> ",
@@ -101,6 +100,40 @@ static void bad_core_relo_subprog(void)
101100
test_log_fixup__destroy(skel);
102101
}
103102

103+
static void missing_map(void)
104+
{
105+
char log_buf[8 * 1024];
106+
struct test_log_fixup* skel;
107+
int err;
108+
109+
skel = test_log_fixup__open();
110+
if (!ASSERT_OK_PTR(skel, "skel_open"))
111+
return;
112+
113+
bpf_map__set_autocreate(skel->maps.missing_map, false);
114+
115+
bpf_program__set_autoload(skel->progs.use_missing_map, true);
116+
bpf_program__set_log_buf(skel->progs.use_missing_map, log_buf, sizeof(log_buf));
117+
118+
err = test_log_fixup__load(skel);
119+
if (!ASSERT_ERR(err, "load_fail"))
120+
goto cleanup;
121+
122+
ASSERT_TRUE(bpf_map__autocreate(skel->maps.existing_map), "existing_map_autocreate");
123+
ASSERT_FALSE(bpf_map__autocreate(skel->maps.missing_map), "missing_map_autocreate");
124+
125+
ASSERT_HAS_SUBSTR(log_buf,
126+
"8: <invalid BPF map reference>\n"
127+
"BPF map 'missing_map' is referenced but wasn't created\n",
128+
"log_buf");
129+
130+
if (env.verbosity > VERBOSE_NONE)
131+
printf("LOG: \n=================\n%s=================\n", log_buf);
132+
133+
cleanup:
134+
test_log_fixup__destroy(skel);
135+
}
136+
104137
void test_log_fixup(void)
105138
{
106139
if (test__start_subtest("bad_core_relo_trunc_none"))
@@ -111,4 +144,6 @@ void test_log_fixup(void)
111144
bad_core_relo(250, TRUNC_FULL /* truncate also libbpf's message patch */);
112145
if (test__start_subtest("bad_core_relo_subprog"))
113146
bad_core_relo_subprog();
147+
if (test__start_subtest("missing_map"))
148+
missing_map();
114149
}

tools/testing/selftests/bpf/progs/test_log_fixup.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,30 @@ int bad_relo_subprog(const void *ctx)
3535
return bad_subprog() + bpf_core_field_size(t->pid);
3636
}
3737

38+
struct {
39+
__uint(type, BPF_MAP_TYPE_ARRAY);
40+
__uint(max_entries, 1);
41+
__type(key, int);
42+
__type(value, int);
43+
} existing_map SEC(".maps");
44+
45+
struct {
46+
__uint(type, BPF_MAP_TYPE_ARRAY);
47+
__uint(max_entries, 1);
48+
__type(key, int);
49+
__type(value, int);
50+
} missing_map SEC(".maps");
51+
52+
SEC("?raw_tp/sys_enter")
53+
int use_missing_map(const void *ctx)
54+
{
55+
int zero = 0, *value;
56+
57+
value = bpf_map_lookup_elem(&existing_map, &zero);
58+
59+
value = bpf_map_lookup_elem(&missing_map, &zero);
60+
61+
return value != NULL;
62+
}
63+
3864
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)