From 1404d12a7e29ab02225bc13fd36794f6c6206db1 Mon Sep 17 00:00:00 2001
From: Rafael Ortiz <rafael.ortiz@redcanary.com>
Date: Mon, 21 Jun 2021 10:29:01 -0400
Subject: [PATCH] add support for older (and newer) map definitions

---
 aya/src/bpf.rs                    | 32 +++++++++-
 aya/src/maps/hash_map/hash_map.rs | 12 +---
 aya/src/maps/mod.rs               |  4 +-
 aya/src/obj/mod.rs                | 97 ++++++++++++++++++-------------
 4 files changed, 92 insertions(+), 53 deletions(-)

diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs
index 91d3a3aa..b513f09b 100644
--- a/aya/src/bpf.rs
+++ b/aya/src/bpf.rs
@@ -50,13 +50,41 @@ unsafe_impl_pod!(i8, u8, i16, u16, i32, u32, i64, u64);
 #[repr(C)]
 #[derive(Copy, Clone, Debug)]
 pub(crate) struct bpf_map_def {
+    // minimum features required by old BPF programs
     pub(crate) map_type: u32,
     pub(crate) key_size: u32,
     pub(crate) value_size: u32,
     pub(crate) max_entries: u32,
     pub(crate) map_flags: u32,
-    pub(crate) id: u32,
-    pub(crate) pinning: u32,
+    // optional features
+    pub(crate) inner_map_fd: u32, // id
+    pub(crate) numa_node: u32,    // pinning
+    pub(crate) map_name: [u8; BPF_OBJ_NAME_LEN],
+    pub(crate) map_ifindex: u32,
+    pub(crate) btf_id: u32,
+    pub(crate) btf_key_type_id: u32,
+    pub(crate) btf_value_type_id: u32,
+    pub(crate) btf_vmlinux_value_type_id: u32,
+}
+
+impl Default for bpf_map_def {
+    fn default() -> Self {
+        Self {
+            map_type: 0,
+            key_size: 0,
+            value_size: 0,
+            max_entries: 0,
+            map_flags: 0,
+            inner_map_fd: 0,
+            numa_node: 0,
+            map_name: [0u8; BPF_OBJ_NAME_LEN],
+            map_ifindex: 0,
+            btf_id: 0,
+            btf_key_type_id: 0,
+            btf_value_type_id: 0,
+            btf_vmlinux_value_type_id: 0
+        }
+    }
 }
 
 /// The main entry point into the library, used to work with eBPF programs and maps.
diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs
index 63a260f9..1ba608f2 100644
--- a/aya/src/maps/hash_map/hash_map.rs
+++ b/aya/src/maps/hash_map/hash_map.rs
@@ -162,9 +162,7 @@ mod tests {
                 key_size: 4,
                 value_size: 4,
                 max_entries: 1024,
-                map_flags: 0,
-                id: 0,
-                pinning: 0,
+                ..Default::default()
             },
             section_index: 0,
             data: Vec::new(),
@@ -215,9 +213,7 @@ mod tests {
                     key_size: 4,
                     value_size: 4,
                     max_entries: 1024,
-                    map_flags: 0,
-                    id: 0,
-                    pinning: 0,
+                    ..Default::default()
                 },
                 section_index: 0,
                 data: Vec::new(),
@@ -273,9 +269,7 @@ mod tests {
                     key_size: 4,
                     value_size: 4,
                     max_entries: 1024,
-                    map_flags: 0,
-                    id: 0,
-                    pinning: 0,
+                    ..Default::default()
                 },
                 section_index: 0,
                 data: Vec::new(),
diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs
index cb9176f7..f96e4b32 100644
--- a/aya/src/maps/mod.rs
+++ b/aya/src/maps/mod.rs
@@ -426,9 +426,7 @@ mod tests {
                 key_size: 4,
                 value_size: 4,
                 max_entries: 1024,
-                map_flags: 0,
-                id: 0,
-                pinning: 0,
+                ..Default::default()
             },
             section_index: 0,
             data: Vec::new(),
diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs
index 45b1a3ce..5c714bb4 100644
--- a/aya/src/obj/mod.rs
+++ b/aya/src/obj/mod.rs
@@ -493,8 +493,7 @@ fn parse_map(section: &Section, name: &str) -> Result<Map, ParseError> {
             value_size: section.data.len() as u32,
             max_entries: 1,
             map_flags: 0, /* FIXME: set rodata readonly */
-            id: 0,
-            pinning: 0,
+            ..Default::default()
         };
         (def, section.data.to_vec())
     } else {
@@ -510,13 +509,31 @@ fn parse_map(section: &Section, name: &str) -> Result<Map, ParseError> {
 }
 
 fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
-    if mem::size_of::<bpf_map_def>() > data.len() {
+    if mem::size_of::<bpf_map_def>() < data.len() {
         return Err(ParseError::InvalidMapDefinition {
             name: name.to_owned(),
         });
     }
 
-    Ok(unsafe { ptr::read_unaligned(data.as_ptr() as *const bpf_map_def) })
+    let mut map_def = bpf_map_def{
+        ..Default::default()
+    };
+
+    unsafe {
+        // std::ptr::copy is const, we can't use it because data.len() isn't known at
+        // compile time
+        let mut p = data.as_ptr();
+        let mut q = &mut map_def as *mut bpf_map_def as *mut u8;
+        for _ in 0..=data.len() {
+            *q = *p;
+            q = q.add(1);
+            p = p.add(1);
+        }
+    }
+
+    Ok(map_def)
+
+    //Ok(unsafe { ptr::read_unaligned(data.as_ptr() as *const bpf_map_def) })
 }
 
 fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
@@ -657,10 +674,19 @@ mod tests {
 
     #[test]
     fn test_parse_map_def() {
+        #![allow(unused_variables)] // map_def is used by the assertion
         assert!(matches!(
-            parse_map_def("foo", &[]),
+            parse_map_def("foo", &[0u8; std::mem::size_of::<bpf_map_def>() + 10]),
             Err(ParseError::InvalidMapDefinition { .. })
         ));
+        let map_def = bpf_map_def {
+            map_type: 1,
+            key_size: 2,
+            value_size: 3,
+            max_entries: 4,
+            map_flags: 5,
+            ..Default::default()
+        };
         assert!(matches!(
             parse_map_def(
                 "foo",
@@ -670,32 +696,32 @@ mod tests {
                     value_size: 3,
                     max_entries: 4,
                     map_flags: 5,
-                    id: 0,
-                    pinning: 0
+                    ..Default::default()
                 })
             ),
-            Ok(bpf_map_def {
-                map_type: 1,
-                key_size: 2,
-                value_size: 3,
-                max_entries: 4,
-                map_flags: 5,
-                id: 0,
-                pinning: 0
-            })
+            Ok(map_def)
         ));
     }
 
     #[test]
     fn test_parse_map_error() {
         assert!(matches!(
-            parse_map(&fake_section("maps/foo", &[]), "foo"),
+            parse_map(&fake_section("maps/foo", &[0u8; std::mem::size_of::<bpf_map_def>() + 10]), "foo"),
             Err(ParseError::InvalidMapDefinition { .. })
         ))
     }
 
     #[test]
     fn test_parse_map() {
+        #![allow(unused_variables)] // def is used by the assertion
+        let def = bpf_map_def {
+            map_type: 1,
+            key_size: 2,
+            value_size: 3,
+            max_entries: 4,
+            map_flags: 5,
+            ..Default::default()
+        };
         assert!(matches!(
             parse_map(
                 &fake_section(
@@ -706,8 +732,7 @@ mod tests {
                         value_size: 3,
                         max_entries: 4,
                         map_flags: 5,
-                        id: 0,
-                        pinning: 0
+                        ..Default::default()
                     })
                 ),
                 "foo"
@@ -715,15 +740,7 @@ mod tests {
             Ok(Map {
                 section_index: 0,
                 name,
-                def: bpf_map_def {
-                    map_type: 1,
-                    key_size: 2,
-                    value_size: 3,
-                    max_entries: 4,
-                    map_flags: 5,
-                    id: 0,
-                    pinning: 0
-                },
+                def,
                 data
             }) if name == "foo" && data.is_empty()
         ))
@@ -731,7 +748,18 @@ mod tests {
 
     #[test]
     fn test_parse_map_data() {
+        #![allow(unused_variables)] // def is used by the assertion
         let map_data = b"map data";
+        let _map_type = BPF_MAP_TYPE_ARRAY;
+        let value_size = map_data.len() as u32;
+        let def = bpf_map_def {
+            map_type: _map_type as u32,
+            key_size: 4,
+            value_size,
+            max_entries: 1,
+            map_flags: 0,
+            ..Default::default()
+        };
         assert!(matches!(
             parse_map(
                 &fake_section(
@@ -743,15 +771,7 @@ mod tests {
             Ok(Map {
                 section_index: 0,
                 name,
-                def: bpf_map_def {
-                    map_type: _map_type,
-                    key_size: 4,
-                    value_size,
-                    max_entries: 1,
-                    map_flags: 0,
-                    id: 0,
-                    pinning: 0
-                },
+                def,
                 data
             }) if name == ".bss" && data == map_data && value_size == map_data.len() as u32
         ))
@@ -809,8 +829,7 @@ mod tests {
                     value_size: 3,
                     max_entries: 4,
                     map_flags: 5,
-                    id: 0,
-                    pinning: 0
+                    ..Default::default()
                 })
             ),),
             Ok(())