This is not a WebGL 2 restriction. This is fundamentally how GPUs and their 3D rendering APIs all work.
Because they're designed to render many triangles and fragments in parallel, they structurally eliminate data dependencies between these operations. A particular chunk of memory is either read-only (texture) or write-only (frame buffer) for the full rendering pass.
If you want to change to read some memory, all pending writes to it need to finish first (a full pipeline flush). This would be unimaginably slow if it had to happen for every pixel — your super fast parallel processor would effective become serial. So the APIs are designed so you can't write code that could incur such a serial penalty.
Every post-processing system you've ever seen — yes, even that one that ran at ridiculously high frame rates — works using the "ping pong" approach you describe. You render the scene to one buffer, then read that buffer to apply post effects and write them to a second buffer, which is ultimately shown to the screen.
Your assumption that this would be unreasonably slow due to "copying" is not quite right. Whether writing to a new buffer or overwriting the value in-place, we still have to do the same work:
Sampling a buffer
Calculating a new colour based on the value read
Writing that colour to a buffer
The only difference is whether the write destination is the same as the read destination. These are cached separately on GPUs so you don't incur a performance penalty from writing to a different spot of memory — in fact you gain performance because there's no need to interleave loads and stores from the same memory chunk.
Modern games will often have many of these off-screen buffers, used for things like deferred rendering G-buffers, previous frames' output for motion blur, screenspace reflection, or temporal antialiasing, and all sorts of other effects.
It's not free, but generally fill rate (the number of times you write a value out — whether to the same place or a new one) is more likely to be a bottleneck on modern GPUs than the amount of memory used for render targets.