use super::super::idr::translate_any_kva;
use super::{
AccessorCtx, BPF_MAP_TYPE_CGRP_STORAGE, BPF_MAP_TYPE_INODE_STORAGE, BPF_MAP_TYPE_SK_STORAGE,
BPF_MAP_TYPE_TASK_STORAGE, BpfMapInfo,
};
const TASK_STORAGE_BUCKETS_MAX: u32 = 1 << 16;
const TASK_STORAGE_ITER_MAX: usize = 1_000_000;
pub(super) fn iter_local_storage_entries(
ctx: &AccessorCtx<'_>,
map: &BpfMapInfo,
) -> Vec<(Vec<u8>, Vec<u8>)> {
if map.map_type != BPF_MAP_TYPE_TASK_STORAGE
&& map.map_type != BPF_MAP_TYPE_INODE_STORAGE
&& map.map_type != BPF_MAP_TYPE_SK_STORAGE
&& map.map_type != BPF_MAP_TYPE_CGRP_STORAGE
{
return Vec::new();
}
let Some(ts) = &ctx.offsets.task_storage_offsets else {
return Vec::new();
};
let smap_kva = map.map_kva;
let Some(smap_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
smap_kva,
ctx.l5,
ctx.tcr_el1,
) else {
return Vec::new();
};
let bucket_log = ctx.mem.read_u32(smap_pa, ts.smap_bucket_log);
let buckets_kva = ctx.mem.read_u64(smap_pa, ts.smap_buckets);
if buckets_kva == 0 {
return Vec::new();
}
let n_buckets = 1u32.checked_shl(bucket_log).unwrap_or(0);
if n_buckets == 0 || n_buckets > TASK_STORAGE_BUCKETS_MAX {
tracing::debug!(
map_name = %map.name(),
bucket_log,
n_buckets,
cap = TASK_STORAGE_BUCKETS_MAX,
"local_storage walker: out-of-range bucket_log, returning empty"
);
return Vec::new();
}
let value_size = map.value_size as usize;
if value_size > super::MAX_VALUE_SIZE {
return Vec::new();
}
let value_off_in_elem = ts.elem_sdata + ts.sdata_data;
let mut out = Vec::new();
let mut total_visited = 0usize;
let mut owner_cache: std::collections::HashMap<u64, u64> = std::collections::HashMap::new();
for i in 0..n_buckets {
let bucket_kva = buckets_kva + (i as u64) * (ts.bucket_size as u64);
let Some(bucket_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
bucket_kva,
ctx.l5,
ctx.tcr_el1,
) else {
continue;
};
let first_ptr = ctx
.mem
.read_u64(bucket_pa, ts.bucket_list + ts.hlist_head_first);
let mut node_ptr = first_ptr;
loop {
if node_ptr == 0 {
break;
}
total_visited += 1;
if total_visited > TASK_STORAGE_ITER_MAX {
return out;
}
let elem_kva = node_ptr;
let Some(elem_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
elem_kva,
ctx.l5,
ctx.tcr_el1,
) else {
break;
};
let local_storage_kva = ctx.mem.read_u64(elem_pa, ts.elem_local_storage);
let owner_kva = if local_storage_kva == 0 {
0
} else if let Some(&cached) = owner_cache.get(&local_storage_kva) {
cached
} else {
let resolved = match translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
local_storage_kva,
ctx.l5,
ctx.tcr_el1,
) {
Some(ls_pa) => ctx.mem.read_u64(ls_pa, ts.ls_owner),
None => 0,
};
owner_cache.insert(local_storage_kva, resolved);
resolved
};
let mut val_buf: Vec<u8> = Vec::with_capacity(value_size);
let slice = unsafe { std::slice::from_raw_parts_mut(val_buf.as_mut_ptr(), value_size) };
let n = ctx
.mem
.read_bytes(elem_pa + value_off_in_elem as u64, slice);
if n == value_size {
unsafe {
val_buf.set_len(value_size);
}
out.push((owner_kva.to_le_bytes().to_vec(), val_buf));
}
node_ptr = ctx.mem.read_u64(elem_pa, ts.hlist_node_next);
}
}
out
}