Hello. Imagine you have static mut in a special linker section. Then you are changing global allocator to ignore that pointer. In happy case, would it be UB?
In principle, yes. The documentation will, IIRC, requirefrom_raw to be passed a true Box-like heap-allocated peace of memory, otherwise it's at least library UB.
Furthermore, the behavior of using the global allocator (in the abstract machine) is also different from the actual #[global_allocator] implementation. This distinction is necessary e.g. to justify compiler optimizations that optimize away an unnecessary alloc-dealloc pair.
For that reason, my guess would be that what you're doing here might indeed very well even be considered UB on a language level, not just on a library level. I'm not 100% sure on that though, because I don't know off the top of my head what precisely are the language semantics around this.
In any case: If you also had a custom alloc implementation and somehow convinced it to return a pointer to that static mut under certain conditions, and then obtain the Box through Box::new or something comparable whilst fulfilling said special conditions, that would probably be a different story, and would probably be completely fine.
There is also a paragraph that, as far as I can tell, allows for the possibility of using pointers not from the global allocator:
Even though Box has the same representation and C ABI as a C pointer, this does not mean that you can convert an arbitrary T* into a Box and expect things to work. Box values will always be fully aligned, non-null pointers. Moreover, the destructor for Box will attempt to free the value with the global allocator. In general, the best practice is to only use Box for pointers that originated from the global allocator.
At least at present, you should avoid using Box types for functions that are defined in C but invoked from Rust. In those cases, you should directly mirror the C types as closely as possible. Using types like Box where the C definition is just using T* can lead to undefined behavior, as described in rust-lang/unsafe-code-guidelines#198.
That is a strange paragraph! First it effectively says it will be freed with the global allocator, then it says it's "best practice" to use pointers from the global allocator? That implies that it's not UB to free things with the global allocator that weren't allocated from it, which is the OP's desire, but still weird to call out explicitly.
Further, the closest to a "Safety" section I could find relevant to "free the value with the global allocator" is the Allocator::deallocate method, which has:
which contradicts the first. Seems like a doc issue, but I guess whatever T-* group would need to weigh in first.
The second paragraph I think is just don't use extern fn foo(bar: Box<Bar>) - though it seems that's explicitly legal as of Rust 1.41? (see #62514) Also confusing.
Not necessarily -- you could leak or forget it.[1] I think it's just worded in an overly-weak or not-explicit-enough manner. I.e. it should be updated to say "mixing allocators? Just don't" or "mixing allocators? Ensure that you (a) don't deallocate (b) ... (z)".
From a t-opsem perspective, the most interesting part of this isn't the from_raw, it's the drop.
My first guess is that this has to be made UB. drop calls Global::deallocate, which is a language-primitive operation -- it does (usually) call the user-defined deallocate, but it also has extra "magic" attached to it, and part of that magic involves making the deallocated memory inaccessible no matter what the deallocate function does. That magic is also UB if the memory wasn't returned by allocate in the first place.
The memory as seen from "inside" the allocator and from "outside" the allocator is not quite the same: there is magic happening when allocate returns that turns this memory into a primitive allocation with language-understood rules, and then when deallocate is called this primitive allocation is un-done and memory is available to the allocator again. That makes it UB to call deallocate on memory not returned by allocate (or to call it on memory that has already previously been deallocated).
forget(Box::from_raw(&mut ALLOC)) is harmless from an opsem perspective. The reason the docs are confusing is that Box::from_raw(&mut ALLOC) returns a Box that does not satisfy its safety invariant (there are safe operations that can cause UB, such as drop). So even though this operation does not immediately cause UB, it may well be responsible for causing UB later. We often struggle to clearly explain this: if we just say that it is UB, people complain that it's actually UB later so we should tone down the docs, but if we don't say it is UB, people complain that it can easily cause UB with safe code so we need to make the docs more scary. Over time we then end up with some weird mix in between those two stances...
Fundamentally it boils down to whether one focuses on validity invariants or safety invariants. Ideally we'd always only describe safety invariants, as those are nicely compositional, but sometimes unsafe code needs to do terrible things like temporarily violate the safety invariant and being extra careful not to call any operation that would rely on that invariant. Most library types don't allow this, but types like Box are so fundamental that there is high demand for allowing shenanigans like that.