aboutsummaryrefslogtreecommitdiffstats
path: root/samples
diff options
context:
space:
mode:
authorJesper Dangaard Brouer <brouer@redhat.com>2017-05-02 08:31:56 -0400
committerDavid S. Miller <davem@davemloft.net>2017-05-03 09:30:24 -0400
commit156450d9d964447adfb44a231c634d2f5609d110 (patch)
treeb3f44925e03340a41e4ca5dd6d454e057cd04952 /samples
parent55de170382a92d6da6fc9f23efc21eb2f3d25126 (diff)
samples/bpf: make bpf_load.c code compatible with ELF maps section changes
This patch does proper parsing of the ELF "maps" section, in-order to be both backwards and forwards compatible with changes to the map definition struct bpf_map_def, which gets compiled into the ELF file. The assumption is that new features with value zero, means that they are not in-use. For backward compatibility where loading an ELF file with a smaller struct bpf_map_def, only copy objects ELF size, leaving rest of loaders struct zero. For forward compatibility where ELF file have a larger struct bpf_map_def, only copy loaders own struct size and verify that rest of the larger struct is zero, assuming this means the newer feature was not activated, thus it should be safe for this older loader to load this newer ELF file. Fixes: fb30d4b71214 ("bpf: Add tests for map-in-map") Fixes: 409526bea3c3 ("samples/bpf: bpf_load.c detect and abort if ELF maps section size is wrong") Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
-rw-r--r--samples/bpf/bpf_load.c224
1 files changed, 155 insertions, 69 deletions
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 4221dc359453..fedec29c7817 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -39,6 +39,16 @@ int event_fd[MAX_PROGS];
39int prog_cnt; 39int prog_cnt;
40int prog_array_fd = -1; 40int prog_array_fd = -1;
41 41
42/* Keeping relevant info on maps */
43struct bpf_map_data {
44 int fd;
45 char *name;
46 size_t elf_offset;
47 struct bpf_map_def def;
48};
49struct bpf_map_data map_data[MAX_MAPS];
50int map_data_count = 0;
51
42static int populate_prog_array(const char *event, int prog_fd) 52static int populate_prog_array(const char *event, int prog_fd)
43{ 53{
44 int ind = atoi(event), err; 54 int ind = atoi(event), err;
@@ -186,42 +196,39 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
186 return 0; 196 return 0;
187} 197}
188 198
189static int load_maps(struct bpf_map_def *maps, int nr_maps, 199static int load_maps(struct bpf_map_data *maps, int nr_maps,
190 const char **map_names, fixup_map_cb fixup_map) 200 fixup_map_cb fixup_map)
191{ 201{
192 int i; 202 int i;
193 /* 203
194 * Warning: Using "maps" pointing to ELF data_maps->d_buf as
195 * an array of struct bpf_map_def is a wrong assumption about
196 * the ELF maps section format.
197 */
198 for (i = 0; i < nr_maps; i++) { 204 for (i = 0; i < nr_maps; i++) {
199 if (fixup_map) 205 if (fixup_map)
200 fixup_map(&maps[i], map_names[i], i); 206 fixup_map(&maps[i].def, maps[i].name, i);
201 207
202 if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 208 if (maps[i].def.type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
203 maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) { 209 maps[i].def.type == BPF_MAP_TYPE_HASH_OF_MAPS) {
204 int inner_map_fd = map_fd[maps[i].inner_map_idx]; 210 int inner_map_fd = map_fd[maps[i].def.inner_map_idx];
205 211
206 map_fd[i] = bpf_create_map_in_map(maps[i].type, 212 map_fd[i] = bpf_create_map_in_map(maps[i].def.type,
207 maps[i].key_size, 213 maps[i].def.key_size,
208 inner_map_fd, 214 inner_map_fd,
209 maps[i].max_entries, 215 maps[i].def.max_entries,
210 maps[i].map_flags); 216 maps[i].def.map_flags);
211 } else { 217 } else {
212 map_fd[i] = bpf_create_map(maps[i].type, 218 map_fd[i] = bpf_create_map(maps[i].def.type,
213 maps[i].key_size, 219 maps[i].def.key_size,
214 maps[i].value_size, 220 maps[i].def.value_size,
215 maps[i].max_entries, 221 maps[i].def.max_entries,
216 maps[i].map_flags); 222 maps[i].def.map_flags);
217 } 223 }
218 if (map_fd[i] < 0) { 224 if (map_fd[i] < 0) {
219 printf("failed to create a map: %d %s\n", 225 printf("failed to create a map: %d %s\n",
220 errno, strerror(errno)); 226 errno, strerror(errno));
221 return 1; 227 return 1;
222 } 228 }
229 maps[i].fd = map_fd[i];
223 230
224 if (maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) 231 if (maps[i].def.type == BPF_MAP_TYPE_PROG_ARRAY)
225 prog_array_fd = map_fd[i]; 232 prog_array_fd = map_fd[i];
226 } 233 }
227 return 0; 234 return 0;
@@ -251,7 +258,8 @@ static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname,
251} 258}
252 259
253static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, 260static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols,
254 GElf_Shdr *shdr, struct bpf_insn *insn) 261 GElf_Shdr *shdr, struct bpf_insn *insn,
262 struct bpf_map_data *maps, int nr_maps)
255{ 263{
256 int i, nrels; 264 int i, nrels;
257 265
@@ -261,6 +269,8 @@ static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols,
261 GElf_Sym sym; 269 GElf_Sym sym;
262 GElf_Rel rel; 270 GElf_Rel rel;
263 unsigned int insn_idx; 271 unsigned int insn_idx;
272 bool match = false;
273 int j, map_idx;
264 274
265 gelf_getrel(data, i, &rel); 275 gelf_getrel(data, i, &rel);
266 276
@@ -274,11 +284,21 @@ static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols,
274 return 1; 284 return 1;
275 } 285 }
276 insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; 286 insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
277 /* 287
278 * Warning: Using sizeof(struct bpf_map_def) here is a 288 /* Match FD relocation against recorded map_data[] offset */
279 * wrong assumption about ELF maps section format 289 for (map_idx = 0; map_idx < nr_maps; map_idx++) {
280 */ 290 if (maps[map_idx].elf_offset == sym.st_value) {
281 insn[insn_idx].imm = map_fd[sym.st_value / sizeof(struct bpf_map_def)]; 291 match = true;
292 break;
293 }
294 }
295 if (match) {
296 insn[insn_idx].imm = maps[map_idx].fd;
297 } else {
298 printf("invalid relo for insn[%d] no map_data match\n",
299 insn_idx);
300 return 1;
301 }
282 } 302 }
283 303
284 return 0; 304 return 0;
@@ -297,40 +317,112 @@ static int cmp_symbols(const void *l, const void *r)
297 return 0; 317 return 0;
298} 318}
299 319
300static int get_sorted_map_names(Elf *elf, Elf_Data *symbols, int maps_shndx, 320static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
301 int strtabidx, char **map_names) 321 Elf *elf, Elf_Data *symbols, int strtabidx)
302{ 322{
303 GElf_Sym map_symbols[MAX_MAPS]; 323 int map_sz_elf, map_sz_copy;
304 int i, nr_maps = 0; 324 bool validate_zero = false;
325 Elf_Data *data_maps;
326 int i, nr_maps;
327 GElf_Sym *sym;
328 Elf_Scn *scn;
329 int copy_sz;
330
331 if (maps_shndx < 0)
332 return -EINVAL;
333 if (!symbols)
334 return -EINVAL;
335
336 /* Get data for maps section via elf index */
337 scn = elf_getscn(elf, maps_shndx);
338 if (scn)
339 data_maps = elf_getdata(scn, NULL);
340 if (!scn || !data_maps) {
341 printf("Failed to get Elf_Data from maps section %d\n",
342 maps_shndx);
343 return -EINVAL;
344 }
305 345
306 for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { 346 /* For each map get corrosponding symbol table entry */
307 assert(nr_maps < MAX_MAPS); 347 sym = calloc(MAX_MAPS+1, sizeof(GElf_Sym));
308 if (!gelf_getsym(symbols, i, &map_symbols[nr_maps])) 348 for (i = 0, nr_maps = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
349 assert(nr_maps < MAX_MAPS+1);
350 if (!gelf_getsym(symbols, i, &sym[nr_maps]))
309 continue; 351 continue;
310 if (map_symbols[nr_maps].st_shndx != maps_shndx) 352 if (sym[nr_maps].st_shndx != maps_shndx)
311 continue; 353 continue;
354 /* Only increment iif maps section */
312 nr_maps++; 355 nr_maps++;
313 } 356 }
314 357
315 qsort(map_symbols, nr_maps, sizeof(GElf_Sym), cmp_symbols); 358 /* Align to map_fd[] order, via sort on offset in sym.st_value */
359 qsort(sym, nr_maps, sizeof(GElf_Sym), cmp_symbols);
360
361 /* Keeping compatible with ELF maps section changes
362 * ------------------------------------------------
363 * The program size of struct bpf_map_def is known by loader
364 * code, but struct stored in ELF file can be different.
365 *
366 * Unfortunately sym[i].st_size is zero. To calculate the
367 * struct size stored in the ELF file, assume all struct have
368 * the same size, and simply divide with number of map
369 * symbols.
370 */
371 map_sz_elf = data_maps->d_size / nr_maps;
372 map_sz_copy = sizeof(struct bpf_map_def);
373 if (map_sz_elf < map_sz_copy) {
374 /*
375 * Backward compat, loading older ELF file with
376 * smaller struct, keeping remaining bytes zero.
377 */
378 map_sz_copy = map_sz_elf;
379 } else if (map_sz_elf > map_sz_copy) {
380 /*
381 * Forward compat, loading newer ELF file with larger
382 * struct with unknown features. Assume zero means
383 * feature not used. Thus, validate rest of struct
384 * data is zero.
385 */
386 validate_zero = true;
387 }
316 388
389 /* Memcpy relevant part of ELF maps data to loader maps */
317 for (i = 0; i < nr_maps; i++) { 390 for (i = 0; i < nr_maps; i++) {
318 char *map_name; 391 unsigned char *addr, *end;
319 392 struct bpf_map_def *def;
320 map_name = elf_strptr(elf, strtabidx, map_symbols[i].st_name); 393 const char *map_name;
321 if (!map_name) { 394 size_t offset;
322 printf("cannot get map symbol\n"); 395
323 return -1; 396 map_name = elf_strptr(elf, strtabidx, sym[i].st_name);
324 } 397 maps[i].name = strdup(map_name);
325 398 if (!maps[i].name) {
326 map_names[i] = strdup(map_name);
327 if (!map_names[i]) {
328 printf("strdup(%s): %s(%d)\n", map_name, 399 printf("strdup(%s): %s(%d)\n", map_name,
329 strerror(errno), errno); 400 strerror(errno), errno);
330 return -1; 401 free(sym);
402 return -errno;
403 }
404
405 /* Symbol value is offset into ELF maps section data area */
406 offset = sym[i].st_value;
407 def = (struct bpf_map_def *)(data_maps->d_buf + offset);
408 maps[i].elf_offset = offset;
409 memset(&maps[i].def, 0, sizeof(struct bpf_map_def));
410 memcpy(&maps[i].def, def, map_sz_copy);
411
412 /* Verify no newer features were requested */
413 if (validate_zero) {
414 addr = (unsigned char*) def + map_sz_copy;
415 end = (unsigned char*) def + map_sz_elf;
416 for (; addr < end; addr++) {
417 if (*addr != 0) {
418 free(sym);
419 return -EFBIG;
420 }
421 }
331 } 422 }
332 } 423 }
333 424
425 free(sym);
334 return nr_maps; 426 return nr_maps;
335} 427}
336 428
@@ -341,7 +433,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
341 GElf_Ehdr ehdr; 433 GElf_Ehdr ehdr;
342 GElf_Shdr shdr, shdr_prog; 434 GElf_Shdr shdr, shdr_prog;
343 Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL; 435 Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL;
344 char *shname, *shname_prog, *map_names[MAX_MAPS] = { NULL }; 436 char *shname, *shname_prog;
437 int nr_maps = 0;
345 438
346 /* reset global variables */ 439 /* reset global variables */
347 kern_version = 0; 440 kern_version = 0;
@@ -389,8 +482,12 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
389 } 482 }
390 memcpy(&kern_version, data->d_buf, sizeof(int)); 483 memcpy(&kern_version, data->d_buf, sizeof(int));
391 } else if (strcmp(shname, "maps") == 0) { 484 } else if (strcmp(shname, "maps") == 0) {
485 int j;
486
392 maps_shndx = i; 487 maps_shndx = i;
393 data_maps = data; 488 data_maps = data;
489 for (j = 0; j < MAX_MAPS; j++)
490 map_data[j].fd = -1;
394 } else if (shdr.sh_type == SHT_SYMTAB) { 491 } else if (shdr.sh_type == SHT_SYMTAB) {
395 strtabidx = shdr.sh_link; 492 strtabidx = shdr.sh_link;
396 symbols = data; 493 symbols = data;
@@ -405,27 +502,17 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
405 } 502 }
406 503
407 if (data_maps) { 504 if (data_maps) {
408 int nr_maps; 505 nr_maps = load_elf_maps_section(map_data, maps_shndx,
409 int prog_elf_map_sz; 506 elf, symbols, strtabidx);
410 507 if (nr_maps < 0) {
411 nr_maps = get_sorted_map_names(elf, symbols, maps_shndx, 508 printf("Error: Failed loading ELF maps (errno:%d):%s\n",
412 strtabidx, map_names); 509 nr_maps, strerror(-nr_maps));
413 if (nr_maps < 0)
414 goto done;
415
416 /* Deduce map struct size stored in ELF maps section */
417 prog_elf_map_sz = data_maps->d_size / nr_maps;
418 if (prog_elf_map_sz != sizeof(struct bpf_map_def)) {
419 printf("Error: ELF maps sec wrong size (%d/%lu),"
420 " old kern.o file?\n",
421 prog_elf_map_sz, sizeof(struct bpf_map_def));
422 ret = 1; 510 ret = 1;
423 goto done; 511 goto done;
424 } 512 }
425 513 if (load_maps(map_data, nr_maps, fixup_map))
426 if (load_maps(data_maps->d_buf, nr_maps,
427 (const char **)map_names, fixup_map))
428 goto done; 514 goto done;
515 map_data_count = nr_maps;
429 516
430 processed_sec[maps_shndx] = true; 517 processed_sec[maps_shndx] = true;
431 } 518 }
@@ -453,7 +540,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
453 processed_sec[shdr.sh_info] = true; 540 processed_sec[shdr.sh_info] = true;
454 processed_sec[i] = true; 541 processed_sec[i] = true;
455 542
456 if (parse_relo_and_apply(data, symbols, &shdr, insns)) 543 if (parse_relo_and_apply(data, symbols, &shdr, insns,
544 map_data, nr_maps))
457 continue; 545 continue;
458 546
459 if (memcmp(shname_prog, "kprobe/", 7) == 0 || 547 if (memcmp(shname_prog, "kprobe/", 7) == 0 ||
@@ -488,8 +576,6 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
488 576
489 ret = 0; 577 ret = 0;
490done: 578done:
491 for (i = 0; i < MAX_MAPS; i++)
492 free(map_names[i]);
493 close(fd); 579 close(fd);
494 return ret; 580 return ret;
495} 581}