unix: enforce recvmmsg buffer size requirements (#5095)
The documentation already stated that the receive buffer should be a multiple of 64 KiB when the UV_UDP_RECVMMSG is used, but make that more prominent in the documentation and enforce it in the code. Refs: https://github.com/libuv/libuv/security/advisories/GHSA-r846-fxvr-f3rx
This commit is contained in:
parent
9f0101dcb8
commit
8877568581
@ -76,7 +76,8 @@ Data types
|
|||||||
*/
|
*/
|
||||||
UV_UDP_REUSEPORT = 64,
|
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
|
UV_UDP_RECVMMSG = 256
|
||||||
};
|
};
|
||||||
@ -168,7 +169,8 @@ API
|
|||||||
The remaining bits can be used to set one of these flags:
|
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
|
* `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
|
.. versionadded:: 1.7.0
|
||||||
.. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag.
|
.. 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.
|
`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).
|
.. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms).
|
||||||
The use of this feature requires a buffer larger than
|
The :c:type:`uv_alloc_cb` for this handle should create
|
||||||
2 * 64KB to be passed to `alloc_cb`.
|
buffers that are multiples of 64 KiB.
|
||||||
.. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly,
|
.. 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
|
it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to
|
||||||
:c:func:`uv_udp_init_ex`.
|
:c:func:`uv_udp_init_ex`.
|
||||||
|
|||||||
@ -705,7 +705,8 @@ enum uv_udp_flags {
|
|||||||
*/
|
*/
|
||||||
UV_UDP_REUSEPORT = 64,
|
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
|
UV_UDP_RECVMMSG = 256
|
||||||
};
|
};
|
||||||
|
|||||||
@ -217,6 +217,8 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf, int flag) {
|
|||||||
|
|
||||||
/* prepare structures for recvmmsg */
|
/* prepare structures for recvmmsg */
|
||||||
chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
|
chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
|
||||||
|
if (chunks == 0)
|
||||||
|
return UV_EINVAL;
|
||||||
if (chunks > ARRAY_SIZE(iov))
|
if (chunks > ARRAY_SIZE(iov))
|
||||||
chunks = ARRAY_SIZE(iov);
|
chunks = ARRAY_SIZE(iov);
|
||||||
for (k = 0; k < chunks; ++k) {
|
for (k = 0; k < chunks; ++k) {
|
||||||
@ -316,7 +318,10 @@ static void uv__udp_recvmsg(uv_udp_t* handle, int flag) {
|
|||||||
|
|
||||||
if (uv_udp_using_recvmmsg(handle)) {
|
if (uv_udp_using_recvmmsg(handle)) {
|
||||||
nread = uv__udp_recvmmsg(handle, &buf, flag);
|
nread = uv__udp_recvmmsg(handle, &buf, flag);
|
||||||
if (nread > 0)
|
if (nread <= 0) {
|
||||||
|
handle->recv_cb(handle, nread, &buf, NULL, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
count -= nread;
|
count -= nread;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,6 +181,7 @@ TEST_DECLARE (udp_recvmsg_unreachable_error)
|
|||||||
TEST_DECLARE (udp_recvmsg_unreachable_error6)
|
TEST_DECLARE (udp_recvmsg_unreachable_error6)
|
||||||
TEST_DECLARE (udp_send_pollerr_no_recv)
|
TEST_DECLARE (udp_send_pollerr_no_recv)
|
||||||
TEST_DECLARE (udp_mmsg)
|
TEST_DECLARE (udp_mmsg)
|
||||||
|
TEST_DECLARE (udp_mmsg_small_buf)
|
||||||
TEST_DECLARE (udp_multicast_join)
|
TEST_DECLARE (udp_multicast_join)
|
||||||
TEST_DECLARE (udp_multicast_join6)
|
TEST_DECLARE (udp_multicast_join6)
|
||||||
TEST_DECLARE (udp_multicast_ttl)
|
TEST_DECLARE (udp_multicast_ttl)
|
||||||
@ -826,6 +827,7 @@ TASK_LIST_START
|
|||||||
TEST_ENTRY (udp_options6)
|
TEST_ENTRY (udp_options6)
|
||||||
TEST_ENTRY (udp_no_autobind)
|
TEST_ENTRY (udp_no_autobind)
|
||||||
TEST_ENTRY (udp_mmsg)
|
TEST_ENTRY (udp_mmsg)
|
||||||
|
TEST_ENTRY (udp_mmsg_small_buf)
|
||||||
TEST_ENTRY (udp_multicast_interface)
|
TEST_ENTRY (udp_multicast_interface)
|
||||||
TEST_ENTRY (udp_multicast_interface6)
|
TEST_ENTRY (udp_multicast_interface6)
|
||||||
TEST_ENTRY (udp_multicast_join)
|
TEST_ENTRY (udp_multicast_join)
|
||||||
|
|||||||
@ -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) {
|
TEST_IMPL(udp_mmsg) {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
uv_buf_t buf;
|
uv_buf_t buf;
|
||||||
@ -148,3 +173,31 @@ TEST_IMPL(udp_mmsg) {
|
|||||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||||
return 0;
|
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