BTF Special Fields in BPF Maps
Overview
BPF map values can contain special BTF-typed fields (spin locks, timers,
kptrs, list heads, etc.). These fields require special handling during map
copy and update operations because they hold kernel resources that cannot
be naively memcpy'd.
Two functions enforce this:
check_and_init_map_value(map, dst): called after copying a map value
to a temporary/userspace buffer. Reinitializes special fields in the copy
so kernel pointers and locks are not leaked to userspace.
bpf_obj_free_fields(map->record, ptr): called when overwriting or
freeing a map value. Releases resources held by the old value (cancels
timers, drops kptr references, frees list heads, unpins uptrs).
Which Map Types Support Special Fields
Not all map types can have special BTF fields. The allowlists are enforced
in map_check_btf() (kernel/bpf/syscall.c). A map type not listed for a
given field type will get -EOPNOTSUPP at map creation time.
| Field Type |
Allowed Map Types |
BPF_SPIN_LOCK, BPF_RES_SPIN_LOCK |
HASH, ARRAY, CGROUP_STORAGE, SK_STORAGE, INODE_STORAGE, TASK_STORAGE, CGRP_STORAGE |
BPF_TIMER, BPF_WORKQUEUE, BPF_TASK_WORK |
HASH, LRU_HASH, ARRAY |
BPF_KPTR_UNREF, BPF_KPTR_REF, BPF_KPTR_PERCPU, BPF_REFCOUNT |
HASH, PERCPU_HASH, LRU_HASH, LRU_PERCPU_HASH, ARRAY, PERCPU_ARRAY, SK_STORAGE, INODE_STORAGE, TASK_STORAGE, CGRP_STORAGE |
BPF_UPTR |
TASK_STORAGE |
BPF_LIST_HEAD, BPF_RB_ROOT |
HASH, LRU_HASH, ARRAY |
If a map type is not in any of these allowlists, it cannot have special BTF
fields and missing check_and_init_map_value / bpf_obj_free_fields calls
are not bugs.
Required Handling in Map Operations
Lookup (kernel to userspace copy)
When copy_map_value() or copy_map_value_long() copies a map value into
a buffer that will be returned to userspace, check_and_init_map_value()
must be called on the destination buffer afterward. This zeroes out special
fields so kernel addresses and lock state are not exposed.
Reference implementations:
- Syscall path:
bpf_map_copy_value() in kernel/bpf/syscall.c — calls
map->ops->map_lookup_elem() for the pointer, then copy_map_value() +
check_and_init_map_value() on the destination buffer
- Percpu:
bpf_percpu_array_copy(), bpf_percpu_hash_copy() — handle
their own copy + init internally
- Batch:
generic_map_lookup_batch() — delegates to bpf_map_copy_value()
Update (userspace to kernel copy)
When copy_map_value() or copy_map_value_long() overwrites an existing
map value with userspace data, bpf_obj_free_fields() must be called to
release resources held by the old value before or after the copy overwrites
them. Without this, timers keep firing, kptr references leak, and list
entries become unreachable.
Reference implementations (these call bpf_obj_free_fields() internally):
- Regular:
array_map_update_elem(), htab_map_update_elem() (via
check_and_free_fields())
- Percpu:
bpf_percpu_array_update(), bpf_percpu_hash_update() (via
pcpu_copy_value())
- Batch:
generic_map_update_batch() — delegates to the map's
map_update_elem callback
BPF-001: Missing BTF Field Handling in Map Copy/Update
When a new map operation or map type is added that uses copy_map_value()
or copy_map_value_long(), verify:
- Does the map type support special BTF fields? (check the table above)
- For lookups: is
check_and_init_map_value() called on the destination
after copying?
- For updates: is
bpf_obj_free_fields() called to clean up the old value?
- Compare with the reference implementation for the same operation type.
Percpu and non-percpu variants of the same map type may have different
allowlists — verify the exact BPF_MAP_TYPE_* enum value.
REPORT as bugs: Map operations on field-capable map types that copy
values with copy_map_value() without the corresponding
check_and_init_map_value() (lookups) or bpf_obj_free_fields() (updates).
Do not report for map types that are not in the map_check_btf() allowlists.
1---2name: btf-special-fields-in-bpf-maps3description: BPF map values can contain special BTF-typed fields (spin locks, timers, kptrs, list heads, etc.).4---5# BTF Special Fields in BPF Maps67## Overview89BPF map values can contain special BTF-typed fields (spin locks, timers,10kptrs, list heads, etc.). These fields require special handling during map11copy and update operations because they hold kernel resources that cannot12be naively memcpy'd.1314Two functions enforce this:1516- `check_and_init_map_value(map, dst)`: called after copying a map value17 to a temporary/userspace buffer. Reinitializes special fields in the copy18 so kernel pointers and locks are not leaked to userspace.19- `bpf_obj_free_fields(map->record, ptr)`: called when overwriting or20 freeing a map value. Releases resources held by the old value (cancels21 timers, drops kptr references, frees list heads, unpins uptrs).2223## Which Map Types Support Special Fields2425Not all map types can have special BTF fields. The allowlists are enforced26in `map_check_btf()` (`kernel/bpf/syscall.c`). A map type not listed for a27given field type will get `-EOPNOTSUPP` at map creation time.2829| Field Type | Allowed Map Types |30|-----------|-------------------|31| `BPF_SPIN_LOCK`, `BPF_RES_SPIN_LOCK` | HASH, ARRAY, CGROUP_STORAGE, SK_STORAGE, INODE_STORAGE, TASK_STORAGE, CGRP_STORAGE |32| `BPF_TIMER`, `BPF_WORKQUEUE`, `BPF_TASK_WORK` | HASH, LRU_HASH, ARRAY |33| `BPF_KPTR_UNREF`, `BPF_KPTR_REF`, `BPF_KPTR_PERCPU`, `BPF_REFCOUNT` | HASH, PERCPU_HASH, LRU_HASH, LRU_PERCPU_HASH, ARRAY, PERCPU_ARRAY, SK_STORAGE, INODE_STORAGE, TASK_STORAGE, CGRP_STORAGE |34| `BPF_UPTR` | TASK_STORAGE |35| `BPF_LIST_HEAD`, `BPF_RB_ROOT` | HASH, LRU_HASH, ARRAY |3637If a map type is not in any of these allowlists, it cannot have special BTF38fields and missing `check_and_init_map_value` / `bpf_obj_free_fields` calls39are not bugs.4041## Required Handling in Map Operations4243### Lookup (kernel to userspace copy)4445When `copy_map_value()` or `copy_map_value_long()` copies a map value into46a buffer that will be returned to userspace, `check_and_init_map_value()`47must be called on the destination buffer afterward. This zeroes out special48fields so kernel addresses and lock state are not exposed.4950Reference implementations:51- Syscall path: `bpf_map_copy_value()` in `kernel/bpf/syscall.c` — calls52 `map->ops->map_lookup_elem()` for the pointer, then `copy_map_value()` +53 `check_and_init_map_value()` on the destination buffer54- Percpu: `bpf_percpu_array_copy()`, `bpf_percpu_hash_copy()` — handle55 their own copy + init internally56- Batch: `generic_map_lookup_batch()` — delegates to `bpf_map_copy_value()`5758### Update (userspace to kernel copy)5960When `copy_map_value()` or `copy_map_value_long()` overwrites an existing61map value with userspace data, `bpf_obj_free_fields()` must be called to62release resources held by the old value before or after the copy overwrites63them. Without this, timers keep firing, kptr references leak, and list64entries become unreachable.6566Reference implementations (these call `bpf_obj_free_fields()` internally):67- Regular: `array_map_update_elem()`, `htab_map_update_elem()` (via68 `check_and_free_fields()`)69- Percpu: `bpf_percpu_array_update()`, `bpf_percpu_hash_update()` (via70 `pcpu_copy_value()`)71- Batch: `generic_map_update_batch()` — delegates to the map's72 `map_update_elem` callback7374## BPF-001: Missing BTF Field Handling in Map Copy/Update7576When a new map operation or map type is added that uses `copy_map_value()`77or `copy_map_value_long()`, verify:78791. Does the map type support special BTF fields? (check the table above)802. For lookups: is `check_and_init_map_value()` called on the destination81 after copying?823. For updates: is `bpf_obj_free_fields()` called to clean up the old value?834. Compare with the reference implementation for the same operation type.8485Percpu and non-percpu variants of the same map type may have different86allowlists — verify the exact `BPF_MAP_TYPE_*` enum value.8788**REPORT as bugs**: Map operations on field-capable map types that copy89values with `copy_map_value()` without the corresponding90`check_and_init_map_value()` (lookups) or `bpf_obj_free_fields()` (updates).91Do not report for map types that are not in the `map_check_btf()` allowlists.