Fix FreeBSD integrity check: use sysctl instead of /proc/curproc/file

ober

85fb3ac0f54cab36498a99e632b989b4e103ec22

diff --git a/.gitignore b/.gitignore
index 9e09c94..29da621 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 *.so
 *.wpo
 *.boot
+*.sls
 *~
 \#*
 .#*
diff --git a/jerboa-native-rs/src/integrity.rs b/jerboa-native-rs/src/integrity.rs
index 9c5761a..5ee7b9a 100644
--- a/jerboa-native-rs/src/integrity.rs
+++ b/jerboa-native-rs/src/integrity.rs
@@ -3,14 +3,49 @@ use ring::{digest, signature};
 #[allow(deprecated)]
 use ring::constant_time;
 
-/// Portable path to the current executable.
-fn self_exe_path() -> &'static str {
+/// Read the current executable's binary contents.
+/// On Linux, reads /proc/self/exe directly.
+/// On FreeBSD, uses sysctl KERN_PROC_PATHNAME to resolve the path first
+/// (procfs may not be mounted).
+fn read_self_exe() -> Result<Vec<u8>, String> {
     #[cfg(target_os = "linux")]
-    { "/proc/self/exe" }
+    {
+        std::fs::read("/proc/self/exe")
+            .map_err(|e| format!("cannot read /proc/self/exe: {}", e))
+    }
     #[cfg(target_os = "freebsd")]
-    { "/proc/curproc/file" }
+    {
+        use std::os::raw::c_int;
+        extern "C" {
+            fn sysctl(
+                name: *const c_int,
+                namelen: u32,
+                oldp: *mut u8,
+                oldlenp: *mut usize,
+                newp: *const u8,
+                newlen: usize,
+            ) -> c_int;
+        }
+        let mut buf = vec![0u8; 4096];
+        let mut len = buf.len();
+        let mib: [c_int; 4] = [1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1];
+        let rc = unsafe {
+            sysctl(mib.as_ptr(), 4, buf.as_mut_ptr(), &mut len, std::ptr::null(), 0)
+        };
+        if rc < 0 {
+            return Err("sysctl KERN_PROC_PATHNAME failed".to_string());
+        }
+        // len includes the NUL terminator
+        let path = std::str::from_utf8(&buf[..len.saturating_sub(1)])
+            .map_err(|e| format!("invalid UTF-8 in exe path: {}", e))?;
+        std::fs::read(path)
+            .map_err(|e| format!("cannot read {}: {}", path, e))
+    }
     #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
-    { "/proc/self/exe" }
+    {
+        std::fs::read("/proc/self/exe")
+            .map_err(|e| format!("cannot read /proc/self/exe: {}", e))
+    }
 }
 
 /// Read /proc/self/exe and compute its SHA-256 hash.
@@ -32,10 +67,10 @@ pub extern "C" fn jerboa_integrity_hash_self(
             return -1;
         }
 
-        let binary = match std::fs::read(self_exe_path()) {
+        let binary = match read_self_exe() {
             Ok(b) => b,
             Err(e) => {
-                set_last_error(format!("cannot read /proc/self/exe: {}", e));
+                set_last_error(e);
                 return -1;
             }
         };
@@ -66,10 +101,10 @@ pub extern "C" fn jerboa_integrity_verify_hash(
             return -1;
         }
 
-        let binary = match std::fs::read(self_exe_path()) {
+        let binary = match read_self_exe() {
             Ok(b) => b,
             Err(e) => {
-                set_last_error(format!("cannot read /proc/self/exe: {}", e));
+                set_last_error(e);
                 return -1;
             }
         };
@@ -115,10 +150,10 @@ pub extern "C" fn jerboa_integrity_sign_verify(
             return -1;
         }
 
-        let mut binary = match std::fs::read(self_exe_path()) {
+        let mut binary = match read_self_exe() {
             Ok(b) => b,
             Err(e) => {
-                set_last_error(format!("cannot read /proc/self/exe: {}", e));
+                set_last_error(e);
                 return -1;
             }
         };