How to pass data from to a content process when it starts up?

Hi,

When a content process is started, a bunch of pref values are sent via
some -intPrefs/-boolPrefs/-stringPrefs arguments on the command line. This
is
ugly and limiting and causes multiple problems, so I'd like to find a
different
way to send this data.

The use case is pretty simple, because it's one way data transfer. The
important thing is that it must happen very early, i.e. before the normal
IPC
mechanism gets going.

I figured shared memory would be a reasonable way to do this, something like
the following.

- The parent sets up a shared memory segment, and writes some data to it.

- The parent spawns the child, and passes identifying information about the
  shared memory segment to the child (e.g. via the command line).

- The child gets the shared memory segment identifer, uses it to open the
  segment, and reads the data.

- The child disposes of the shared memory segment.

At first I tried using NSPR's shared memory functions, but they're not used
anywhere else in Firefox, and they have bugs, and shmget() is blocked by the
Linux sandbox.

So then I tried using base::SharedMemory instead, from
ipc/chromium/src/base/shared_memory.h, basically like this:

Parent:
    SharedMemory shm;
    shm.Create(name, size);
    shm.Open();
    shm.Map();
    char* p = shm.memory();
    ... write data to p ...
    ... launch child process, passing `name` via cmdline ...
    shm.Unmap();    // done automatically when shm is destroyed
    shm.Close();    // done automatically when shm is destroyed

Child:
    ... get `name` from the command line...
    SharedMemory shm;
    shm.Open(name);
    shm.Map();
    char* p = shm.memory();
    ... read data from p ...
    shm.Delete();   // this is a no-op on Windows
    shm.Unmap();    // done automatically when shm is destroyed
    shm.Close();    // done automatically when shm is destroyed

This works fine on Unix. If the shared memory file is closed by the parent
before it's opened by the child, that's ok, because it persists until it is
explicitly deleted.

But it doesn't work on Windows. On Windows Delete() does nothing. Instead, a
file mapping object is auto-deleted when its refcount falls to zero. So if
the
parent calls Close() before the child calls Open() -- which happens in
practice -- then the file mapping object is auto-deleted and the child
Open()
fails.

If I change the parent to heap-allocate `shm` so it's not auto-destroyed at
the
end of the function, things work out, but we'll end up with leaks: the
SharedMemory object, opened file view, the handle, and the file mapping will
all leak.

I then tried using SharedMemory::ShareToProcess(), but that requires that
the
child process already exist and the parent has its PID, which is a pain.

I then found this blog post from Raymond Chen:
https://blogs.msdn.microsoft.com/oldnewthing/20031211-00/?p=41543

It describes exactly what I want, but it requires using the bInheritHandle
parameter, which base::SharedMemory doesn't do. I could change it to do so,
but
then it looks like I'd need to deal with I then found
LaunchOptions::handles_to_inherit as well... and at this point I figure it's
worth asking for help!

Does anybody have suggestions about the best way to do this? Thanks.

Nick
0
Nicholas
2/14/2018 7:02:10 AM
mozilla.dev.platform 6337 articles. 0 followers. Post Follow

3 Replies
46 Views

Similar Articles

[PageSpeed] 6

Hi Nick,

SandboxBroker::AddHandleToShare was added to add the handles to the sandbox policy, before it was realised that we'd need to do this for the non-sandboxed process launch as well, hence LaunchOptions::handles_to_inherit.

I think we should change [1] to pass the LaunchOptions and then use them within SandboxBroker::LaunchApp to add the handles to the policy and get rid of SandboxBroker::AddHandleToShare.

Cheers,
Bob

[1] https://searchfox.org/mozilla-central/rev/d03ad8843e3bf2e856126bc53b0475c595e5183b/ipc/glue/GeckoChildProcessHost.cpp#1046

