Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tidy up handling of intermediate pid fds #665

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

smcv
Copy link
Collaborator

@smcv smcv commented Oct 30, 2024

  • Use PIPE_READ_END, PIPE_WRITE_END to clarify use of a socketpair

    Both sockets in the socket pair are technically bidirectional, but we
    are using them as though they were unidirectional, with the same
    convention as pipe(): the first socket in the array is used for reading,
    and the second is for writing.

  • Close unused ends of intermediate_pids_sockets sooner

    Instead of making this conditional and keeping track of the correct
    condition under which to call it, we can use cleanup_fdp(), which is
    a no-op when called with a pointer to a negative number, to close the
    socket unconditionally.

    In the parent bwrap monitor process (outside the sandbox), we never
    want to use the write end (which is reserved for the child), so we
    can and should close it as soon as we have forked.

    Conversely, in the child process, we never want to use the read end
    (which is reserved for the parent), so we should close that as soon
    as we know we are in the child.

  • Use cleanup_fd to close intermediate pid sockets

    This closes the fd and sets the variable to -1 as a single operation,
    which is easier to reason about because it does not leave any variables
    containing dangling references to invalid fds.


cc @refi64 @WGH- @mcatanzaro

I think #576 would have better clarity if it was rebased on this and used similar techniques. Reviews welcome, even from non-maintainers.

bubblewrap.c Show resolved Hide resolved
Both sockets in the socket pair are technically bidirectional, but we
are using them as though they were unidirectional, with the same
convention as pipe(): the first socket in the array is used for reading,
and the second is for writing.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Instead of making this conditional and keeping track of the correct
condition under which to call it, we can use cleanup_fdp(), which is
a no-op when called with a pointer to a negative number, to close the
socket unconditionally.

In the parent bwrap monitor process (outside the sandbox), we never
want to use the write end (which is reserved for the child), so we
can and should close it as soon as we have forked.

Conversely, in the child process, we never want to use the read end
(which is reserved for the parent), so we should close that as soon
as we know we are in the child.

Signed-off-by: Simon McVittie <smcv@collabora.com>
This closes the fd and sets the variable to -1 as a single operation,
which is easier to reason about because it does not leave any variables
containing dangling references to invalid fds.

Signed-off-by: Simon McVittie <smcv@collabora.com>
@@ -3143,12 +3143,12 @@ main (int argc,
if (pid != 0)
{
/* Parent, outside sandbox, privileged (initially) */
cleanup_fdp (&intermediate_pids_sockets[PIPE_WRITE_END]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about assigning -1 to the passe pointer in cleanup_fdp(), so a potential second close() call won't close an uninvolved descriptor.

For the cleanup attribute annotated variables compilers should be able to optimize the assignment out.

Another unrelated thought: systemd uses a safe_close() wrapper instead of directly close(3) to assert EBADF was not returned, to spot file descriptor mixups, see https://github.com/systemd/systemd/blob/v256.7/src/basic/fd-util.c#L59.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about assigning -1 to the passe pointer in cleanup_fdp()

Oh... I thought it already did that, but in fact it doesn't, so this PR needs redoing with either a different function or an augmented version of cleanup_fdp() (as do #666 and #667).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This closes the fd and sets the variable to -1 as a single operation

[citation needed]

smcv added a commit to smcv/bubblewrap that referenced this pull request Oct 30, 2024
This avoids leaving dangling references to fds that no longer exist,
clarifying ownership.

This commit does not cover the socket pairs used to transfer the pid
of a descendant process (see containers#665 for that) and privilege-separated
operations (see containers#666).

Signed-off-by: Simon McVittie <smcv@collabora.com>
@smcv smcv marked this pull request as draft October 30, 2024 15:05
@smcv
Copy link
Collaborator Author

smcv commented Oct 30, 2024

Another unrelated thought: systemd uses a safe_close() wrapper instead of directly close(3) to assert EBADF was not returned, to spot file descriptor mixups,

GLib does similarly, in g_close(). A potential problem with that technique is that the assertion is not going to be async-signal-safe, which is troublesome in a process that forks. On the other hand, bubblewrap is single-threaded, so it's usually OK for us to call non-async-signal-safe functions after fork().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants