C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Draft proposal for fixing undefined behavior in `atomic_notify_*` operations

From: Ryan P. Nicholl <rnicholl_at_[hidden]>
Date: Sat, 17 Jan 2026 22:31:21 +0000
> You never replied to my question about what it means if the memory has been deallocated and is not even part of the address space. Linux's futex(2) and FreeBSD's _umtx_op should return EFAULT for that, but your proposal says waiters might be spuriously unblocked, it doesn't allow a waiting operation to fail (potentially terminating, depending how the implementation handles errors from the system call). If the storage has already been reused for another atomic of a different type, worse things can happen than spuriously unblocking waits on the new object: macOS will return EINVAL if the wake and wait calls don't agree on the size of the value at that address.

I was able to test FreeBSD in a Virtual machine, it seems that they do not current cause any program with atomic notify one functions. _umtx_op has the required semantics to implement this without issue, if the efault error code is ignored (which it seems to be)

I believe such problematic implementations don't exist, with the exception of -fsanitize on GCC. However, this likely just needs to be whitelisted, as it seems to consistently work fine with sanitizers disabled even to a loop called against random memory addresses. I wasn't able to get MSVC sanitizers working because I was probably using the wrong compiler options somewhere but the underlying WakeByAddress Win32 call is safe and I had no issues with sanitizers disabled.

--
Ryan P. Nicholl
Tel: (678)-358-7765
On Thursday, January 15th, 2026 at 13:37, Jonathan Wakely <cxx_at_[hidden]> wrote:
> On Thu, 15 Jan 2026 at 19:50, Ryan P. Nicholl <rnicholl_at_[hidden]> wrote:
>
>> The implementation of std::atomic_notify_* on libc++, gcc, musl and microsoft STL is already in compliance with the proposed wording change.
>
> Your wording talks about a "pointer to M" but if M is outside its lifetime, then it's an invalid pointer value. You would need to talk about a pointer to the storage of M, otherwise your wording doesn't achieve what you want.
>
> You never replied to my question about what it means if the memory has been deallocated and is not even part of the address space. Linux's futex(2) and FreeBSD's _umtx_op should return EFAULT for that, but your proposal says waiters might be spuriously unblocked, it doesn't allow a waiting operation to fail (potentially terminating, depending how the implementation handles errors from the system call). If the storage has already been reused for another atomic of a different type, worse things can happen than spuriously unblocking waits on the new object: macOS will return EINVAL if the wake and wait calls don't agree on the size of the value at that address.
>
>> They are already fully implemented, this proposal would only be a standardese fix. You cannot tell me this is not possible to do, it is already done. I can write code that will work fine on all platforms, but according to the standard C++ language it's not allowed. AFAIK, all platforms already do this. If you know an implementation that doesn't do this with std::atomic_notify_*, I would like to know what that is.
>
> GCC's implementation immediately dereferences the pointer parameter of atomic_notify_one, which is undefined if the std::atomic object is outside its lifetime. It would also not be valid in a constant expression, but that function is constexpr now, so you need to consider what should happen there. Changing those properties of the implementation wouldn't be difficult if needed, but it's not true that they already implement what you want. It's only true if you say that what is undefined in the abstract machine is fine, because it happens to work.
>
> Within the implementation of __atomic_wait_address there are operations such as casts and lvalue-to-rvalue conversions which are implementation-defined if the pointer is an invalid pointer value, such as a pointer to a region of storage that no longer contains an object.
>
>> Saying "this can't be done" without doing any investigation on your own is not that productive.
>
> Saying "this can be done" when you're talking about requiring operations on invalid pointer values outside of an object's lifetime is just brushing aside a lot of rules of the abstract machine. Again, you opened the entire discussion talking about the abstract machine, but the proposal is basically requiring atomic_notify_one to operate entirely outside the abstract machine, treating its parameter as an opaque pattern of bits with no relation to an object (it's hard to interpret the wording any other way if the calls are allowed invalid pointer values as arguments, or can race with destructors of the object).
>
>> There are certainly some OS where you can't directly wait on the atomic address itself if the wait area is larger than the atomic region, using a single system call, that doesn't mean it isn't possible to do using another algorithm, though. As it turns out, address monitor queues work for an arbitrarily sized type. Is it a bit slower? Yes. Is it unimplementable? No. Do all atomic implementations use monitor queues for types that don't fit in the os sync primitives? As far as I can tell, yes. And of course, even lock based atomics work fine with monitor queues too, so I see no reason not to allow it. If you can wait on any value of any size, you can create a monitor queue for values of arbitrary sizes.

Received on 2026-01-17 22:31:29