On Wednesday, 14 February 2018 07:02:43 UTC, Nicholas Nethercote  wrote:
> Hi,
> 
> When a content process is started, a bunch of pref values are sent via
> some -intPrefs/-boolPrefs/-stringPrefs arguments on the command line. This
> is
> ugly and limiting and causes multiple problems, so I'd like to find a
> different
> way to send this data.
> 
> The use case is pretty simple, because it's one way data transfer. The
> important thing is that it must happen very early, i.e. before the normal
> IPC
> mechanism gets going.
> 
> I figured shared memory would be a reasonable way to do this, something like
> the following.
> 
> - The parent sets up a shared memory segment, and writes some data to it.
> 
> - The parent spawns the child, and passes identifying information about the
>   shared memory segment to the child (e.g. via the command line).
> 
> - The child gets the shared memory segment identifer, uses it to open the
>   segment, and reads the data.
> 
> - The child disposes of the shared memory segment.
> 
> At first I tried using NSPR's shared memory functions, but they're not used
> anywhere else in Firefox, and they have bugs, and shmget() is blocked by the
> Linux sandbox.
> 
> So then I tried using base::SharedMemory instead, from
> ipc/chromium/src/base/shared_memory.h, basically like this:
> 
> Parent:
>     SharedMemory shm;
>     shm.Create(name, size);
>     shm.Open();
>     shm.Map();
>     char* p = shm.memory();
>     ... write data to p ...
>     ... launch child process, passing `name` via cmdline ...
>     shm.Unmap();    // done automatically when shm is destroyed
>     shm.Close();    // done automatically when shm is destroyed
> 
> Child:
>     ... get `name` from the command line...
>     SharedMemory shm;
>     shm.Open(name);
>     shm.Map();
>     char* p = shm.memory();
>     ... read data from p ...
>     shm.Delete();   // this is a no-op on Windows
>     shm.Unmap();    // done automatically when shm is destroyed
>     shm.Close();    // done automatically when shm is destroyed
> 
> This works fine on Unix. If the shared memory file is closed by the parent
> before it's opened by the child, that's ok, because it persists until it is
> explicitly deleted.
> 
> But it doesn't work on Windows. On Windows Delete() does nothing. Instead, a
> file mapping object is auto-deleted when its refcount falls to zero. So if
> the
> parent calls Close() before the child calls Open() -- which happens in
> practice -- then the file mapping object is auto-deleted and the child
> Open()
> fails.
> 
> If I change the parent to heap-allocate `shm` so it's not auto-destroyed at
> the
> end of the function, things work out, but we'll end up with leaks: the
> SharedMemory object, opened file view, the handle, and the file mapping will
> all leak.
> 
> I then tried using SharedMemory::ShareToProcess(), but that requires that
> the
> child process already exist and the parent has its PID, which is a pain.
> 
> I then found this blog post from Raymond Chen:
> https://blogs.msdn.microsoft.com/oldnewthing/20031211-00/?p=41543
> 
> It describes exactly what I want, but it requires using the bInheritHandle
> parameter, which base::SharedMemory doesn't do. I could change it to do so,
> but
> then it looks like I'd need to deal with I then found
> LaunchOptions::handles_to_inherit as well... and at this point I figure it's
> worth asking for help!
> 
> Does anybody have suggestions about the best way to do this? Thanks.
> 
> Nick

0
bowen
2/14/2018 9:23:03 AM
Perhaps we should also change the various GeckoChildProcessHost Launch methods to accept LaunchOptions or a similar structure instead of aExtraOpts.

