How to free a lazy_static? (mem leak)


#1

I using lazy_static to build a kernel module (freebsd), like so: (using spin crate)

pub static ref MODULE: Arc<Mutex<Hello>> = Arc::new(Mutex::new(Hello::default()));

Now, this won’t be any problem in an executable but in a kernel module whatever is in Arc<> will be leaked at kldunload.
I can not do Arc<Option> since the Option would be immutable.

Mutex<Option> works because I can to MODULE.lock().take() at unload and free all memory but I do need that Arc<>…

Is there any way to “force free” the lazy static, or empty an Arc somehow without editing the Arc source code?


#2

One option is Mutex<Option<Arc<Mutex<Hello>>> but that’s a bit of a mouthfull.


#3

Haha, yeah that’s an ugly one…


#4

Can you use Weak instead of Arc?


#5

I guess but I’d have to keep a strong reference around, perhaps in another lazy static. Maybe just easiest to build a custom Arc-Mutex-Option kind of type for this…


#6

If you can guarantee that the nothing will try to dereference MODULE after you unload the kernel module, you can use ptr::drop_in_place (unsafe) to drop the Arc.


#7

Can I do this even if I don’t have mutable access to the Arc? I explored various ways of dropping it but end up giving up since it’s a ‘ref’ and not ‘mut ref’… (I guess I could look up the answer myself but for sake of clarity for other readers I ask here)


#8

OTH, when doing a Arc<Mutex<Option<T>>> I can safely take() T and drop it while mutex locked and I will only leak the size of Mutex struct (a few bytes). Maybe that is acceptable since you generally don’t load and unload modules. Writing some nice accessors will reduce bloat also.


#9

You can do it with an UnsafeCell.


#10

Can I do this even if I don’t have mutable access to the Arc? I explored various ways of dropping it but end up giving up since it’s a ‘ref’ and not ‘mut ref’… (I guess I could look up the answer myself but for sake of clarity for other readers I ask here)

Yes, drop_in_place takes a *mut T, not an &mut T (pointers and references are very different beasts and you can get a mutable pointer from a shared reference). As long as you can guarantee that there are no other existing references to that particular copy of the Arc<...>, you should be fine. However, if you do it this way, you won’t be able to reinitialize the module’s state unless MODULE somehow gets restored to its initial state (I don’t know how module loading works in freebsd).

OTH, when doing a Arc<Mutex<Option>> I can safely take() T and drop it while mutex locked and I will only leak the size of Mutex struct (a few bytes). Maybe that is acceptable since you generally don’t load and unload modules. Writing some nice accessors will reduce bloat also.

That should leak the the size of Option<T>. If T is just some Box<U>, that’s not a big issue (just a pointer) but if T is large, you’ll leak a lot of memory. Regardless, leaking heap memory in the kernel sounds like a bad idea anyways.


Given that the kernel will (I assume) call initialize/deinitialize functions on load/unload, you may want to drop the lazy_static dependency entirely and implement this from scratch. Also, I’m pretty sure you don’t even need the Arc (just Mutex<Hello> should do) as you’re dealing with statics (unless Hello needs to outlive your kernel module).


#11

I just remembered that your use case is pretty similar to the log crate’s, which has a bit of custom code to handle the synchronization: https://github.com/rust-lang-nursery/log/blob/master/src/lib.rs#L271


#12

Couldn’t you ptr::write a new value in place?


#13

Maybe? You’d probably want core::ptr::write_volatile but I’m not entirely sure that even that will be thread safe. I expect it is as the kernel will need to use some synchronization primitive to communicate the fact that the module has been re-initiialized to other threads/cores but…


#14

I don’t know freebsd, but I’d expect module loading/unloading to be highly synchronized already.


#15

Thanks for the tips! I want to share the Mutex<T> to other pieces of code that does callbacks from other threads so Arc seemed like a good choice. Of course I could share a reference but then I end up with keeping lifetimes around in many different structures, seemed easier at this point to just use the Arc.

For the proof-of-concept I think lazy_static and 64 bytes of memory leak is acceptable (or dropping it with ptr::drop_in_place). Maybe later I’ll think of some custom alternative to lazy_static.

Another drawback with current design is that I have to put all fields of Hello in an inner: Option<HelloInner> so that I can initialize the lazy_static completely with inner being None, and then in a later ‘module load’ callback to Hello, clone the Arc and hand over pointer to backgrounds thread. I can’t clone MODULE in Hello’s initializer.


#16

If you use nightly and
if the access to the static (but not necessary the values it refers to) is synchronized
through the lifecycle of the kernel module you could consider static mut, too.

E.g. (be aware of types etc. as I dindn’t run this)

#![feature(drop_types_in_const)] //not sure why it's still needed

pub static ref MODULE: Option<Arc<Mutex<Hello>> = None;

/// in module load (in function)
unsafe { MODULE = Some(Arc::new(Mutex::new(Hello::default()))) };

/// in module unload (in function)
unsafe { MODULE = None };

/// e.g.  access 
unsafe fn get_module() -> Arc<Mutex<Hello>> {
    MODULE.as_ref().unwrap().clone()
}

Note that using the drop_in_place/prt::write variation does
break the unsafe contract and while the compiler doesn’t know
about it, it makes accessing the lazy_static variable no longer
"safe" (in the rust sense) e.g. it is roughly equivalent to
making aboves get_module safe, and is probably ok
for your use case.

Why I personally prefer static mut in this case I also don’t know,
why it’s not stable yet (but asked why in the RFC)

PS:
Be aware of multi threaded code possible accessing the static while/after unload,
as you (have to) sidestep Rust safeties :smiling_imp: (or have Mutex<Option<Arc<Mutex<Hello>>>> and might still fail because of a panic in Option::unwrap …) which is not much better in a kernel modules :sweat_smile:


#17

References to statics have a static lifetime so you shouldn’t need lifetime parameters. The reference type would just be &'static Mutex<T>.