Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250913124834.GS1827@brightrain.aerifal.cx>
Date: Sat, 13 Sep 2025 08:48:34 -0400
From: Rich Felker <dalias@...c.org>
To: Demi Marie Obenour <demiobenour@...il.com>
Cc: musl@...ts.openwall.com
Subject: Re: closefrom and close_range wrappers

On Fri, Sep 12, 2025 at 09:40:22PM -0400, Demi Marie Obenour wrote:
> Would it make sense for musl to provide close_range and
> closefrom?  The main uses of them are to close unwanted
> FDs after a fork and before exec.  close_range() can also
> be used to mark them close-on-exec instead.
> 
> The main advantage of closefrom() is that it is portable
> to more systems.  Closing file descriptors one doesn't own
> is not an issue because the code that does own these FDs
> will never get a chance to run.  In particular, calling any
> stdio functions that use anything but stdin, stdout, and
> stderr would be UB.  stdin, stdout, and stderr use FD 0, 1,
> and 2, respectively, and those are almost always left open.
> 
> Yes, this is all a consequence of fork()/exec() being an
> extremely poor API, but I don't know of a better solution.
> There are third-party libraries (including glib and wlroots)
> that expect one to either use an API like this or emulate it
> by scanning /proc/self/fd (yuck).  Yes, these libraries should
> be atomically setting O_CLOEXEC, but their authors disagree
> and maintaining downstream forks is not practical.

I need to look back at where we left this last time it was discussed.

As Laurent replied, per the standard (POSIX) these operations are
fundamentally programming errors. Closing a fd that "doesn't belong to
you" (that may be some implementation detail of the POSIX
implementation or some part of the execution environment hosting your
application) produces undefined behavior. Nonetheless, people do want
to do it for various reasons and the existing ways of doing it without
the syscall are "worse".

One question that naturally arises whenever adding an interface like
this is what we do on kernel versions that lack it. Do we fail it and
leave the application to figure out what to do when it fails, or
emulate it? If taking the failing option, there's not a whole lot of
value over the caller just using syscall() directly, but maybe it
makes sense. I think there are some macro constants needed to use
the syscall that potentially make it difficult to do without libc
exposing anything (possible conflicts pulling in kernel headers).

I'll try to find past discussion of this. If anyone else has it handy
to cite, go ahead.

Rich

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.