Howcase: Shifting Ghost – A kernel-level IDS using eBPF, no_std and NASA P10 rules

Hi everyone,

I've been working on a sovereign defense framework called Shifting Ghost. It’s a kernel-space IDS built in Rust using eBPF/XDP, designed to move beyond passive detection toward active adversarial attrition.

The Architecture:

  • Zero-Allocation / No-std: To meet the strict requirements of kernel-space and safety-critical standards, I implemented an Arena Allocator to avoid dynamic heap issues.
  • NASA P10 Compliance: I followed the "Power of Ten" rules for safety-critical code to ensure the system is as deterministic as hardware.
  • Performance: Leveraging XDP for near-zero latency packet rejection (~50ns overhead).

The "Poisoning" Doctrine: The project includes a strategic White Paper on Adversarial Inhibition. Instead of just dropping packets, the system uses Singular Spectrum Analysis (SSA) and Extended Kalman Filters (EKF) to identify attackers and feed them deterministic noise, effectively poisoning their exfiltration cycle.

Stack: Rust, eBPF (Aya/Libbpf), Linux (Debian 13), and a lot of LaTeX for the theoretical framework.

I'd love to get your feedback on:

  1. My approach to Arena Allocation in a no_std kernel context.
  2. The safety of the eBPF map sharing logic between the 3 crates.
  3. The integration of signal processing (SSA) for deceptive security.

**Link to Repo & White Papers: shiva-protocol

Thanks for your critiques!

I’m re-labeling this as code-review then. Looking at the PDFs you included with the repo, it sounds like most of the content is AI-generated, is that right? Oh yes, it’s called out here:

Reading the previous paragraph leaves me a bit confused now though

does this imply you’re claiming you haven’t reviewed your code yet yourself? Also, if that’s the documented principle, why ask for feedback on it in a forum; wouldn’t that be opposite of what you’re documenting the approach is supposed to be here?

I have looked at a few parts of the sg-arena crate in your repo and I’m finding it’s fairly poor-quality code & design. I could list specifics (obvious soundness issues, unnecessary impls, some not very convincing arguments for soundness or design choices in internal comments [especially w.r.t. to the Send/Sync traits]) but I’m somewhat wondering how much that feedback really is worth in the first place, if you excuse my scepticism.

I might be inclined to call the whole thing basically just “AI slop”, and the fact that it’s using highly over-promising language, way too much jargony-sounding words, and the repo is only 3 days old doesn’t really help that appearance.

Hi steffahn, thank you for your brutal but necessary honesty.

To be completely transparent: Yes, I am a 19-year-old engineering student and I rely heavily on AI (Vibe Coding) to help me architect and generate parts of this project. The 'NASA P10' documentation and some of the jargon are indeed reflections of that AI-assisted workflow.

Regarding the comment about 'not reviewing the code yet': that was a poorly phrased disclaimer. My intent was to say that I am still in the process of learning how to verify these complex safety guarantees myself.

I asked for feedback here precisely because I know my knowledge of Send/Sync and advanced Rust memory safety is not yet at a senior level. I’m not trying to sell a finished product, but to learn the 'hard way' by getting critiques from experts like you.

If you have the patience to list even just one or two specific soundness issues in sg-arena, it would be worth more to my education than a thousand lines of AI code. I’m here to learn how to move away from 'AI slop' toward real, sound Rust engineering.

Again, I apologize if the presentation felt over-promising. It's the excitement of a student, not an attempt to deceive.

To the code writer. This block probably has UB: reserve isn't stated to return initialized memory, and if so (*ev) which forms &mut EbpfEvent has undefined behavior (see RingBufEntry in aya_ebpf::maps::ring_buf - Rust). It's not like Rust compiler can use this knowledge - after all, a foreign function underlies it, and after compilation the code turns out all right for the verifier.

#[inline(always)]
fn try_execve_monitor(_ctx: &TracePointContext) -> Result<i64, ()> {
    let Some(mut entry) = PACKET_EVENTS.reserve::<EbpfEvent>(0) else {
        return Err(());
    };

    let ev = entry.as_mut_ptr();

    unsafe {
        (*ev).timestamp_ns = aya_ebpf::helpers::bpf_ktime_get_ns();
        (*ev).pid          = aya_ebpf::helpers::bpf_get_current_pid_tgid() as u32;
        (*ev).uid          = aya_ebpf::helpers::bpf_get_current_uid_gid() as u32;
        (*ev).src_ip       = 0;
        (*ev).dst_ip       = 0;
        (*ev).src_port     = 0;
        (*ev).dst_port     = 0;
        (*ev).protocol     = 0;
        (*ev).event_type   = EVENT_EXECVE;
        (*ev)._pad         = [0u8; 2];
        if let Ok(comm) = aya_ebpf::helpers::bpf_get_current_comm() {
            (*ev).comm = comm;
        }
    }

    entry.submit(0);
    Ok(0)
}

To the operator (as distinguished from LLM): could you tell what protection functions are actually implemented in your package at the moment?

By the way @shiva, please observe that – as we’re mentioning in our pinned welcoming thread, too [Welcome to the Rust programming language users forum] – we consider this forum to be a place for people to talk to other people. We cannot sustain hosting anyone’s AI-generated questions or answers, as that could invite a flood of spam, and produce a more difficult social situation.

Given the styling of your original post, and the speed and wording of your reply, it seems obvious that you’re been using AI to help with writing posts in this forum, so I’d ask you to please stop doing that and instead write replies by hand. You don’t need to sound professional, we’d much rather just communicate with you directly.

To the operator: You are right to distinguish. I am an engineering student using LLMs as a powerful drafting tool, but I am the one responsible for the vision and the learning process.

