[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] libxl: Protect fds with CLOEXEC even with forking threads
# HG changeset patch # User Ian Jackson <ian.jackson@xxxxxxxxxxxxx> # Date 1334150058 -3600 # Node ID 8a88f6e209dbecfeb0426970540df22351f74fe4 # Parent 4fef2701410b48ad3de422ed4e9a6b4cee527195 libxl: Protect fds with CLOEXEC even with forking threads We introduce a new "carefd" concept, which relates to fds that we care about not being inherited by long-lived children. As yet we do not use this anywhere in libxl. Until all locations in libxl which make such fds are converted, libxl__postfork may not work entirely properly. If these locations do not use O_CLOEXEC (or use calls for which there is no O_CLOEXEC) then multithreaded programs may not work properly. This introduces a new API call libxl_postfork_child_noexec which must be called by applications which make long-running non-execing children. Add the appropriate call to xl's postfork function. Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx> Committed-by: Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx> --- diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/Makefile --- a/tools/libxl/Makefile Wed Apr 11 14:14:17 2012 +0100 +++ b/tools/libxl/Makefile Wed Apr 11 14:14:18 2012 +0100 @@ -53,7 +53,7 @@ LIBXL_LIBS += -lyajl LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \ libxl_internal.o libxl_utils.o libxl_uuid.o libxl_json.o \ - libxl_qmp.o libxl_event.o $(LIBXL_OBJS-y) + libxl_qmp.o libxl_event.o libxl_fork.o $(LIBXL_OBJS-y) LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) $(CFLAGS_libxenstore) $(CFLAGS_libblktapctl) -include $(XEN_ROOT)/tools/config.h diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/libxl.c --- a/tools/libxl/libxl.c Wed Apr 11 14:14:17 2012 +0100 +++ b/tools/libxl/libxl.c Wed Apr 11 14:14:18 2012 +0100 @@ -68,6 +68,9 @@ int libxl_ctx_alloc(libxl_ctx **pctx, in /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */ + rc = libxl__atfork_init(ctx); + if (rc) goto out; + rc = libxl__poller_init(ctx, &ctx->poller_app); if (rc) goto out; diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/libxl_event.h --- a/tools/libxl/libxl_event.h Wed Apr 11 14:14:17 2012 +0100 +++ b/tools/libxl/libxl_event.h Wed Apr 11 14:14:18 2012 +0100 @@ -371,6 +371,19 @@ void libxl_osevent_occurred_fd(libxl_ctx */ void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl); + +/* + * An application which initialises a libxl_ctx in a parent process + * and then forks a child which does not quickly exec, must + * instead libxl_postfork_child_noexec in the child. One call + * on any existing (or specially made) ctx is sufficient; after + * this all previously existing libxl_ctx's are invalidated and + * must not be used - or even freed. It is harmless to call this + * postfork function and then exec anyway. + */ +void libxl_postfork_child_noexec(libxl_ctx *ctx); + + #endif /* diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/libxl_fork.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/libxl/libxl_fork.c Wed Apr 11 14:14:18 2012 +0100 @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 Citrix Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ +/* + * Internal child process machinery for use by other parts of libxl + */ + +#include "libxl_osdeps.h" /* must come before any other headers */ + +#include "libxl_internal.h" + +/* + * carefd arrangements + * + * carefd_begin and _unlock take out the no_forking lock, which we + * also take and release in our pthread_atfork handlers. So when this + * lock is held the whole process cannot fork. We therefore protect + * our fds from leaking into children made by other threads. + * + * We maintain a list of all the carefds, so that if the application + * wants to fork a long-running but non-execing child, we can close + * them all. + * + * So the record function sets CLOEXEC for the benefit of execing + * children, and makes a note of the fd for the benefit of non-execing + * ones. + */ + +struct libxl__carefd { + LIBXL_LIST_ENTRY(libxl__carefd) entry; + int fd; +}; + +static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER; +static int atfork_registered; +static LIBXL_LIST_HEAD(, libxl__carefd) carefds = + LIBXL_LIST_HEAD_INITIALIZER(carefds); + +static void atfork_lock(void) +{ + int r = pthread_mutex_lock(&no_forking); + assert(!r); +} + +static void atfork_unlock(void) +{ + int r = pthread_mutex_unlock(&no_forking); + assert(!r); +} + +int libxl__atfork_init(libxl_ctx *ctx) +{ + int r, rc; + + atfork_lock(); + if (atfork_registered) { rc = 0; goto out; } + + r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock); + if (r) { + assert(r == ENOMEM); + libxl__alloc_failed(ctx, __func__, 0,0); + } + + atfork_registered = 1; + rc = 0; + out: + atfork_unlock(); + return rc; +} + +void libxl__carefd_begin(void) { atfork_lock(); } +void libxl__carefd_unlock(void) { atfork_unlock(); } + +libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd) +{ + libxl__carefd *cf = 0; + + libxl_fd_set_cloexec(ctx, fd, 1); + cf = libxl__zalloc(NULL, sizeof(*cf)); + cf->fd = fd; + LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry); + return cf; +} + +libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd) +{ + libxl__carefd *cf = 0; + + cf = libxl__carefd_record(ctx, fd); + libxl__carefd_unlock(); + return cf; +} + +void libxl_postfork_child_noexec(libxl_ctx *ctx) +{ + libxl__carefd *cf, *cf_tmp; + int r; + + atfork_lock(); + LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) { + if (cf->fd >= 0) { + r = close(cf->fd); + if (r) + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING, + "failed to close fd=%d" + " (perhaps of another libxl ctx)", cf->fd); + } + free(cf); + } + LIBXL_LIST_INIT(&carefds); + atfork_unlock(); +} + +int libxl__carefd_close(libxl__carefd *cf) +{ + if (!cf) return 0; + int r = cf->fd < 0 ? 0 : close(cf->fd); + int esave = errno; + atfork_lock(); + LIBXL_LIST_REMOVE(cf, entry); + atfork_unlock(); + free(cf); + errno = esave; + return r; +} + +int libxl__carefd_fd(const libxl__carefd *cf) +{ + if (!cf) return -1; + return cf->fd; +} + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/libxl_internal.h --- a/tools/libxl/libxl_internal.h Wed Apr 11 14:14:17 2012 +0100 +++ b/tools/libxl/libxl_internal.h Wed Apr 11 14:14:18 2012 +0100 @@ -611,6 +611,9 @@ void libxl__poller_put(libxl_ctx *ctx, l void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p); +int libxl__atfork_init(libxl_ctx *ctx); + + /* from xl_dom */ _hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid); _hidden int libxl__domain_shutdown_reason(libxl__gc *gc, uint32_t domid); @@ -1307,6 +1310,66 @@ _hidden void libxl__ao_complete(libxl__e /* For use by ao machinery ONLY */ _hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao); + +/* + * File descriptors and CLOEXEC + */ + +/* + * For libxl functions which create file descriptors, at least one + * of the following must be true: + * (a) libxl does not care if copies of this open-file are inherited + * by random children and might remain open indefinitely + * (b) libxl must take extra care for the fd (the actual descriptor, + * not the open-file) as below. We call this a "carefd". + * + * The rules for opening a carefd are: + * (i) Before bringing any carefds into existence, + * libxl code must call libxl__carefd_begin. + * (ii) Then for each carefd brought into existence, + * libxl code must call libxl__carefd_record + * and remember the libxl__carefd_record*. + * (iii) Then it must call libxl__carefd_unlock. + * (iv) When in a child process the fd is to be passed across + * exec by libxl, the libxl code must unset FD_CLOEXEC + * on the fd eg by using libxl_fd_set_cloexec. + * (v) Later, when the fd is to be closed in the same process, + * libxl code must not call close. Instead, it must call + * libxl__carefd_close. + * Steps (ii) and (iii) can be combined by calling the convenience + * function libxl__carefd_opened. + */ +/* libxl__carefd_begin and _unlock (or _opened) must be called always + * in pairs. They may be called with the CTX lock held. In between + * _begin and _unlock, the following are prohibited: + * - anything which might block + * - any callbacks to the application + * - nested calls to libxl__carefd_begin + * - fork (libxl__fork) + * In general nothing should be done before _unlock that could be done + * afterwards. + */ +typedef struct libxl__carefd libxl__carefd; + +void libxl__carefd_begin(void); +void libxl__carefd_unlock(void); + +/* fd may be -1, in which case this returns a dummy libxl__fd_record + * on which it _carefd_close is a no-op. Cannot fail. */ +libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd); + +/* Combines _record and _unlock in a single call. If fd==-1, + * still does the unlock, but returns 0. Cannot fail. */ +libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd); + +/* Works just like close(2). You may pass NULL, in which case it's + * a successful no-op. */ +int libxl__carefd_close(libxl__carefd*); + +/* You may pass NULL in which case the answer is -1. */ +int libxl__carefd_fd(const libxl__carefd*); + + /* * Convenience macros. */ diff -r 4fef2701410b -r 8a88f6e209db tools/libxl/xl.c --- a/tools/libxl/xl.c Wed Apr 11 14:14:17 2012 +0100 +++ b/tools/libxl/xl.c Wed Apr 11 14:14:18 2012 +0100 @@ -96,6 +96,9 @@ static void parse_global_config(const ch void postfork(void) { + libxl_postfork_child_noexec(ctx); /* in case we don't exit/exec */ + ctx = 0; + if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) { fprintf(stderr, "cannot reinit xl context after fork\n"); exit(-1); _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |