4 ms·
"No mutable aliases" is a mistake; it prevents many useful programs. Now that virtual address space is cheap, it's possible to recompile C (or presumably C++)
by o11c 2y ago
"No mutable aliases" is a mistake; it prevents many useful programs.
Now that virtual address space is cheap, it's possible to recompile C (or presumably C++) with a fully-safe runtime (requiring annotation only around nasty things like `union sigval`), but this is an ABI break and has nontrivial overhead (note that AddressSanitizers has ~2x overhead and only catches some optimistic cases) unless you mandate additional annotation.
- murderfs 2y agovirtual address space is cheap, but changing it is massively expensive. If you have to do a TLB shootdown on every free, you're likely going to have worse performance than just using ASan.
- o11c 2y agoDealing with malloc/free is trivial and cheap - just give every allocated object a couple of reference counts. The hard part is figuring out which words of memory should be treated as pointers, so that you know when to alter the reference counts. Most C programs don't rely on all the weird guarantees that C mandates (relying on asm, which is also problematic, is probably more common), but for the ones that do it is quite problematic.
- akira2501 2y ago> just give every allocated object a couple of reference counts. Works great with a single thread.
- o11c 2y agoMulti-threaded refcounts aren't actually that hard? There's overhead (depending on how much you're willing to annotate it and how much you can infer), but the only "hard" thing is the race between accessing a field and and changing the refcount of the object it points to, and [even ignoring alternative CAS approaches] that's easy enough if you control the allocator (do not return memory to the OS until all running threads have checked in). Note that, in contrast the the common refcount approach, it's probably better to introduce a "this is in use; crash on free" flag to significantly reduce the overhead.
- acbits 2y agohttps://github.com/acbits/reftrack-plugin https://github.com/acbits/reftrack-plugin I wrote a compiler extension just for this issue since there wasn't any.
- steveklabnik 2y agoThe borrow checker works irrespective of the heap. Memory safety involves all pointers, not just ones that own a heap allocation.
- o11c 2y agoIf we're trying to minimize annotation while maximizing C compatibility, it will be necessary to heap-allocate stack frames. This cost can be mitigated with annotations, once again. In this case, a global "forbid leaks even if unused" flag would cover it. Static allocations only need full heap compatibility if `dlclose` isn't a nop. And TLS is the forgotten step-child, but at the lowest level it's ultimately just implemented on normal allocations.
- dwattttt 2y ago> it will be necessary to heap-allocate stack frames. I sure hope you don't use any stack frames while writing the stack frame allocator.
- mananaysiempre 2y ago> AddressSanitizer has ~2x overhead I’ve got some programs where the ASan overhead is 10× or more. Admittedly, they are somewhat peculiar—one’s an interpreter for a low-level bytecode, the other’s largely a do-nothing benchmark for measuring the overhead of a heartbeat scheduler. The point is, the overhead can vary a lot depending on e.g. how many mallocs your code does. This does not contradict your point in any way, to be clear. I was just very surprised when I first hit that behaviour expecting ASan’s usual overhead of “not bad, definitely not Valgrind”, so I wanted to share it.
- tialaramex 2y ago> "No mutable aliases" is a mistake; it prevents many useful programs. Does it? You didn't list any. It certainly prevents writing a tremendous number of programs which are nonsense.
- o11c 2y agoThe entirety of Rust's `std::cell` is a confession that yes, we really do need mutable aliases. We just pretend they the aliases aren't mutable except for a nanosecond around the actual mutation.
- alilleybrinker 2y agoAlternatively, Rust's cell types are proof that you usually don't need mutable aliasing, and you can have it at hand when you need it while reaping the benefits of stronger static guarantees without it most of the time.
- steveklabnik 2y agoIt’s more than that, they disable the aliasing based optimizations, and provide APIs that restrict how and when you can mutate in order to make sure data races don’t happen. Controlled mutable aliasing is fine. Uncontrolled is dangerous.
- JoshTriplett 2y agoCells still don't allow simultaneous mutable aliases; they just allow the partitioning of regions of mutable access to occur at runtime rather than compile time.
- jerf 2y agoAnd Rc<T> is a "confession" that we still need reference counting semantics. And macros are a "confession" that pure Rust code isn't powerful enough. And "unsafe" is a "confession" that we really do need "unsafe". And so on and so forth with all kinds of features. Except that's a really harsh and unproductive way to phrase it. The existence of "unsafe" is not a "confession" that safe code is impossible so why should even try. Progress in safety is still made when an unsafe capability is removed from the normal, common, suggested way to do things and it is moved to something you need to ask for and take responsibility for, and hemmed in by other safe constructs around it.
- stouset 2y ago> it prevents many useful programs Every set of constraints prevents many useful programs. If those useful programs can still be specified in slightly different ways but it prevents many more broken programs, those constraints may be a net improvement on the status quo.
- morning-coffee 2y ago> "No mutable aliases" is a mistake; it prevents many useful programs. Yes, it prevents many useful programs. I think it also prevents many many many more useless broken incorrect programs from wreaking havoc or being used as exploit delivery vehicles.
- bluGill 2y agoWriting correct mutable code when there are alias in play is difficult and should be avoided I agree. However sometimes it is the right answer to the problem and so it should be possible for the best programmers to do. However only a minority of code needs that, and when it is needed you need to do a lot of extra review - I wish there was a way to make doing it accidentally impossible on C++.
- tsimionescu 2y agoHow would this fix memory safety issues like std::sort(vec1.begin(), vec2.end()) (where vec1 and vec2 are different vectors, of course)? Or strlen(malloc(100))?
- deleted 2y ago[deleted]
- o11c 2y agoWith a safe runtime, a pointer is really something like a `struct { u32 allocation_base, allocation_offset;}`. (it may be worth doing fancy variable-bit-width math to allow many small allocations but only a few large ones; it's also likely worth it to have a dedicated "leaf" section of memory that is intended not to contain any pointers) An implementation of `sort` would start with: `assert (begin.allocation_base == end.allocation_base)`. Most likely, this would be implicit when `end - begin` or `begin < end` is called (but not `!=`, which is well-defined between unrelated pointers). If we ignore the uninitialized data (which is not the same kind of UB, and usually not interesting), the `strlen` loop would assert when, after not encountering a NUL, `s.allocation_offset` exceeds `100` (which is known in the allocation metadata).
- tsimionescu 2y agoThat makes sense, thank you! I'd bet that there are corners of non-UB vlaid programs that would be sensitive to the fact that pointers are no longer simple numbers, but maybe that's wrong or at least could be worked around. I would add that to make this memory safe for multi-threaded programs, you also need to implcitly synchronize on fat pointer accesses, to prevent corrupted pointers from forming.
- badmintonbaseba 2y agoTracking allocations is necessary, but not sufficient. struct S { int arr1[100]; std::string s; int arr2[100]; }; void foo(S& s) { //arr1 and arr2 are in the same complete object //so they are in the same allocation std::sort(std::begin(arr1), std::end(arr2)); } To make it sound you really need to track arrays specifically (including implicit single-element ones), not just allocations. It's surely somewhat feasible, as the constexpr interpreters in compilers do track it, but something like that would probably be way inefficient at runtime.
- myworkinisgood 2y ago[flagged]
- dang 2y agoYou broke the site guidelines repeatedly in this thread, and posting attacks like this on others will definitely get you banned here if you keep doing it. If you'd please review https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html and stick to the rules when posting here, we'd appreciate it.
- leni536 2y agoDon't deploy ASAN builds to production, it's a debugging tool. It might very well introduce attack vectors on its own, it's not designed to be a hardening feature.
- randomNumber7 2y agoThe tinyCC compiler, written by fabrice bellard, has a feature that enables pointer checking and makes the resulting C code safe.
- steveklabnik 2y ago> When a pointer comes from unchecked code, it is assumed to be valid. It certainly helps, but is not a full solution.