What is actually implemented now:

  1. Baseline eBPF Monitoring: The hooks for execve and basic packet capture via XDP are functional.
  2. The Arena Allocator: It exists but, as pointed out by steffahn, its Send/Sync safety and general soundness are currently flawed and need a total rewrite.
  3. The Math (SSA/Kalman): The logic is drafted in the White Papers and partially in the Rust crates, but it is not yet stable or fully integrated into the kernel-space fast path.

The UB you found: Thank you for the pointer to aya_ebpf::maps::ring_buf. I realized I was forming a reference to uninitialized memory. I will refactor this using core::ptr::write to ensure no invalid references are created before initialization.

I am here to learn where the 'AI-generated' logic fails in real-world systems programming. Your feedback is exactly why I open-sourced this.

Also, this code's logic seems to be mangled (see: "sized at 16 bytes" vs "must be exactly 64 bytes"):

// https://github.com/tahakouiyasse/shiva-protocol/blob/b58f38c8cdf88e36c46315ff8386bae2f4ed2b48/sg-common/src/lib.rs#L86-L120

/// Sized at 16 bytes — fits two u64 registers, ideal for hashing.
#[repr(C, align(64))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable)]
pub struct FlowKey {
    pub src_ip:   u32,
    pub dst_ip:   u32,
    pub src_port: u16,
    pub dst_port: u16,
    pub protocol: u8,
    pub _pad1:    u8,
    pub _pad2:    u16,
    pub _final_padding: [u8; 48], 
}

...

// L'assertion de taille (Ligne 99+) doit maintenant correspondre à 64
const _FLOW_KEY_SIZE: () = assert!(
    core::mem::size_of::<FlowKey>() == 64,
    "FlowKey must be exactly 64 bytes"
);

Especially considering that this structure must be the same as

// https://github.com/tahakouiyasse/shiva-protocol/blob/b58f38c8cdf88e36c46315ff8386bae2f4ed2b48/sg-ebpf/src/map.rs#L30-L41
/// Layout is identical to `FlowKey` (#[repr(C)], same fields, same order).
/// Must stay in sync with `FlowKey` in sg-common/src/lib.rs.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct BpfFlowKey {
    pub src_ip:   u32,
    pub dst_ip:   u32,
    pub src_port: u16,
    pub dst_port: u16,
    pub protocol: u8,
    pub _pad:     [u8; 3],
}

Sorry steffahn, you're right. I was using AI to help with my English and to sound more professional because I was intimidated. I'll stop now. I'm a 19yo student from Morocco, I'm still learning Rust and I got too excited with what AI can generate. I really want to learn the 'real' way. I will rewrite my arena crate by hand to fix the issues you mentioned. Thank you for the patience.

You may be interested to know that many Rust crate authors don't reimplement things from scratch but pull in dependencies; there must be an arena crate ready somewhere. (Since this is a uni project I assume.)

You caught me again. This is exactly what happens when I let the LLM 'optimize' things I don't fully grasp yet. The allignment at 64 was an AI hallucination that i didn't verify. I'm going to delete the over-engineered parts and the inconsistent padding. I'll focus on making the structures match perfectly between common and ebpf. I'm sorry for the mess, I'm learning that 'fancy-looking' code is often just brokencode.

Thanks, I appreciate that a lot!

So the soundness issue that I’ve spotted was the following:

Your main arena allocation API has this type signature

    pub(crate) fn alloc_slice<T: Copy>(
        &self,
        n: usize,
    ) -> Result<&'static mut [T], ArenaError>

to allocate space for any type T: Copy.

As the generated comment in the documentation explains

                    // T is Copy (no Drop), so
                    // treating the uninitialized bytes as zeroed `MaybeUninit<T>`
                    // and casting to `T` is valid because the backing array was
                    // zero-initialised at link time (`[0u8; ARENA_SIZE_BYTES]`).

this apparently involves zero-initialization. However zero-initialization is not something that’s allowed for all T: Copy types. This is a problem, as the API would then allow someone to read the value behind the &mut T reference.

As @ProgramCrafter pointed out, many things in Rust are already implemented well by someone else, and it’s the same in this case: I could recommend e.g. the bytemuck crate which contains definitions for a trait Zeroable that’s designed to limit a type such that zero-initialization is allowable.

But of course, arena-allocation itself, too, is already implemented in several flavors, so maybe you can re-use some pre-existing approach and might be reduce your own need for unsafe alltogether, or at least learn from them about what to pay attention to.


Similarly, as someone who has reviewed a lot of unsafe implementations for soundness issues I can tell you: unsafe code can sometimes be really hard to get right, especially when generic type parameters are involved.[1]

If you want to generate code with AI, you might want to consider not allowing it to use any unsafe code at all – or at least never in generic functions – and keep writing the load-bearing - for the purpose of memory safety - parts of the code by hand. If AI would be involved in any way, IMHO it’s much better to use it for review, then you’ll get the pair-programming benefit (you understand the code yourself, and also a AI model gave feedback) instead of potentially degrading the quality.


  1. Because you’ll start to need to worry about all kinds of types, and it can be easy to overlook corner cases. As with the T: Copy case here, but it can be even more tricky.

    Whereas non-generic code, it’s easier to at least write a good amount of tests for, run them through miri and gain some confidence that way. ↩︎

I've decided to take down the repo for now. Your feedback made me realize that I was hiding behind AI-generated complexity and jargon instead of actually learning the fundamentals of Rust memory safety. I'm going to start over, focusing on a much simpler and 'sound' version without the over-engineered arena. Thank you for saving me from building on a broken foundation. I'll be back when I have something I actually wrote and understand 100%.