summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/windows
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/windows')
-rw-r--r--library/std/src/sys/windows/args.rs6
-rw-r--r--library/std/src/sys/windows/c.rs100
-rw-r--r--library/std/src/sys/windows/c/windows_sys.lst5
-rw-r--r--library/std/src/sys/windows/c/windows_sys.rs26
-rw-r--r--library/std/src/sys/windows/net.rs2
-rw-r--r--library/std/src/sys/windows/os_str.rs12
-rw-r--r--library/std/src/sys/windows/path.rs42
-rw-r--r--library/std/src/sys/windows/process.rs15
-rw-r--r--library/std/src/sys/windows/rand.rs5
-rw-r--r--library/std/src/sys/windows/stdio.rs7
-rw-r--r--library/std/src/sys/windows/stdio/tests.rs6
11 files changed, 163 insertions, 63 deletions
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 5bfd8b52e..6b597f499 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -226,7 +226,7 @@ pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> i
// that it actually gets passed through on the command line or otherwise
// it will be dropped entirely when parsed on the other end.
ensure_no_nuls(arg)?;
- let arg_bytes = arg.bytes();
+ let arg_bytes = arg.as_os_str_bytes();
let (quote, escape) = match quote {
Quote::Always => (true, true),
Quote::Auto => {
@@ -297,7 +297,9 @@ pub(crate) fn make_bat_command_line(
// * `|<>` pipe/redirect characters.
const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>";
let force_quotes = match arg {
- Arg::Regular(arg) if !force_quotes => arg.bytes().iter().any(|c| SPECIAL.contains(c)),
+ Arg::Regular(arg) if !force_quotes => {
+ arg.as_os_str_bytes().iter().any(|c| SPECIAL.contains(c))
+ }
_ => force_quotes,
};
append_arg(&mut cmd, arg, force_quotes)?;
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 2bc40c474..d9ccba0e9 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -12,13 +12,13 @@ use crate::os::windows::io::{AsRawHandle, BorrowedHandle};
use crate::ptr;
use core::ffi::NonZero_c_ulong;
-#[path = "c/windows_sys.rs"] // c.rs is included from two places so we need to specify this
mod windows_sys;
pub use windows_sys::*;
pub type DWORD = c_ulong;
pub type NonZeroDWORD = NonZero_c_ulong;
pub type LARGE_INTEGER = c_longlong;
+#[cfg_attr(target_vendor = "uwp", allow(unused))]
pub type LONG = c_long;
pub type UINT = c_uint;
pub type WCHAR = u16;
@@ -267,6 +267,8 @@ pub unsafe fn getaddrinfo(
windows_sys::getaddrinfo(node.cast::<u8>(), service.cast::<u8>(), hints, res)
}
+cfg_if::cfg_if! {
+if #[cfg(not(target_vendor = "uwp"))] {
pub unsafe fn NtReadFile(
filehandle: BorrowedHandle<'_>,
event: HANDLE,
@@ -313,6 +315,8 @@ pub unsafe fn NtWriteFile(
key.map(|k| k as *const u32).unwrap_or(ptr::null()),
)
}
+}
+}
// Functions that aren't available on every version of Windows that we support,
// but we still use them and just provide some form of a fallback implementation.
@@ -376,4 +380,98 @@ compat_fn_with_fallback! {
) -> NTSTATUS {
panic!("keyed events not available")
}
+
+ // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically.
+ #[cfg(target_vendor = "uwp")]
+ pub fn NtCreateFile(
+ filehandle: *mut HANDLE,
+ desiredaccess: FILE_ACCESS_RIGHTS,
+ objectattributes: *const OBJECT_ATTRIBUTES,
+ iostatusblock: *mut IO_STATUS_BLOCK,
+ allocationsize: *const i64,
+ fileattributes: FILE_FLAGS_AND_ATTRIBUTES,
+ shareaccess: FILE_SHARE_MODE,
+ createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
+ createoptions: NTCREATEFILE_CREATE_OPTIONS,
+ eabuffer: *const ::core::ffi::c_void,
+ ealength: u32
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ #[cfg(target_vendor = "uwp")]
+ pub fn NtReadFile(
+ filehandle: BorrowedHandle<'_>,
+ event: HANDLE,
+ apcroutine: PIO_APC_ROUTINE,
+ apccontext: *mut c_void,
+ iostatusblock: &mut IO_STATUS_BLOCK,
+ buffer: *mut crate::mem::MaybeUninit<u8>,
+ length: ULONG,
+ byteoffset: Option<&LARGE_INTEGER>,
+ key: Option<&ULONG>
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ #[cfg(target_vendor = "uwp")]
+ pub fn NtWriteFile(
+ filehandle: BorrowedHandle<'_>,
+ event: HANDLE,
+ apcroutine: PIO_APC_ROUTINE,
+ apccontext: *mut c_void,
+ iostatusblock: &mut IO_STATUS_BLOCK,
+ buffer: *const u8,
+ length: ULONG,
+ byteoffset: Option<&LARGE_INTEGER>,
+ key: Option<&ULONG>
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ #[cfg(target_vendor = "uwp")]
+ pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 {
+ Status as u32
+ }
+}
+
+// # Arm32 shim
+//
+// AddVectoredExceptionHandler and WSAStartup use platform-specific types.
+// However, Microsoft no longer supports thumbv7a so definitions for those targets
+// are not included in the win32 metadata. We work around that by defining them here.
+//
+// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys
+cfg_if::cfg_if! {
+if #[cfg(not(target_vendor = "uwp"))] {
+ #[link(name = "kernel32")]
+ extern "system" {
+ pub fn AddVectoredExceptionHandler(
+ first: u32,
+ handler: PVECTORED_EXCEPTION_HANDLER,
+ ) -> *mut c_void;
+ }
+ pub type PVECTORED_EXCEPTION_HANDLER = Option<
+ unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32,
+ >;
+ #[repr(C)]
+ pub struct EXCEPTION_POINTERS {
+ pub ExceptionRecord: *mut EXCEPTION_RECORD,
+ pub ContextRecord: *mut CONTEXT,
+ }
+ #[cfg(target_arch = "arm")]
+ pub enum CONTEXT {}
+}}
+
+#[link(name = "ws2_32")]
+extern "system" {
+ pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32;
+}
+#[cfg(target_arch = "arm")]
+#[repr(C)]
+pub struct WSADATA {
+ pub wVersion: u16,
+ pub wHighVersion: u16,
+ pub szDescription: [u8; 257],
+ pub szSystemStatus: [u8; 129],
+ pub iMaxSockets: u16,
+ pub iMaxUdpDg: u16,
+ pub lpVendorInfo: PSTR,
}
diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst
index 3e454199f..631aedd26 100644
--- a/library/std/src/sys/windows/c/windows_sys.lst
+++ b/library/std/src/sys/windows/c/windows_sys.lst
@@ -1930,6 +1930,7 @@ Windows.Win32.Foundation.SetLastError
Windows.Win32.Foundation.STATUS_DELETE_PENDING
Windows.Win32.Foundation.STATUS_END_OF_FILE
Windows.Win32.Foundation.STATUS_INVALID_PARAMETER
+Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED
Windows.Win32.Foundation.STATUS_PENDING
Windows.Win32.Foundation.STATUS_SUCCESS
Windows.Win32.Foundation.TRUE
@@ -2170,7 +2171,6 @@ Windows.Win32.Networking.WinSock.WSARecv
Windows.Win32.Networking.WinSock.WSASend
Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND
Windows.Win32.Networking.WinSock.WSASocketW
-Windows.Win32.Networking.WinSock.WSAStartup
Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE
Windows.Win32.Networking.WinSock.WSASYSNOTREADY
Windows.Win32.Networking.WinSock.WSATRY_AGAIN
@@ -2418,12 +2418,10 @@ Windows.Win32.System.Console.STD_HANDLE
Windows.Win32.System.Console.STD_INPUT_HANDLE
Windows.Win32.System.Console.STD_OUTPUT_HANDLE
Windows.Win32.System.Console.WriteConsoleW
-Windows.Win32.System.Diagnostics.Debug.AddVectoredExceptionHandler
Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128
Windows.Win32.System.Diagnostics.Debug.CONTEXT
Windows.Win32.System.Diagnostics.Debug.CONTEXT
Windows.Win32.System.Diagnostics.Debug.CONTEXT
-Windows.Win32.System.Diagnostics.Debug.EXCEPTION_POINTERS
Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD
Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE
Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT
@@ -2436,7 +2434,6 @@ Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS
Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS
Windows.Win32.System.Diagnostics.Debug.FormatMessageW
Windows.Win32.System.Diagnostics.Debug.M128A
-Windows.Win32.System.Diagnostics.Debug.PVECTORED_EXCEPTION_HANDLER
Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT
Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT
Windows.Win32.System.Environment.FreeEnvironmentStringsW
diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs
index 36a30f6ba..023770871 100644
--- a/library/std/src/sys/windows/c/windows_sys.rs
+++ b/library/std/src/sys/windows/c/windows_sys.rs
@@ -40,13 +40,6 @@ extern "system" {
}
#[link(name = "kernel32")]
extern "system" {
- pub fn AddVectoredExceptionHandler(
- first: u32,
- handler: PVECTORED_EXCEPTION_HANDLER,
- ) -> *mut ::core::ffi::c_void;
-}
-#[link(name = "kernel32")]
-extern "system" {
pub fn CancelIo(hfile: HANDLE) -> BOOL;
}
#[link(name = "kernel32")]
@@ -712,10 +705,6 @@ extern "system" {
}
#[link(name = "ws2_32")]
extern "system" {
- pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32;
-}
-#[link(name = "ws2_32")]
-extern "system" {
pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET;
}
#[link(name = "ws2_32")]
@@ -3029,17 +3018,6 @@ pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32;
pub type EXCEPTION_DISPOSITION = i32;
pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32;
#[repr(C)]
-pub struct EXCEPTION_POINTERS {
- pub ExceptionRecord: *mut EXCEPTION_RECORD,
- pub ContextRecord: *mut CONTEXT,
-}
-impl ::core::marker::Copy for EXCEPTION_POINTERS {}
-impl ::core::clone::Clone for EXCEPTION_POINTERS {
- fn clone(&self) -> Self {
- *self
- }
-}
-#[repr(C)]
pub struct EXCEPTION_RECORD {
pub ExceptionCode: NTSTATUS,
pub ExceptionFlags: u32,
@@ -3748,9 +3726,6 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32;
pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32;
pub const PROGRESS_CONTINUE: u32 = 0u32;
pub type PSTR = *mut u8;
-pub type PVECTORED_EXCEPTION_HANDLER = ::core::option::Option<
- unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32,
->;
pub type PWSTR = *mut u16;
pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32;
pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32;
@@ -3888,6 +3863,7 @@ pub type STARTUPINFOW_FLAGS = u32;
pub const STATUS_DELETE_PENDING: NTSTATUS = -1073741738i32;
pub const STATUS_END_OF_FILE: NTSTATUS = -1073741807i32;
pub const STATUS_INVALID_PARAMETER: NTSTATUS = -1073741811i32;
+pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = -1073741822i32;
pub const STATUS_PENDING: NTSTATUS = 259i32;
pub const STATUS_SUCCESS: NTSTATUS = 0i32;
pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32;
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index 2404bbe2b..1ae42cb7e 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -159,7 +159,7 @@ impl Socket {
}
let mut timeout = c::timeval {
- tv_sec: timeout.as_secs() as c_long,
+ tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long,
tv_usec: (timeout.subsec_nanos() / 1000) as c_long,
};
diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs
index 2f2b0e56e..16c4f55c6 100644
--- a/library/std/src/sys/windows/os_str.rs
+++ b/library/std/src/sys/windows/os_str.rs
@@ -152,11 +152,21 @@ impl Buf {
impl Slice {
#[inline]
+ pub fn as_os_str_bytes(&self) -> &[u8] {
+ self.inner.as_bytes()
+ }
+
+ #[inline]
+ pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice {
+ mem::transmute(Wtf8::from_bytes_unchecked(s))
+ }
+
+ #[inline]
pub fn from_str(s: &str) -> &Slice {
unsafe { mem::transmute(Wtf8::from_str(s)) }
}
- pub fn to_str(&self) -> Option<&str> {
+ pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> {
self.inner.as_str()
}
diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs
index c3573d14c..c9c2d10e6 100644
--- a/library/std/src/sys/windows/path.rs
+++ b/library/std/src/sys/windows/path.rs
@@ -1,7 +1,6 @@
use super::{c, fill_utf16_buf, to_u16s};
use crate::ffi::{OsStr, OsString};
use crate::io;
-use crate::mem;
use crate::path::{Path, PathBuf, Prefix};
use crate::ptr;
@@ -11,16 +10,6 @@ mod tests;
pub const MAIN_SEP_STR: &str = "\\";
pub const MAIN_SEP: char = '\\';
-/// # Safety
-///
-/// `bytes` must be a valid wtf8 encoded slice
-#[inline]
-unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr {
- // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8,
- // which is compatible with &[u8].
- mem::transmute(bytes)
-}
-
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/' || b == b'\\'
@@ -33,12 +22,12 @@ pub fn is_verbatim_sep(b: u8) -> bool {
/// Returns true if `path` looks like a lone filename.
pub(crate) fn is_file_name(path: &OsStr) -> bool {
- !path.bytes().iter().copied().any(is_sep_byte)
+ !path.as_os_str_bytes().iter().copied().any(is_sep_byte)
}
pub(crate) fn has_trailing_slash(path: &OsStr) -> bool {
- let is_verbatim = path.bytes().starts_with(br"\\?\");
+ let is_verbatim = path.as_os_str_bytes().starts_with(br"\\?\");
let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte };
- if let Some(&c) = path.bytes().last() { is_separator(c) } else { false }
+ if let Some(&c) = path.as_os_str_bytes().last() { is_separator(c) } else { false }
}
/// Appends a suffix to a path.
@@ -60,7 +49,7 @@ impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
fn get_prefix(path: &OsStr) -> [u8; LEN] {
let mut prefix = [0; LEN];
// SAFETY: Only ASCII characters are modified.
- for (i, &ch) in path.bytes().iter().take(LEN).enumerate() {
+ for (i, &ch) in path.as_os_str_bytes().iter().take(LEN).enumerate() {
prefix[i] = if ch == b'/' { b'\\' } else { ch };
}
prefix
@@ -93,7 +82,7 @@ impl<'a> PrefixParserSlice<'a, '_> {
}
fn prefix_bytes(&self) -> &'a [u8] {
- &self.path.bytes()[..self.index]
+ &self.path.as_os_str_bytes()[..self.index]
}
fn finish(self) -> &'a OsStr {
@@ -101,7 +90,7 @@ impl<'a> PrefixParserSlice<'a, '_> {
// &[u8] and back. This is safe to do because (1) we only look at ASCII
// contents of the encoding and (2) new &OsStr values are produced only
// from ASCII-bounded slices of existing &OsStr values.
- unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) }
+ unsafe { OsStr::from_os_str_bytes_unchecked(&self.path.as_os_str_bytes()[self.index..]) }
}
}
@@ -173,7 +162,7 @@ fn parse_drive(path: &OsStr) -> Option<u8> {
drive.is_ascii_alphabetic()
}
- match path.bytes() {
+ match path.as_os_str_bytes() {
[drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
_ => None,
}
@@ -182,7 +171,7 @@ fn parse_drive(path: &OsStr) -> Option<u8> {
// Parses a drive prefix exactly, e.g. "C:"
fn parse_drive_exact(path: &OsStr) -> Option<u8> {
// only parse two bytes: the drive letter and the drive separator
- if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
+ if path.as_os_str_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
parse_drive(path)
} else {
None
@@ -196,21 +185,26 @@ fn parse_drive_exact(path: &OsStr) -> Option<u8> {
fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
let separator = if verbatim { is_verbatim_sep } else { is_sep_byte };
- match path.bytes().iter().position(|&x| separator(x)) {
+ match path.as_os_str_bytes().iter().position(|&x| separator(x)) {
Some(separator_start) => {
let separator_end = separator_start + 1;
- let component = &path.bytes()[..separator_start];
+ let component = &path.as_os_str_bytes()[..separator_start];
// Panic safe
// The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
- let path = &path.bytes()[separator_end..];
+ let path = &path.as_os_str_bytes()[separator_end..];
// SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
// is encoded in a single byte, therefore `bytes[separator_start]` and
// `bytes[separator_end]` must be code point boundaries and thus
// `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
- unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) }
+ unsafe {
+ (
+ OsStr::from_os_str_bytes_unchecked(component),
+ OsStr::from_os_str_bytes_unchecked(path),
+ )
+ }
}
None => (path, OsStr::new("")),
}
@@ -329,7 +323,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
// Verbatim paths should not be modified.
if prefix.map(|x| x.is_verbatim()).unwrap_or(false) {
// NULs in verbatim paths are rejected for consistency.
- if path.bytes().contains(&0) {
+ if path.as_os_str_bytes().contains(&0) {
return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"strings passed to WinAPI cannot contain NULs",
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index df3667c0f..e3493cbb8 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -395,7 +395,7 @@ fn resolve_exe<'a>(
// Test if the file name has the `exe` extension.
// This does a case-insensitive `ends_with`.
let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() {
- exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..]
+ exe_path.as_os_str_bytes()[exe_path.len() - EXE_SUFFIX.len()..]
.eq_ignore_ascii_case(EXE_SUFFIX.as_bytes())
} else {
false
@@ -425,7 +425,7 @@ fn resolve_exe<'a>(
// From the `CreateProcessW` docs:
// > If the file name does not contain an extension, .exe is appended.
// Note that this rule only applies when searching paths.
- let has_extension = exe_path.bytes().contains(&b'.');
+ let has_extension = exe_path.as_os_str_bytes().contains(&b'.');
// Search the directories given by `search_paths`.
let result = search_paths(parent_paths, child_paths, |mut path| {
@@ -595,7 +595,16 @@ pub struct Process {
impl Process {
pub fn kill(&mut self) -> io::Result<()> {
- cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?;
+ let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) };
+ if result == c::FALSE {
+ let error = unsafe { c::GetLastError() };
+ // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been
+ // terminated (by us, or for any other reason). So check if the process was actually
+ // terminated, and if so, do not return an error.
+ if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() {
+ return Err(crate::io::Error::from_raw_os_error(error as i32));
+ }
+ }
Ok(())
}
diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs
index bca4e38d9..5d8fd1378 100644
--- a/library/std/src/sys/windows/rand.rs
+++ b/library/std/src/sys/windows/rand.rs
@@ -1,5 +1,3 @@
-use crate::ffi::c_void;
-use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::c;
@@ -25,6 +23,9 @@ pub fn hashmap_random_keys() -> (u64, u64) {
#[cfg(not(target_vendor = "uwp"))]
#[inline(never)]
fn fallback_rng() -> (u64, u64) {
+ use crate::ffi::c_void;
+ use crate::io;
+
let mut v = (0, 0);
let ret = unsafe {
c::RtlGenRandom(&mut v as *mut _ as *mut c_void, mem::size_of_val(&v) as c::ULONG)
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
index 2e3e0859d..3fcaaa508 100644
--- a/library/std/src/sys/windows/stdio.rs
+++ b/library/std/src/sys/windows/stdio.rs
@@ -11,6 +11,9 @@ use crate::sys::cvt;
use crate::sys::handle::Handle;
use core::str::utf8_char_width;
+#[cfg(test)]
+mod tests;
+
// Don't cache handles but get them fresh for every read/write. This allows us to track changes to
// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490.
pub struct Stdin {
@@ -383,6 +386,10 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {
debug_assert!(utf16.len() <= c::c_int::MAX as usize);
debug_assert!(utf8.len() <= c::c_int::MAX as usize);
+ if utf16.is_empty() {
+ return Ok(0);
+ }
+
let result = unsafe {
c::WideCharToMultiByte(
c::CP_UTF8, // CodePage
diff --git a/library/std/src/sys/windows/stdio/tests.rs b/library/std/src/sys/windows/stdio/tests.rs
new file mode 100644
index 000000000..1e53e0bee
--- /dev/null
+++ b/library/std/src/sys/windows/stdio/tests.rs
@@ -0,0 +1,6 @@
+use super::utf16_to_utf8;
+
+#[test]
+fn zero_size_read() {
+ assert_eq!(utf16_to_utf8(&[], &mut []).unwrap(), 0);
+}