how to make stderr have file descriptor 3 on linux and still behave properly there
When you fork exec, the only I/O units that the process keeps are the file descriptors. Whatever you have for file descriptor 0 will be stdin/STDIN after exec, no matter what you were using it for and no matter what conventions you set up for how you use it. Whatever your conventions are, they don't change how fork/exec work nor how the process initialization sets up stdin in C (which is what Perl uses to set up its STDIN).
So if you have conventions that make it so your code will use fd 3 for stderr when you've set it up for that, those have no impact on what the standard process initialization code will do when you fork/exec nor how the child process will set up stderr based on what fd 2 was in the parent process (which will also mean that your code would use fd 2 as stderr when the process is freshly initialized). So your conventions can't "fix" Perl so that, when somebody buries fd 1 with local(*STDOUT) Perl can somehow cause child processes to not use that buried fd 1 as their stdout.