windows: avoid length overflow in NewNTString NewNTString initializes an NTString with RtlInitString, which cannot accurately represent sources whose NUL-terminated byte length exceeds MAX_USHORT. Reject strings whose byte representation cannot fit in the uint16 length fields before calling RtlInitString. Add a boundary round-trip test for NTString. Fixes golang/go#80103 Change-Id: Id38882a952330305e6c23d0adc86a8bf7920d30e Reviewed-on: https://go-review.googlesource.com/c/sys/+/793100 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 9755bca..e6966b4 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go
@@ -1728,11 +1728,15 @@ // the more common *uint16 string type. func NewNTString(s string) (*NTString, error) { var nts NTString - s8, err := BytePtrFromString(s) + s8, err := ByteSliceFromString(s) if err != nil { return nil, err } - RtlInitString(&nts, s8) + // The source string plus its terminating NUL must fit within MAX_USHORT. + if len(s8) > MAX_USHORT { + return nil, syscall.EINVAL + } + RtlInitString(&nts, &s8[0]) return &nts, nil }
diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index 98bba20..92da6bd 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go
@@ -1516,6 +1516,43 @@ } } +func TestRoundtripNTString(t *testing.T) { + // NTString maximum string length must fit in a uint16, less for terminal NUL. + maxString := strings.Repeat("*", windows.MAX_USHORT-1) + for _, test := range []struct { + s string + wantErr bool + }{{ + s: "", + }, { + s: "hello", + }, { + s: maxString, + }, { + s: maxString + "*", + wantErr: true, + }, { + s: "a\x00a", + wantErr: true, + }} { + nts, err := windows.NewNTString(test.s) + if (err != nil) != test.wantErr { + t.Errorf("NewNTString(%q): %v, wantErr:%v", test.s, err, test.wantErr) + continue + } + if err != nil { + if !errors.Is(err, syscall.EINVAL) { + t.Errorf("NewNTString(%q): %v, want %v", test.s, err, syscall.EINVAL) + } + continue + } + s2 := nts.String() + if test.s != s2 { + t.Errorf("round trip of %q = %q, wanted original", test.s, s2) + } + } +} + func TestIsProcessorFeaturePresent(t *testing.T) { // according to // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
diff --git a/windows/types_windows.go b/windows/types_windows.go index d2574a7..75a50b3 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go
@@ -169,6 +169,7 @@ FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192 FORMAT_MESSAGE_MAX_WIDTH_MASK = 255 + MAX_USHORT = 0xffff MAX_PATH = 260 MAX_LONG_PATH = 32768