]> Cypherpunks repositories - gostls13.git/commitdiff
internal/poll: cap reads and writes to 1GB on windows
authorAlex Brainman <alex.brainman@gmail.com>
Sun, 12 Aug 2018 01:16:26 +0000 (11:16 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Sun, 9 Sep 2018 03:57:19 +0000 (03:57 +0000)
Fixes #26923

Change-Id: I62fec814220ccdf7acd8d79a133d1add3f24cf98
Reviewed-on: https://go-review.googlesource.com/129137
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/internal/poll/fd_windows.go

index b08cec26256ec46317b1088811a65357dc28cf49..b5aaafda0235a2dc8c5959d7d7e9342bdc4c0595 100644 (file)
@@ -116,11 +116,17 @@ func (o *operation) InitBufs(buf *[][]byte) {
                o.bufs = o.bufs[:0]
        }
        for _, b := range *buf {
-               var p *byte
+               if len(b) == 0 {
+                       o.bufs = append(o.bufs, syscall.WSABuf{})
+                       continue
+               }
+               for len(b) > maxRW {
+                       o.bufs = append(o.bufs, syscall.WSABuf{Len: maxRW, Buf: &b[0]})
+                       b = b[maxRW:]
+               }
                if len(b) > 0 {
-                       p = &b[0]
+                       o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: &b[0]})
                }
-               o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: p})
        }
 }
 
@@ -461,6 +467,11 @@ func (fd *FD) Shutdown(how int) error {
        return syscall.Shutdown(fd.Sysfd, how)
 }
 
+// Windows ReadFile and WSARecv use DWORD (uint32) parameter to pass buffer length.
+// This prevents us reading blocks larger than 4GB.
+// See golang.org/issue/26923.
+const maxRW = 1 << 30 // 1GB is large enough and keeps subsequent reads aligned
+
 // Read implements io.Reader.
 func (fd *FD) Read(buf []byte) (int, error) {
        if err := fd.readLock(); err != nil {
@@ -468,6 +479,10 @@ func (fd *FD) Read(buf []byte) (int, error) {
        }
        defer fd.readUnlock()
 
+       if len(buf) > maxRW {
+               buf = buf[:maxRW]
+       }
+
        var n int
        var err error
        if fd.isFile || fd.isDir || fd.isConsole {
@@ -581,6 +596,10 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) {
        }
        defer fd.decref()
 
+       if len(b) > maxRW {
+               b = b[:maxRW]
+       }
+
        fd.l.Lock()
        defer fd.l.Unlock()
        curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
@@ -611,6 +630,9 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {
        if len(buf) == 0 {
                return 0, nil, nil
        }
+       if len(buf) > maxRW {
+               buf = buf[:maxRW]
+       }
        if err := fd.readLock(); err != nil {
                return 0, nil, err
        }
@@ -639,30 +661,42 @@ func (fd *FD) Write(buf []byte) (int, error) {
        }
        defer fd.writeUnlock()
 
-       var n int
-       var err error
-       if fd.isFile || fd.isDir || fd.isConsole {
-               fd.l.Lock()
-               defer fd.l.Unlock()
-               if fd.isConsole {
-                       n, err = fd.writeConsole(buf)
+       ntotal := 0
+       for len(buf) > 0 {
+               b := buf
+               if len(b) > maxRW {
+                       b = b[:maxRW]
+               }
+               var n int
+               var err error
+               if fd.isFile || fd.isDir || fd.isConsole {
+                       fd.l.Lock()
+                       defer fd.l.Unlock()
+                       if fd.isConsole {
+                               n, err = fd.writeConsole(b)
+                       } else {
+                               n, err = syscall.Write(fd.Sysfd, b)
+                       }
+                       if err != nil {
+                               n = 0
+                       }
                } else {
-                       n, err = syscall.Write(fd.Sysfd, buf)
+                       if race.Enabled {
+                               race.ReleaseMerge(unsafe.Pointer(&ioSync))
+                       }
+                       o := &fd.wop
+                       o.InitBuf(b)
+                       n, err = wsrv.ExecIO(o, func(o *operation) error {
+                               return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
+                       })
                }
+               ntotal += n
                if err != nil {
-                       n = 0
+                       return ntotal, err
                }
-       } else {
-               if race.Enabled {
-                       race.ReleaseMerge(unsafe.Pointer(&ioSync))
-               }
-               o := &fd.wop
-               o.InitBuf(buf)
-               n, err = wsrv.ExecIO(o, func(o *operation) error {
-                       return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
-               })
+               buf = buf[n:]
        }
-       return n, err
+       return ntotal, nil
 }
 
 // writeConsole writes len(b) bytes to the console File.
@@ -709,7 +743,7 @@ func (fd *FD) writeConsole(b []byte) (int, error) {
 }
 
 // Pwrite emulates the Unix pwrite system call.
-func (fd *FD) Pwrite(b []byte, off int64) (int, error) {
+func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
        // Call incref, not writeLock, because since pwrite specifies the
        // offset it is independent from other writes.
        if err := fd.incref(); err != nil {
@@ -724,16 +758,27 @@ func (fd *FD) Pwrite(b []byte, off int64) (int, error) {
                return 0, e
        }
        defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
-       o := syscall.Overlapped{
-               OffsetHigh: uint32(off >> 32),
-               Offset:     uint32(off),
-       }
-       var done uint32
-       e = syscall.WriteFile(fd.Sysfd, b, &done, &o)
-       if e != nil {
-               return 0, e
+
+       ntotal := 0
+       for len(buf) > 0 {
+               b := buf
+               if len(b) > maxRW {
+                       b = b[:maxRW]
+               }
+               var n uint32
+               o := syscall.Overlapped{
+                       OffsetHigh: uint32(off >> 32),
+                       Offset:     uint32(off),
+               }
+               e = syscall.WriteFile(fd.Sysfd, b, &n, &o)
+               ntotal += int(n)
+               if e != nil {
+                       return ntotal, e
+               }
+               buf = buf[n:]
+               off += int64(n)
        }
-       return int(done), nil
+       return ntotal, nil
 }
 
 // Writev emulates the Unix writev system call.
@@ -765,13 +810,26 @@ func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
                return 0, err
        }
        defer fd.writeUnlock()
-       o := &fd.wop
-       o.InitBuf(buf)
-       o.sa = sa
-       n, err := wsrv.ExecIO(o, func(o *operation) error {
-               return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
-       })
-       return n, err
+
+       ntotal := 0
+       for len(buf) > 0 {
+               b := buf
+               if len(b) > maxRW {
+                       b = b[:maxRW]
+               }
+               o := &fd.wop
+               o.InitBuf(b)
+               o.sa = sa
+               n, err := wsrv.ExecIO(o, func(o *operation) error {
+                       return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
+               })
+               ntotal += int(n)
+               if err != nil {
+                       return ntotal, err
+               }
+               buf = buf[n:]
+       }
+       return ntotal, nil
 }
 
 // Call ConnectEx. This doesn't need any locking, since it is only
@@ -986,6 +1044,10 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er
        }
        defer fd.readUnlock()
 
+       if len(p) > maxRW {
+               p = p[:maxRW]
+       }
+
        o := &fd.rop
        o.InitMsg(p, oob)
        o.rsa = new(syscall.RawSockaddrAny)
@@ -1004,6 +1066,10 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er
 
 // WriteMsg wraps the WSASendMsg network call.
 func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) {
+       if len(p) > maxRW {
+               return 0, 0, errors.New("packet is too large (only 1GB is allowed)")
+       }
+
        if err := fd.writeLock(); err != nil {
                return 0, 0, err
        }