BpfMap key/value access size verification
(beginning there of)
Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ie7cf8cd51b4e272fc76a281df6231ed27955ed3f
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
index 86c0756..a7720ea 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
@@ -49,16 +49,20 @@
protected:
// flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY
BpfMap<Key, Value>(const char* pathname, uint32_t flags) {
- int map_fd = mapRetrieve(pathname, flags);
- if (map_fd >= 0) mMapFd.reset(map_fd);
+ mMapFd.reset(mapRetrieve(pathname, flags));
+ if (mMapFd < 0) abort();
+ if (isAtLeastKernelVersion(4, 14, 0)) {
+ if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+ if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+ }
}
public:
explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
- int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
- if (map_fd >= 0) mMapFd.reset(map_fd);
+ mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
+ if (mMapFd < 0) abort();
}
base::Result<Key> getFirstKey() const {
@@ -100,14 +104,16 @@
}
// Function that tries to get map from a pinned path.
- base::Result<void> init(const char* path);
+ [[clang::reinitializes]] base::Result<void> init(const char* path);
#ifdef TEST_BPF_MAP
// due to Android SELinux limitations which prevent map creation by anyone besides the bpfloader
// this should only ever be used by test code, it is equivalent to:
// .reset(createMap(type, keysize, valuesize, max_entries, map_flags)
// TODO: derive map_flags from BpfMap vs BpfMapRO
- base::Result<void> resetMap(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
+ [[clang::reinitializes]] base::Result<void> resetMap(bpf_map_type map_type,
+ uint32_t max_entries,
+ uint32_t map_flags = 0) {
int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
if (map_fd < 0) {
auto err = ErrnoErrorf("Unable to create map.");
@@ -152,14 +158,23 @@
// Move assignment operator
BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
- mMapFd = std::move(other.mMapFd);
- other.reset(-1);
+ if (this != &other) {
+ mMapFd = std::move(other.mMapFd);
+ other.reset();
+ }
return *this;
}
void reset(base::unique_fd fd) = delete;
- void reset(int fd) { mMapFd.reset(fd); }
+ [[clang::reinitializes]] void reset(int fd = -1) {
+ mMapFd.reset(fd);
+ if ((fd >= 0) && isAtLeastKernelVersion(4, 14, 0)) {
+ if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+ if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+ if (bpfGetFdMapFlags(mMapFd) != 0) abort(); // TODO: fix for BpfMapRO
+ }
+ }
bool isValid() const { return mMapFd != -1; }
@@ -200,6 +215,14 @@
if (mMapFd == -1) {
return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
}
+ if (isAtLeastKernelVersion(4, 14, 0)) {
+ // Normally we should return an error here instead of calling abort,
+ // but this cannot happen at runtime without a massive code bug (K/V type mismatch)
+ // and as such it's better to just blow the system up and let the developer fix it.
+ // Crashes are much more likely to be noticed than logs and missing functionality.
+ if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+ if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+ }
return {};
}
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index abf83da..4b29c44 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -150,6 +150,36 @@
});
}
+// requires 4.14+ kernel
+
+#define DEFINE_BPF_GET_FD_INFO(NAME, FIELD) \
+inline int bpfGetFd ## NAME(const BPF_FD_TYPE map_fd) { \
+ struct bpf_map_info map_info = {}; \
+ union bpf_attr attr = { .info = { \
+ .bpf_fd = BPF_FD_TO_U32(map_fd), \
+ .info_len = sizeof(map_info), \
+ .info = ptr_to_u64(&map_info), \
+ }}; \
+ int rv = bpf(BPF_OBJ_GET_INFO_BY_FD, attr); \
+ if (rv) return rv; \
+ if (attr.info.info_len < offsetof(bpf_map_info, FIELD) + sizeof(map_info.FIELD)) { \
+ errno = EOPNOTSUPP; \
+ return -1; \
+ }; \
+ return map_info.FIELD; \
+}
+
+// All 6 of these fields are already present in Linux v4.14 (even ACK 4.14-P)
+// while BPF_OBJ_GET_INFO_BY_FD is not implemented at all in v4.9 (even ACK 4.9-Q)
+DEFINE_BPF_GET_FD_INFO(MapType, type) // int bpfGetFdMapType(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD_INFO(MapId, id) // int bpfGetFdMapId(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD_INFO(KeySize, key_size) // int bpfGetFdKeySize(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD_INFO(ValueSize, value_size) // int bpfGetFdValueSize(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD_INFO(MaxEntries, max_entries) // int bpfGetFdMaxEntries(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD_INFO(MapFlags, map_flags) // int bpfGetFdMapFlags(const BPF_FD_TYPE map_fd)
+
+#undef DEFINE_BPF_GET_FD_INFO
+
} // namespace bpf
} // namespace android