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

perf bpf-utils: Harden get_bpf_prog_info_linear

In get_bpf_prog_info_linear two calls to bpf_obj_get_info_by_fd are
made, the first to compute memory requirements for a struct perf_bpil
and the second to fill it in. Previously the code would warn when the
second call didn't match the first. Such races can be common place in
things like perf test, whose perf trace tests will frequently load BPF
programs. Rather than a debug message, return actual errors for this
case. Out of paranoia also validate the read bpf_prog_info array
value. Change the type of ptr to avoid mismatched pointer type
compiler warnings. Add some additional debug print outs and sanity
asserts.

Closes: https://lore.kernel.org/lkml/CAP-5=fWJQcmUOP7MuCA2ihKnDAHUCOBLkQFEkQES-1ZZTrgf8Q@mail.gmail.com/
Fixes: 6ac22d036f86 ("perf bpf: Pull in bpf_program__get_prog_info_linear()")
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250902181713.309797-4-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
01be43f2 1654a0e4

+33 -10
+33 -10
tools/perf/util/bpf-utils.c
··· 115 115 __u32 info_len = sizeof(info); 116 116 __u32 data_len = 0; 117 117 int i, err; 118 - void *ptr; 118 + __u8 *ptr; 119 119 120 120 if (arrays >> PERF_BPIL_LAST_ARRAY) 121 121 return ERR_PTR(-EINVAL); ··· 126 126 pr_debug("can't get prog info: %s", strerror(errno)); 127 127 return ERR_PTR(-EFAULT); 128 128 } 129 + if (info.type >= __MAX_BPF_PROG_TYPE) 130 + pr_debug("%s:%d: unexpected program type %u\n", __func__, __LINE__, info.type); 129 131 130 132 /* step 2: calculate total size of all arrays */ 131 133 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { ··· 175 173 desc->count_offset, count); 176 174 bpf_prog_info_set_offset_u32(&info_linear->info, 177 175 desc->size_offset, size); 176 + assert(ptr >= info_linear->data); 177 + assert(ptr < &info_linear->data[data_len]); 178 178 bpf_prog_info_set_offset_u64(&info_linear->info, 179 179 desc->array_offset, 180 180 ptr_to_u64(ptr)); ··· 190 186 free(info_linear); 191 187 return ERR_PTR(-EFAULT); 192 188 } 189 + if (info_linear->info.type >= __MAX_BPF_PROG_TYPE) { 190 + pr_debug("%s:%d: unexpected program type %u\n", 191 + __func__, __LINE__, info_linear->info.type); 192 + } 193 193 194 194 /* step 6: verify the data */ 195 + ptr = info_linear->data; 195 196 for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { 196 197 const struct bpil_array_desc *desc = &bpil_array_desc[i]; 197 - __u32 v1, v2; 198 + __u32 count1, count2, size1, size2; 199 + __u64 ptr2; 198 200 199 201 if ((arrays & (1UL << i)) == 0) 200 202 continue; 201 203 202 - v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 203 - v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 204 + count1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); 205 + count2 = bpf_prog_info_read_offset_u32(&info_linear->info, 204 206 desc->count_offset); 205 - if (v1 != v2) 206 - pr_warning("%s: mismatch in element count\n", __func__); 207 + if (count1 != count2) { 208 + pr_warning("%s: mismatch in element count %u vs %u\n", __func__, count1, count2); 209 + free(info_linear); 210 + return ERR_PTR(-ERANGE); 211 + } 207 212 208 - v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 209 - v2 = bpf_prog_info_read_offset_u32(&info_linear->info, 213 + size1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); 214 + size2 = bpf_prog_info_read_offset_u32(&info_linear->info, 210 215 desc->size_offset); 211 - if (v1 != v2) 212 - pr_warning("%s: mismatch in rec size\n", __func__); 216 + if (size1 != size2) { 217 + pr_warning("%s: mismatch in rec size %u vs %u\n", __func__, size1, size2); 218 + free(info_linear); 219 + return ERR_PTR(-ERANGE); 220 + } 221 + ptr2 = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); 222 + if (ptr_to_u64(ptr) != ptr2) { 223 + pr_warning("%s: mismatch in array %p vs %llx\n", __func__, ptr, ptr2); 224 + free(info_linear); 225 + return ERR_PTR(-ERANGE); 226 + } 227 + ptr += roundup(count1 * size1, sizeof(__u64)); 213 228 } 214 229 215 230 /* step 7: update info_len and data_len */