No, the OOM killer is not really related to overcommit at all. You could just send a SIGBUS in response to a failed CoW event instead of having an OOM killer.
The issue is that if you don’t have an OOM killer and just return ENOMEM to allocations, then RAM can become exhausted and then ANY application that wants to allocate memory will see allocation failures.
This means that you may not be able to, say, switch to another window with Alt+Tab because the window manager would need to allocate memory to display the window list, but can’t because RAM is exhausted. Likewise you probably can’t SSH into the system because sshd’s allocations will fail, can’t type into terminals because they can’t allocate memory to process the text you typed, etc.
Unless all programs and constantly tested are written to cope with a system with exhausted RAM (and obviously most software is never tested for allocation failures and just crashes or aborts on allocation failures), you can’t have a functioning system where memory allocation fails and there are no memory quotas.
Conversely, with an OOM killer, when you press Alt+Tab and the kernel can’t find any RAM to give to the window manager, it will notice that 99% of the RAM is being hogged by another process and kill that other process, resulting in the window manager getting its memory and the system being usable (unless there’s a memory leak in the window manager, in which case it will be the one killed).
There is another problem I didn’t mention in the previous post, which is that this behavior also happens within a single process.
Even scrolling or resizing a browser window may fail, since allocation may be required to do so (in case of CPU painting, this is even unavoidable since when resizing a window you need to allocate a larger back buffer to render the content it, unless you want to, say, preallocate a 16384x16384 back buffer and not support any window larger than that, wasting 1GB of RAM per tab in the process).
The only real usefulness for fallible memory allocation (other than embedded use case where the whole system is written with allocation failures in mind) is thus to reject unusually large allocations without aborting the whole process, and it’s not that useful since while such large allocations will be rejected, a huge number of small allocations will also effectively require an abort due to issue described above.