From 20a2c379c420f03e283d61f2448ba893626ec7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 29 May 2019 16:04:55 +0300 Subject: [PATCH] Clamp number of vectors to IOV_MAX / UIO_MAXIOV for GOutputStream writev() calls in the implementations More vectors will give an error and we can simply clamp here and consider it like a short write instead. In case of GSocketOutputStream this is done here instead of inside GSocket before calling sendmsg() because we we can't generically handle short writes when sending messages on a socket, e.g. for datagram sockets this causes only part of the datagram to be sent and an error would be more useful in this case than sending corrupted data. Also reduce the fallback limit to 16 in gsocket.c as that's the minimum value required by POSIX and add a static assertion that the limit is never bigger than G_MAXINT as that's the type recvmmsg/sendmmsg take. --- gio/gioprivate.h | 17 +++++++++++++++++ gio/glocalfileoutputstream.c | 9 +++++---- gio/gsocket.c | 27 +++++++++++---------------- gio/gsocketoutputstream.c | 9 +++++---- gio/gunixoutputstream.c | 17 +++++++++-------- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/gio/gioprivate.h b/gio/gioprivate.h index a917510b9..2bc54e49b 100644 --- a/gio/gioprivate.h +++ b/gio/gioprivate.h @@ -35,6 +35,23 @@ gboolean g_output_stream_async_close_is_via_threads (GOutputStream *stream); void g_socket_connection_set_cached_remote_address (GSocketConnection *connection, GSocketAddress *address); +/* POSIX defines IOV_MAX/UIO_MAXIOV as the maximum number of iovecs that can + * be sent in one go. We define our own version of it here as there are two + * possible names, and also define a fall-back value if none of the constants + * are defined */ +#if defined(IOV_MAX) +#define G_IOV_MAX IOV_MAX +#elif defined(UIO_MAXIOV) +#define G_IOV_MAX UIO_MAXIOV +#else +/* 16 is the minimum value required by POSIX */ +#define G_IOV_MAX 16 +#endif + +/* The various functions taking iovecs as parameter use a plain int + * for the number of vectors. Limit it to G_MAXINT for this reason. + */ +G_STATIC_ASSERT (G_IOV_MAX <= G_MAXINT); G_END_DECLS diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 6d44989bf..a3dd62172 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -42,6 +42,7 @@ #endif #include "glib-private.h" +#include "gioprivate.h" #ifdef G_OS_WIN32 #include @@ -243,11 +244,11 @@ g_local_file_output_stream_writev (GOutputStream *stream, if (bytes_written) *bytes_written = 0; - /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. - * We handle this like a short write in this case + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. */ - if (n_vectors > G_MAXINT) - n_vectors = G_MAXINT; + if (n_vectors > G_IOV_MAX) + n_vectors = G_IOV_MAX; file = G_LOCAL_FILE_OUTPUT_STREAM (stream); diff --git a/gio/gsocket.c b/gio/gsocket.c index 06514d102..3cdce1053 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -74,6 +74,7 @@ #include "gcredentials.h" #include "gcredentialsprivate.h" #include "glibintl.h" +#include "gioprivate.h" #ifdef G_OS_WIN32 /* For Windows XP runtime compatibility, but use the system's if_nametoindex() if available */ @@ -4960,14 +4961,11 @@ g_socket_send_messages_with_timeout (GSocket *socket, struct mmsghdr *msgvec; gint i, num_sent; -#ifdef UIO_MAXIOV -#define MAX_NUM_MESSAGES UIO_MAXIOV -#else -#define MAX_NUM_MESSAGES 1024 -#endif - - if (num_messages > MAX_NUM_MESSAGES) - num_messages = MAX_NUM_MESSAGES; + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. + */ + if (num_messages > G_IOV_MAX) + num_messages = G_IOV_MAX; msgvec = g_newa (struct mmsghdr, num_messages); @@ -5482,14 +5480,11 @@ g_socket_receive_messages_with_timeout (GSocket *socket, struct mmsghdr *msgvec; guint i, num_received; -#ifdef UIO_MAXIOV -#define MAX_NUM_MESSAGES UIO_MAXIOV -#else -#define MAX_NUM_MESSAGES 1024 -#endif - - if (num_messages > MAX_NUM_MESSAGES) - num_messages = MAX_NUM_MESSAGES; + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. + */ + if (num_messages > G_IOV_MAX) + num_messages = G_IOV_MAX; msgvec = g_newa (struct mmsghdr, num_messages); diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c index fcca56efc..482ee39b9 100644 --- a/gio/gsocketoutputstream.c +++ b/gio/gsocketoutputstream.c @@ -33,6 +33,7 @@ #include "gioerror.h" #include "glibintl.h" #include "gfiledescriptorbased.h" +#include "gioprivate.h" struct _GSocketOutputStreamPrivate { @@ -146,8 +147,8 @@ g_socket_output_stream_writev (GOutputStream *stream, /* Clamp the number of vectors if more given than we can write in one go. * The caller has to handle short writes anyway. */ - if (n_vectors > G_MAXINT) - n_vectors = G_MAXINT; + if (n_vectors > G_IOV_MAX) + n_vectors = G_IOV_MAX; res = g_socket_send_message_with_timeout (output_stream->priv->socket, NULL, vectors, n_vectors, @@ -194,8 +195,8 @@ g_socket_output_stream_pollable_writev_nonblocking (GPollableOutputStream *poll /* Clamp the number of vectors if more given than we can write in one go. * The caller has to handle short writes anyway. */ - if (n_vectors > G_MAXINT) - n_vectors = G_MAXINT; + if (n_vectors > G_IOV_MAX) + n_vectors = G_IOV_MAX; return g_socket_send_message_with_timeout (output_stream->priv->socket, NULL, vectors, n_vectors, diff --git a/gio/gunixoutputstream.c b/gio/gunixoutputstream.c index 5536e00b4..506e09a8c 100644 --- a/gio/gunixoutputstream.c +++ b/gio/gunixoutputstream.c @@ -37,6 +37,7 @@ #include "gasynchelper.h" #include "gfiledescriptorbased.h" #include "glibintl.h" +#include "gioprivate.h" /** @@ -425,11 +426,11 @@ g_unix_output_stream_writev (GOutputStream *stream, if (bytes_written) *bytes_written = 0; - /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. - * We handle this like a short write in this case + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. */ - if (n_vectors > G_MAXINT) - n_vectors = G_MAXINT; + if (n_vectors > G_IOV_MAX) + n_vectors = G_IOV_MAX; unix_stream = G_UNIX_OUTPUT_STREAM (stream); @@ -635,11 +636,11 @@ g_unix_output_stream_pollable_writev_nonblocking (GPollableOutputStream *stream return G_POLLABLE_RETURN_WOULD_BLOCK; } - /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. - * We handle this like a short write in this case + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. */ - if (n_vectors > G_MAXINT) - n_vectors = G_MAXINT; + if (n_vectors > G_IOV_MAX) + n_vectors = G_IOV_MAX; if (G_OUTPUT_VECTOR_IS_IOVEC) {