Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <5142262A.4060606@eservices.virginia.edu>
Date: Thu, 14 Mar 2013 15:34:02 -0400
From: Zvi Gilboa <zg7s@...rvices.virginia.edu>
To: <musl@...ts.openwall.com>
Subject: Re: question: hard-coded file descriptors in stdin/stdout/stderr

 >> how do you implement fork?

My main objective was to be efficient and avoid "ugliness," that is, 
eliminate all improbabilities on the parent's side (and obviously also 
the child's), and accordingly ensure that the state following each step 
in the implementation is exactly what I expect it to be.  If you are 
familiar with the Cygwin implementation, then this must ring a bell with 
respect to LoadLibrary (kernel32.dll) and the fact that does not allow 
you to specify the library's base address.

It is important to note that one main problem with the Cygwin 
implementation (which I believe has been acknowledged by their 
developers as well), is that it does not "disable" the loading of 
hard-linked (compile-time linked) libraries, and thus has no control 
over the bases addresses of these modules.  This forces the parent to 
check "after the fact" whether the base addresses of modules in the 
parent and child agree, and accordingly "die" if they don't... with 
respect to dynamically-linked libraries, the Cygwin fork() tries very 
hard to "help" Windows choose the right base-address, yet with no 
guarantee as for the outcome of that effort (so more improbability and 
after-the-fact checking).  For these and several other reasons, I have 
taken a rather different approach.

Following that very long introduction, here is an outline of what I 
do... quite a few steps, yet each of them is extremely fast and fully 
determinable (note that I'm only using the Native API, so "create 
process" refers to NtCreateProcess in ntdll.dll, not to the various APIs 
in kernel32.dll)


0) normalize the heap and perform some basic sanity checks

1) "backup" the parent's import table, mark all relevant handles as 
inheritable

2) nullify the parent's import table, then obtain handle to its 
executable section (ZwOpenSection)

3) create process with NO console attached --> using the above section, 
then adjust necessary settings (process group [aka "job"], etc.)

4) clone process sections (enumerate the sections, map them to the 
child, efficient crc32 checks to verify synchronization allow us to take 
advantage of the speed of mapping, and accordingly use 
ZwWriteProcessMemory only where necessary)

--> the main advantage of using ZwOpenSection and ZwMapViewOfSection 
(which lies at the heart of the whole thing) is that it allows us to 
specify the section's (aka module's, dll's) base address.

5) clone process thread with new EIP (&child)
--> this clones the current thread's stack and sets the EIP in the child 
to &child

6) resume thread:
--> the child is in child(), waiting for an event to be signaled (to be 
sure, at hand is an NT signal)
--> since child has resumed, we now have access to all features of 
PEB_LDR_DATA
--> nonetheless, the child is waiting for us, and could wait forever -- 
until we either signal the event, or terminate it
--> which guarantees accurate knowledge of the parent about the state of 
the child process

7) update child's PEB_LDR_DATA, and gracefully "restore" its import table
--> so that GetModuleHandle returns a HANDLE
--> so that resources can be retrieved
--> and so that DllMain gets called whenever a new thread is being created

* the mapped sections (modules) are already there, so there is no need 
to call LdrLoadLibrary :)
* as long as the parent and child have the same loaded modules, no true 
knowledge of what LdrLoadLibrary does is necessary.  This is true since 
the entire PEB_LDR_DATA resides in the process's address space.
* in other words: we do not implement LdrLoadLibrary, but rather ensure 
that in the end of the day, there would be no difference between what we 
did, and what LdrLoadLibrary would have done...

8) clone process heap (not for the faint of heart)

9) re-attach to console (if needed)

10) signal event so that the child process continues to execute

11) child: set EAX to 0, restore esp, ret --> so that fork() returns zero

12) parent: restore the import table (this is not needed in and of 
itself, but better safe than sorry)

13) parent: return child_pid as required by fork()


AND THAT'S IT!:)  As said above, many steps, yet all are extremely fast 
and fully determinable.


 >> so you need to do hacks if you want posix
on windows


YES, indeed.  Although I'd rather call them "translations":)  But again, 
it would be nice if I didn't have to worry about about hard-coded 
numbers.  Correct use of the pipes is possible as well.  It is actually 
quite easy to duplicate handles on Windows, as well as connect multiple 
processes to the same console and/or console handles.  One simply needs 
to experiment with the low-level functions (for instance 
WriteConsoleInput, which in fact has some basic documentation on MSDN).


So, all in all taken, does that mean my suggestion has been accepted?








On 03/14/2013 02:17 PM, Szabolcs Nagy wrote:
> * Zvi Gilboa <zg7s@...rvices.virginia.edu> [2013-03-14 13:51:19 -0400]:
>> ... since you are asking...  inspired by musl-libc, I am currently
>> writing a win32/win64 open-source library that implements/provides
>> POSIX system calls (see note below).  I believe that having a
>> powerful libc with an MIT license available on Windows would
>> actually be of great value to the open source community for all
> ok
>
>> The main issue here is that the standard file descriptors on Windows
>> are -10 (STD_INPUT_HANDLE), -11 (STD_OUTPUT_HANDLE), and -12
> ouch
>
> i think windows file handles have very
> different semantics than posix fds
> (dup2, fork,..)
>
> so you need to do hacks if you want posix
> on windows
>
>> * as for psxcalls: this is a C library, that exclusively uses the
>> Native API.  It attaches to ntdll.dll during run-time, and can thus
>> be compiled as a "native" static library with no external
>> dependencies.  While it is currently in its very initial stage (and
>> not yet online), the major "obstacle" features -- including fork()
>> -- have already been implemented.  To remove all doubts, I am aware
>> of Cygwin's existence, yet am looking for a high-performance
>> solution that would be both "clean"
>> (psxcalls-->libc-->user-library-or-application), and flexibly
>> licensed.
> how do you implement fork?
>
> (windows version of some tools actually allow redirecting
> io of child processes i wonder how your fork would interact
> with that, eg gawk can read/write to "/dev/fd/n" so what
> would happen if you fork such a child, can you communicate
> with it with pipes?)
>
> (at some point there was discussion about porting musl to
> various systems including windows and the conclusion was
> that it should be done on the syscall layer and fork would
> be ugly and slow no matter what you do)

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.