Merge branch 'v1.x' into v1.x
This commit is contained in:
commit
e1ebb65f31
@ -634,6 +634,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-socket-buffer-size.c
|
||||
test/test-spawn.c
|
||||
test/test-stdio-over-pipes.c
|
||||
test/test-io-64-safe.c
|
||||
test/test-strscpy.c
|
||||
test/test-strtok.c
|
||||
test/test-tcp-alloc-cb-fail.c
|
||||
|
||||
@ -259,6 +259,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-socket-buffer-size.c \
|
||||
test/test-spawn.c \
|
||||
test/test-stdio-over-pipes.c \
|
||||
test/test-io-64-safe.c \
|
||||
test/test-strscpy.c \
|
||||
test/test-strtok.c \
|
||||
test/test-tcp-alloc-cb-fail.c \
|
||||
|
||||
@ -297,6 +297,13 @@ API
|
||||
`base` and `len` members of the uv_buf_t struct. The user is responsible for
|
||||
freeing `base` after the uv_buf_t is done. Return struct passed by value.
|
||||
|
||||
.. warning:: It is discouraged to set `len` to a large value as that may
|
||||
result in spurious failures. Specifically, Windows may fail on
|
||||
writes larger than about 511 MB, and various Unicies may fail
|
||||
on I/O larger than about 2 GB (0x7ffff000 bytes). Instead it is
|
||||
generally better to split the data into multiple `uv_write`
|
||||
calls (attach the `write_cb` to the last one).
|
||||
|
||||
.. c:function:: char** uv_setup_args(int argc, char** argv)
|
||||
|
||||
Store the program arguments. Required for getting / setting the process title
|
||||
|
||||
@ -76,7 +76,8 @@ Data types
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
* Indicates that recvmmsg should be used, if available. The uv_alloc_cb
|
||||
* for this handle should create buffers that are multiples of 64 KiB.
|
||||
*/
|
||||
UV_UDP_RECVMMSG = 256
|
||||
};
|
||||
@ -168,7 +169,8 @@ API
|
||||
The remaining bits can be used to set one of these flags:
|
||||
|
||||
* `UV_UDP_RECVMMSG`: if set, and the platform supports it, :man:`recvmmsg(2)` will
|
||||
be used.
|
||||
be used. The :c:type:`uv_alloc_cb` for this handle should create
|
||||
buffers that are multiples of 64 KiB.
|
||||
|
||||
.. versionadded:: 1.7.0
|
||||
.. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag.
|
||||
@ -481,8 +483,8 @@ API
|
||||
`suggested_size` in `alloc_cb` for udp_recv is always set to the size of 1 max size dgram.
|
||||
|
||||
.. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms).
|
||||
The use of this feature requires a buffer larger than
|
||||
2 * 64KB to be passed to `alloc_cb`.
|
||||
The :c:type:`uv_alloc_cb` for this handle should create
|
||||
buffers that are multiples of 64 KiB.
|
||||
.. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly,
|
||||
it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to
|
||||
:c:func:`uv_udp_init_ex`.
|
||||
|
||||
@ -705,7 +705,8 @@ enum uv_udp_flags {
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
* Indicates that recvmmsg should be used, if available. The uv_alloc_cb
|
||||
* for this handle should create buffers that are multiples of 64 KiB.
|
||||
*/
|
||||
UV_UDP_RECVMMSG = 256
|
||||
};
|
||||
|
||||
121
src/unix/fs.c
121
src/unix/fs.c
@ -167,7 +167,7 @@ static int uv__fs_close(int fd) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_fsync(uv_fs_t* req) {
|
||||
static int uv__fs_fsync(uv_fs_t* req) {
|
||||
#if defined(__APPLE__)
|
||||
/* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache
|
||||
* to the drive platters. This is in contrast to Linux's fdatasync and fsync
|
||||
@ -191,7 +191,7 @@ static ssize_t uv__fs_fsync(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
|
||||
static int uv__fs_fdatasync(uv_fs_t* req) {
|
||||
#if defined(__linux__) || defined(__sun) || defined(__NetBSD__)
|
||||
return fdatasync(req->file);
|
||||
#elif defined(__APPLE__)
|
||||
@ -233,7 +233,7 @@ static struct timespec uv__fs_to_timespec(double time) {
|
||||
#endif
|
||||
|
||||
|
||||
static ssize_t uv__fs_futime(uv_fs_t* req) {
|
||||
static int uv__fs_futime(uv_fs_t* req) {
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
@ -263,7 +263,7 @@ static ssize_t uv__fs_futime(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_mkdtemp(uv_fs_t* req) {
|
||||
static int uv__fs_mkdtemp(uv_fs_t* req) {
|
||||
return mkdtemp((char*) req->path) ? 0 : -1;
|
||||
}
|
||||
|
||||
@ -359,7 +359,7 @@ clobber:
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_open(uv_fs_t* req) {
|
||||
static int uv__fs_open(uv_fs_t* req) {
|
||||
#ifdef O_CLOEXEC
|
||||
return open(req->path, req->flags | O_CLOEXEC, req->mode);
|
||||
#else /* O_CLOEXEC */
|
||||
@ -388,11 +388,11 @@ static ssize_t uv__fs_open(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__preadv_or_pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
int is_pread) {
|
||||
static int uv__preadv_or_pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
int is_pread) {
|
||||
ssize_t total;
|
||||
ssize_t r;
|
||||
size_t i;
|
||||
@ -435,18 +435,18 @@ typedef size_t uv__iovcnt;
|
||||
#endif
|
||||
|
||||
|
||||
static ssize_t uv__preadv_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
static int uv__preadv_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/1);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
static int uv__pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/0);
|
||||
}
|
||||
|
||||
@ -454,14 +454,14 @@ static ssize_t uv__pwritev_emul(int fd,
|
||||
/* The function pointer cache is an uintptr_t because _Atomic void*
|
||||
* doesn't work on macos/ios/etc...
|
||||
*/
|
||||
static ssize_t uv__preadv_or_pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
_Atomic uintptr_t* cache,
|
||||
int is_pread) {
|
||||
static int uv__preadv_or_pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
_Atomic uintptr_t* cache,
|
||||
int is_pread) {
|
||||
union {
|
||||
ssize_t (*f)(int, const struct iovec*, uv__iovcnt, off_t);
|
||||
int (*f)(int, const struct iovec*, uv__iovcnt, off_t);
|
||||
void* p;
|
||||
} u;
|
||||
|
||||
@ -485,7 +485,7 @@ static ssize_t uv__preadv_or_pwritev(int fd,
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__preadv(int fd,
|
||||
static int uv__preadv(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off) {
|
||||
@ -494,16 +494,16 @@ static ssize_t uv__preadv(int fd,
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off) {
|
||||
static int uv__pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off) {
|
||||
static _Atomic uintptr_t cache;
|
||||
return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/0);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||
static int uv__fs_read(uv_fs_t* req) {
|
||||
const struct iovec* bufs;
|
||||
unsigned int iovmax;
|
||||
size_t nbufs;
|
||||
@ -520,17 +520,35 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||
if (nbufs > iovmax)
|
||||
nbufs = iovmax;
|
||||
|
||||
/* Truncate multi-buf reads to UV__IO_MAX_BYTES total, dropping trailing bufs. */
|
||||
if (nbufs > 1) {
|
||||
size_t total;
|
||||
size_t n;
|
||||
for (total = 0, n = 0; n < nbufs; n++) {
|
||||
if (bufs[n].iov_len > UV__IO_MAX_BYTES - total)
|
||||
break;
|
||||
total += bufs[n].iov_len;
|
||||
}
|
||||
nbufs = n > 0 ? n : 1;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
if (off < 0) {
|
||||
if (nbufs == 1)
|
||||
r = read(fd, bufs->iov_base, bufs->iov_len);
|
||||
else if (nbufs > 1)
|
||||
if (nbufs == 1) {
|
||||
r = read(fd, bufs->iov_base,
|
||||
bufs->iov_len > UV__IO_MAX_BYTES ? UV__IO_MAX_BYTES : bufs->iov_len);
|
||||
} else if (nbufs > 1) {
|
||||
r = readv(fd, bufs, nbufs);
|
||||
}
|
||||
} else {
|
||||
if (nbufs == 1)
|
||||
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
|
||||
else if (nbufs > 1)
|
||||
if (nbufs == 1) {
|
||||
r = pread(fd, bufs->iov_base,
|
||||
bufs->iov_len > UV__IO_MAX_BYTES ? UV__IO_MAX_BYTES : bufs->iov_len,
|
||||
off);
|
||||
}
|
||||
else if (nbufs > 1) {
|
||||
r = uv__preadv(fd, bufs, nbufs, off);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __PASE__
|
||||
@ -567,7 +585,7 @@ static int uv__fs_scandir_sort(const uv__dirent_t** a, const uv__dirent_t** b) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_scandir(uv_fs_t* req) {
|
||||
static int uv__fs_scandir(uv_fs_t* req) {
|
||||
uv__dirent_t** dents;
|
||||
int n;
|
||||
|
||||
@ -732,7 +750,7 @@ static ssize_t uv__fs_pathmax_size(const char* path) {
|
||||
return pathmax;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_readlink(uv_fs_t* req) {
|
||||
static int uv__fs_readlink(uv_fs_t* req) {
|
||||
ssize_t maxlen;
|
||||
ssize_t len;
|
||||
char* buf;
|
||||
@ -791,7 +809,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_realpath(uv_fs_t* req) {
|
||||
static int uv__fs_realpath(uv_fs_t* req) {
|
||||
char* buf;
|
||||
char* tmp;
|
||||
|
||||
@ -829,7 +847,7 @@ static ssize_t uv__fs_realpath(uv_fs_t* req) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) {
|
||||
static int uv__fs_sendfile_emul(uv_fs_t* req) {
|
||||
struct pollfd pfd;
|
||||
int use_pread;
|
||||
off_t offset;
|
||||
@ -1027,7 +1045,7 @@ static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off,
|
||||
#endif /* __linux__ */
|
||||
|
||||
|
||||
static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
static int uv__fs_sendfile(uv_fs_t* req) {
|
||||
int in_fd;
|
||||
int out_fd;
|
||||
|
||||
@ -1116,7 +1134,7 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
*/
|
||||
if (r == 0 || ((errno == EAGAIN || errno == EINTR) && len != 0)) {
|
||||
req->off += len;
|
||||
return (ssize_t) len;
|
||||
return len;
|
||||
}
|
||||
|
||||
if (errno == EINVAL ||
|
||||
@ -1139,7 +1157,7 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
static int uv__fs_utime(uv_fs_t* req) {
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
@ -1174,7 +1192,7 @@ static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
||||
static int uv__fs_lutime(uv_fs_t* req) {
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
@ -1196,7 +1214,7 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_write(uv_fs_t* req) {
|
||||
static int uv__fs_write(uv_fs_t* req) {
|
||||
const struct iovec* bufs;
|
||||
size_t nbufs;
|
||||
ssize_t r;
|
||||
@ -1225,7 +1243,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_copyfile(uv_fs_t* req) {
|
||||
static int uv__fs_copyfile(uv_fs_t* req) {
|
||||
uv_fs_t fs_req;
|
||||
uv_file srcfd;
|
||||
uv_file dstfd;
|
||||
@ -1633,7 +1651,7 @@ static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_write_all(uv_fs_t* req) {
|
||||
static int uv__fs_write_all(uv_fs_t* req) {
|
||||
unsigned int iovmax;
|
||||
unsigned int nbufs;
|
||||
uv_buf_t* bufs;
|
||||
@ -1682,7 +1700,7 @@ static ssize_t uv__fs_write_all(uv_fs_t* req) {
|
||||
static void uv__fs_work(struct uv__work* w) {
|
||||
int retry_on_eintr;
|
||||
uv_fs_t* req;
|
||||
ssize_t r;
|
||||
int r;
|
||||
|
||||
req = container_of(w, uv_fs_t, work_req);
|
||||
retry_on_eintr = !(req->fs_type == UV_FS_CLOSE ||
|
||||
@ -2150,6 +2168,8 @@ int uv_fs_sendfile(uv_loop_t* loop,
|
||||
req->flags = in_fd; /* hack */
|
||||
req->file = out_fd;
|
||||
req->off = off;
|
||||
if (len > UV__IO_MAX_BYTES)
|
||||
return UV_EINVAL;
|
||||
req->bufsml[0].len = len;
|
||||
POST;
|
||||
}
|
||||
@ -2217,6 +2237,9 @@ int uv_fs_write(uv_loop_t* loop,
|
||||
if (bufs == NULL || nbufs == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (uv__count_bufs(bufs, nbufs) > UV__IO_MAX_BYTES)
|
||||
return UV_EINVAL;
|
||||
|
||||
req->file = file;
|
||||
|
||||
req->nbufs = nbufs;
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h> /* offsetof */
|
||||
#include <stdint.h>
|
||||
@ -56,6 +57,14 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* android ndk workaround */
|
||||
#ifndef LLONG_MAX
|
||||
#define LLONG_MAX 9223372036854775807LL
|
||||
#endif
|
||||
#ifndef LLONG_MIN
|
||||
#define LLONG_MIN (-9223372036854775807LL - 1)
|
||||
#endif
|
||||
|
||||
#ifndef __NR_io_uring_setup
|
||||
# define __NR_io_uring_setup 425
|
||||
#endif
|
||||
@ -1516,25 +1525,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
*/
|
||||
pe->events &= w->pevents | POLLERR | POLLHUP;
|
||||
|
||||
/* Work around an epoll quirk where it sometimes reports just the
|
||||
* EPOLLERR or EPOLLHUP event. In order to force the event loop to
|
||||
* move forward, we merge in the read/write events that the watcher
|
||||
* is interested in; uv__read() and uv__write() will then deal with
|
||||
* the error or hangup in the usual fashion.
|
||||
*
|
||||
* Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
|
||||
* reads the available data, calls uv_read_stop(), then sometime later
|
||||
* calls uv_read_start() again. By then, libuv has forgotten about the
|
||||
* hangup and the kernel won't report EPOLLIN again because there's
|
||||
* nothing left to read. If anything, libuv is to blame here. The
|
||||
* current hack is just a quick bandaid; to properly fix it, libuv
|
||||
* needs to remember the error/hangup event. We should get that for
|
||||
* free when we switch over to edge-triggered I/O.
|
||||
*/
|
||||
if (pe->events == POLLERR || pe->events == POLLHUP)
|
||||
pe->events |=
|
||||
w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
|
||||
|
||||
if (pe->events != 0) {
|
||||
/* Run signal watchers last. This also affects child process watchers
|
||||
* because those are implemented in terms of signal watchers.
|
||||
|
||||
@ -60,6 +60,12 @@ void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
if (events & UV__POLLRDHUP)
|
||||
pevents |= UV_DISCONNECT;
|
||||
|
||||
/* On error or hangup, mix in events the user is interested in so the
|
||||
* appropriate read/write callbacks are invoked. */
|
||||
if (events & (POLLERR | POLLHUP))
|
||||
pevents |=
|
||||
w->pevents & (UV_READABLE | UV_PRIORITIZED | UV_WRITABLE | UV_DISCONNECT);
|
||||
|
||||
handle->poll_cb(handle, 0, pevents);
|
||||
}
|
||||
|
||||
|
||||
@ -1058,11 +1058,14 @@ static void uv__read(uv_stream_t* stream) {
|
||||
|
||||
if (!is_ipc) {
|
||||
do {
|
||||
nread = read(uv__stream_fd(stream), buf.base, buf.len);
|
||||
}
|
||||
while (nread < 0 && errno == EINTR);
|
||||
nread = read(uv__stream_fd(stream),
|
||||
buf.base,
|
||||
buf.len > UV__IO_MAX_BYTES ? UV__IO_MAX_BYTES : buf.len);
|
||||
} while (nread < 0 && errno == EINTR);
|
||||
} else {
|
||||
/* ipc uses recvmsg */
|
||||
if (buf.len > UV__IO_MAX_BYTES)
|
||||
buf.len = UV__IO_MAX_BYTES;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_iov = (struct iovec*) &buf;
|
||||
msg.msg_iovlen = 1;
|
||||
@ -1074,8 +1077,7 @@ static void uv__read(uv_stream_t* stream) {
|
||||
|
||||
do {
|
||||
nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
|
||||
}
|
||||
while (nread < 0 && errno == EINTR);
|
||||
} while (nread < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
if (nread < 0) {
|
||||
@ -1218,17 +1220,16 @@ void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
* operating systems, devices like PTYs sometimes produce partial reads even
|
||||
* when more data is available.
|
||||
*/
|
||||
if ((events & POLLHUP) &&
|
||||
if ((events & (POLLHUP | UV__POLLRDHUP)) &&
|
||||
!(events & POLLIN) &&
|
||||
(stream->flags & UV_HANDLE_READING) &&
|
||||
!(stream->flags & UV_HANDLE_READ_EOF)) {
|
||||
uv_buf_t buf = { NULL, 0 };
|
||||
uv__stream_eof(stream, &buf);
|
||||
if (uv__stream_fd(stream) == -1)
|
||||
return; /* read_cb closed stream. */
|
||||
}
|
||||
|
||||
if (uv__stream_fd(stream) == -1)
|
||||
return; /* read_cb closed stream. */
|
||||
|
||||
if (events & (POLLOUT | POLLERR | POLLHUP)) {
|
||||
uv__write(stream);
|
||||
uv__write_callbacks(stream);
|
||||
@ -1295,6 +1296,7 @@ static void uv__stream_connect(uv_stream_t* stream) {
|
||||
|
||||
|
||||
static int uv__check_before_write(uv_stream_t* stream,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
uv_stream_t* send_handle) {
|
||||
assert((stream->type == UV_TCP ||
|
||||
@ -1309,6 +1311,13 @@ static int uv__check_before_write(uv_stream_t* stream,
|
||||
if (nbufs < 1 || nbufs > 1024*1024)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Reject writes above UV__IO_MAX_BYTES to be consistent with EINVAL on platforms
|
||||
* such as macOS that fail when the total size of the iov exceeds 2GB,
|
||||
* and catch/prevent sign-extension bugs.
|
||||
*/
|
||||
if (uv__count_bufs(bufs, nbufs) > UV__IO_MAX_BYTES)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (uv__stream_fd(stream) < 0)
|
||||
return UV_EBADF;
|
||||
|
||||
@ -1347,7 +1356,7 @@ int uv_write2(uv_write_t* req,
|
||||
int empty_queue;
|
||||
int err;
|
||||
|
||||
err = uv__check_before_write(stream, nbufs, send_handle);
|
||||
err = uv__check_before_write(stream, bufs, nbufs, send_handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -1436,7 +1445,7 @@ int uv_try_write2(uv_stream_t* stream,
|
||||
if (stream->connect_req != NULL || stream->write_queue_size != 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
err = uv__check_before_write(stream, nbufs, send_handle);
|
||||
err = uv__check_before_write(stream, bufs, nbufs, send_handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
||||
@ -178,7 +178,11 @@ void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
|
||||
handle = container_of(w, uv_udp_t, io_watcher);
|
||||
assert(handle->type == UV_UDP);
|
||||
|
||||
if (revents & POLLIN)
|
||||
/* Trigger a recv and send to find out what POLLERR occurred. */
|
||||
if (revents & POLLERR)
|
||||
revents |= POLLIN | POLLOUT;
|
||||
|
||||
if (revents & (POLLIN | POLLERR))
|
||||
uv__udp_recvmsg(handle, 0);
|
||||
|
||||
/* Just Linux support for now. */
|
||||
@ -213,6 +217,8 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf, int flag) {
|
||||
|
||||
/* prepare structures for recvmmsg */
|
||||
chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
|
||||
if (chunks == 0)
|
||||
return UV_EINVAL;
|
||||
if (chunks > ARRAY_SIZE(iov))
|
||||
chunks = ARRAY_SIZE(iov);
|
||||
for (k = 0; k < chunks; ++k) {
|
||||
@ -312,8 +318,11 @@ static void uv__udp_recvmsg(uv_udp_t* handle, int flag) {
|
||||
|
||||
if (uv_udp_using_recvmmsg(handle)) {
|
||||
nread = uv__udp_recvmmsg(handle, &buf, flag);
|
||||
if (nread > 0)
|
||||
count -= nread;
|
||||
if (nread <= 0) {
|
||||
handle->recv_cb(handle, nread, &buf, NULL, 0);
|
||||
return;
|
||||
}
|
||||
count -= nread;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -721,9 +730,6 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
unsigned int addrlen) {
|
||||
int err;
|
||||
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* already sending a message */
|
||||
if (handle->send_queue_count != 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
@ -453,7 +453,10 @@ int uv__udp_is_connected(uv_udp_t* handle) {
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
|
||||
int uv__udp_check_before_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
unsigned int addrlen;
|
||||
|
||||
if (handle->type != UV_UDP)
|
||||
@ -480,6 +483,12 @@ int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
|
||||
addrlen = 0;
|
||||
}
|
||||
|
||||
if (nbufs < 1 || nbufs > 1024 * 1024)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (uv__count_bufs(bufs, nbufs) > UV__IO_MAX_BYTES)
|
||||
return UV_EINVAL;
|
||||
|
||||
return addrlen;
|
||||
}
|
||||
|
||||
@ -492,10 +501,7 @@ int uv_udp_send(uv_udp_send_t* req,
|
||||
uv_udp_send_cb send_cb) {
|
||||
int addrlen;
|
||||
|
||||
if (nbufs < 1 || nbufs > 1024 * 1024)
|
||||
return UV_EINVAL;
|
||||
|
||||
addrlen = uv__udp_check_before_send(handle, addr);
|
||||
addrlen = uv__udp_check_before_send(handle, bufs, nbufs, addr);
|
||||
if (addrlen < 0)
|
||||
return addrlen;
|
||||
|
||||
@ -509,10 +515,7 @@ int uv_udp_try_send(uv_udp_t* handle,
|
||||
const struct sockaddr* addr) {
|
||||
int addrlen;
|
||||
|
||||
if (nbufs < 1 || nbufs > 1024 * 1024)
|
||||
return UV_EINVAL;
|
||||
|
||||
addrlen = uv__udp_check_before_send(handle, addr);
|
||||
addrlen = uv__udp_check_before_send(handle, bufs, nbufs, addr);
|
||||
if (addrlen < 0)
|
||||
return addrlen;
|
||||
|
||||
@ -527,6 +530,7 @@ int uv_udp_try_send2(uv_udp_t* handle,
|
||||
struct sockaddr* addrs[/*count*/],
|
||||
unsigned int flags) {
|
||||
unsigned int i;
|
||||
int addrlen;
|
||||
|
||||
if (count < 1)
|
||||
return UV_EINVAL;
|
||||
@ -534,9 +538,11 @@ int uv_udp_try_send2(uv_udp_t* handle,
|
||||
if (flags != 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (nbufs[i] < 1 || nbufs[i] > 1024 * 1024)
|
||||
return UV_EINVAL;
|
||||
for (i = 0; i < count; i++) {
|
||||
addrlen = uv__udp_check_before_send(handle, bufs[i], nbufs[i], addrs[i]);
|
||||
if (addrlen < 0)
|
||||
return addrlen;
|
||||
}
|
||||
|
||||
if (handle->send_queue_count > 0)
|
||||
return UV_EAGAIN;
|
||||
@ -663,8 +669,11 @@ size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) {
|
||||
size_t bytes;
|
||||
|
||||
bytes = 0;
|
||||
for (i = 0; i < nbufs; i++)
|
||||
bytes += (size_t) bufs[i].len;
|
||||
for (i = 0; i < nbufs; i++) {
|
||||
if (bufs[i].len > (size_t) INT32_MAX - bytes)
|
||||
return INT32_MAX;
|
||||
bytes += bufs[i].len;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -227,6 +227,12 @@ void uv__work_done(uv_async_t* handle);
|
||||
|
||||
size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
|
||||
|
||||
/* On some platforms, notably macOS, attempting a read or write > 2GB returns
|
||||
* an EINVAL. On Linux, IO syscalls will transfer at most this many bytes.
|
||||
* Use this limit everywhere to avoid platform-specific failures.
|
||||
*/
|
||||
#define UV__IO_MAX_BYTES 0x7ffff000
|
||||
|
||||
int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
|
||||
|
||||
void uv__fs_scandir_cleanup(uv_fs_t* req);
|
||||
|
||||
15
src/win/fs.c
15
src/win/fs.c
@ -889,6 +889,7 @@ void fs__read(uv_fs_t* req) {
|
||||
bytes = 0;
|
||||
do {
|
||||
DWORD incremental_bytes;
|
||||
DWORD to_read;
|
||||
|
||||
if (offset != -1) {
|
||||
offset_.QuadPart = offset + bytes;
|
||||
@ -896,9 +897,12 @@ void fs__read(uv_fs_t* req) {
|
||||
overlapped.OffsetHigh = offset_.HighPart;
|
||||
}
|
||||
|
||||
to_read = req->fs.info.bufs[index].len;
|
||||
if (to_read > UV__IO_MAX_BYTES)
|
||||
to_read = UV__IO_MAX_BYTES;
|
||||
result = ReadFile(handle,
|
||||
req->fs.info.bufs[index].base,
|
||||
req->fs.info.bufs[index].len,
|
||||
to_read,
|
||||
&incremental_bytes,
|
||||
overlapped_ptr);
|
||||
bytes += incremental_bytes;
|
||||
@ -1103,7 +1107,7 @@ void fs__write(uv_fs_t* req) {
|
||||
|
||||
result = WriteFile(handle,
|
||||
req->fs.info.bufs[index].base,
|
||||
req->fs.info.bufs[index].len,
|
||||
(DWORD) req->fs.info.bufs[index].len,
|
||||
&incremental_bytes,
|
||||
overlapped_ptr);
|
||||
bytes += incremental_bytes;
|
||||
@ -3329,6 +3333,11 @@ int uv_fs_write(uv_loop_t* loop,
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (uv__count_bufs(bufs, nbufs) > UV__IO_MAX_BYTES) {
|
||||
SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
req->file.fd = fd;
|
||||
|
||||
req->fs.info.nbufs = nbufs;
|
||||
@ -3698,6 +3707,8 @@ int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
|
||||
req->file.fd = fd_in;
|
||||
req->fs.info.fd_out = fd_out;
|
||||
req->fs.info.offset = in_offset;
|
||||
if (length > UV__IO_MAX_BYTES)
|
||||
return UV_EINVAL;
|
||||
req->fs.info.bufsml[0].len = length;
|
||||
POST;
|
||||
}
|
||||
|
||||
111
src/win/pipe.c
111
src/win/pipe.c
@ -391,6 +391,32 @@ int uv_pipe(uv_file fds[2], int read_flags, int write_flags) {
|
||||
}
|
||||
|
||||
|
||||
static DWORD uv__pipe_attach_iocp(HANDLE pipeHandle,
|
||||
HANDLE iocp,
|
||||
uv_pipe_t* handle) {
|
||||
UCHAR sfcnm_flags;
|
||||
DWORD err = 0;
|
||||
|
||||
if (CreateIoCompletionPort(pipeHandle, iocp, (ULONG_PTR) handle, 0) == NULL) {
|
||||
err = GetLastError();
|
||||
handle->flags |= UV_HANDLE_EMULATE_IOCP;
|
||||
}
|
||||
|
||||
sfcnm_flags = FILE_SKIP_SET_EVENT_ON_HANDLE;
|
||||
if (!err)
|
||||
sfcnm_flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
|
||||
|
||||
if (SetFileCompletionNotificationModes(pipeHandle, sfcnm_flags)) {
|
||||
if (sfcnm_flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)
|
||||
handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
|
||||
} else {
|
||||
err = GetLastError();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
||||
uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags) {
|
||||
/* The parent_pipe is always the server_pipe and kept by libuv.
|
||||
@ -430,13 +456,9 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
if (CreateIoCompletionPort(server_pipe,
|
||||
loop->iocp,
|
||||
(ULONG_PTR) parent_pipe,
|
||||
0) == NULL) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
err = uv__pipe_attach_iocp(server_pipe, loop->iocp, parent_pipe);
|
||||
if (err)
|
||||
uv_fatal_error(err, "uv__pipe_attach_iocp");
|
||||
|
||||
parent_pipe->handle = server_pipe;
|
||||
*child_pipe_ptr = client_pipe;
|
||||
@ -521,13 +543,10 @@ static int uv__set_pipe_handle(uv_loop_t* loop,
|
||||
handle->pipe.conn.readfile_thread_handle = NULL;
|
||||
InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
|
||||
} else {
|
||||
/* Overlapped pipe. Try to associate with IOCP. */
|
||||
if (CreateIoCompletionPort(pipeHandle,
|
||||
loop->iocp,
|
||||
(ULONG_PTR) handle,
|
||||
0) == NULL) {
|
||||
handle->flags |= UV_HANDLE_EMULATE_IOCP;
|
||||
}
|
||||
/* Overlapped pipe. Try to associate with IOCP.
|
||||
* Will set compatibility flags internally if this fails
|
||||
* (because some other process already has activated IOCP). */
|
||||
uv__pipe_attach_iocp(pipeHandle, loop->iocp, handle);
|
||||
}
|
||||
|
||||
handle->handle = pipeHandle;
|
||||
@ -540,6 +559,8 @@ static int uv__set_pipe_handle(uv_loop_t* loop,
|
||||
|
||||
static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_pipe_accept_t* req, BOOL firstInstance) {
|
||||
DWORD err;
|
||||
|
||||
assert(req->pipeHandle == INVALID_HANDLE_VALUE);
|
||||
|
||||
req->pipeHandle =
|
||||
@ -554,12 +575,9 @@ static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
}
|
||||
|
||||
/* Associate it with IOCP so we can get events. */
|
||||
if (CreateIoCompletionPort(req->pipeHandle,
|
||||
loop->iocp,
|
||||
(ULONG_PTR) handle,
|
||||
0) == NULL) {
|
||||
uv_fatal_error(GetLastError(), "CreateIoCompletionPort");
|
||||
}
|
||||
err = uv__pipe_attach_iocp(req->pipeHandle, loop->iocp, handle);
|
||||
if (err)
|
||||
uv_fatal_error(err, "uv__pipe_attach_iocp");
|
||||
|
||||
/* Stash a handle in the server object for use from places such as
|
||||
* getsockname and chmod. As we transfer ownership of these to client
|
||||
@ -1106,6 +1124,8 @@ void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
|
||||
static void uv__pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_pipe_accept_t* req, BOOL firstInstance) {
|
||||
BOOL success;
|
||||
|
||||
assert(handle->flags & UV_HANDLE_LISTENING);
|
||||
|
||||
if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) {
|
||||
@ -1120,22 +1140,22 @@ static void uv__pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
/* Prepare the overlapped structure. */
|
||||
memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
|
||||
|
||||
if (!ConnectNamedPipe(req->pipeHandle, &req->u.io.overlapped) &&
|
||||
GetLastError() != ERROR_IO_PENDING) {
|
||||
success = ConnectNamedPipe(req->pipeHandle, &req->u.io.overlapped);
|
||||
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
|
||||
/* Process the req without IOCP. */
|
||||
SET_REQ_SUCCESS(req);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
} else if (!UV_SUCCEEDED_WITH_IOCP(success)) {
|
||||
if (GetLastError() == ERROR_PIPE_CONNECTED) {
|
||||
SET_REQ_SUCCESS(req);
|
||||
} else {
|
||||
CloseHandle(req->pipeHandle);
|
||||
req->pipeHandle = INVALID_HANDLE_VALUE;
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
}
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait for completion via IOCP */
|
||||
handle->reqs_pending++;
|
||||
}
|
||||
|
||||
@ -1183,6 +1203,7 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
|
||||
/* Initialize the client handle and copy the pipeHandle to the client */
|
||||
pipe_client->handle = req->pipeHandle;
|
||||
pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
|
||||
pipe_client->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
|
||||
|
||||
/* Prepare the req to pick up a new connection */
|
||||
server->pipe.serv.pending_accepts = req->next_pending;
|
||||
@ -1392,8 +1413,6 @@ static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
if (!QueueUserWorkItem(&uv_pipe_zero_readfile_thread_proc,
|
||||
req,
|
||||
WT_EXECUTELONGFUNCTION)) {
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
@ -1410,18 +1429,15 @@ static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
NULL,
|
||||
&req->u.io.overlapped);
|
||||
|
||||
if (!result && GetLastError() != ERROR_IO_PENDING) {
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(result)) {
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
} else if (!UV_SUCCEEDED_WITH_IOCP(result)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
} else if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
assert(req->wait_handle == INVALID_HANDLE_VALUE);
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_read_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@ -1434,6 +1450,8 @@ static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
return;
|
||||
|
||||
error:
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
handle->flags |= UV_HANDLE_READ_PENDING;
|
||||
handle->reqs_pending++;
|
||||
@ -1622,6 +1640,9 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (write_buf.len > UV__IO_MAX_BYTES)
|
||||
return ERROR_INVALID_PARAMETER; /* Maps to UV_EINVAL. */
|
||||
|
||||
if ((handle->flags &
|
||||
(UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) ==
|
||||
(UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) {
|
||||
@ -1695,21 +1716,18 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
write_buf.len,
|
||||
NULL,
|
||||
&req->u.io.overlapped);
|
||||
|
||||
if (!result && GetLastError() != ERROR_IO_PENDING) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
if (result) {
|
||||
/* Request completed immediately. */
|
||||
req->u.io.queued_bytes = 0;
|
||||
} else {
|
||||
/* Request queued by the kernel. */
|
||||
req->u.io.queued_bytes = write_buf.len;
|
||||
handle->write_queue_size += req->u.io.queued_bytes;
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(result)) {
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
} else if (!UV_SUCCEEDED_WITH_IOCP(result)) {
|
||||
return GetLastError();
|
||||
} else if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_write_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
@ -1968,8 +1986,11 @@ static int uv__pipe_read_data(uv_loop_t* loop,
|
||||
/* Ensure we read at most the smaller of:
|
||||
* (a) the length of the user-allocated buffer.
|
||||
* (b) the maximum data length as specified by the `max_bytes` argument.
|
||||
* (c) the amount of data that can be read non-blocking
|
||||
* (c) the amount of data that can be read non-blocking.
|
||||
* (d) UV__IO_MAX_BYTES.
|
||||
*/
|
||||
if (buf.len > UV__IO_MAX_BYTES)
|
||||
buf.len = UV__IO_MAX_BYTES;
|
||||
if (max_bytes > buf.len)
|
||||
max_bytes = buf.len;
|
||||
|
||||
|
||||
@ -111,7 +111,9 @@ int uv_read_stop(uv_stream_t* handle) {
|
||||
}
|
||||
|
||||
|
||||
static int uv__check_before_write(uv_stream_t* handle, unsigned int nbufs) {
|
||||
static int uv__check_before_write(uv_stream_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs) {
|
||||
/* We're not beholden to IOV_MAX but limit the buffer count to catch sign
|
||||
* conversion bugs where a caller passes in a signed negative number that
|
||||
* then gets converted to a really large unsigned number.
|
||||
@ -120,6 +122,10 @@ static int uv__check_before_write(uv_stream_t* handle, unsigned int nbufs) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (uv__count_bufs(bufs, nbufs) > UV__IO_MAX_BYTES) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_WRITABLE)) {
|
||||
return UV_EPIPE;
|
||||
}
|
||||
@ -136,7 +142,7 @@ int uv_write(uv_write_t* req,
|
||||
uv_loop_t* loop = handle->loop;
|
||||
int err;
|
||||
|
||||
err = uv__check_before_write(handle, nbufs);
|
||||
err = uv__check_before_write(handle, bufs, nbufs);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
@ -174,7 +180,7 @@ int uv_write2(uv_write_t* req,
|
||||
return uv_write(req, handle, bufs, nbufs, cb);
|
||||
}
|
||||
|
||||
err = uv__check_before_write(handle, nbufs);
|
||||
err = uv__check_before_write(handle, bufs, nbufs);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
@ -194,7 +200,7 @@ int uv_try_write(uv_stream_t* stream,
|
||||
unsigned int nbufs) {
|
||||
int err;
|
||||
|
||||
err = uv__check_before_write(stream, nbufs);
|
||||
err = uv__check_before_write(stream, bufs, nbufs);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1103,6 +1103,8 @@ void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
break;
|
||||
}
|
||||
assert(buf.base != NULL);
|
||||
if (buf.len > UV__IO_MAX_BYTES)
|
||||
buf.len = UV__IO_MAX_BYTES;
|
||||
|
||||
flags = 0;
|
||||
if (WSARecv(handle->socket,
|
||||
|
||||
@ -1129,9 +1129,6 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
struct sockaddr_storage converted;
|
||||
int err;
|
||||
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr != NULL) {
|
||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||
if (err)
|
||||
|
||||
@ -28,7 +28,9 @@
|
||||
#include "uv.h"
|
||||
|
||||
/* Refs: https://github.com/libuv/libuv/issues/4369 */
|
||||
#if defined(__ANDROID__)
|
||||
/* Refs: https://github.com/libuv/libuv/issues/5092 */
|
||||
#if defined(__ANDROID__) && __ANDROID_API__ >= __ANDROID_API_Q__
|
||||
#define USE_FDSAN
|
||||
#include <android/fdsan.h>
|
||||
#endif
|
||||
|
||||
@ -149,7 +151,7 @@ void log_tap_result(int test_count,
|
||||
|
||||
void enable_fdsan(void) {
|
||||
/* Refs: https://github.com/libuv/libuv/issues/4369 */
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(USE_FDSAN)
|
||||
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
|
||||
#endif
|
||||
}
|
||||
|
||||
182
test/test-io-64-safe.c
Normal file
182
test/test-io-64-safe.c
Normal file
@ -0,0 +1,182 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/* Verify that passing INT32_MAX as a buffer length is rejected with UV_EINVAL
|
||||
* at the various I/O entry points that enforce UV__IO_MAX_BYTES.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define TEST_FILE "tmp_io_64_safe"
|
||||
|
||||
static void on_udp_send(uv_udp_send_t* req, int status) {
|
||||
/* Should never be called: uv_udp_send must reject synchronously. */
|
||||
ASSERT(0 && "on_udp_send callback must not be invoked");
|
||||
(void) req;
|
||||
(void) status;
|
||||
}
|
||||
|
||||
TEST_IMPL(io_64_safe) {
|
||||
uv_loop_t* loop;
|
||||
uv_fs_t open_req;
|
||||
uv_fs_t fs_req;
|
||||
uv_write_t write_req;
|
||||
uv_udp_t udp;
|
||||
uv_tcp_t tcp;
|
||||
uv_udp_send_t send_req;
|
||||
struct sockaddr_in addr;
|
||||
uv_buf_t* t2_bufs[1];
|
||||
unsigned int t2_nbufs[1];
|
||||
struct sockaddr* t2_addrs[1];
|
||||
uv_buf_t buf;
|
||||
uv_buf_t bufs2[2];
|
||||
uv_file fd;
|
||||
uv_file in_fd;
|
||||
uv_file out_fd;
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
/* A buf whose length just exceeds UV__IO_MAX_BYTES (0x7ffff000). */
|
||||
buf = uv_buf_init(NULL, INT32_MAX);
|
||||
|
||||
/* Two buffers whose individual sizes are reasonable but whose sum exceeds
|
||||
* UV__IO_MAX_BYTES (0x7ffff000). Each is 1 GiB + 1 byte.
|
||||
*/
|
||||
bufs2[0] = uv_buf_init(NULL, 0x40000001u);
|
||||
bufs2[1] = uv_buf_init(NULL, 0x40000001u);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* uv_fs_write: reject synchronous filesystem write > UV__IO_MAX_BYTES. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
fd = uv_fs_open(NULL, &open_req, TEST_FILE,
|
||||
UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC, S_IRUSR | S_IWUSR, NULL);
|
||||
ASSERT_GE(fd, 0);
|
||||
uv_fs_req_cleanup(&open_req);
|
||||
|
||||
ASSERT_EQ(UV_EINVAL, uv_fs_write(NULL, &fs_req, fd, &buf, 1, 0, NULL));
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
|
||||
/* nbufs > 1 where sum > UV__IO_MAX_BYTES */
|
||||
ASSERT_EQ(UV_EINVAL, uv_fs_write(NULL, &fs_req, fd, bufs2, 2, 0, NULL));
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
|
||||
uv_fs_close(NULL, &fs_req, fd, NULL);
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* uv_fs_sendfile: reject len > UV__IO_MAX_BYTES. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
in_fd = uv_fs_open(NULL, &open_req, TEST_FILE,
|
||||
UV_FS_O_RDONLY | UV_FS_O_CREAT, S_IRUSR | S_IWUSR, NULL);
|
||||
ASSERT_GE(in_fd, 0);
|
||||
uv_fs_req_cleanup(&open_req);
|
||||
|
||||
out_fd = uv_fs_open(NULL, &open_req, TEST_FILE,
|
||||
UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND, S_IRUSR | S_IWUSR,
|
||||
NULL);
|
||||
ASSERT_GE(out_fd, 0);
|
||||
uv_fs_req_cleanup(&open_req);
|
||||
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_fs_sendfile(NULL, &fs_req, out_fd, in_fd, 0,
|
||||
(size_t) INT32_MAX, NULL));
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
|
||||
uv_fs_close(NULL, &fs_req, in_fd, NULL);
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
uv_fs_close(NULL, &fs_req, out_fd, NULL);
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
}
|
||||
|
||||
uv_fs_unlink(NULL, &fs_req, TEST_FILE, NULL);
|
||||
uv_fs_req_cleanup(&fs_req);
|
||||
|
||||
{
|
||||
/* uv_write: reject stream write > UV__IO_MAX_BYTES before queuing. */
|
||||
ASSERT_OK(uv_tcp_init(loop, &tcp));
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_write(&write_req, (uv_stream_t*) &tcp, &buf, 1, NULL));
|
||||
|
||||
/* nbufs > 1 where sum > UV__IO_MAX_BYTES */
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_write(&write_req, (uv_stream_t*) &tcp, bufs2, 2, NULL));
|
||||
|
||||
/* uv_try_write: same check via the synchronous path. */
|
||||
ASSERT_EQ(UV_EINVAL, uv_try_write((uv_stream_t*) &tcp, &buf, 1));
|
||||
|
||||
/* nbufs > 1 via try_write */
|
||||
ASSERT_EQ(UV_EINVAL, uv_try_write((uv_stream_t*) &tcp, bufs2, 2));
|
||||
|
||||
uv_close((uv_handle_t*) &tcp, NULL);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_OK(uv_udp_init(loop, &udp));
|
||||
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
|
||||
|
||||
/* uv_udp_try_send: reject UDP send > UV__IO_MAX_BYTES. */
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_try_send(&udp, &buf, 1,
|
||||
(const struct sockaddr*) &addr));
|
||||
|
||||
/* nbufs > 1 via try_send */
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_try_send(&udp, bufs2, 2,
|
||||
(const struct sockaddr*) &addr));
|
||||
|
||||
/* uv_udp_send (async): reject synchronously before queuing. */
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_send(&send_req, &udp, &buf, 1,
|
||||
(const struct sockaddr*) &addr, on_udp_send));
|
||||
|
||||
/* nbufs > 1 via async send */
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_send(&send_req, &udp, bufs2, 2,
|
||||
(const struct sockaddr*) &addr, on_udp_send));
|
||||
|
||||
/* uv_udp_try_send2: reject per-batch size > UV__IO_MAX_BYTES. */
|
||||
t2_bufs[0] = &buf;
|
||||
t2_nbufs[0] = 1;
|
||||
t2_addrs[0] = (struct sockaddr*) &addr;
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_try_send2(&udp, 1, t2_bufs, t2_nbufs, t2_addrs, 0));
|
||||
|
||||
/* nbufs > 1 per batch via try_send2 */
|
||||
t2_bufs[0] = bufs2;
|
||||
t2_nbufs[0] = 2;
|
||||
ASSERT_EQ(UV_EINVAL,
|
||||
uv_udp_try_send2(&udp, 1, t2_bufs, t2_nbufs, t2_addrs, 0));
|
||||
|
||||
uv_close((uv_handle_t*) &udp, NULL);
|
||||
}
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
}
|
||||
@ -181,6 +181,7 @@ TEST_DECLARE (udp_recvmsg_unreachable_error)
|
||||
TEST_DECLARE (udp_recvmsg_unreachable_error6)
|
||||
TEST_DECLARE (udp_send_pollerr_no_recv)
|
||||
TEST_DECLARE (udp_mmsg)
|
||||
TEST_DECLARE (udp_mmsg_small_buf)
|
||||
TEST_DECLARE (udp_multicast_join)
|
||||
TEST_DECLARE (udp_multicast_join6)
|
||||
TEST_DECLARE (udp_multicast_ttl)
|
||||
@ -465,6 +466,7 @@ TEST_FS_DECLARE (fs_invalid_mkdir_name)
|
||||
TEST_FS_DECLARE (fs_wtf)
|
||||
#endif
|
||||
TEST_FS_DECLARE (fs_get_system_error)
|
||||
TEST_DECLARE (io_64_safe)
|
||||
TEST_DECLARE (strscpy)
|
||||
TEST_DECLARE (strtok)
|
||||
TEST_DECLARE (threadpool_queue_work_simple)
|
||||
@ -826,6 +828,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (udp_options6)
|
||||
TEST_ENTRY (udp_no_autobind)
|
||||
TEST_ENTRY (udp_mmsg)
|
||||
TEST_ENTRY (udp_mmsg_small_buf)
|
||||
TEST_ENTRY (udp_multicast_interface)
|
||||
TEST_ENTRY (udp_multicast_interface6)
|
||||
TEST_ENTRY (udp_multicast_join)
|
||||
@ -1196,6 +1199,7 @@ TASK_LIST_START
|
||||
TEST_FS_ENTRY (fs_get_system_error)
|
||||
TEST_ENTRY (get_osfhandle_valid_handle)
|
||||
TEST_ENTRY (open_osfhandle_valid_handle)
|
||||
TEST_ENTRY (io_64_safe)
|
||||
TEST_ENTRY (strscpy)
|
||||
TEST_ENTRY (strtok)
|
||||
TEST_ENTRY (threadpool_queue_work_simple)
|
||||
|
||||
@ -106,6 +106,31 @@ static void recv_cb(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
|
||||
static void small_alloc_cb(uv_handle_t* handle,
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf) {
|
||||
CHECK_HANDLE(handle);
|
||||
buf->len = 64;
|
||||
buf->base = malloc(buf->len);
|
||||
ASSERT_NOT_NULL(buf->base);
|
||||
alloc_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
static void small_recv_cb(uv_udp_t* handle,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* rcvbuf,
|
||||
const struct sockaddr* addr,
|
||||
unsigned flags) {
|
||||
CHECK_HANDLE(handle);
|
||||
ASSERT_EQ(UV_EINVAL, nread);
|
||||
uv_close((uv_handle_t*) &recver, close_cb);
|
||||
uv_close((uv_handle_t*) &sender, close_cb);
|
||||
free(rcvbuf->base);
|
||||
recv_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(udp_mmsg) {
|
||||
struct sockaddr_in addr;
|
||||
uv_buf_t buf;
|
||||
@ -148,3 +173,31 @@ TEST_IMPL(udp_mmsg) {
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(udp_mmsg_small_buf) {
|
||||
struct sockaddr_in addr;
|
||||
uv_loop_t* loop;
|
||||
uv_buf_t buf;
|
||||
|
||||
loop = uv_default_loop();
|
||||
ASSERT_OK(uv_udp_init_ex(loop, &recver, AF_UNSPEC | UV_UDP_RECVMMSG));
|
||||
if (uv_udp_using_recvmmsg(&recver)) {
|
||||
ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
|
||||
ASSERT_OK(uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0));
|
||||
ASSERT_OK(uv_udp_recv_start(&recver, small_alloc_cb, small_recv_cb));
|
||||
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
|
||||
ASSERT_OK(uv_udp_init(loop, &sender));
|
||||
buf = uv_buf_init("PING", 4);
|
||||
ASSERT_EQ(4, uv_udp_try_send(&sender, &buf, 1, (const struct sockaddr*) &addr));
|
||||
ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT));
|
||||
ASSERT_EQ(1, recv_cb_called);
|
||||
ASSERT_EQ(2, close_cb_called);
|
||||
} else {
|
||||
uv_close((uv_handle_t*) &recver, close_cb);
|
||||
ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT));
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
}
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user