Perf splits from libbpf's hashmap to end aliasing bugs and leaks
Ian Rogers replaces the untyped table with a type-safe API after years of NULL-check mistakes and strict-aliasing violations.
Linux perf is getting its own hashmap, a change meant to stop crashes and leaks that the current code has been papering over.
Ian Rogers wrote on the kernel mailing list that perf's hashmap is a copy of libbpf's internal untyped table. It is compiled in only when libbpf is disabled or dynamically linked, and otherwise resolved against a static libbpf archive. That sharing has blocked two cleanups. The implementation casts caller output pointers to long pointers and dereferences them, which violates C's strict-aliasing rules and is one reason perf still builds with strict aliasing turned off. Allocation failure is reported with the kernel-style error-pointer convention rather than a NULL return and errno, unlike the rest of user-space perf. Rogers said that mismatch has repeatedly produced bugs where callers tested for NULL instead of an error pointer, including missing checks in the function cache and PMU code.
The untyped interface has also made iteration and deletion easy to get wrong. Keys and values are stored as longs or void pointers, so smaller types such as booleans, ints, and process IDs cannot be passed safely as outputs. A break inside the iterator only leaves the current bucket chain, not the whole map. A failed delete leaves the caller's old-key and old-value pointers untouched, unlike insert.
The changes fix three caller bugs along the way: an uninitialized pointer passed to a thread reference drop when a delete misses, a leak of symbol histogram entries when annotation histograms are cleared, and a leaked per-package mask key. Perf's hashmap is then split from libbpf so the aliasing and error-return problems can be fixed without waiting on libbpf. A zero-overhead generic declaration gives each table compile-time key and value types. All 17 maps in perf are converted one at a time, with each step left buildable, and the old untyped API is removed at the end.