diff mbox series

[mptcp-next] mptcp: ignore unsupported msg flags

Message ID fb3746774c97c3042beb7d76cf9f23e57d7f85e1.1618413785.git.pabeni@redhat.com
State Superseded, archived
Headers show
Series [mptcp-next] mptcp: ignore unsupported msg flags | expand

Commit Message

Paolo Abeni April 14, 2021, 3:23 p.m. UTC
Currently mptcp_sendmsg()/mptcp_recvmsg() fail with EOPNOTSUPP
if the user-space provides some unsupported flag. That is
unexpected and may foul existing applications migrated to MPTCP,
which expect a different behavior.

Change the mentioned function to silently ignore the unsupported
flags.

Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/162
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/protocol.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Comments

Florian Westphal April 14, 2021, 3:51 p.m. UTC | #1
Paolo Abeni <pabeni@redhat.com> wrote:
> Currently mptcp_sendmsg()/mptcp_recvmsg() fail with EOPNOTSUPP
> if the user-space provides some unsupported flag. That is
> unexpected and may foul existing applications migrated to MPTCP,
> which expect a different behavior.
> 
> Change the mentioned function to silently ignore the unsupported
> flags.
> 
> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/162
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
>  net/mptcp/protocol.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index 2d895c3c8746..bdec946b9b04 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -1585,8 +1585,8 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
>  	int ret = 0;
>  	long timeo;
>  
> -	if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
> -		return -EOPNOTSUPP;
> +	/* ignore unsupported flags */
> +	msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL;

Ignoring MSG_MORE is fine, but DONTWAIT and NOSIGNAL?

I'd prefer hard errors for those until mptcp does the right thing for
them.
Paolo Abeni April 14, 2021, 4:07 p.m. UTC | #2
On Wed, 2021-04-14 at 17:51 +0200, Florian Westphal wrote:
> Paolo Abeni <pabeni@redhat.com> wrote:
> > Currently mptcp_sendmsg()/mptcp_recvmsg() fail with EOPNOTSUPP
> > if the user-space provides some unsupported flag. That is
> > unexpected and may foul existing applications migrated to MPTCP,
> > which expect a different behavior.
> > 
> > Change the mentioned function to silently ignore the unsupported
> > flags.
> > 
> > Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/162
> > Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> > ---
> >  net/mptcp/protocol.c | 8 ++++----
> >  1 file changed, 4 insertions(+), 4 deletions(-)
> > 
> > diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> > index 2d895c3c8746..bdec946b9b04 100644
> > --- a/net/mptcp/protocol.c
> > +++ b/net/mptcp/protocol.c
> > @@ -1585,8 +1585,8 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
> >  	int ret = 0;
> >  	long timeo;
> >  
> > -	if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
> > -		return -EOPNOTSUPP;
> > +	/* ignore unsupported flags */
> > +	msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL;
> 
> Ignoring MSG_MORE is fine, but DONTWAIT and NOSIGNAL?

The intent of this patch is ignoring the unsupported flags. For sendmsg
that is all of them except MSG_MORE, MSG_DONTWAIT, MSG_NOSIGNAL:

MSG_OOB
MSG_PEEK
MSG_DONTROUTE
MSG_TRYHARD
MSG_CTRUNC
MSG_PROBE
MSG_TRUNC
MSG_EOR
MSG_FIN
MSG_SYN
MSG_CONFIRM
MSG_RST
MSG_ERRQUEUE
MSG_WAITFORONE
MSG_SENDPAGE_NOPOLICY
MSG_SENDPAGE_NOTLAST
MSG_BATCH
MSG_EOF
MSG_NO_SHARED_FRAGS
MSG_SENDPAGE_DECRYPTED
MSG_ZEROCOPY
MSG_FASTOPEN
MSG_CMSG_CLOEXEC

> I'd prefer hard errors for those until mptcp does the right thing for
> them.

should we define a 'bail on use' mask with a subset of the above? The
candidates I see are:

MSG_TRUNC
MSG_PEEK
MSG_OOB

Thanks,

