From: Ersek, Laszlo on 18 Apr 2010 22:25 On Sun, 18 Apr 2010, Joshua Maurice wrote: > how to make sure I don't get resources leaking across fork + exec calls No resource can "leak" through fork()/exec() calls that don't exist. Main program connects to inetd. Inetd forks and executes single-shot wrapper daemon. Daemon has (at least) two phases, init phase and servicing phase. In the init phase, before initializing the wrapped library, it sets FD_CLOEXEC on fd's 0 and 1, redirects fd 2 to a log file and sets FD_CLOEXEC on it. As the final step, it initializes the library. Then it starts taking requests on stdin and writing answers to stdout. There is no ancestry between your main process and the wrapper process. If the library has constructor functions, then don't link it into the wrapper daemon at build time; open it with dlopen() after setting FD_CLOEXEC on [012]. lacos
From: Joshua Maurice on 18 Apr 2010 22:31 On Apr 18, 7:25 pm, "Ersek, Laszlo" <la...(a)caesar.elte.hu> wrote: > On Sun, 18 Apr 2010, Joshua Maurice wrote: > > how to make sure I don't get resources leaking across fork + exec calls > > No resource can "leak" through fork()/exec() calls that don't exist. > > Main program connects to inetd. Inetd forks and executes single-shot > wrapper daemon. Daemon has (at least) two phases, init phase and servicing > phase. In the init phase, before initializing the wrapped library, it sets > FD_CLOEXEC on fd's 0 and 1, redirects fd 2 to a log file and sets > FD_CLOEXEC on it. As the final step, it initializes the library. Then it > starts taking requests on stdin and writing answers to stdout. There is no > ancestry between your main process and the wrapper process. > > If the library has constructor functions, then don't link it into the > wrapper daemon at build time; open it with dlopen() after setting > FD_CLOEXEC on [012]. And again, for the rest of the world which may not have complete control over the process, what do those people do? My team in my company writes a library which is used by other teams, and we also use libraries written by other companies. It's kind of difficult to do what you suggest in the real world for general purpose libraries, both users and writers. Then some of us use C++ and not C, so dlopen because a bit less practical as well when using or writing a C++ library. Many developers don't actually control the entire code base, nor all of the entry and exit points, and instead have to work with other pieces of code, some quite old, and possibly not the most correct. There should be simple code to achieve the desired semantics: to create a new process from an executable image and without leaking any resources into that new process. I suppose that as a practical matter, it will only come up if you do unbounded forking, but it still rubs me the wrong way.
From: David Given on 20 Apr 2010 14:07 On 20/04/10 01:15, Ersek, Laszlo wrote: > On Mon, 19 Apr 2010, David Given wrote: [...] >> So, basically, you're saying, don't use fopen()? In any multithreaded >> apps? And in any *library* which might be used by a multithreaded app? > > s/fopen/fork, I guess. I did mean fopen(): if I have a multithreaded application where one thread, *any* thread, creates a file descriptor, and another thread, *any* thread, calls exec(), then there's a potential problem. [...] > Reimplement open(), forward request to actual (or rather, next, as in > RTLD_NEXT) open(), but add O_CLOEXEC. If you're lucky, fopen() / > freopen() / whatever will go through open(). [...] > $ LD_PRELOAD=./cloexec.so strace touch testfile 2>&1 | grep O_WRONLY > open("testfile", O_WRONLY|O_CREAT|O_NCTTY|O_NONBLOCK|O_CLOEXEC, 0666) = 3 Hmm. I'd forgotten you can do that. It's not entirely reliable --- if the program calls the syscall directly rather than going through libc, then the hack won't work. It's a promising approach, though, and is likely to work in most cases. [...] > Now do something similar with socket(): [...] > Call pipe2() instead of pipe(): Ah, but it's not me that's calling socket() or pipe() --- it's a third-party library that's not under my control. And setting the CLOEXEC flag for socket() as described still has a (rare, but potential) race condition between socket() returning and the call to fcntl() immediately afterward. Again, this will probably work in most cases, but there's still that rare situation where something horrible will happen that makes me uneasy. If only there were a nice portable way of telling the kernel that all file descriptors for my process should be CLOEXEC until further notice... -- ┌─── dg@cowlark.com ───── http://www.cowlark.com ───── │ │ "In the beginning was the word. │ And the word was: Content-type: text/plain" --- Unknown sage
From: Casper H.S. Dik on 19 Apr 2010 04:30 William Ahern <william(a)wilbur.25thandClement.com> writes: >The Solaris closefrom() man page suggests it may be using /proc, which would >not be cool for chroot'd applications. Is this the case? OpenBSD's >closefrom() is a system call, which seems more reasonable given the most >common use for this is as a step immediately before or upon a fork or exec. Correct. It doesn't use a specific system call. Casper -- Expressed in this posting are my opinions. They are in no way related to opinions held by my employer, Sun Microsystems. Statements on Sun products included here are not gospel and may be fiction rather than truth.
From: Jonathan de Boyne Pollard on 20 Apr 2010 06:04
> >> >> [... usual trolling by Rainer Weikusat ...] >> > Hmm. I reviewed the win32 documentation. It appears that I was > mistaken, and that it has the exact same problem. You inherit all > (file) handles or no (file) handles, nothing inbetween as any sane use > of fork + exec or CreateProcess would want. > Go and read the message that I just posted. It mentions the relevant Win32 API and C library functions, and even hyperlinks to the MSDN documentation for one of them. |