diff --git a/CMakeLists.txt b/CMakeLists.txt index b391e7c3c..73d5aff89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,6 +584,7 @@ if(LIBUV_BUILD_TESTS) test/test-loop-close.c test/test-loop-configure.c test/test-loop-handles.c + test/test-loop-oom.c test/test-loop-stop.c test/test-loop-time.c test/test-metrics.c diff --git a/Makefile.am b/Makefile.am index 9b9e6be71..f3808b696 100644 --- a/Makefile.am +++ b/Makefile.am @@ -206,12 +206,13 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-ipc-send-recv.c \ test/test-ipc.c \ test/test-list.h \ - test/test-loop-handles.c \ test/test-loop-alive.c \ test/test-loop-close.c \ + test/test-loop-configure.c \ + test/test-loop-handles.c \ + test/test-loop-oom.c \ test/test-loop-stop.c \ test/test-loop-time.c \ - test/test-loop-configure.c \ test/test-metrics.c \ test/test-multiple-listen.c \ test/test-mutexes.c \ diff --git a/src/unix/async.c b/src/unix/async.c index 75a181bc1..538ae7876 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -308,8 +308,14 @@ static int uv__async_start(uv_loop_t* loop) { return err; #endif - uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]); - uv__io_start(loop, &loop->async_io_watcher, POLLIN); + err = uv__io_init_start(loop, &loop->async_io_watcher, uv__async_io, + pipefd[0], POLLIN); + if (err < 0) { + uv__close(pipefd[0]); + if (pipefd[1] != -1) + uv__close(pipefd[1]); + return err; + } loop->async_wfd = pipefd[1]; #if UV__KQUEUE_EVFILT_USER diff --git a/src/unix/core.c b/src/unix/core.c index e64ea81d6..115cbe699 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -867,7 +867,7 @@ static unsigned int next_power_of_two(unsigned int val) { return val; } -static void maybe_resize(uv_loop_t* loop, unsigned int len) { +static int maybe_resize(uv_loop_t* loop, unsigned int len) { uv__io_t** watchers; void* fake_watcher_list; void* fake_watcher_count; @@ -875,7 +875,7 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { unsigned int i; if (len <= loop->nwatchers) - return; + return 0; /* Preserve fake watcher list and count at the end of the watchers */ if (loop->watchers != NULL) { @@ -891,7 +891,7 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) - abort(); + return UV_ENOMEM; for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; watchers[nwatchers] = fake_watcher_list; @@ -899,11 +899,11 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { loop->watchers = watchers; loop->nwatchers = nwatchers; + return 0; } void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { - assert(cb != NULL); assert(fd >= -1); uv__queue_init(&w->pending_queue); uv__queue_init(&w->watcher_queue); @@ -914,14 +914,18 @@ void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { } -void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + int err; + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); assert(0 != events); assert(w->fd >= 0); assert(w->fd < INT_MAX); w->pevents |= events; - maybe_resize(loop, w->fd + 1); + err = maybe_resize(loop, w->fd + 1); + if (err) + return err; #if !defined(__sun) /* The event ports backend needs to rearm all file descriptors on each and @@ -929,7 +933,7 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { * short-circuit here if the event mask is unchanged. */ if (w->events == w->pevents) - return; + return 0; #endif if (uv__queue_empty(&w->watcher_queue)) @@ -939,6 +943,25 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { loop->watchers[w->fd] = w; loop->nfds++; } + + return 0; +} + + +int uv__io_init_start(uv_loop_t* loop, + uv__io_t* w, + uv__io_cb cb, + int fd, + unsigned int events) { + int err; + + assert(cb != NULL); + assert(fd > -1); + uv__io_init(w, cb, fd); + err = uv__io_start(loop, w, events); + if (err) + uv__io_init(w, NULL, -1); + return err; } diff --git a/src/unix/internal.h b/src/unix/internal.h index b1d2b2175..5002c5fde 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -257,7 +257,12 @@ void uv__make_close_pending(uv_handle_t* handle); int uv__getiovmax(void); void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd); -void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); +int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); +int uv__io_init_start(uv_loop_t* loop, + uv__io_t* w, + uv__io_cb cb, + int fd, + unsigned int events); void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events); void uv__io_close(uv_loop_t* loop, uv__io_t* w); void uv__io_feed(uv_loop_t* loop, uv__io_t* w); diff --git a/src/unix/linux.c b/src/unix/linux.c index 86c12b4f0..5fa4627da 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -2460,6 +2460,7 @@ static int compare_watchers(const struct watcher_list* a, static int init_inotify(uv_loop_t* loop) { + int err; int fd; if (loop->inotify_fd != -1) @@ -2469,10 +2470,14 @@ static int init_inotify(uv_loop_t* loop) { if (fd < 0) return UV__ERR(errno); - loop->inotify_fd = fd; - uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); - uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); + err = uv__io_init_start(loop, &loop->inotify_read_watcher, uv__inotify_read, + fd, POLLIN); + if (err) { + uv__close(fd); + return err; + } + loop->inotify_fd = fd; return 0; } diff --git a/src/unix/loop.c b/src/unix/loop.c index 179ee999d..5d3f0c7a3 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -32,12 +32,11 @@ int uv_loop_init(uv_loop_t* loop) { void* saved_data; int err; - saved_data = loop->data; memset(loop, 0, sizeof(*loop)); loop->data = saved_data; - lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + lfields = uv__calloc(1, sizeof(*lfields)); if (lfields == NULL) return UV_ENOMEM; loop->internal_fields = lfields; @@ -116,6 +115,11 @@ fail_rwlock_init: fail_signal_init: uv__platform_loop_delete(loop); + if (loop->backend_fd != -1) { + uv__close(loop->backend_fd); + loop->backend_fd = -1; + } + fail_platform_init: uv_mutex_destroy(&lfields->loop_metrics.lock); diff --git a/src/unix/signal.c b/src/unix/signal.c index f23c887d0..ccaa72db4 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -259,22 +259,28 @@ static void uv__signal_unregister_handler(int signum) { static int uv__signal_loop_once_init(uv_loop_t* loop) { + int* pipefd; int err; /* Return if already initialized. */ - if (loop->signal_pipefd[0] != -1) + pipefd = loop->signal_pipefd; + if (pipefd[0] != -1) return 0; - err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE); + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err) return err; - uv__io_init(&loop->signal_io_watcher, - uv__signal_event, - loop->signal_pipefd[0]); - uv__io_start(loop, &loop->signal_io_watcher, POLLIN); + err = uv__io_init_start(loop, &loop->signal_io_watcher, uv__signal_event, + pipefd[0], POLLIN); + if (err) { + uv__close(pipefd[0]); + uv__close(pipefd[1]); + pipefd[0] = -1; + pipefd[1] = -1; + } - return 0; + return err; } diff --git a/src/win/core.c b/src/win/core.c index bc63b0667..5f41c87ad 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -114,7 +114,7 @@ static int uv__loops_add(uv_loop_t* loop) { failed_loops_realloc: uv_mutex_unlock(&uv__loops_lock); - return ERROR_OUTOFMEMORY; + return UV_ENOMEM; } diff --git a/test/test-list.h b/test/test-list.h index 24dbcdd71..0dea54469 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -33,6 +33,7 @@ TEST_DECLARE (loop_stop_before_run) TEST_DECLARE (loop_update_time) TEST_DECLARE (loop_backend_timeout) TEST_DECLARE (loop_configure) +TEST_DECLARE (loop_init_oom) TEST_DECLARE (default_loop_close) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) @@ -605,6 +606,7 @@ TASK_LIST_START TEST_ENTRY (loop_update_time) TEST_ENTRY (loop_backend_timeout) TEST_ENTRY (loop_configure) + TEST_ENTRY (loop_init_oom) TEST_ENTRY (default_loop_close) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) diff --git a/test/test-loop-oom.c b/test/test-loop-oom.c new file mode 100644 index 000000000..5f5b58c9e --- /dev/null +++ b/test/test-loop-oom.c @@ -0,0 +1,62 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include +#include + +static int limit; +static int alloc; + +static void* t_realloc(void* p, size_t n) { + alloc += n; + if (alloc > limit) + return NULL; + p = realloc(p, n); + ASSERT_NOT_NULL(p); + return p; +} + +static void* t_calloc(size_t m, size_t n) { + return t_realloc(NULL, m * n); +} + +static void* t_malloc(size_t n) { + return t_realloc(NULL, n); +} + +TEST_IMPL(loop_init_oom) { + uv_loop_t loop; + int err; + + ASSERT_OK(uv_replace_allocator(t_malloc, t_realloc, t_calloc, free)); + for (;;) { + err = uv_loop_init(&loop); + if (err == 0) + break; + ASSERT_EQ(err, UV_ENOMEM); + limit += 8; + alloc = 0; + } + ASSERT_OK(uv_loop_close(&loop)); + return 0; +}