Paolo
Florian Westphal April 15, 2021, 8:27 a.m. UTC | #3
Paolo Abeni <pabeni@redhat.com> wrote:
> The intent of this patch is ignoring the unsupported flags. For sendmsg
> that is all of them except MSG_MORE, MSG_DONTWAIT, MSG_NOSIGNAL:
> 
> MSG_OOB
> MSG_PEEK
> MSG_DONTROUTE
> MSG_TRYHARD
> MSG_CTRUNC
> MSG_PROBE
> MSG_TRUNC
> MSG_EOR
> MSG_FIN
> MSG_SYN
> MSG_CONFIRM
> MSG_RST
> MSG_ERRQUEUE
> MSG_WAITFORONE
> MSG_SENDPAGE_NOPOLICY
> MSG_SENDPAGE_NOTLAST
> MSG_BATCH
> MSG_EOF
> MSG_NO_SHARED_FRAGS
> MSG_SENDPAGE_DECRYPTED
> MSG_ZEROCOPY
> MSG_FASTOPEN
> MSG_CMSG_CLOEXEC

Looks like TCP ignores all of them except fastopen:

sendto(5, "foo\n", 4, 0, NULL, 0)       = 4
sendto(5, "foo\n", 4, MSG_OOB, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_PEEK, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_DONTROUTE, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_CTRUNC, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_PROBE, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_TRUNC, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_DONTWAIT, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_EOR, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_WAITALL, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_FIN, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_SYN, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_CONFIRM, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_RST, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_ERRQUEUE, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_NOSIGNAL, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_MORE, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_WAITFORONE, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_SENDPAGE_NOTLAST, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_BATCH, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_NO_SHARED_FRAGS, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x100000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x200000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x400000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x800000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x1000000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x2000000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_ZEROCOPY, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x8000000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, 0x10000000 /* MSG_??? */, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_FASTOPEN, NULL, 0) = -1 EISCONN (Transport
		endpoint is already connected)
sendto(5, "foo\n", 4, MSG_CMSG_CLOEXEC, NULL, 0) = 4
sendto(5, "foo\n", 4, MSG_CMSG_COMPAT, NULL, 0) = 4

(fd 4 is a established tcp socket, after successful connect()).

> should we define a 'bail on use' mask with a subset of the above? The
> candidates I see are:
 
> MSG_TRUNC
> MSG_PEEK
> MSG_OOB

Looks like we only need to handle FASTOPEN.
Paolo Abeni April 15, 2021, 9:33 a.m. UTC | #4
On Thu, 2021-04-15 at 10:27 +0200, Florian Westphal wrote:
> Paolo Abeni <pabeni@redhat.com> wrote:
> > The intent of this patch is ignoring the unsupported flags. For sendmsg
> > that is all of them except MSG_MORE, MSG_DONTWAIT, MSG_NOSIGNAL:
> > 
> > MSG_OOB
> > MSG_PEEK
> > MSG_DONTROUTE
> > MSG_TRYHARD
> > MSG_CTRUNC
> > MSG_PROBE
> > MSG_TRUNC
> > MSG_EOR
> > MSG_FIN
> > MSG_SYN
> > MSG_CONFIRM
> > MSG_RST
> > MSG_ERRQUEUE
> > MSG_WAITFORONE
> > MSG_SENDPAGE_NOPOLICY
> > MSG_SENDPAGE_NOTLAST
> > MSG_BATCH
> > MSG_EOF
> > MSG_NO_SHARED_FRAGS
> > MSG_SENDPAGE_DECRYPTED
> > MSG_ZEROCOPY
> > MSG_FASTOPEN
> > MSG_CMSG_CLOEXEC
> 
> Looks like TCP ignores all of them except fastopen:
> 
> sendto(5, "foo\n", 4, 0, NULL, 0)       = 4
> sendto(5, "foo\n", 4, MSG_OOB, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_PEEK, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_DONTROUTE, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_CTRUNC, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_PROBE, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_TRUNC, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_DONTWAIT, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_EOR, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_WAITALL, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_FIN, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_SYN, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_CONFIRM, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_RST, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_ERRQUEUE, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_NOSIGNAL, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_MORE, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_WAITFORONE, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_SENDPAGE_NOTLAST, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_BATCH, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_NO_SHARED_FRAGS, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x100000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x200000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x400000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x800000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x1000000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x2000000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_ZEROCOPY, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x8000000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, 0x10000000 /* MSG_??? */, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_FASTOPEN, NULL, 0) = -1 EISCONN (Transport
> 		endpoint is already connected)
> sendto(5, "foo\n", 4, MSG_CMSG_CLOEXEC, NULL, 0) = 4
> sendto(5, "foo\n", 4, MSG_CMSG_COMPAT, NULL, 0) = 4
> 
> (fd 4 is a established tcp socket, after successful connect()).

Thank you for the estensive testing!

Looking at the code even MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_OOB
MSG_ZEROCOPY has some effect on TCP.

MSG_ZEROCOPY needs to be enabled via setsockopt()

