From nobody Sun May 24 18:41:33 2026 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C979936F915 for ; Fri, 22 May 2026 20:14:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779480852; cv=none; b=aoCcAD8Yk6xRQlh+dJfD+O8A1w5ZHYpFJeyteuF6Si5U78p2HMorEC1SzFOVm9Bb6qsQYqXKs/uPjA1UL4Q1Rh9Djgh3YhuZAHl6HT9+9Q3zLjbgXzyCkl0rXKIj37dzGjLd8B4vQUe2lysBUiHD50X37yyuI3hlD9SXqOT0sBI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779480852; c=relaxed/simple; bh=T+T8duR9CZjW08w8okteh8iEB6zt41jrJ2g1lgvOUxI=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Nid90bDapXxQqFOT2XzPDmOeCxGqJ3fAtBWi61xFcci39g3tqaVKY9PcokGYSNE9UdhQ3yu15/SlkANEVQx7KGH1nRLbx3ZfwCIgVsAvlpJH0g+mPc5rv7T6hpn0hHNkIB0IRsbBf87Dcvy06DrOYbLFBpAIH+iSMIend2K2ujQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bTinLAoJ; arc=none smtp.client-ip=209.85.219.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bTinLAoJ" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-8b4000e51fdso83324446d6.1 for ; Fri, 22 May 2026 13:14:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779480850; x=1780085650; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=Vy69sv1RlbQPcrl5t6Uo1NfBAD3NLb/c+Z2AAhcBMw8=; b=bTinLAoJb4yMuTDJ6HSGwAxUJClaJtAe6blKxubk81soh+eR6PQjqJlLgbbnkHr6r9 rBH0lCNcxWCfLWsuhHVaLohIB028YqTKrizmvoocA6MUO72VE3+Ej6MrpIayw/kLcKk6 N8bGrPSGjpyZZR2+jW7jkwRV/i/IvQ0u4wLVlMUU6FEMN2S2GXwgyvfrdenO3HLtrzX8 EOHbaOohGTP5Z9zOfgbyGR5/RLTlN3kGDqPtDQcEz3T77jj2My0e2EJcijgsoWTfInFJ aHN5JvP8vvcOpGM4izCBGrHpThyyJ0H6BDLFjKC6P+3fvWFelZwA2SzZMPChUvIH6rnz QsXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779480850; x=1780085650; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Vy69sv1RlbQPcrl5t6Uo1NfBAD3NLb/c+Z2AAhcBMw8=; b=ph5GoUBxJfNYEjcZJDIJClW1wfllWdz3jN0+iSgV2flUy3GSxuj69TcJ/pCr2MzWGC CugTuBRGar8l+K+GicjRjmEu7LWqmOQA1tv5T4T5KTmzg/u0Mj7K0gxzD9388VT8kJZ4 fZlDgkQl2nac/lvXWxpLpoy2jejfLUOpWNgJ2EWpzsG20prAYT+BQr6ge36zyKBC8G3o AoYVFVmabGtmxN6KhEZXeGZ9w3+eQeo7YYphTelYi6tEsH9sCkuO2Q7p4Go/xjTBcewo REvFjGyVuf7tIc/+ivZS+6hqBu7aePXVIDl5d9I+FO6IjjcxPL4F10E1HqHBLgNqMnfq Khzw== X-Forwarded-Encrypted: i=1; AFNElJ8jZ99vrRAoEHIU0CAyF7+n5PcX3quffzPXFGLdR/x72OyzAu+d1z8xmwKmt3TcY0iywnLK54BhdgHYCxc=@vger.kernel.org X-Gm-Message-State: AOJu0YwysF2Xl7RapwtpHsn7XTztozV3Onkb2l/UvNlSci/N8pBR6zjF A2ZSDrtQU+UCAMb+bd2mBH0gLPoHYwDnRl2GtPGzbXWMcu6Nmk8eNnMG X-Gm-Gg: Acq92OH8RCy9oh1esOzaXZykqk/12OaBEZOSfsioQFuRNI8bW/0bibDMyYmEUHr6cjq Wi6OQsYoVF9H70743cujAU3zLHKvzhrQy6YO1aiKBWK9JRmTQndxgdi88TYx+iEBxKTMxQKwig3 qm8xEvaqSO6/FAvl9U5l69upggcJarDqwUYm5J1YPN7zydKq6PlNhnh7VVD6GfQ9fCbSyO56LWZ oqtc9WSrpZ2n1Uy2fs50rqIXcnvCUm5MLaZCDRGxE4dxAMI5cobkh2frA9zGZ8JjI+gfB7lf2he Mhwj7uHe2vZkcCXyL/15aQxXGWCsToi0zf+DZqO9n67QS6qiX1AVm3zTQOXcJYtGFYCSYjVSajU n9xEuWM44zJW9aCZEO5uAgohPjeGKWp9w1t6umocMn7+KA0bf8eG8Sg6OP17zXmxtOfpCs+MheR 1P+ZOSz196gitUV6IXveeFGNfkrzFU5S3ftkIH0q8oXWWutZbP1m4PZqfupV9qGoajR37NgcFR0 eRkApsp3EjYaz0Sr+6E X-Received: by 2002:a05:6214:d6f:b0:89f:9bc2:20d1 with SMTP id 6a1803df08f44-8cc7b6048aamr91125386d6.7.1779480849610; Fri, 22 May 2026 13:14:09 -0700 (PDT) Received: from server0 (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8cc812e60ffsm30543336d6.23.2026.05.22.13.14.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 May 2026 13:14:09 -0700 (PDT) From: Michael Bommarito To: Andrii Nakryiko , Eduard Zingerman Cc: Alexei Starovoitov , Daniel Borkmann , Martin KaFai Lau , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , bpf@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4] libbpf: harden parse_vma_segs() path parsing Date: Fri, 22 May 2026 16:13:53 -0400 Message-ID: <20260522201353.1454653-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" parse_vma_segs() in tools/lib/bpf/usdt.c parses /proc//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//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: 74cc6311cec9 ("libbpf: Add USDT notes parsing and resolution logic") Cc: stable@vger.kernel.org Reviewed-by: Emil Tsalapatis Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito --- v4: - Carry Emil's Reviewed-by. - Simplify the /proc//root prefix handling with sscanf() %n, removing the unreachable snprintf() length check. - Initialize and check the %n output before using it, so partial literal matches after the pid cannot use an unassigned offset. - Add a short comment for the %n return-value rule. - Declare the maps-line buffer as line[4096] to match the %4095 scanset width. - Reword the maps-line comment without seq_file implementation detail. v3: - Correct Fixes tag to the initial USDT implementation commit, per BPF CI review after adding second site. v2: - Replace the unbounded /proc//root sscanf() path peeling with bounded prefix handling, addressing review feedback on v1 and preserving paths containing spaces. - Keep the v1 maps parser fix using bounded fscanf() scansets and a suppressed scanset drain for over-long records. - Re-ran real parse_vma_segs() ASAN harnesses for the original maps overflow, the proc-root overflow, proc-root paths with spaces, and adjacent successful parses after an over-long maps record. Reproduced with Debian 12 on rootless podman: an unprivileged container process mkdirs 50 nested 200-char directories and mmaps a file at the bottom, producing a >10KB /proc//maps line. A harness on the host then calls the real parse_vma_segs() against the container's PID; libbpf is built with -fsanitize=3Daddress and the only local source change is dropping the "static" keyword on parse_vma_segs so the symbol is linkable from the harness. Stock libbpf reports: =3D=3DERROR: AddressSanitizer: stack-buffer-overflow WRITE of size >10KB at thread T0 #0 scanf_common -> #1 __isoc99_fscanf #3 parse_vma_segs tools/lib/bpf/usdt.c:509 Address ... in frame parse_vma_segs at offset 8512, just past line[PATH_MAX]. Patched libbpf parses the same maps cleanly. Follow-up calls return 0 with seg_cnt > 0 for libc.so.6 and for ld-linux-x86-64.so.2 (format drain), which appears in maps after the over-long entry. On normal hardened builds the stack canary aborts the consumer; on builds without stack protector the bytes past line[] are attacker-influenced path bytes. Selftest gate =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D tools/testing/selftests/bpf/test_progs -t usdt under QEMU x86_64 (KVM) on the patched kernel: all 6 subtests pass (usdt/basic, basic_optimized, optimized_attach, multispec, urand_auto_attach, urand_pid_attach) on both stock and patched libbpf, diff-clean. The in-tree selftest does not itself exercise long maps records. tools/lib/bpf/usdt.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index e3710933fd52a..57fb82bb81b58 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 =20 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; =20 *seg_cnt =3D 0; @@ -480,8 +480,13 @@ static int parse_vma_segs(int pid, const char *lib_pat= h, struct elf_seg **segs, * /proc//root/. They will be reported as just / in * /proc//maps. */ - if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) =3D=3D 2 && pid = =3D=3D tmp_pid) + /* %n is not counted in sscanf() return value, so initialize it. */ + n =3D 0; + if (sscanf(lib_path, "/proc/%d/root%n", &tmp_pid, &n) =3D=3D 1 && + n > 0 && pid =3D=3D tmp_pid && lib_path[n] =3D=3D '/') { + libbpf_strlcpy(path, lib_path + n, sizeof(path)); goto proceed; + } =20 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_pat= h, 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/user= s/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) =3D=3D 5) { void *tmp; =20 --=20 2.53.0