On Wednesday, 14 February 2018 09:23:05 UTC, bo...@mozilla.com  wrote:
> Hi Nick,
> 
> SandboxBroker::AddHandleToShare was added to add the handles to the sandbox policy, before it was realised that we'd need to do this for the non-sandboxed process launch as well, hence LaunchOptions::handles_to_inherit.
> 
> I think we should change [1] to pass the LaunchOptions and then use them within SandboxBroker::LaunchApp to add the handles to the policy and get rid of SandboxBroker::AddHandleToShare.
> 
> Cheers,
> Bob
> 
> [1] https://searchfox.org/mozilla-central/rev/d03ad8843e3bf2e856126bc53b0475c595e5183b/ipc/glue/GeckoChildProcessHost.cpp#1046
> 
> On Wednesday, 14 February 2018 07:02:43 UTC, Nicholas Nethercote  wrote:
> > Hi,
> > 
> > When a content process is started, a bunch of pref values are sent via
> > some -intPrefs/-boolPrefs/-stringPrefs arguments on the command line. This
> > is
> > ugly and limiting and causes multiple problems, so I'd like to find a
> > different
> > way to send this data.
> > 
> > The use case is pretty simple, because it's one way data transfer. The
> > important thing is that it must happen very early, i.e. before the normal
> > IPC
> > mechanism gets going.
> > 
> > I figured shared memory would be a reasonable way to do this, something like
> > the following.
> > 
> > - The parent sets up a shared memory segment, and writes some data to it.
> > 
> > - The parent spawns the child, and passes identifying information about the
> >   shared memory segment to the child (e.g. via the command line).
> > 
> > - The child gets the shared memory segment identifer, uses it to open the
> >   segment, and reads the data.
> > 
> > - The child disposes of the shared memory segment.
> > 
> > At first I tried using NSPR's shared memory functions, but they're not used
> > anywhere else in Firefox, and they have bugs, and shmget() is blocked by the
> > Linux sandbox.
> > 
> > So then I tried using base::SharedMemory instead, from
> > ipc/chromium/src/base/shared_memory.h, basically like this:
> > 
> > Parent:
> >     SharedMemory shm;
> >     shm.Create(name, size);
> >     shm.Open();
> >     shm.Map();
> >     char* p = shm.memory();
> >     ... write data to p ...
> >     ... launch child process, passing `name` via cmdline ...
> >     shm.Unmap();    // done automatically when shm is destroyed
> >     shm.Close();    // done automatically when shm is destroyed
> > 
> > Child:
> >     ... get `name` from the command line...
> >     SharedMemory shm;
> >     shm.Open(name);
> >     shm.Map();
> >     char* p = shm.memory();
> >     ... read data from p ...
> >     shm.Delete();   // this is a no-op on Windows
> >     shm.Unmap();    // done automatically when shm is destroyed
> >     shm.Close();    // done automatically when shm is destroyed
> > 
> > This works fine on Unix. If the shared memory file is closed by the parent
> > before it's opened by the child, that's ok, because it persists until it is
> > explicitly deleted.
> > 
> > But it doesn't work on Windows. On Windows Delete() does nothing. Instead, a
> > file mapping object is auto-deleted when its refcount falls to zero. So if
> > the
> > parent calls Close() before the child calls Open() -- which happens in
> > practice -- then the file mapping object is auto-deleted and the child
> > Open()
> > fails.
> > 
> > If I change the parent to heap-allocate `shm` so it's not auto-destroyed at
> > the
> > end of the function, things work out, but we'll end up with leaks: the
> > SharedMemory object, opened file view, the handle, and the file mapping will
> > all leak.
> > 
> > I then tried using SharedMemory::ShareToProcess(), but that requires that
> > the
> > child process already exist and the parent has its PID, which is a pain.
> > 
> > I then found this blog post from Raymond Chen:
> > https://blogs.msdn.microsoft.com/oldnewthing/20031211-00/?p=41543
> > 
> > It describes exactly what I want, but it requires using the bInheritHandle
> > parameter, which base::SharedMemory doesn't do. I could change it to do so,
> > but
> > then it looks like I'd need to deal with I then found
> > LaunchOptions::handles_to_inherit as well... and at this point I figure it's
> > worth asking for help!
> > 
> > Does anybody have suggestions about the best way to do this? Thanks.
> > 
> > Nick

0
bowen
2/14/2018 9:35:48 AM
Thank you! That was exactly the info I needed, and I have it working
locally.

However, I didn't do exactly as you suggested -- passing
LaunchOptions to Sandbroker::LaunchApp dragged me into #include hell:

- base::LaunchOptions must be visible in
  security/sandbox/win/src/sandboxbroker/sandboxBroker.{h,cpp}, which
requires
  including ipc/chromium/src/base/process_util.h.

- We end up including headers from two significantly different versions of
the
  Chromium code: ipc/chromium/base/process_util.h and
  security/sandbox/chromium/sandbox/win/src/src/sandbox.h and we hit trouble
  with repeated declarations, e.g.:
  - DISALLOW_COPY_AND_ASSIGN, from ipc/chromium/src/base/basictypes.h and
    security/sandbox/chromium/base/macros.h
  - DISALLOW_IMPLICIT_CONSTRUCTORS, likewise
  - LinkerInitialized
  - etc.

To avoid this, I chose to pass in a std::vector<HANDLE> instead of a
LaunchOptions to SandboxBroker::LaunchApp.

Nick

On Wed, Feb 14, 2018 at 8:23 PM, <bowen@mozilla.com> wrote:

