From 867009fdabb310e9f720d620d201af6f3e2bfe5c Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 12 May 2025 12:10:18 +0900 Subject: [PATCH 01/13] Add DEFINE_BPF_PROG_KVER to android_bpf_defs.h Bug: 410982483 Test: m bpfMemEvents.bpf bpfMemEventsTest.bpf Change-Id: I97b7cbe5d033dbe4b15a52b1487e9ace3d12ee7d --- include/defs/android_bpf_defs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index 35cf057..60adf56 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -44,6 +44,9 @@ SEC(SECTION_NAME) \ int the_prog +#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, kver) \ + DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) + #define LICENSE(NAME) char _license[] SEC("license") = (NAME) #else // LIBBPF DISABLED From 728276f83cec86997222317559246040ac8db9e2 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 12 May 2025 12:11:24 +0900 Subject: [PATCH 02/13] Add DEFINE_BPF_RINGBUF to android_bpf_defs.h Test: m bpfMemEvents.bpf bpfMemEventsTest.bpf Bug: 410982483 Change-Id: I30eacdc39a4307b446bc51dfbd69fe0d3f48713b --- include/defs/android_bpf_defs.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index 60adf56..e1011c5 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -40,6 +40,24 @@ #define DEFINE_BPF_MAP_GRO(the_map, TYPE, KeyType, ValueType, num_entries, gid) \ DEFINE_BPF_MAP_BASE(the_map, TYPE, KeyType, ValueType, num_entries, gid) +#define DEFINE_BPF_RINGBUF(the_map, ValueType, num_entries, usr, grp, md) \ + struct { \ + __uint(type, BPF_MAP_TYPE_RINGBUF); \ + __uint(max_entries, num_entries); \ + } the_map SEC(".maps"); \ + \ + static inline __always_inline __unused int bpf_##the_map##_output(ValueType* v) { \ + return bpf_ringbuf_output(&the_map, v, sizeof(*v), 0); \ + }; \ + \ + static inline __always_inline __unused ValueType* bpf_##the_map##_reserve() { \ + return bpf_ringbuf_reserve(&the_map, sizeof(ValueType), 0); \ + } \ + \ + static inline __always_inline __unused void bpf_##the_map##_submit(ValueType* v) { \ + bpf_ringbuf_submit(v, 0); \ + } + #define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \ SEC(SECTION_NAME) \ int the_prog From 4ed7d8f0931d820acd29252cba0b10375d296143 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 12 May 2025 12:14:10 +0900 Subject: [PATCH 03/13] Fix __always_inline macro conflict and add linux/bpf.h include The bpf_helpers.h in libbpf defines the `__always_inline` macro using the `inline` keyword (`inline __attribute__((always_inline))`). `cdefs.h` defines `__always_inline` using only the attribute (`__attribute__((__always_inline__))`). This leads to "duplicate 'inline' declaration" errors depending on the order in which these headers are included. To prevent such conflicts, this CL explicitly redefines `__always_inline` using `__attribute__((__always_inline__))` immediately after including . This CL also adds an include of for BPF map types (e.g. BPF_MAP_TYPE_RINGBUF). Bug: 410982483 Test: m bpfMemEvents.bpf bpfMemEventsTest.bpf Change-Id: Ib55805c09a7946b537f55512f9d88141b561e0ab --- include/defs/android_bpf_defs.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index e1011c5..58386e8 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -11,6 +11,14 @@ #endif // USE_VMLINUX #include +// bpf_helpers.h defines __always_inline using "inline __attribute__((always_inline))". +// To prevent potential "duplicate 'inline' declaration" issues depending on the include order, +// redefine using the cdefs.h definition of __always_inline. +#undef __always_inline +#define __always_inline __attribute__((__always_inline__)) + +#include + #define DEFINE_BPF_MAP_BASE(the_map, TYPE, KeyType, ValueType, num_entries, gid) \ struct { \ __uint(type, BPF_MAP_TYPE_##TYPE); \ From 26d2b1c73f9ba9f2b8218e323c9462785ada792e Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 12 May 2025 16:51:43 +0900 Subject: [PATCH 04/13] Define CRITICAL as no-op for libbpf_prog Test: m bpfRingbufProg.bpf Change-Id: I874cadca706f7ae9964ad7d2981884320cc20d68 --- include/defs/android_bpf_defs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index 58386e8..2a28f62 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -74,6 +74,7 @@ DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) #define LICENSE(NAME) char _license[] SEC("license") = (NAME) +#define CRITICAL(NAME) #else // LIBBPF DISABLED From fbadb820016db6b9a96a33a939f9c70a28224299 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 12 May 2025 16:58:02 +0900 Subject: [PATCH 05/13] Add libbpf_prog bpfRingbufProg.bpf target This is a preparation for migrating to the Rust platform bpfloader. Bug: 410982483 Test: m bpfRingbufProg.bpf bpfRingbufProg.o Change-Id: I094635be14e56aac53384ac517538c2587367c44 --- include/defs/android_bpf_defs.h | 1 + progs/Android.bp | 8 ++++++++ progs/bpfRingbufProg.c | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index 2a28f62..139edd5 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -18,6 +18,7 @@ #define __always_inline __attribute__((__always_inline__)) #include +#include #define DEFINE_BPF_MAP_BASE(the_map, TYPE, KeyType, ValueType, num_entries, gid) \ struct { \ diff --git a/progs/Android.bp b/progs/Android.bp index 3d9c5a6..9401df5 100644 --- a/progs/Android.bp +++ b/progs/Android.bp @@ -31,3 +31,11 @@ bpf { name: "bpfRingbufProg.o", srcs: ["bpfRingbufProg.c"], } + +libbpf_prog { + name: "bpfRingbufProg.bpf", + srcs: ["bpfRingbufProg.c"], + header_libs: [ + "android_bpf_defs", + ], +} diff --git a/progs/bpfRingbufProg.c b/progs/bpfRingbufProg.c index 4f268bf..19147e0 100644 --- a/progs/bpfRingbufProg.c +++ b/progs/bpfRingbufProg.c @@ -14,7 +14,13 @@ * limitations under the License. */ +// Because include_dirs is not allowed under system/bpf, include +// only if the code is built for libbpf_prog target. +#ifdef ENABLE_LIBBPF +#include +#else #include "bpf_helpers.h" +#endif // This can't be easily changed since the program is loaded on boot and may be // run against tests at a slightly different version. From 886a957f4c3ab20ff820db77610df626dfaea308 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 13 May 2025 12:42:50 +0900 Subject: [PATCH 06/13] Add LLVM eBPF builtins methods to android_bpf_defs.h Bug: 410982483 Test: m filterPowerSupplyEvents.o filterPowerSupplyEvents.bpf Change-Id: I52eb9623fd73ebd5620482ba3bdf5f303ebf2958 --- include/defs/android_bpf_defs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/defs/android_bpf_defs.h b/include/defs/android_bpf_defs.h index 139edd5..3d08b15 100644 --- a/include/defs/android_bpf_defs.h +++ b/include/defs/android_bpf_defs.h @@ -77,6 +77,11 @@ #define LICENSE(NAME) char _license[] SEC("license") = (NAME) #define CRITICAL(NAME) +// LLVM eBPF builtins: they directly generate BPF_LD_ABS/BPF_LD_IND (skb may be ignored?) +unsigned long long load_byte(void* skb, unsigned long long off) asm("llvm.bpf.load.byte"); +unsigned long long load_half(void* skb, unsigned long long off) asm("llvm.bpf.load.half"); +unsigned long long load_word(void* skb, unsigned long long off) asm("llvm.bpf.load.word"); + #else // LIBBPF DISABLED #include From d8a79700ddbcb520dff5b6083795b5789866a270 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Wed, 14 May 2025 12:37:04 +0900 Subject: [PATCH 07/13] Update program name in bpfRingbufProg.c Update program name to match the attach point definition, with forward slashes `/` replaced with underscores `_`. This is required to pin the program to the same path the legacy platform bpf loader uses. Bug: 410982483 Test: TH Change-Id: Ibdb24707c58792a61da6a6c7fe5f57fb7267f6e5 --- progs/bpfRingbufProg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/progs/bpfRingbufProg.c b/progs/bpfRingbufProg.c index 19147e0..de17483 100644 --- a/progs/bpfRingbufProg.c +++ b/progs/bpfRingbufProg.c @@ -31,7 +31,8 @@ DEFINE_BPF_RINGBUF(test_ringbuf, __u64, 4096, AID_ROOT, AID_ROOT, 0660); // This program is for test purposes only - it should never be attached to a // socket, only executed manually with BPF_PROG_RUN. -DEFINE_BPF_PROG_KVER("skfilter/ringbuf_test", AID_ROOT, AID_ROOT, test_ringbuf_prog, KVER(5, 8, 0)) +DEFINE_BPF_PROG_KVER("skfilter/ringbuf_test", AID_ROOT, AID_ROOT, skfilter_ringbuf_test, + KVER(5, 8, 0)) (void* __unused ctx) { __u64* output = bpf_test_ringbuf_reserve(); if (output == NULL) return 1; From 4f1cdd4f9181a8e5956b15d08992d3bfa2c208cd Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 13 May 2025 15:28:31 +0900 Subject: [PATCH 08/13] Add dir and prefix fields to BpfFileDesc The "dir" field specifies the directory where the BPF file is located. The "prefix" field specifies the subdirectory under /sys/fs/bpf/ where maps and programs are pinned. These fields are required for memevents and vendor bpf. This CL is a no-op for the existing timeInState.bpf loading Bug: 410982483 Test: timeInState.bpf loaded successfully Change-Id: Ic509b19490b9561173f84ace10d68c7725f8055b --- loader/bpfloader.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 4b61100..82842b4 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -141,6 +141,10 @@ struct ProgDesc { struct BpfFileDesc { filename: &'static str, + // The directory where the BPF file is located. + dir: &'static str, + // Maps and Progs are pinned under /sys/fs/bpf/. + prefix: &'static str, // Warning: setting this to 'true' will cause the system to boot loop if there are any issues // loading the bpf program. critical: bool, @@ -157,6 +161,8 @@ const PERM_UGR: mode_t = S_IRUSR | S_IRGRP; const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { filename: "timeInState.bpf", + dir: "/etc/bpf/", + prefix: "", critical: false, owner: AID_ROOT, group: AID_SYSTEM, @@ -186,7 +192,7 @@ const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { info!("Loading {}", file_desc.filename); - let filepath = Path::new("/etc/bpf/").join(file_desc.filename); + let filepath = Path::new(file_desc.dir).join(file_desc.filename); ensure!(filepath.exists(), "File not found {}", filepath.display()); let filename = filepath.file_stem().ok_or_else(|| anyhow!("Failed to parse stem from filename"))?; @@ -196,7 +202,7 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { let open_file = ob.open_file(&filepath)?; let mut loaded_file = open_file.load()?; - let bpffs_path = "/sys/fs/bpf/".to_owned(); + let bpffs_path = "/sys/fs/bpf/".to_owned() + file_desc.prefix; for mut map in loaded_file.maps_mut() { let mut desc_found = false; From 84b3a975255d4fb28d7a9238ff1199f86e11f0de Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Tue, 13 May 2025 16:45:52 +0900 Subject: [PATCH 09/13] Create /sys/fs/bpf dir to pin bpf maps and progs This is a no-op because maps and progs of timeInState are pinned under /sys/fs/bpf/ and this directory already exists. Bug: 410982483 Test: timeInState.bpf loaded successfully Change-Id: Ied42ebcbde5c8727f19c31b8dc9930dac2ef5f79 --- loader/bpfloader.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 82842b4..3100c49 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -20,7 +20,7 @@ use android_ids::{AID_ROOT, AID_SYSTEM}; use android_logger::AndroidLogger; use anyhow::{anyhow, ensure}; use libbpf_rs::{set_print, MapCore, ObjectBuilder, PrintLevel}; -use libc::{mode_t, S_IRGRP, S_IRUSR, S_IWGRP, S_IWUSR}; +use libc::{mode_t, S_IRGRP, S_IRUSR, S_IRWXG, S_IRWXO, S_IRWXU, S_ISVTX, S_IWGRP, S_IWUSR}; use log::{debug, error, info, warn, Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; use std::{ cmp::max, @@ -190,6 +190,22 @@ const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { ], }]; +fn create_dir(dir_path: &Path) -> Result<(), anyhow::Error> { + if dir_path.exists() { + return Ok(()); + } + fs::create_dir(dir_path) + .map_err(|e| anyhow!("Failed to create {}: {e}", dir_path.display()))?; + // The cast is not unnecessary on all platforms. + #[allow(clippy::unnecessary_cast)] + fs::set_permissions( + dir_path, + Permissions::from_mode((S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) as u32), + ) + .map_err(|e| anyhow!("Failed to set permissions for {}: {e}", dir_path.display()))?; + Ok(()) +} + fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { info!("Loading {}", file_desc.filename); let filepath = Path::new(file_desc.dir).join(file_desc.filename); @@ -203,6 +219,7 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { let mut loaded_file = open_file.load()?; let bpffs_path = "/sys/fs/bpf/".to_owned() + file_desc.prefix; + create_dir(Path::new(&bpffs_path))?; for mut map in loaded_file.maps_mut() { let mut desc_found = false; From 9d6cb3567b8c732d31a2e7cf4e3938073da1de57 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Wed, 14 May 2025 10:43:28 +0900 Subject: [PATCH 10/13] Add min_kver and max_kver fields to MapDesc and ProgDesc This is a no-op. Bug: 410982483 Test: TH Change-Id: Ia09667b95c2d534b4b204fea4656e7db3e27e6c6 --- loader/bpfloader.rs | 63 ++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 3100c49..481b81c 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -34,6 +34,13 @@ use std::{ sync::{Arc, Mutex}, }; +const fn kver(a: u32, b: u32, c: u32) -> u32 { + (a << 24) + (b << 16) + c +} + +const KVER_NONE: u32 = kver(0, 0, 0); +const KVER_INF: u32 = 0xFFFFFFFF; + enum KernelLevel { // Commented out unused due to rust complaining... // EMERG = 0, @@ -130,13 +137,33 @@ fn libbpf_print(level: PrintLevel, mut msg: String) { } } +#[allow(dead_code)] struct MapDesc { name: &'static str, perms: mode_t, + // Map is loaded if kernel_version() is >= min_kver and < max_kver + min_kver: u32, + max_kver: u32, } +impl MapDesc { + pub const fn new(name: &'static str, perms: mode_t) -> Self { + MapDesc { name, perms, min_kver: KVER_NONE, max_kver: KVER_INF } + } +} + +#[allow(dead_code)] struct ProgDesc { name: &'static str, + // Prog is loaded if kernel_version() is >= min_kver and < max_kver + min_kver: u32, + max_kver: u32, +} + +impl ProgDesc { + pub const fn new(name: &'static str) -> Self { + ProgDesc { name, min_kver: KVER_NONE, max_kver: KVER_INF } + } } struct BpfFileDesc { @@ -167,26 +194,26 @@ const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { owner: AID_ROOT, group: AID_SYSTEM, maps: &[ - MapDesc { name: "cpu_last_pid_map", perms: PERM_GWO }, - MapDesc { name: "cpu_last_update_map", perms: PERM_GWO }, - MapDesc { name: "cpu_policy_map", perms: PERM_GWO }, - MapDesc { name: "freq_to_idx_map", perms: PERM_GWO }, - MapDesc { name: "nr_active_map", perms: PERM_GWO }, - MapDesc { name: "pid_task_aggregation_map", perms: PERM_GWO }, - MapDesc { name: "pid_time_in_state_map", perms: PERM_GRO }, - MapDesc { name: "pid_tracked_hash_map", perms: PERM_GWO }, - MapDesc { name: "pid_tracked_map", perms: PERM_GWO }, - MapDesc { name: "policy_freq_idx_map", perms: PERM_GWO }, - MapDesc { name: "policy_nr_active_map", perms: PERM_GWO }, - MapDesc { name: "total_time_in_state_map", perms: PERM_GRW }, - MapDesc { name: "uid_concurrent_times_map", perms: PERM_GRW }, - MapDesc { name: "uid_last_update_map", perms: PERM_GRW }, - MapDesc { name: "uid_time_in_state_map", perms: PERM_GRW }, + MapDesc::new("cpu_last_pid_map", PERM_GWO), + MapDesc::new("cpu_last_update_map", PERM_GWO), + MapDesc::new("cpu_policy_map", PERM_GWO), + MapDesc::new("freq_to_idx_map", PERM_GWO), + MapDesc::new("nr_active_map", PERM_GWO), + MapDesc::new("pid_task_aggregation_map", PERM_GWO), + MapDesc::new("pid_time_in_state_map", PERM_GRO), + MapDesc::new("pid_tracked_hash_map", PERM_GWO), + MapDesc::new("pid_tracked_map", PERM_GWO), + MapDesc::new("policy_freq_idx_map", PERM_GWO), + MapDesc::new("policy_nr_active_map", PERM_GWO), + MapDesc::new("total_time_in_state_map", PERM_GRW), + MapDesc::new("uid_concurrent_times_map", PERM_GRW), + MapDesc::new("uid_last_update_map", PERM_GRW), + MapDesc::new("uid_time_in_state_map", PERM_GRW), ], progs: &[ - ProgDesc { name: "tracepoint_power_cpu_frequency" }, - ProgDesc { name: "tracepoint_sched_sched_process_free" }, - ProgDesc { name: "tracepoint_sched_sched_switch" }, + ProgDesc::new("tracepoint_power_cpu_frequency"), + ProgDesc::new("tracepoint_sched_sched_process_free"), + ProgDesc::new("tracepoint_sched_sched_switch"), ], }]; From bc4f085435ec70e322e6266663680d488e2d55c8 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Wed, 14 May 2025 11:09:06 +0900 Subject: [PATCH 11/13] Add kernel version check for prog and map This is a no-op because maps and progs of timeInState are always loaded. They have min_kver: KVER_NONE and max_kver: KVER_INF. Bug: 410982483 Test: atest bpfloader_tests with next CL Change-Id: I5db1909126fb3ca43939f39653f8a2334f130e94 --- loader/bpfloader.rs | 67 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 481b81c..05e9e39 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -20,8 +20,12 @@ use android_ids::{AID_ROOT, AID_SYSTEM}; use android_logger::AndroidLogger; use anyhow::{anyhow, ensure}; use libbpf_rs::{set_print, MapCore, ObjectBuilder, PrintLevel}; -use libc::{mode_t, S_IRGRP, S_IRUSR, S_IRWXG, S_IRWXO, S_IRWXU, S_ISVTX, S_IWGRP, S_IWUSR}; +use libc::{ + mode_t, uname, utsname, S_IRGRP, S_IRUSR, S_IRWXG, S_IRWXO, S_IRWXU, S_ISVTX, S_IWGRP, S_IWUSR, +}; use log::{debug, error, info, warn, Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; +use std::ffi::CStr; +use std::mem::MaybeUninit; use std::{ cmp::max, env, fs, @@ -137,7 +141,6 @@ fn libbpf_print(level: PrintLevel, mut msg: String) { } } -#[allow(dead_code)] struct MapDesc { name: &'static str, perms: mode_t, @@ -152,7 +155,6 @@ impl MapDesc { } } -#[allow(dead_code)] struct ProgDesc { name: &'static str, // Prog is loaded if kernel_version() is >= min_kver and < max_kver @@ -233,6 +235,49 @@ fn create_dir(dir_path: &Path) -> Result<(), anyhow::Error> { Ok(()) } +fn leading_number(str: &str) -> u32 { + let mut num_str = String::new(); + for c in str.chars() { + if c.is_ascii_digit() { + num_str.push(c); + } else { + break; + } + } + num_str.parse().unwrap_or(0) +} + +// Parses a kernel release string into a tuple of (major, minor, sub) version numbers. +// Examples: +// - "6.1.128-android14" returns (6, 1, 128) +// - "6.1.128android14" returns (6, 1, 128) +// - "6.1" returns (6, 1, 0) +fn parse_release(release: &str) -> (u32, u32, u32) { + let mut iter = release.splitn(3, '.'); + let major = leading_number(iter.next().unwrap_or("")); + let minor = leading_number(iter.next().unwrap_or("")); + let sub = leading_number(iter.next().unwrap_or("")); + (major, minor, sub) +} + +fn kernel_version() -> Result { + let mut buf: MaybeUninit = MaybeUninit::zeroed(); + // SAFETY: If uname returns 0, the buf should be properly initialized. + if unsafe { uname(buf.as_mut_ptr()) } != 0 { + return Err(anyhow!("Failed to call uname system call.")); + } + // SAFETY: `uname` returned 0, so the buf should be properly initialized. + let buf = unsafe { buf.assume_init() }; + // SAFETY: buf.release is part of the utsname struct populated by uname. + let release_cstr = unsafe { CStr::from_ptr(buf.release.as_ptr()) }; + let release = release_cstr + .to_str() + .map_err(|e| anyhow!("utsname release string is not valid UTF-8: {}", e))?; + + let (major, minor, sub) = parse_release(release); + Ok(kver(major, minor, sub)) +} + fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { info!("Loading {}", file_desc.filename); let filepath = Path::new(file_desc.dir).join(file_desc.filename); @@ -248,6 +293,8 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { let bpffs_path = "/sys/fs/bpf/".to_owned() + file_desc.prefix; create_dir(Path::new(&bpffs_path))?; + let kvers = kernel_version()?; + for mut map in loaded_file.maps_mut() { let mut desc_found = false; let name = @@ -256,6 +303,13 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { for map_desc in file_desc.maps { if map_desc.name == name { desc_found = true; + if kvers < map_desc.min_kver || kvers >= map_desc.max_kver { + info!( + "skipping map {} min_kver:{:x} max_kver:{:x} kvers:{:x}", + name, map_desc.min_kver, map_desc.max_kver, kvers + ); + continue; + } let pinpath_str = bpffs_path.clone() + "map_" + filename + "_" + &name; let pinpath = Path::new(&pinpath_str); debug!("Pinning: {}", pinpath.display()); @@ -291,6 +345,13 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { for prog_desc in file_desc.progs { if prog_desc.name == name { desc_found = true; + if kvers < prog_desc.min_kver || kvers >= prog_desc.max_kver { + info!( + "skipping program {} min_kver:{:x} max_kver:{:x} kvers:{:x}", + name, prog_desc.min_kver, prog_desc.max_kver, kvers + ); + continue; + } let pinpath_str = bpffs_path.clone() + "prog_" + filename + "_" + &name; let pinpath = Path::new(&pinpath_str); debug!("Pinning: {}", pinpath.display()); From 5076bd332daff707e5825d16cda8ae4aa90fa54c Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Fri, 16 May 2025 12:56:07 +0900 Subject: [PATCH 12/13] Add test for parsing kernel release string Test: atest bpfloader_tests Bug: 410982483 Change-Id: I09ace37a2624b08e96a91374a59e7d8df3bcce90 --- TEST_MAPPING | 3 +++ loader/Android.bp | 15 +++++++++++++-- loader/bpfloader.rs | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index 6a65edc..165e460 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "libbpf_load_test" + }, + { + "name": "bpfloader_tests" } ], "hwasan-postsubmit": [ diff --git a/loader/Android.bp b/loader/Android.bp index 8990ac0..15ff68a 100644 --- a/loader/Android.bp +++ b/loader/Android.bp @@ -102,8 +102,8 @@ cc_test { require_root: true, } -rust_binary { - name: "bpfloader", +rust_defaults { + name: "bpfloader_defaults", srcs: [ "bpfloader.rs", ], @@ -116,6 +116,17 @@ rust_binary { "liblibbpf_rs", "liblibc", ], +} + +rust_test { + name: "bpfloader_tests", + defaults: ["bpfloader_defaults"], + test_suites: ["general-tests"], +} + +rust_binary { + name: "bpfloader", + defaults: ["bpfloader_defaults"], required: [ "timeInState.bpf", ], diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index 05e9e39..b54fa01 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -423,3 +423,20 @@ fn main() { bpf_android_bindgen::execNetBpfLoadDone(); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_parse_release() { + assert_eq!(parse_release("6.1.128-android14-11-g213d628eb429-ab13297919"), (6, 1, 128)); + assert_eq!(parse_release("6.1.128_android14"), (6, 1, 128)); + assert_eq!(parse_release("6.1.128.4"), (6, 1, 128)); + assert_eq!(parse_release("6.1.android14"), (6, 1, 0)); + assert_eq!(parse_release("6.1-android14"), (6, 1, 0)); + assert_eq!(parse_release("6.1"), (6, 1, 0)); + assert_eq!(parse_release("6"), (6, 0, 0)); + assert_eq!(parse_release("android14"), (0, 0, 0)); + } +} From c1e1ad596f19092c3ea22df42ef25179daafe228 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Wed, 14 May 2025 12:06:35 +0900 Subject: [PATCH 13/13] Support specifying owner and group for each map and prog Currently, the owner and group apply to each BPF object file. However, each map and program can specify a different owner and group, even if they are in the same BPF object file. This is a no-op for existing timeInState.bpf Bug: 410982483 Test: TH Change-Id: I0fa2731385645e598b4d3bb7d27200dbb6d3a8ec --- loader/bpfloader.rs | 64 ++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/loader/bpfloader.rs b/loader/bpfloader.rs index b54fa01..3371ce0 100644 --- a/loader/bpfloader.rs +++ b/loader/bpfloader.rs @@ -144,27 +144,31 @@ fn libbpf_print(level: PrintLevel, mut msg: String) { struct MapDesc { name: &'static str, perms: mode_t, + owner: u32, + group: u32, // Map is loaded if kernel_version() is >= min_kver and < max_kver min_kver: u32, max_kver: u32, } impl MapDesc { - pub const fn new(name: &'static str, perms: mode_t) -> Self { - MapDesc { name, perms, min_kver: KVER_NONE, max_kver: KVER_INF } + pub const fn new(name: &'static str, perms: mode_t, group: u32) -> Self { + MapDesc { name, perms, owner: AID_ROOT, group, min_kver: KVER_NONE, max_kver: KVER_INF } } } struct ProgDesc { name: &'static str, + owner: u32, + group: u32, // Prog is loaded if kernel_version() is >= min_kver and < max_kver min_kver: u32, max_kver: u32, } impl ProgDesc { - pub const fn new(name: &'static str) -> Self { - ProgDesc { name, min_kver: KVER_NONE, max_kver: KVER_INF } + pub const fn new(name: &'static str, group: u32) -> Self { + ProgDesc { name, owner: AID_ROOT, group, min_kver: KVER_NONE, max_kver: KVER_INF } } } @@ -177,8 +181,6 @@ struct BpfFileDesc { // Warning: setting this to 'true' will cause the system to boot loop if there are any issues // loading the bpf program. critical: bool, - owner: u32, - group: u32, maps: &'static [MapDesc], progs: &'static [ProgDesc], } @@ -193,29 +195,27 @@ const FILE_ARR: &[BpfFileDesc] = &[BpfFileDesc { dir: "/etc/bpf/", prefix: "", critical: false, - owner: AID_ROOT, - group: AID_SYSTEM, maps: &[ - MapDesc::new("cpu_last_pid_map", PERM_GWO), - MapDesc::new("cpu_last_update_map", PERM_GWO), - MapDesc::new("cpu_policy_map", PERM_GWO), - MapDesc::new("freq_to_idx_map", PERM_GWO), - MapDesc::new("nr_active_map", PERM_GWO), - MapDesc::new("pid_task_aggregation_map", PERM_GWO), - MapDesc::new("pid_time_in_state_map", PERM_GRO), - MapDesc::new("pid_tracked_hash_map", PERM_GWO), - MapDesc::new("pid_tracked_map", PERM_GWO), - MapDesc::new("policy_freq_idx_map", PERM_GWO), - MapDesc::new("policy_nr_active_map", PERM_GWO), - MapDesc::new("total_time_in_state_map", PERM_GRW), - MapDesc::new("uid_concurrent_times_map", PERM_GRW), - MapDesc::new("uid_last_update_map", PERM_GRW), - MapDesc::new("uid_time_in_state_map", PERM_GRW), + MapDesc::new("cpu_last_pid_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("cpu_last_update_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("cpu_policy_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("freq_to_idx_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("nr_active_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("pid_task_aggregation_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("pid_time_in_state_map", PERM_GRO, AID_SYSTEM), + MapDesc::new("pid_tracked_hash_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("pid_tracked_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("policy_freq_idx_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("policy_nr_active_map", PERM_GWO, AID_SYSTEM), + MapDesc::new("total_time_in_state_map", PERM_GRW, AID_SYSTEM), + MapDesc::new("uid_concurrent_times_map", PERM_GRW, AID_SYSTEM), + MapDesc::new("uid_last_update_map", PERM_GRW, AID_SYSTEM), + MapDesc::new("uid_time_in_state_map", PERM_GRW, AID_SYSTEM), ], progs: &[ - ProgDesc::new("tracepoint_power_cpu_frequency"), - ProgDesc::new("tracepoint_sched_sched_process_free"), - ProgDesc::new("tracepoint_sched_sched_switch"), + ProgDesc::new("tracepoint_power_cpu_frequency", AID_SYSTEM), + ProgDesc::new("tracepoint_sched_sched_process_free", AID_SYSTEM), + ProgDesc::new("tracepoint_sched_sched_switch", AID_SYSTEM), ], }]; @@ -323,12 +323,12 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { ) }, )?; - chown(pinpath, Some(file_desc.owner), Some(file_desc.group)).map_err(|e| { + chown(pinpath, Some(map_desc.owner), Some(map_desc.group)).map_err(|e| { anyhow!( "Failed to chown {} with owner: {} group: {} err: {e}", pinpath.display(), - file_desc.owner, - file_desc.group + map_desc.owner, + map_desc.group ) })?; break; @@ -364,12 +364,12 @@ fn libbpf_worker(file_desc: &BpfFileDesc) -> Result<(), anyhow::Error> { ) }, )?; - chown(pinpath, Some(file_desc.owner), Some(file_desc.group)).map_err(|e| { + chown(pinpath, Some(prog_desc.owner), Some(prog_desc.group)).map_err(|e| { anyhow!( "Failed to chown {} with owner: {} group: {} err: {e}", pinpath.display(), - file_desc.owner, - file_desc.group + prog_desc.owner, + prog_desc.group ) })?; break;