On Thu, Jul 12, 2018 at 06:05:14PM +0200, Jeremie Courreges-Anglas wrote:
> --8<--
> space = sbspace(so, &so->so_snd);
> if (flags & MSG_OOB)
> space += 1024;
> if ((atomic && resid > so->so_snd.sb_hiwat) ||
> (so->so_proto->pr_domain->dom_family != AF_UNIX &&
> clen > so->so_snd.sb_hiwat))
> snderr(EMSGSIZE);
> if (space < clen ||
> (space - clen < resid &&
> (atomic || space < so->so_snd.sb_lowat))) {
> if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT))
> snderr(EWOULDBLOCK);
> sbunlock(so, &so->so_snd);
> error = sbwait(so, &so->so_snd);
> so->so_state &= ~SS_ISSENDING;
> if (error)
> goto out;
> goto restart;
> }
> space -= clen;
> -->8--
>
> I hate those conditions. I do not fully understand their true intent
> and I feel they are already brittle; I'm afraid that adding more
> conditionals will worsen clarity. How are those three (actually way
> more than three) conditions actually supposed to interact?
>
> If people have a clearer idea than I do about what exactly should happen
> here, please share.
If it is a temporary problem, that will go away when the content
of the socket buffer is sent away, we should block or return
EWOULDBLOCK. For a permanent problem return EMSGSIZE. Non atomic
operations can be split in smaller chunks, so there are no permanent
problems. Control messages are considerd atomic. AF_UNIX needs
special treatment as file descriptor passing is dificult. On top
of that consider integer overflow.
revision 1.100
date: 2012/04/24 16:35:08; author: deraadt; state: Exp; lines: +13 -3;
In sosend() for AF_UNIX control message sending, correctly calculate
the size (internalized ones can be larger on some architectures) for
fitting into the socket. Avoid getting confused by sb_hiwat as well.
This fixes a variety of issues where sendmsg() would fail to deliver
a fd set or fail to wait; even leading to file leakage.
Worked on this with claudio for about a week...
revision 1.145
date: 2016/01/06 10:06:50; author: stefan; state: Exp; lines: +27 -30;
Prevent integer overflows in sosend() and soreceive() by converting
min()+uiomovei() to ulmin()+uiomove() and re-arranging space computations
in sosend(). The soreceive() part was also reported by Martin Natano.
ok bluhm@ and also discussed with tedu@
So first of all we should split the AF_UNIX cases to keep it readable.
And I don't want to change the AF_UNIX code as the commit message
indicates that it was hard to develop the current solution.
>From the bug reports it seems that we should check that the UDP
packets and the IP_SENDSRCADDR fit into the socket buffer. If not
it is a permanent EMSGSIZE error. So make sure that resid + clen
<= so->so_snd.sb_hiwat. Then write it the other way around to
prevent signed integer overflow.
The result of this considerations is the diff below. I have not
tested it. Does the orignal bug go away with it? Some hackathons
ago jeremy@ mentioned that the ruby test suite found a bug in this
area. So maybe we should try it.
Does this make sense?
bluhm
Index: kern/uipc_socket.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.225
diff -u -p -r1.225 uipc_socket.c
--- kern/uipc_socket.c 5 Jul 2018 14:45:07 -0000 1.225
+++ kern/uipc_socket.c 12 Jul 2018 17:24:28 -0000
@@ -462,10 +462,14 @@ restart:
space = sbspace(so, &so->so_snd);
if (flags & MSG_OOB)
space += 1024;
- if ((atomic && resid > so->so_snd.sb_hiwat) ||
- (so->so_proto->pr_domain->dom_family != AF_UNIX &&
- clen > so->so_snd.sb_hiwat))
- snderr(EMSGSIZE);
+ if (so->so_proto->pr_domain->dom_family == AF_UNIX) {
+ if (atomic && resid > so->so_snd.sb_hiwat)
+ snderr(EMSGSIZE);
+ } else {
+ if (clen > so->so_snd.sb_hiwat ||
+ (atomic && resid > so->so_snd.sb_hiwat - clen))
+ snderr(EMSGSIZE);
+ }
if (space < clen ||
(space - clen < resid &&
(atomic || space < so->so_snd.sb_lowat))) {