There are perfectly legitimate situations in which the code which calls exec() may not know which file descriptors need to be open. For example, let's say you have a shell script which takes a filename parameter and passes it to some other executable for processing. You run it as follows: "./my_script <(some_cmdline)". This causes the (bash) shell to create a pipe file descriptor, say FD 3, and pass it as "/dev/fd/3" to the script. In order for this to work, the script *must* preserve FD 3 when calling exec() for the lower-level executable so that the child's open() call can access the original descriptor. However, without knowing how the script will be called there is no way to know that FD 3 will even be open, much less that the executable will need access to it after the exec() call.
This can come up not only in shell scripts, but in any case where you might pass a filename received on the command-line to a child process. I would say that the current Linux model of marking file descriptors as "current process only" or "inheritable" in the open() call is the correct one, apart from the choice of default. Once an FD has been designated for use by child processes it should remain open by default across fork()/exec() calls, unless there is a compelling reason to close it. (I would, however, be in favor of a safe and simple way to explicitly close all but a designated set of descriptors without performing a close() syscall for potentially millions of possible FDs.)