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

libbpf: Support weak typed ksyms.

Currently weak typeless ksyms have default value zero, when they don't
exist in the kernel. However, weak typed ksyms are rejected by libbpf
if they can not be resolved. This means that if a bpf object contains
the declaration of a nonexistent weak typed ksym, it will be rejected
even if there is no program that references the symbol.

Nonexistent weak typed ksyms can also default to zero just like
typeless ones. This allows programs that access weak typed ksyms to be
accepted by verifier, if the accesses are guarded. For example,

extern const int bpf_link_fops3 __ksym __weak;

/* then in BPF program */

if (&bpf_link_fops3) {
/* use bpf_link_fops3 */
}

If actual use of nonexistent typed ksym is not guarded properly,
verifier would see that register is not PTR_TO_BTF_ID and wouldn't
allow to use it for direct memory reads or passing it to BPF helpers.

Signed-off-by: Hao Luo <haoluo@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210812003819.2439037-1-haoluo@google.com

authored by

Hao Luo and committed by
Andrii Nakryiko
2211c825 cf7a5cba

+96 -7
+9 -7
tools/lib/bpf/libbpf.c
··· 5277 5277 } 5278 5278 insn[1].imm = ext->kcfg.data_off; 5279 5279 } else /* EXT_KSYM */ { 5280 - if (ext->ksym.type_id) { /* typed ksyms */ 5280 + if (ext->ksym.type_id && ext->is_set) { /* typed ksyms */ 5281 5281 insn[0].src_reg = BPF_PSEUDO_BTF_ID; 5282 5282 insn[0].imm = ext->ksym.kernel_btf_id; 5283 5283 insn[1].imm = ext->ksym.kernel_btf_obj_fd; 5284 - } else { /* typeless ksyms */ 5284 + } else { /* typeless ksyms or unresolved typed ksyms */ 5285 5285 insn[0].imm = (__u32)ext->ksym.addr; 5286 5286 insn[1].imm = ext->ksym.addr >> 32; 5287 5287 } ··· 6608 6608 break; 6609 6609 } 6610 6610 } 6611 - if (id <= 0) { 6612 - pr_warn("extern (%s ksym) '%s': failed to find BTF ID in kernel BTF(s).\n", 6613 - __btf_kind_str(kind), ksym_name); 6611 + if (id <= 0) 6614 6612 return -ESRCH; 6615 - } 6616 6613 6617 6614 *res_btf = btf; 6618 6615 *res_btf_fd = btf_fd; ··· 6626 6629 struct btf *btf = NULL; 6627 6630 6628 6631 id = find_ksym_btf_id(obj, ext->name, BTF_KIND_VAR, &btf, &btf_fd); 6629 - if (id < 0) 6632 + if (id == -ESRCH && ext->is_weak) { 6633 + return 0; 6634 + } else if (id < 0) { 6635 + pr_warn("extern (var ksym) '%s': not found in kernel BTF\n", 6636 + ext->name); 6630 6637 return id; 6638 + } 6631 6639 6632 6640 /* find local type_id */ 6633 6641 local_type_id = ext->ksym.type_id;
+31
tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
··· 6 6 #include <bpf/btf.h> 7 7 #include "test_ksyms_btf.skel.h" 8 8 #include "test_ksyms_btf_null_check.skel.h" 9 + #include "test_ksyms_weak.skel.h" 9 10 10 11 static int duration; 11 12 ··· 82 81 test_ksyms_btf_null_check__destroy(skel); 83 82 } 84 83 84 + static void test_weak_syms(void) 85 + { 86 + struct test_ksyms_weak *skel; 87 + struct test_ksyms_weak__data *data; 88 + int err; 89 + 90 + skel = test_ksyms_weak__open_and_load(); 91 + if (CHECK(!skel, "test_ksyms_weak__open_and_load", "failed\n")) 92 + return; 93 + 94 + err = test_ksyms_weak__attach(skel); 95 + if (CHECK(err, "test_ksyms_weak__attach", "skeleton attach failed: %d\n", err)) 96 + goto cleanup; 97 + 98 + /* trigger tracepoint */ 99 + usleep(1); 100 + 101 + data = skel->data; 102 + ASSERT_EQ(data->out__existing_typed, 0, "existing typed ksym"); 103 + ASSERT_NEQ(data->out__existing_typeless, -1, "existing typeless ksym"); 104 + ASSERT_EQ(data->out__non_existent_typeless, 0, "nonexistent typeless ksym"); 105 + ASSERT_EQ(data->out__non_existent_typed, 0, "nonexistent typed ksym"); 106 + 107 + cleanup: 108 + test_ksyms_weak__destroy(skel); 109 + } 110 + 85 111 void test_ksyms_btf(void) 86 112 { 87 113 int percpu_datasec; ··· 133 105 134 106 if (test__start_subtest("null_check")) 135 107 test_null_check(); 108 + 109 + if (test__start_subtest("weak_ksyms")) 110 + test_weak_syms(); 136 111 }
+56
tools/testing/selftests/bpf/progs/test_ksyms_weak.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Test weak ksyms. 4 + * 5 + * Copyright (c) 2021 Google 6 + */ 7 + 8 + #include "vmlinux.h" 9 + 10 + #include <bpf/bpf_helpers.h> 11 + 12 + int out__existing_typed = -1; 13 + __u64 out__existing_typeless = -1; 14 + 15 + __u64 out__non_existent_typeless = -1; 16 + __u64 out__non_existent_typed = -1; 17 + 18 + /* existing weak symbols */ 19 + 20 + /* test existing weak symbols can be resolved. */ 21 + extern const struct rq runqueues __ksym __weak; /* typed */ 22 + extern const void bpf_prog_active __ksym __weak; /* typeless */ 23 + 24 + 25 + /* non-existent weak symbols. */ 26 + 27 + /* typeless symbols, default to zero. */ 28 + extern const void bpf_link_fops1 __ksym __weak; 29 + 30 + /* typed symbols, default to zero. */ 31 + extern const int bpf_link_fops2 __ksym __weak; 32 + 33 + SEC("raw_tp/sys_enter") 34 + int pass_handler(const void *ctx) 35 + { 36 + struct rq *rq; 37 + 38 + /* tests existing symbols. */ 39 + rq = (struct rq *)bpf_per_cpu_ptr(&runqueues, 0); 40 + if (rq) 41 + out__existing_typed = rq->cpu; 42 + out__existing_typeless = (__u64)&bpf_prog_active; 43 + 44 + /* tests non-existent symbols. */ 45 + out__non_existent_typeless = (__u64)&bpf_link_fops1; 46 + 47 + /* tests non-existent symbols. */ 48 + out__non_existent_typed = (__u64)&bpf_link_fops2; 49 + 50 + if (&bpf_link_fops2) /* can't happen */ 51 + out__non_existent_typed = (__u64)bpf_per_cpu_ptr(&bpf_link_fops2, 0); 52 + 53 + return 0; 54 + } 55 + 56 + char _license[] SEC("license") = "GPL";