]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[BUG] stream_sock: BUF_INFINITE_FORWARD broke splice on 64-bit platforms
authorWilly Tarreau <w@1wt.eu>
Sat, 28 Nov 2009 06:47:10 +0000 (07:47 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 28 Nov 2009 06:47:10 +0000 (07:47 +0100)
Yohan Tordjman at Dstorage found that upgrading haproxy to 1.4-dev4
caused truncated objects to be returned. An strace quickly exhibited
the issue which was 100% reproducible :

4297  epoll_wait(0, {}, 10, 0)          = 0
4297  epoll_wait(0, {{EPOLLIN, {u32=7, u64=7}}}, 10, 1000) = 1
4297  splice(0x7, 0, 0x5, 0, 0xffffffffffffffff, 0x3) = -1 EINVAL (Invalid argument)
4297  shutdown(7, 1 /* send */)         = 0
4297  close(7)                          = 0
4297  shutdown(2, 1 /* send */)         = 0
4297  close(2)                          = 0

This is caused by the fact that the forward length is taken from
BUF_INFINITE_FORWARD, which is -1. The problem does not appear
in 32-bit mode because this value is first cast to an unsigned
long, truncating it to 32-bit (4 GB). Setting an upper bound
fixes the issue.

Also, a second error check has been added for splice. If EINVAL
is returned, we fall back to recv().

src/stream_sock.c

index 479222aee2d66bd0ca7a81f8c09f90d12faabd91..c1f0dfae8c5b26a641391066e493a3ff29992b5b 100644 (file)
@@ -84,6 +84,10 @@ _syscall6(int, splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_ou
  */
 #define SPLICE_FULL_HINT       16*1448
 
+/* how many data we attempt to splice at once when the buffer is configured for
+ * infinite forwarding */
+#define MAX_SPLICE_AT_ONCE     (1<<30)
+
 /* Returns :
  *   -1 if splice is not possible or not possible anymore and we must switch to
  *      user-land copy (eg: to_forward reached)
@@ -138,7 +142,11 @@ static int stream_sock_splice_in(struct buffer *b, struct stream_interface *si)
        /* At this point, b->pipe is valid */
 
        while (1) {
-               max = b->to_forward;
+               if (b->to_forward == BUF_INFINITE_FORWARD)
+                       max = MAX_SPLICE_AT_ONCE;
+               else
+                       max = b->to_forward;
+
                if (!max) {
                        /* It looks like the buffer + the pipe already contain
                         * the maximum amount of data to be transferred. Try to
@@ -188,7 +196,7 @@ static int stream_sock_splice_in(struct buffer *b, struct stream_interface *si)
                                break;
                        }
 
-                       if (errno == ENOSYS) {
+                       if (errno == ENOSYS || errno == EINVAL) {
                                /* splice not supported on this end, disable it */
                                b->flags &= ~BF_KERN_SPLICING;
                                si->flags &= ~SI_FL_CAP_SPLICE;