internal/driver: guard BuildID slice in locateBinaries against short values (#998)
The LLVM debug-file lookup sliced m.BuildID[:2] and m.BuildID[2:] to
construct a filesystem path of the form <path>/<first2>/<rest>.debug.
The existing guard only checked m.BuildID != "", so a BuildID with
fewer than two characters (e.g. a single byte) caused a panic:
runtime error: slice bounds out of range [:2] with length 1
The profile.proto format imposes no minimum length on BuildID, and the
profile.CheckValid() function does not validate it either. A crafted
profile with a one-character BuildID therefore reliably crashes any
process that calls locateBinaries, including tools or servers that
accept and analyze user-supplied profiles.
Fix: wrap the LLVM path construction in a len(m.BuildID) >= 2 guard,
matching the documented precondition of the LLVM build-id protocol
('the first two characters are used as directory').
Add a test case with BuildID="X" to TestSymbolizationPath to prevent
regression.diff --git a/internal/driver/fetch.go b/internal/driver/fetch.go
index 6d967a2..0809cd0 100644
--- a/internal/driver/fetch.go
+++ b/internal/driver/fetch.go
@@ -430,7 +430,9 @@
// Llvm buildid protocol: the first two characters of the build id
// are used as directory, and the remaining part is in the filename.
// e.g. `/ab/cdef0123456.debug`
- fileNames = append(fileNames, filepath.Join(path, m.BuildID[:2], m.BuildID[2:]+".debug"))
+ if len(m.BuildID) >= 2 {
+ fileNames = append(fileNames, filepath.Join(path, m.BuildID[:2], m.BuildID[2:]+".debug"))
+ }
}
if m.File != "" {
// Try both the basename and the full path, to support the same directory
diff --git a/internal/driver/fetch_test.go b/internal/driver/fetch_test.go
index 3dcd7bb..36ec2f2 100644
--- a/internal/driver/fetch_test.go
+++ b/internal/driver/fetch_test.go
@@ -77,6 +77,10 @@
{"", "", "fghij10001", filepath.Join(tempdir, "pprof/binaries/fg/hij10001.debug"), 0},
{"/nowhere:/alternate/architecture", "/usr/bin/binary", "fedcb10000", "/usr/bin/binary", 1},
{"/nowhere:/alternate/architecture", "/usr/bin/binary", "abcde10002", "/usr/bin/binary", 1},
+ // A single-character BuildID must not panic when the LLVM debug-file
+ // lookup slices BuildID[:2] / BuildID[2:]. Prior to the fix, this caused
+ // a "slice bounds out of range" panic.
+ {"", "/usr/bin/binary", "X", "/usr/bin/binary", 0},
} {
os.Setenv("PPROF_BINARY_PATH", tc.env)
p := &profile.Profile{