mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
libbpf: Harden parse_vma_segs() path parsing
parse_vma_segs() in tools/lib/bpf/usdt.c parses /proc/<pid>/maps
with two widthless scansets, "%s" into mode[16] and "%[^\n]"
into line[4096]. A VMA name in maps is not limited to that local
buffer; a deeply nested backing path can produce a maps record long
enough to overflow the stack buffer.
Bound both scansets to the declared buffer sizes ("%15s" for mode[16]
and "%4095[^\n]" for line[4096]) and drain any residue past line[4094]
with "%*[^\n]" before the trailing "\n". Without the drain, the residue
of an over-long record would stay in the stream and break the next
"%zx-%zx" parse, so the loop would exit early and silently skip later
maps records.
Also stop using sscanf(..., "%s") to peel the /proc/<pid>/root prefix
from lib_path. Parse the pid and prefix length with "%n", check for the
following slash, and copy the remainder with libbpf_strlcpy(). That
removes a second unbounded stack write and preserves paths containing
spaces.
Fixes: 74cc6311ce ("libbpf: Add USDT notes parsing and resolution logic")
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Link: https://lore.kernel.org/bpf/20260522201353.1454653-1-michael.bommarito@gmail.com
This commit is contained in:
parent
e42e53ae23
commit
fee9a38174
|
|
@ -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 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs,
|
|||
* 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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user