Technically, yes.
I myself use this template quite a lot, and therefore almost all programs with signal support that I looked at. It is expected that it will work in practice and be portable in different systems, even if it is not dictated by any standards.
The POSIX.1 standard defines it as an Undefined Behavior, not because it expects programs to avoid such access, but since defining secure access situations will be too complicated and possibly limit future implementations, since there is a well-known workaround for all such access: dedicated stream, capturing signals.
The question of which objects a safe signal can access is complex. Instead of opening an entire can of worms, the standard POSIX compilers simply skipped it and announced undefined behavior.
The most difficult part to determine is the details associated with the parallel representations of access and traps. Not only by other threads in the same process, but also by the core. (Since we consider only objects with a static storage duration, we can avoid shared memory and all the complexity associated with them there.) In particular, if an object has trap representations and the object is changed atomically, it is possible that the intermediate steps of the assignment cause a trap. And this trap can cause an increase in signal, although some architectures may have hardware limitations.
So, everything related to trap representations is basically too difficult to resolve in the standard.
Well, suppose that the standard restricts read-only access to objects with a static storage duration that will not be simultaneously changed by an intermittent thread, any other process thread, or kernel; and write access to objects with a static storage duration that are not simultaneously read or changed by an intermittent thread, not by any other process thread, or by the kernel. And that access to the object does not contain trap representations at all.
We still have several hardware-specific signals: SIGSEGV , SIGBUS , SIGILL and SIGFPE at least. Unfortunately, some architectures may have additional signals that are not currently known, so we need to determine the type of signal that is affected: signals that are raised by the kernel when accessing memory ( SIGFPE , only if the architecture increases it when loading the value, and not only when performing arithmetic, etc. to such values). If access to an object with a static storage duration can raise one of these signals, then access is unsafe, as this can lead to a cascade of signal handlers. (Since standard POSIX signals are not queued, only the first signal of each type receives execution, and the state of the process can be lost, causing the kernel to kill the process.)
From the point of view of the POSIX C compiler, the whole situation becomes much more complicated if you consider a signal handler that receives a pointer as a payload ( si_value.sival_ptr in siginfo_t ): does access to Undefined Behavior depend on whether the purpose is static storage or no?
In all existing POSIXy systems, access to objects of duration of static storage through atomic built-in modules or when they are not read / modified with any other threads or the kernel, and with intermediate storage formats do not cause a signal increase in the POSIX real-time signal processor or in a POSIX signal handler that does not rise when accessing memory is safe. Most likely, this will not be guaranteed in the future. And this is at the core of why the POSIX standard does not standardize it.
The cold fact is that there is a POSIX-compatible workaround for all templates that require access to an object with a static storage duration: a separate thread designed to process signals through sigwaitinfo() , and all these signals are blocked in all other flows. This stream is not limited to using the safe async-signal functions, as well as the limitations of other signal handlers. (If we look at the interaction between signal delivery and the code that it interrupts, even with handlers defined using the SA_RESTART flag, we can argue that the thread-based approach is the best of two.)
Simply put: since there are well-known workarounds and identifying safe access cases will be too complicated and limit future implementations, the POSIX standard does not standardize this traditional use case at all. This is not because it is not expected to work — quite the opposite; it works fine in all modern POSIXy systems, but because there are no difficulties and possible restrictions for determining safe access cases (except errno and volatile sig_atomic_t ) that require and have support from POSIX C compilers).