Skip to content

Commit be0cc93

Browse files
shibijmgopherbot
authored andcommitted
net: avoid using Windows' TransmitFile on non-server machines
Windows API's TransmitFile function is limited to two concurrent operations on workstation and client versions of Windows. This change modifies the net.sendFile function to perform no work in such cases so that TransmitFile is avoided. Fixes #73746 Change-Id: Iba70d5d2758bf986e80c78254c8e9e10b39bb368 GitHub-Last-Rev: 315ddc0 GitHub-Pull-Request: #73758 Reviewed-on: https://go-review.googlesource.com/c/go/+/673855 Reviewed-by: Alex Brainman <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Quim Muntal <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent 0c7311e commit be0cc93

8 files changed

+72
-12
lines changed

src/internal/syscall/windows/types_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,7 @@ type FILE_COMPLETION_INFORMATION struct {
256256
Port syscall.Handle
257257
Key uintptr
258258
}
259+
260+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
261+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
262+
const VER_NT_WORKSTATION = 0x0000001

src/internal/syscall/windows/version_windows.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,53 @@ import (
1111
"unsafe"
1212
)
1313

14-
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
15-
type _OSVERSIONINFOW struct {
14+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
15+
type _OSVERSIONINFOEXW struct {
1616
osVersionInfoSize uint32
1717
majorVersion uint32
1818
minorVersion uint32
1919
buildNumber uint32
2020
platformId uint32
2121
csdVersion [128]uint16
22+
servicePackMajor uint16
23+
servicePackMinor uint16
24+
suiteMask uint16
25+
productType byte
26+
reserved byte
2227
}
2328

2429
// According to documentation, RtlGetVersion function always succeeds.
25-
//sys rtlGetVersion(info *_OSVERSIONINFOW) = ntdll.RtlGetVersion
30+
//sys rtlGetVersion(info *_OSVERSIONINFOEXW) = ntdll.RtlGetVersion
31+
32+
// Retrieves version information of the current Windows OS
33+
// from the RtlGetVersion API.
34+
func getVersionInfo() *_OSVERSIONINFOEXW {
35+
info := _OSVERSIONINFOEXW{}
36+
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
37+
rtlGetVersion(&info)
38+
return &info
39+
}
2640

2741
// Version retrieves the major, minor, and build version numbers
2842
// of the current Windows OS from the RtlGetVersion API.
2943
func Version() (major, minor, build uint32) {
30-
info := _OSVERSIONINFOW{}
31-
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
32-
rtlGetVersion(&info)
44+
info := getVersionInfo()
3345
return info.majorVersion, info.minorVersion, info.buildNumber
3446
}
3547

48+
// SupportUnlimitedTransmitFile indicates whether the current
49+
// Windows version's TransmitFile function imposes any
50+
// concurrent operation limits.
51+
// Workstation and client versions of Windows limit the number
52+
// of concurrent TransmitFile operations allowed on the system
53+
// to a maximum of two. Please see:
54+
// https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
55+
// https://golang.org/issue/73746
56+
var SupportUnlimitedTransmitFile = sync.OnceValue(func() bool {
57+
info := getVersionInfo()
58+
return info.productType != VER_NT_WORKSTATION
59+
})
60+
3661
var (
3762
supportTCPKeepAliveIdle bool
3863
supportTCPKeepAliveInterval bool

src/internal/syscall/windows/zsyscall_windows.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/net/sendfile.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import (
1212
"syscall"
1313
)
1414

15-
const supportsSendfile = true
16-
1715
// sendFile copies the contents of r to c using the sendfile
1816
// system call to minimize copies.
1917
//
@@ -22,6 +20,9 @@ const supportsSendfile = true
2220
//
2321
// if handled == false, sendFile performed no work.
2422
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
23+
if !supportsSendfile() {
24+
return 0, nil, false
25+
}
2526
var remain int64 = 0 // 0 writes the entire file
2627
lr, ok := r.(*io.LimitedReader)
2728
if ok {

src/net/sendfile_nonwindows.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build linux || (darwin && !ios) || dragonfly || freebsd || solaris
6+
7+
package net
8+
9+
// Always true except for workstation and client versions of Windows
10+
func supportsSendfile() bool {
11+
return true
12+
}

src/net/sendfile_stub.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ package net
88

99
import "io"
1010

11-
const supportsSendfile = false
11+
func supportsSendfile() bool {
12+
return false
13+
}
1214

1315
func sendFile(c *netFD, r io.Reader) (n int64, err error, handled bool) {
1416
return 0, nil, false

src/net/sendfile_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ const (
3131
// expectSendfile runs f, and verifies that internal/poll.SendFile successfully handles
3232
// a write to wantConn during f's execution.
3333
//
34-
// On platforms where supportsSendfile is false, expectSendfile runs f but does not
34+
// On platforms where supportsSendfile() is false, expectSendfile runs f but does not
3535
// expect a call to SendFile.
3636
func expectSendfile(t *testing.T, wantConn Conn, f func()) {
3737
t.Helper()
38-
if !supportsSendfile {
38+
if !supportsSendfile() {
3939
f()
4040
return
4141
}

src/net/sendfile_windows.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package net
6+
7+
import "internal/syscall/windows"
8+
9+
// Workstation and client versions of Windows limit the number
10+
// of concurrent TransmitFile operations allowed on the system
11+
// to a maximum of two. Please see:
12+
// https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
13+
// https://golang.org/issue/73746
14+
func supportsSendfile() bool {
15+
return windows.SupportUnlimitedTransmitFile()
16+
}

0 commit comments

Comments
 (0)