summaryrefslogtreecommitdiff
path: root/tools/lib/bpf
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
commit8f9aa2c90530ab92301a82231ae44f3722becd93 (patch)
treefb282e955b0a880b07131a135257fe3ec764e928 /tools/lib/bpf
parent93467b31bec6da512b51544e5e4584f2745e995e (diff)
parent155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/lib/bpf')
-rw-r--r--tools/lib/bpf/btf.c4
-rw-r--r--tools/lib/bpf/gen_loader.c81
-rw-r--r--tools/lib/bpf/libbpf.c9
-rw-r--r--tools/lib/bpf/strset.c62
-rw-r--r--tools/lib/bpf/usdt.c16
5 files changed, 122 insertions, 50 deletions
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index ceb57b46a878..771aeaa0262b 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -4578,12 +4578,14 @@ static int btf_dedup_prep(struct btf_dedup *d)
case BTF_KIND_RESTRICT:
case BTF_KIND_PTR:
case BTF_KIND_FWD:
- case BTF_KIND_TYPEDEF:
case BTF_KIND_FUNC:
case BTF_KIND_FLOAT:
case BTF_KIND_TYPE_TAG:
h = btf_hash_common(t);
break;
+ case BTF_KIND_TYPEDEF:
+ h = btf_hash_typedef(t);
+ break;
case BTF_KIND_INT:
case BTF_KIND_DECL_TAG:
h = btf_hash_int_decl_tag(t);
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index 9478b8f78f26..40004aa3af71 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -398,13 +398,12 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
blob_fd_array_off(gen, i));
emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0));
emit(gen, BPF_EXIT_INSN());
- if (OPTS_GET(gen->opts, gen_hash, false))
- compute_sha_update_offsets(gen);
-
- pr_debug("gen: finish %s\n", errstr(gen->error));
if (!gen->error) {
struct gen_loader_opts *opts = gen->opts;
+ if (OPTS_GET(opts, gen_hash, false))
+ compute_sha_update_offsets(gen);
+
opts->insns = gen->insn_start;
opts->insns_sz = gen->insn_cur - gen->insn_start;
opts->data = gen->data_start;
@@ -419,6 +418,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
bpf_insn_bswap(insn++);
}
}
+ pr_debug("gen: finish %s\n", errstr(gen->error));
return gen->error;
}
@@ -545,13 +545,22 @@ void bpf_gen__map_create(struct bpf_gen *gen,
default:
break;
}
- /* conditionally update max_entries */
- if (map_idx >= 0)
+
+ /*
+ * Conditionally update max_entries from the host-supplied loader
+ * ctx. This sizes the map at runtime, but for a signed loader
+ * (gen_hash) it would let an untrusted host re-dimension the
+ * program's maps after emit_signature_match(), outside what the
+ * signature attests to. Keep the signer-provided max_entries
+ * baked into the blob in that case.
+ */
+ if (map_idx >= 0 && !OPTS_GET(gen->opts, gen_hash, false))
move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4,
sizeof(struct bpf_loader_ctx) +
sizeof(struct bpf_map_desc) * map_idx +
offsetof(struct bpf_map_desc, max_entries),
true /* check that max_entries != 0 */);
+
/* emit MAP_CREATE command */
emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size);
debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d",
@@ -585,6 +594,23 @@ static void emit_signature_match(struct bpf_gen *gen)
__s64 off;
int i;
+ /*
+ * Reject if the metadata map is not exclusive. Without exclusivity
+ * the cached map->sha[] verified above can be stale: another BPF
+ * program with map access could have mutated the contents between
+ * BPF_OBJ_GET_INFO_BY_FD and loader execution.
+ */
+ emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+ 0, 0, 0, 0));
+ emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH));
+ off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+ if (is_simm16(off)) {
+ emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
+ emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off));
+ } else {
+ gen->error = -ERANGE;
+ }
+
for (i = 0; i < SHA256_DWORD_SIZE; i++) {
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
0, 0, 0, 0));
@@ -1053,7 +1079,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen,
prog_idx, prog_type, insns_off, insn_cnt, license_off);
/* convert blob insns to target endianness */
- if (gen->swapped_endian) {
+ if (gen->swapped_endian && !gen->error) {
struct bpf_insn *insn = gen->data_start + insns_off;
int i;
@@ -1091,7 +1117,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen,
sizeof(struct bpf_core_relo));
/* convert all info blobs to target endianness */
- if (gen->swapped_endian)
+ if (gen->swapped_endian && !gen->error)
info_blob_bswap(gen, func_info, line_info, core_relos, load_attr);
libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name));
@@ -1169,27 +1195,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
value = add_data(gen, pvalue, value_size);
key = add_data(gen, &zero, sizeof(zero));
- /* if (map_desc[map_idx].initial_value) {
+ /*
+ * if (map_desc[map_idx].initial_value) {
* if (ctx->flags & BPF_SKEL_KERNEL)
* bpf_probe_read_kernel(value, value_size, initial_value);
* else
* bpf_copy_from_user(value, value_size, initial_value);
* }
+ *
+ * The runtime initial_value comes from the host-supplied loader
+ * ctx and would overwrite the blob value after emit_signature_match()
+ * has already validated map->sha[]. For a signed loader (gen_hash)
+ * the attested blob value must be authoritative, so skip the override
+ * and leave the hashed value in place.
*/
- emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
- sizeof(struct bpf_loader_ctx) +
- sizeof(struct bpf_map_desc) * map_idx +
- offsetof(struct bpf_map_desc, initial_value)));
- emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
- emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
- 0, 0, 0, value));
- emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
- emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
- offsetof(struct bpf_loader_ctx, flags)));
- emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
- emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
- emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
- emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+ if (!OPTS_GET(gen->opts, gen_hash, false)) {
+ emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
+ sizeof(struct bpf_loader_ctx) +
+ sizeof(struct bpf_map_desc) * map_idx +
+ offsetof(struct bpf_map_desc, initial_value)));
+ emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
+ emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
+ 0, 0, 0, value));
+ emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
+ emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
+ offsetof(struct bpf_loader_ctx, flags)));
+ emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
+ emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
+ emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
+ emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+ }
map_update_attr = add_data(gen, &attr, attr_size);
pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n",
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3a80a018fc7d..83aae7a39d36 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -12280,7 +12280,7 @@ error:
static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
{
DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
- unsigned long offset = 0;
+ long offset = 0;
const char *func_name;
char *func;
int n;
@@ -12302,6 +12302,13 @@ static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf
pr_warn("kprobe name is invalid: %s\n", func_name);
return -EINVAL;
}
+
+ if (offset < 0) {
+ free(func);
+ pr_warn("kprobe offset must be a non-negative integer: %li\n", offset);
+ return -EINVAL;
+ }
+
if (opts.retprobe && offset != 0) {
free(func);
pr_warn("kretprobes do not support offset specification\n");
diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c
index 2464bcbd04e0..ace73c6b3d62 100644
--- a/tools/lib/bpf/strset.c
+++ b/tools/lib/bpf/strset.c
@@ -107,6 +107,41 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz)
set->strs_data_len, set->strs_data_max_len, add_sz);
}
+static long strset_str_append(struct strset *set, const char *s)
+{
+ uintptr_t old_data = (uintptr_t)set->strs_data;
+ size_t old_data_len = set->strs_data_len;
+ uintptr_t old_s = (uintptr_t)s;
+ long len = strlen(s) + 1;
+ void *p;
+
+ /*
+ * Hashmap keys are always offsets within set->strs_data, so to even
+ * look up some string from the "outside", we need to first append it
+ * at the end, so that it can be addressed with an offset. Luckily,
+ * until set->strs_data_len is incremented, that string is just a piece
+ * of garbage for the rest of the code, so no harm, no foul. On the
+ * other hand, if the string is unique, it's already appended and
+ * ready to be used, only a simple set->strs_data_len increment away.
+ */
+ p = strset_add_str_mem(set, len);
+ if (!p)
+ return -ENOMEM;
+
+ /*
+ * The set->strs_data might have reallocated and if 's' pointed
+ * to an internal string within the old buffer, then it became
+ * dangling and needs to be reconstructed before the copy.
+ */
+ if (old_data && old_data != (uintptr_t)set->strs_data &&
+ old_s >= old_data && old_s < old_data + old_data_len)
+ s = set->strs_data + (old_s - old_data);
+
+ memcpy(p, s, len);
+
+ return len;
+}
+
/* Find string offset that corresponds to a given string *s*.
* Returns:
* - >0 offset into string data, if string is found;
@@ -116,16 +151,12 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz)
int strset__find_str(struct strset *set, const char *s)
{
long old_off, new_off, len;
- void *p;
- /* see strset__add_str() for why we do this */
- len = strlen(s) + 1;
- p = strset_add_str_mem(set, len);
- if (!p)
- return -ENOMEM;
+ len = strset_str_append(set, s);
+ if (len < 0)
+ return len;
new_off = set->strs_data_len;
- memcpy(p, s, len);
if (hashmap__find(set->strs_hash, new_off, &old_off))
return old_off;
@@ -142,24 +173,13 @@ int strset__find_str(struct strset *set, const char *s)
int strset__add_str(struct strset *set, const char *s)
{
long old_off, new_off, len;
- void *p;
int err;
- /* Hashmap keys are always offsets within set->strs_data, so to even
- * look up some string from the "outside", we need to first append it
- * at the end, so that it can be addressed with an offset. Luckily,
- * until set->strs_data_len is incremented, that string is just a piece
- * of garbage for the rest of the code, so no harm, no foul. On the
- * other hand, if the string is unique, it's already appended and
- * ready to be used, only a simple set->strs_data_len increment away.
- */
- len = strlen(s) + 1;
- p = strset_add_str_mem(set, len);
- if (!p)
- return -ENOMEM;
+ len = strset_str_append(set, s);
+ if (len < 0)
+ return len;
new_off = set->strs_data_len;
- memcpy(p, s, len);
/* Now attempt to add the string, but only if the string with the same
* contents doesn't exist already (HASHMAP_ADD strategy). If such
diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c
index e3710933fd52..57fb82bb81b5 100644
--- a/tools/lib/bpf/usdt.c
+++ b/tools/lib/bpf/usdt.c
@@ -468,10 +468,10 @@ static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, siz
static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt)
{
- char path[PATH_MAX], line[PATH_MAX], mode[16];
+ char path[PATH_MAX], line[4096], mode[16];
size_t seg_start, seg_end, seg_off;
struct elf_seg *seg;
- int tmp_pid, i, err;
+ int tmp_pid, n, i, err;
FILE *f;
*seg_cnt = 0;
@@ -480,8 +480,13 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs,
* /proc/<pid>/root/<path>. They will be reported as just /<path> in
* /proc/<pid>/maps.
*/
- if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid)
+ /* %n is not counted in sscanf() return value, so initialize it. */
+ n = 0;
+ if (sscanf(lib_path, "/proc/%d/root%n", &tmp_pid, &n) == 1 &&
+ n > 0 && pid == tmp_pid && lib_path[n] == '/') {
+ libbpf_strlcpy(path, lib_path + n, sizeof(path));
goto proceed;
+ }
if (!realpath(lib_path, path)) {
pr_warn("usdt: failed to get absolute path of '%s' (err %s), using path as is...\n",
@@ -504,8 +509,11 @@ proceed:
* 7f5c6f5d1000-7f5c6f5d3000 rw-p 001c7000 08:04 21238613 /usr/lib64/libc-2.17.so
* 7f5c6f5d3000-7f5c6f5d8000 rw-p 00000000 00:00 0
* 7f5c6f5d8000-7f5c6f5d9000 r-xp 00000000 103:01 362990598 /data/users/andriin/linux/tools/bpf/usdt/libhello_usdt.so
+ *
+ * Some VMA names can be longer than the local buffer. Bound the
+ * writes, but still consume the rest of the line.
*/
- while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",
+ while (fscanf(f, "%zx-%zx %15s %zx %*s %*d%4095[^\n]%*[^\n]\n",
&seg_start, &seg_end, mode, &seg_off, line) == 5) {
void *tmp;