From d49152733cd1821ac528b137134d2b62215d5bbd Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Fri, 15 Aug 2025 11:24:42 -0700 Subject: [PATCH 1/2] bpfloader: Sanitize BPF prog and map pin paths Replace periods with underscores in prog/map pin paths to align with libbpf's sanitize_pin_path() behavior. The BPF filesystem (bpffs) disallows periods in filenames, and this change prevents pinning errors for maps with names containing them, such as .rodata sections. Bug: 419635765 Bug: 438637519 Test: manual, load maps names containing . from ag/35064705 Change-Id: I55dd4caf38c3ccc39bccd6ea670b647c330d6793 Signed-off-by: Kalesh Singh --- loader/bpfloader.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 978a3cd..f55e544 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -529,7 +529,12 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { } let pinpath_str = bpffs_path.clone() + "map_" + filename + "_" + &name; - let pinpath = Path::new(&pinpath_str); + + // bpffs disallows periods in path names, so replace them with underscores + // to align with libbpf's sanitize_pin_path() behavior. + let pinpath_sanitized_str = pinpath_str.replace('.', "_"); + + let pinpath = Path::new(&pinpath_sanitized_str); debug!("Pinning: {}", pinpath.display()); map.pin(pinpath).map_err(|e| anyhow!("Failed to pin map {name}: {e}"))?; fs::set_permissions(pinpath, Permissions::from_mode(map_desc.perms as _)).map_err( @@ -574,7 +579,12 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { continue; } let pinpath_str = bpffs_path.clone() + "prog_" + filename + "_" + &name; - let pinpath = Path::new(&pinpath_str); + + // bpffs disallows periods in path names, so replace them with underscores + // to align with libbpf's sanitize_pin_path() behavior. + let pinpath_sanitized_str = pinpath_str.replace('.', "_"); + + let pinpath = Path::new(&pinpath_sanitized_str); debug!("Pinning: {}", pinpath.display()); prog.pin(pinpath).map_err(|e| anyhow!("Failed to pin prog {name}: {e}"))?; fs::set_permissions(pinpath, Permissions::from_mode(PERM_UGR as _)).map_err( From 809c5f56360627191b0d05651231d17046943331 Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Fri, 15 Aug 2025 11:43:35 -0700 Subject: [PATCH 2/2] bpfloader: Add support for auto-attaching programs Introduce an 'auto_attach' flag to the ProgDesc struct. When this flag is true, bpfloader attaches the BPF program and pins the resulting link. This makes the attachment persist across framework reboots, only being removed when the device itself reboots. This simplifies client-side logic, as clients no longer need to manage attachments or handle missed events after a restart. They can assume the program is always attached and simply read from the maps. This is particularly useful for collecting statistics that would otherwise be corrupted by framework restarts and for capturing events that occur during early boot, before client processes are running. Bug: 419635765 Bug: 438637519 Test: manual, load and auto attach ag/35064705 Change-Id: Iffcde8972778b9152f4732e65c221eb72523ea34 Signed-off-by: Kalesh Singh --- loader/bpfloader.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index f55e544..f3040c9 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -176,15 +176,23 @@ struct ProgDesc { // Prog is loaded if kernel_version() is >= min_kver and < max_kver min_kver: u32, max_kver: u32, + auto_attach: bool, } impl ProgDesc { pub const fn new(group: u32, name: &'static str) -> Self { - ProgDesc { name, owner: AID_ROOT, group, min_kver: KVER_NONE, max_kver: KVER_INF } + ProgDesc { + name, + owner: AID_ROOT, + group, + min_kver: KVER_NONE, + max_kver: KVER_INF, + auto_attach: false, + } } pub const fn new_kver(group: u32, min_kver: u32, name: &'static str) -> Self { - ProgDesc { name, owner: AID_ROOT, group, min_kver, max_kver: KVER_INF } + ProgDesc { name, owner: AID_ROOT, group, min_kver, max_kver: KVER_INF, auto_attach: false } } } @@ -585,8 +593,24 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { let pinpath_sanitized_str = pinpath_str.replace('.', "_"); let pinpath = Path::new(&pinpath_sanitized_str); - debug!("Pinning: {}", pinpath.display()); - prog.pin(pinpath).map_err(|e| anyhow!("Failed to pin prog {name}: {e}"))?; + if prog_desc.auto_attach { + debug!("Auto-attaching program: {}", name); + let mut link = + prog.attach().map_err(|e| anyhow!("Failed to attach prog {name}: {e}"))?; + debug!("Pinning link for {}: {}", name, pinpath.display()); + link.pin(pinpath).map_err(|e| anyhow!("Failed to pin link for {name}: {e}"))?; + info!("Successfully attached and pinned program {}", name); + // The Link object's destructor calls bpf_link__destroy(), which + // would normally detach the program when the object goes out of scope. + // By calling disconnect() here, we modify the link so that the + // subsequent bpf_link__destroy() call does not detach the BPF + // resource. This, combined with pinning, ensures the attachment + // persists after the bpfloader process exits. + link.disconnect(); + } else { + debug!("Pinning program {}: {}", name, pinpath.display()); + prog.pin(pinpath).map_err(|e| anyhow!("Failed to pin prog {name}: {e}"))?; + } fs::set_permissions(pinpath, Permissions::from_mode(PERM_UGR as _)).map_err( |e| { anyhow!(