NtCreateProcess redux
Daniel Colascione
dan.colascione@gmail.com
Mon Apr 25 22:05:00 GMT 2011
On 4/25/2011 12:33 PM, Ryan Johnson wrote:
> I know that folks have looked before into NtCreateProcess as a way of
> doing a real fork() in cygwin, but it's very unclear from the various
> list archives why it's still a bad idea today, other than its being
> undocumented.
It's a bad idea because it doesn't work. You can certainly create a
forked child with NtCreateProcess, but without being able to connect it
to csrss and the rest of the win32 subsystem, this new process is
useless. NtCreateProcess-fork works for Interix because it has its own
NT subsystem, but Cygwin has to live within win32, and I don't think
creating a new subsystem is feasible for anyone without access to the NT
source.
> If there's no interest in revisiting NtCreateProcess, I have some really
> crazy ideas to offer, but they would still leave us copying whole
> address spaces and trying to outsmart Windows along the way.
As (I think) cgf once said, Cygwin has been around for a long time, and
most of the crazy ideas that didn't make it into the code were weighed,
judged, and found wanting for one reason or another. I have some crazy
ideas of my own, mostly involving using shared sections instead of
NtCopyVirtualMemory to duplicate memory, but I haven't had time to
implement them[1].
As far as the address space issue goes: when NT creates a new process,
the loader, in ntdll, gains control before the entry point is ever
called, and this loader is what's responsible for the initial VM layout.
Because ntdll is a "known dll", you can't replace it with a friendlier
implementation. After the loader completes its work, the kernel does
some black magic and resets the initial thread's stack so that it begins
executing in the ntdll thread startup routine, so you never actually
_see_ the loader executing.
The only thing that might have a chance of working is to unload
everything except user32, kernel32, and a few other components, then
start fresh with a more constrained module loading strategy.
[1] If process A has section S, the contents of which we'd like to
duplicate in child-process B as S', and B inherits a handle to S, it's
slower to remap S in B and memcpy it to S' than it is to just initialize
S' from A's address space with NtCopyVirtualMemory. But that's the
single-threaded case. It turns out that if we have the child map S
somewhere and have one thread touch S[0], S'[0], S[4096], S'[4096], etc.
while another thread does a mempcy from S to S', we handily beat the
NtCopyVirtualMemory approach.
More information about the Cygwin-developers
mailing list