Currently we implement MSG_DONTWAIT, silently ignore MSG_MORE and we
bail on MSG_OOB, MSG_EOR, MSG_ZEROCOPY

> > should we define a 'bail on use' mask with a subset of the above? The
> > candidates I see are:
>  
> > MSG_TRUNC
> > MSG_PEEK
> > MSG_OOB
> 
> Looks like we only need to handle FASTOPEN.

LGTM, for sendmsg() sake: ignoring MSG_OOB and MSG_EOR does not look
too evil to me ;)

Than there is recvmsg()...

With a quick code inspections looks like TCP support:
MSG_PEEK, MSG_TRUNC, MSG_OOB, MSG_WAITALL, MSG_DONTWAIT, MSG_DONTWAIT, 
MSG_ERRQUEUE

MSG_ERRQUEUE requires to be enabled via IP_RECVERR()

We currently implement MSG_WAITALL and MSG_DONTWAIT, soon MSG_PEEK and
we bail on all the others.

Ignoring MSG_ERRQUEUE or MSG_TRUNC may silently break user-space
application.

I think we could provide a dummy impl for MSG_ERRQUEUE: always
returning empty msg, which will be consistent with the lack of
setsockopt support. We could even call into inet_recv_error(), as the
relevant queue will always be empty, and could possibly simplify a
complete implementation someday.

I also think we should bail on MSG_TRUNC, and silently ignore all the
others.

WDYT?

Thanks!

Paolo
Florian Westphal April 15, 2021, 9:44 a.m. UTC | #5
Paolo Abeni <pabeni@redhat.com> wrote:
> Looking at the code even MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_OOB
> MSG_ZEROCOPY has some effect on TCP.

We handle DONTWAIT in mptcp, and the other ones, afaics, do not result
in a user-visible change (i.e., if we would not support DONTWAIT, we
would cause userspace do block in send() even if userspace specifically
asked for us not to, so that would be a bug).

But for the others I think ignoring is fine.

> MSG_ZEROCOPY needs to be enabled via setsockopt()
> 
> Currently we implement MSG_DONTWAIT, silently ignore MSG_MORE and we
> bail on MSG_OOB, MSG_EOR, MSG_ZEROCOPY
> 
> > > should we define a 'bail on use' mask with a subset of the above? The
> > > candidates I see are:
> >  
> > > MSG_TRUNC
> > > MSG_PEEK
> > > MSG_OOB
> > 
> > Looks like we only need to handle FASTOPEN.
> 
> LGTM, for sendmsg() sake: ignoring MSG_OOB and MSG_EOR does not look
> too evil to me ;)

Agree.

> Than there is recvmsg()...
> 
> With a quick code inspections looks like TCP support:
> MSG_PEEK, MSG_TRUNC, MSG_OOB, MSG_WAITALL, MSG_DONTWAIT, MSG_DONTWAIT, 
> MSG_ERRQUEUE
> 
> MSG_ERRQUEUE requires to be enabled via IP_RECVERR()
> 
> We currently implement MSG_WAITALL and MSG_DONTWAIT, soon MSG_PEEK and
> we bail on all the others.
> 
> Ignoring MSG_ERRQUEUE or MSG_TRUNC may silently break user-space
> application.

Indeed.

> I think we could provide a dummy impl for MSG_ERRQUEUE: always
> returning empty msg, which will be consistent with the lack of
> setsockopt support. We could even call into inet_recv_error(), as the
> relevant queue will always be empty, and could possibly simplify a
> complete implementation someday.
> 
> I also think we should bail on MSG_TRUNC, and silently ignore all the
> others.
> 
> WDYT?

Sounds good.  MSG_TRUNC support should be easy to add as well.
diff mbox series

Patch

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 2d895c3c8746..bdec946b9b04 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1585,8 +1585,8 @@  static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 	int ret = 0;
 	long timeo;
 
-	if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
-		return -EOPNOTSUPP;
+	/* ignore unsupported flags */
+	msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL;
 
 	mptcp_lock_sock(sk, __mptcp_wmem_reserve(sk, min_t(size_t, 1 << 20, len)));
 
@@ -1916,8 +1916,8 @@  static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
 	int target;
 	long timeo;
 
-	if (msg->msg_flags & ~(MSG_WAITALL | MSG_DONTWAIT))
-		return -EOPNOTSUPP;
+	/* ignore unsupported flags */
+	msg->msg_flags &= MSG_WAITALL | MSG_DONTWAIT;
 
 	mptcp_lock_sock(sk, __mptcp_splice_receive_queue(sk));
 	if (unlikely(sk->sk_state == TCP_LISTEN)) {