> Hi Nick,
>
> SandboxBroker::AddHandleToShare was added to add the handles to the
> sandbox policy, before it was realised that we'd need to do this for the
> non-sandboxed process launch as well, hence LaunchOptions::handles_to_
> inherit.
>
> I think we should change [1] to pass the LaunchOptions and then use them
> within SandboxBroker::LaunchApp to add the handles to the policy and get
> rid of SandboxBroker::AddHandleToShare.
>
> Cheers,
> Bob
>
> [1] https://searchfox.org/mozilla-central/rev/
> d03ad8843e3bf2e856126bc53b0475c595e5183b/ipc/glue/
> GeckoChildProcessHost.cpp#1046
>
> On Wednesday, 14 February 2018 07:02:43 UTC, Nicholas Nethercote  wrote:
> > Hi,
> >
> > When a content process is started, a bunch of pref values are sent via
> > some -intPrefs/-boolPrefs/-stringPrefs arguments on the command line.
> This
> > is
> > ugly and limiting and causes multiple problems, so I'd like to find a
> > different
> > way to send this data.
> >
> > The use case is pretty simple, because it's one way data transfer. The
> > important thing is that it must happen very early, i.e. before the normal
> > IPC
> > mechanism gets going.
> >
> > I figured shared memory would be a reasonable way to do this, something
> like
> > the following.
> >
> > - The parent sets up a shared memory segment, and writes some data to it.
> >
> > - The parent spawns the child, and passes identifying information about
> the
> >   shared memory segment to the child (e.g. via the command line).
> >
> > - The child gets the shared memory segment identifer, uses it to open the
> >   segment, and reads the data.
> >
> > - The child disposes of the shared memory segment.
> >
> > At first I tried using NSPR's shared memory functions, but they're not
> used
> > anywhere else in Firefox, and they have bugs, and shmget() is blocked by
> the
> > Linux sandbox.
> >
> > So then I tried using base::SharedMemory instead, from
> > ipc/chromium/src/base/shared_memory.h, basically like this:
> >
> > Parent:
> >     SharedMemory shm;
> >     shm.Create(name, size);
> >     shm.Open();
> >     shm.Map();
> >     char* p = shm.memory();
> >     ... write data to p ...
> >     ... launch child process, passing `name` via cmdline ...
> >     shm.Unmap();    // done automatically when shm is destroyed
> >     shm.Close();    // done automatically when shm is destroyed
> >
> > Child:
> >     ... get `name` from the command line...
> >     SharedMemory shm;
> >     shm.Open(name);
> >     shm.Map();
> >     char* p = shm.memory();
> >     ... read data from p ...
> >     shm.Delete();   // this is a no-op on Windows
> >     shm.Unmap();    // done automatically when shm is destroyed
> >     shm.Close();    // done automatically when shm is destroyed
> >
> > This works fine on Unix. If the shared memory file is closed by the
> parent
> > before it's opened by the child, that's ok, because it persists until it
> is
> > explicitly deleted.
> >
> > But it doesn't work on Windows. On Windows Delete() does nothing.
> Instead, a
> > file mapping object is auto-deleted when its refcount falls to zero. So
> if
> > the
> > parent calls Close() before the child calls Open() -- which happens in
> > practice -- then the file mapping object is auto-deleted and the child
> > Open()
> > fails.
> >
> > If I change the parent to heap-allocate `shm` so it's not auto-destroyed
> at
> > the
> > end of the function, things work out, but we'll end up with leaks: the
> > SharedMemory object, opened file view, the handle, and the file mapping
> will
> > all leak.
> >
> > I then tried using SharedMemory::ShareToProcess(), but that requires
> that
> > the
> > child process already exist and the parent has its PID, which is a pain.
> >
> > I then found this blog post from Raymond Chen:
> > https://blogs.msdn.microsoft.com/oldnewthing/20031211-00/?p=41543
> >
> > It describes exactly what I want, but it requires using the
> bInheritHandle
> > parameter, which base::SharedMemory doesn't do. I could change it to do
> so,
> > but
> > then it looks like I'd need to deal with I then found
> > LaunchOptions::handles_to_inherit as well... and at this point I figure
> it's
> > worth asking for help!
> >
> > Does anybody have suggestions about the best way to do this? Thanks.
> >
> > Nick
>
> _______________________________________________
> dev-platform mailing list
> dev-platform@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-platform
>
0
Nicholas
2/16/2018 12:26:20 AM
Reply: