From a2818c5728833ffc1988eb24b6648dc09554d19d Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Sat, 30 Sep 2023 00:14:34 -0700 Subject: [PATCH] Include info about system call errors in some exceptions from operating on named mutexes (#92603) * Include info about system call errors in some exceptions from operating on named mutexes - Added new PAL APIs for creating and opening mutexes that take a string buffer for system call error info. These are called with a stack-allocated buffer and upon error the system call errors are appended to the exception message. - When there is a system call failure that leads to the PAL API failing, some info is appended to the error string, including the system call, relevant arguments, return value, and `errno` - `chmod` on OSX seemingly can be interrupted by signals, fixed to retry. Also fixed a couple other small things. Fixes https://github.com/dotnet/runtime/issues/89090 --- .../System.Private.CoreLib.csproj | 1 + .../System/Threading/Mutex.CoreCLR.Unix.cs | 135 +++++++ src/coreclr/pal/inc/pal.h | 17 + src/coreclr/pal/src/configure.cmake | 1 + src/coreclr/pal/src/include/pal/mutex.hpp | 16 +- .../pal/src/include/pal/sharedmemory.h | 35 +- src/coreclr/pal/src/include/pal/utils.h | 2 + src/coreclr/pal/src/misc/utils.cpp | 58 +++ .../pal/src/sharedmemory/sharedmemory.cpp | 382 ++++++++++++++++-- src/coreclr/pal/src/synchmgr/wait.cpp | 2 +- src/coreclr/pal/src/synchobj/mutex.cpp | 230 ++++++++--- src/coreclr/vm/qcallentrypoints.cpp | 2 + .../Common/src/System/IO/Win32Marshal.cs | 20 +- .../src/Resources/Strings.resx | 3 + .../System.Private.CoreLib.Shared.projitems | 4 +- .../src/System/Threading/Mutex.Windows.cs | 12 - 16 files changed, 784 insertions(+), 136 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 888f3f8b1498e..20073739b2bbd 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -290,6 +290,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs new file mode 100644 index 0000000000000..52233ffc583b1 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading +{ + /// + /// Synchronization primitive that can also be used for interprocess synchronization + /// + public sealed partial class Mutex : WaitHandle + { + private void CreateMutexCore(bool initiallyOwned, string? name, out bool createdNew) + { + SafeWaitHandle mutexHandle = CreateMutexCore(initiallyOwned, name, out int errorCode, out string? errorDetails); + if (mutexHandle.IsInvalid) + { + mutexHandle.SetHandleAsInvalid(); + if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) + // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 + throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); + if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails); + } + + createdNew = errorCode != Interop.Errors.ERROR_ALREADY_EXISTS; + SafeWaitHandle = mutexHandle; + } + + private static OpenExistingResult OpenExistingWorker(string name, out Mutex? result) + { + ArgumentException.ThrowIfNullOrEmpty(name); + + result = null; + // To allow users to view & edit the ACL's, call OpenMutex + // with parameters to allow us to view & edit the ACL. This will + // fail if we don't have permission to view or edit the ACL's. + // If that happens, ask for less permissions. + SafeWaitHandle myHandle = OpenMutexCore(name, out int errorCode, out string? errorDetails); + + if (myHandle.IsInvalid) + { + myHandle.Dispose(); + + if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) + { + // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 + throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); + } + if (Interop.Errors.ERROR_FILE_NOT_FOUND == errorCode || Interop.Errors.ERROR_INVALID_NAME == errorCode) + return OpenExistingResult.NameNotFound; + if (Interop.Errors.ERROR_PATH_NOT_FOUND == errorCode) + return OpenExistingResult.PathNotFound; + if (Interop.Errors.ERROR_INVALID_HANDLE == errorCode) + return OpenExistingResult.NameInvalid; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails); + } + + result = new Mutex(myHandle); + return OpenExistingResult.Success; + } + + // Note: To call ReleaseMutex, you must have an ACL granting you + // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value + // in a Mutex's ACL is MUTEX_ALL_ACCESS (0x1F0001). + public void ReleaseMutex() + { + if (!Interop.Kernel32.ReleaseMutex(SafeWaitHandle)) + { + throw new ApplicationException(SR.Arg_SynchronizationLockException); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Unix-specific implementation + + private const int SystemCallErrorsBufferSize = 256; + + private static unsafe SafeWaitHandle CreateMutexCore( + bool initialOwner, + string? name, + out int errorCode, + out string? errorDetails) + { + byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize]; + SafeWaitHandle mutexHandle = CreateMutex(initialOwner, name, systemCallErrors, SystemCallErrorsBufferSize); + + // Get the error code even if the handle is valid, as it could be ERROR_ALREADY_EXISTS, indicating that the mutex + // already exists and was opened + errorCode = Marshal.GetLastPInvokeError(); + + errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null; + return mutexHandle; + } + + private static unsafe SafeWaitHandle OpenMutexCore(string name, out int errorCode, out string? errorDetails) + { + byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize]; + SafeWaitHandle mutexHandle = OpenMutex(name, systemCallErrors, SystemCallErrorsBufferSize); + errorCode = mutexHandle.IsInvalid ? Marshal.GetLastPInvokeError() : Interop.Errors.ERROR_SUCCESS; + errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null; + return mutexHandle; + } + + private static unsafe string? GetErrorDetails(byte* systemCallErrors) + { + int systemCallErrorsLength = + new ReadOnlySpan(systemCallErrors, SystemCallErrorsBufferSize).IndexOf((byte)'\0'); + if (systemCallErrorsLength > 0) + { + try + { + return + SR.Format(SR.Unix_SystemCallErrors, Encoding.UTF8.GetString(systemCallErrors, systemCallErrorsLength)); + } + catch { } // avoid hiding the original error due to an error here + } + + return null; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_CreateMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + private static unsafe partial SafeWaitHandle CreateMutex([MarshalAs(UnmanagedType.Bool)] bool initialOwner, string? name, byte* systemCallErrors, uint systemCallErrorsBufferSize); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_OpenMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + private static unsafe partial SafeWaitHandle OpenMutex(string name, byte* systemCallErrors, uint systemCallErrorsBufferSize); + } +} diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 8e9c70d33ec78..b29d207511788 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -1048,6 +1048,15 @@ CreateMutexExW( IN DWORD dwFlags, IN DWORD dwDesiredAccess); +PALIMPORT +HANDLE +PALAPI +PAL_CreateMutexW( + IN BOOL bInitialOwner, + IN LPCWSTR lpName, + IN LPSTR lpSystemCallErrors, + IN DWORD dwSystemCallErrorsBufferSize); + // CreateMutexExW: dwFlags #define CREATE_MUTEX_INITIAL_OWNER ((DWORD)0x1) @@ -1061,6 +1070,14 @@ OpenMutexW( IN BOOL bInheritHandle, IN LPCWSTR lpName); +PALIMPORT +HANDLE +PALAPI +PAL_OpenMutexW( + IN LPCWSTR lpName, + IN LPSTR lpSystemCallErrors, + IN DWORD dwSystemCallErrorsBufferSize); + #ifdef UNICODE #define OpenMutex OpenMutexW #endif diff --git a/src/coreclr/pal/src/configure.cmake b/src/coreclr/pal/src/configure.cmake index 304bc3461e9f3..5366932629c62 100644 --- a/src/coreclr/pal/src/configure.cmake +++ b/src/coreclr/pal/src/configure.cmake @@ -133,6 +133,7 @@ check_function_exists(semget HAS_SYSV_SEMAPHORES) check_function_exists(pthread_mutex_init HAS_PTHREAD_MUTEXES) check_function_exists(ttrace HAVE_TTRACE) check_function_exists(pipe2 HAVE_PIPE2) +check_function_exists(strerrorname_np HAVE_STRERRORNAME_NP) check_cxx_source_compiles(" #include diff --git a/src/coreclr/pal/src/include/pal/mutex.hpp b/src/coreclr/pal/src/include/pal/mutex.hpp index 464f0f72afb45..016668dafb162 100644 --- a/src/coreclr/pal/src/include/pal/mutex.hpp +++ b/src/coreclr/pal/src/include/pal/mutex.hpp @@ -32,6 +32,7 @@ namespace CorUnix PAL_ERROR InternalCreateMutex( + SharedMemorySystemCallErrors *errors, CPalThread *pThread, LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, @@ -47,6 +48,7 @@ namespace CorUnix PAL_ERROR InternalOpenMutex( + SharedMemorySystemCallErrors *errors, CPalThread *pThread, LPCSTR lpName, HANDLE *phMutex @@ -151,10 +153,10 @@ enum class MutexTryAcquireLockResult class MutexHelpers { public: - static void InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex); + static void InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex); static void DestroyMutex(pthread_mutex_t *mutex); - static MutexTryAcquireLockResult TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds); + static MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex, DWORD timeoutMilliseconds); static void ReleaseLock(pthread_mutex_t *mutex); }; #endif // NAMED_MUTEX_USE_PTHREAD_MUTEX @@ -172,7 +174,7 @@ class NamedMutexSharedData bool m_isAbandoned; public: - NamedMutexSharedData(); + NamedMutexSharedData(SharedMemorySystemCallErrors *errors); ~NamedMutexSharedData(); #if NAMED_MUTEX_USE_PTHREAD_MUTEX @@ -214,10 +216,10 @@ class NamedMutexProcessData : public SharedMemoryProcessDataBase bool m_hasRefFromLockOwnerThread; public: - static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef); - static SharedMemoryProcessDataHeader *Open(LPCSTR name); + static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool acquireLockIfCreated, bool *createdRef); + static SharedMemoryProcessDataHeader *Open(SharedMemorySystemCallErrors *errors, LPCSTR name); private: - static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef); + static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef); public: NamedMutexProcessData( @@ -248,7 +250,7 @@ class NamedMutexProcessData : public SharedMemoryProcessDataBase void SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next); public: - MutexTryAcquireLockResult TryAcquireLock(DWORD timeoutMilliseconds); + MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds); void ReleaseLock(); void Abandon(); private: diff --git a/src/coreclr/pal/src/include/pal/sharedmemory.h b/src/coreclr/pal/src/include/pal/sharedmemory.h index 88834b93d0673..fce6d2859cc05 100644 --- a/src/coreclr/pal/src/include/pal/sharedmemory.h +++ b/src/coreclr/pal/src/include/pal/sharedmemory.h @@ -85,6 +85,19 @@ class SharedMemoryException DWORD GetErrorCode() const; }; +class SharedMemorySystemCallErrors +{ +private: + char *m_buffer; + int m_bufferSize; + int m_length; + bool m_isTracking; + +public: + SharedMemorySystemCallErrors(char *buffer, int bufferSize); + void Append(LPCSTR format, ...); +}; + class SharedMemoryHelpers { private: @@ -106,20 +119,22 @@ class SharedMemoryHelpers static void BuildSharedFilesPath(PathCharString& destination, const char *suffix, int suffixByteCount); static bool AppendUInt32String(PathCharString& destination, UINT32 value); - static bool EnsureDirectoryExists(const char *path, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false); + static bool EnsureDirectoryExists(SharedMemorySystemCallErrors *errors, const char *path, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false); private: - static int Open(LPCSTR path, int flags, mode_t mode = static_cast(0)); + static int Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode = static_cast(0)); public: - static int OpenDirectory(LPCSTR path); - static int CreateOrOpenFile(LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr); + static int OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path); + static int CreateOrOpenFile(SharedMemorySystemCallErrors *errors, LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr); static void CloseFile(int fileDescriptor); - static SIZE_T GetFileSize(int fileDescriptor); - static void SetFileSize(int fileDescriptor, SIZE_T byteCount); + static int ChangeMode(LPCSTR path, mode_t mode); + + static SIZE_T GetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor); + static void SetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor, SIZE_T byteCount); - static void *MemoryMapFile(int fileDescriptor, SIZE_T byteCount); + static void *MemoryMapFile(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor, SIZE_T byteCount); - static bool TryAcquireFileLock(int fileDescriptor, int operation); + static bool TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation); static void ReleaseFileLock(int fileDescriptor); static void VerifyStringOperation(bool success); @@ -207,7 +222,7 @@ class SharedMemoryProcessDataHeader SharedMemoryProcessDataHeader *m_nextInProcessDataHeaderList; public: - static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef); + static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef); public: static SharedMemoryProcessDataHeader *PalObject_GetProcessDataHeader(CorUnix::IPalObject *object); @@ -260,7 +275,7 @@ class SharedMemoryManager public: static void AcquireCreationDeletionProcessLock(); static void ReleaseCreationDeletionProcessLock(); - static void AcquireCreationDeletionFileLock(); + static void AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors); static void ReleaseCreationDeletionFileLock(); public: diff --git a/src/coreclr/pal/src/include/pal/utils.h b/src/coreclr/pal/src/include/pal/utils.h index 83cf2b104c1ff..fdd5b3b965a16 100644 --- a/src/coreclr/pal/src/include/pal/utils.h +++ b/src/coreclr/pal/src/include/pal/utils.h @@ -212,3 +212,5 @@ class StringHolder }; #endif /* _PAL_UTILS_H_ */ + +const char *GetFriendlyErrorCodeString(int errorCode); diff --git a/src/coreclr/pal/src/misc/utils.cpp b/src/coreclr/pal/src/misc/utils.cpp index f279ef3d580c1..0d96cc991305a 100644 --- a/src/coreclr/pal/src/misc/utils.cpp +++ b/src/coreclr/pal/src/misc/utils.cpp @@ -366,3 +366,61 @@ BOOL IsRunningOnMojaveHardenedRuntime() } #endif // __APPLE__ + +const char *GetFriendlyErrorCodeString(int errorCode) +{ +#if HAVE_STRERRORNAME_NP + const char *error = strerrorname_np(errorCode); + if (error != nullptr) + { + return error; + } +#else // !HAVE_STRERRORNAME_NP + switch (errorCode) + { + case EACCES: return "EACCES"; + #if EAGAIN == EWOULDBLOCK + case EAGAIN: return "EAGAIN/EWOULDBLOCK"; + #else + case EAGAIN: return "EAGAIN"; + case EWOULDBLOCK: return "EWOULDBLOCK"; + #endif + case EBADF: return "EBADF"; + case EBUSY: return "EBUSY"; + case EDQUOT: return "EDQUOT"; + case EEXIST: return "EEXIST"; + case EFAULT: return "EFAULT"; + case EFBIG: return "EFBIG"; + case EINVAL: return "EINVAL"; + case EINTR: return "EINTR"; + case EIO: return "EIO"; + case EISDIR: return "EISDIR"; + case ELOOP: return "ELOOP"; + case EMFILE: return "EMFILE"; + case EMLINK: return "EMLINK"; + case ENAMETOOLONG: return "ENAMETOOLONG"; + case ENFILE: return "ENFILE"; + case ENODEV: return "ENODEV"; + case ENOENT: return "ENOENT"; + case ENOLCK: return "ENOLCK"; + case ENOMEM: return "ENOMEM"; + case ENOSPC: return "ENOSPC"; + #if ENOTSUP == EOPNOTSUPP + case ENOTSUP: return "ENOTSUP/EOPNOTSUPP"; + #else + case ENOTSUP: return "ENOTSUP"; + case EOPNOTSUPP: return "EOPNOTSUPP"; + #endif + case ENOTDIR: return "ENOTDIR"; + case ENOTEMPTY: return "ENOTEMPTY"; + case ENXIO: return "ENXIO"; + case EOVERFLOW: return "EOVERFLOW"; + case EPERM: return "EPERM"; + case EROFS: return "EROFS"; + case ETXTBSY: return "ETXTBSY"; + case EXDEV: return "EXDEV"; + } +#endif // HAVE_STRERRORNAME_NP + + return strerror(errorCode); +} diff --git a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp index a2342f23efa5c..ea5aae444dad0 100644 --- a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp +++ b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp @@ -1,14 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(SHMEM); // some headers have code with asserts, so do this first + #include "pal/sharedmemory.h" -#include "pal/dbgmsg.h" #include "pal/file.hpp" #include "pal/malloc.hpp" #include "pal/thread.hpp" #include "pal/virtual.h" #include "pal/process.h" +#include "pal/utils.h" #include #include @@ -23,8 +26,6 @@ using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(SHMEM); - #include "pal/sharedmemory.inl" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -59,6 +60,71 @@ DWORD SharedMemoryException::GetErrorCode() const return m_errorCode; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SharedMemorySystemCallErrors + +SharedMemorySystemCallErrors::SharedMemorySystemCallErrors(char *buffer, int bufferSize) + : m_buffer(buffer), m_bufferSize(bufferSize), m_length(0), m_isTracking(bufferSize != 0) +{ + _ASSERTE((buffer == nullptr) == (bufferSize == 0)); + _ASSERTE(bufferSize >= 0); +} + +void SharedMemorySystemCallErrors::Append(LPCSTR format, ...) +{ + if (!m_isTracking) + { + return; + } + + char *buffer = m_buffer; + _ASSERTE(buffer != nullptr); + int bufferSize = m_bufferSize; + _ASSERTE(bufferSize != 0); + int length = m_length; + _ASSERTE(length < bufferSize); + _ASSERTE(buffer[length] == '\0'); + if (length >= bufferSize - 1) + { + return; + } + + if (length != 0) + { + length++; // the previous null terminator will be changed to a space if the append succeeds + } + + va_list args; + va_start(args, format); + int result = _vsnprintf_s(buffer + length, bufferSize - length, bufferSize - 1 - length, format, args); + va_end(args); + + if (result == 0) + { + return; + } + + if (result < 0 || result >= bufferSize - length) + { + // There's not enough space to append this error, discard the append and stop tracking + if (length == 0) + { + buffer[0] = '\0'; + } + m_isTracking = false; + return; + } + + if (length != 0) + { + buffer[length - 1] = ' '; // change the previous null terminator to a space + } + + length += result; + _ASSERTE(buffer[length] == '\0'); + m_length = length; +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SharedMemoryHelpers @@ -94,6 +160,7 @@ SIZE_T SharedMemoryHelpers::AlignUp(SIZE_T value, SIZE_T alignment) } bool SharedMemoryHelpers::EnsureDirectoryExists( + SharedMemorySystemCallErrors *errors, const char *path, bool isGlobalLockAcquired, bool createIfNotExist, @@ -123,15 +190,39 @@ bool SharedMemoryHelpers::EnsureDirectoryExists( if (isGlobalLockAcquired) { - if (mkdir(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0) + int operationResult = mkdir(path, PermissionsMask_AllUsers_ReadWriteExecute); + if (operationResult != 0) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "mkdir(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;", + path, + operationResult, + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } - if (chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0) + + operationResult = ChangeMode(path, PermissionsMask_AllUsers_ReadWriteExecute); + if (operationResult != 0) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "chmod(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;", + path, + operationResult, + GetFriendlyErrorCodeString(errorCode)); + } + rmdir(path); throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } + return true; } @@ -140,13 +231,35 @@ bool SharedMemoryHelpers::EnsureDirectoryExists( if (mkdtemp(tempPath.OpenStringBuffer()) == nullptr) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "mkdtemp(\"%s\") == nullptr; errno == %s;", + (const char *)tempPath, + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } - if (chmod(tempPath, PermissionsMask_AllUsers_ReadWriteExecute) != 0) + + int operationResult = ChangeMode(tempPath, PermissionsMask_AllUsers_ReadWriteExecute); + if (operationResult != 0) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "chmod(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;", + (const char *)tempPath, + operationResult, + GetFriendlyErrorCodeString(errorCode)); + } + rmdir(tempPath); throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } + if (rename(tempPath, path) == 0) { return true; @@ -161,6 +274,27 @@ bool SharedMemoryHelpers::EnsureDirectoryExists( // If the path exists, check that it's a directory if (statResult != 0 || !(statInfo.st_mode & S_IFDIR)) { + if (errors != nullptr) + { + if (statResult != 0) + { + int errorCode = errno; + errors->Append( + "stat(\"%s\", ...) == %d; errno == %s;", + path, + statResult, + GetFriendlyErrorCodeString(errorCode)); + } + else + { + errors->Append( + "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & 0x%x) == 0;", + path, + (int)statInfo.st_mode, + (int)S_IFDIR); + } + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } @@ -176,6 +310,15 @@ bool SharedMemoryHelpers::EnsureDirectoryExists( { return true; } + + if (errors != nullptr) + { + errors->Append( + "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & CurrentUser_ReadWriteExecute) != CurrentUser_ReadWriteExecute;", + path, + (int)statInfo.st_mode); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } @@ -186,19 +329,27 @@ bool SharedMemoryHelpers::EnsureDirectoryExists( { return true; } - if (!createIfNotExist || chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0) + if (!createIfNotExist || ChangeMode(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0) { // We were not asked to create the path or we weren't able to set the new permissions. // As a last resort, check that at least the current user has full access. if ((statInfo.st_mode & PermissionsMask_CurrentUser_ReadWriteExecute) != PermissionsMask_CurrentUser_ReadWriteExecute) { + if (errors != nullptr) + { + errors->Append( + "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & CurrentUser_ReadWriteExecute) != CurrentUser_ReadWriteExecute;", + path, + (int)statInfo.st_mode); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } } return true; } -int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode) +int SharedMemoryHelpers::Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode) { int openErrorCode; @@ -213,6 +364,7 @@ int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode) openErrorCode = errno; } while (openErrorCode == EINTR); + SharedMemoryError sharedMemoryError; switch (openErrorCode) { case ENOENT: @@ -221,29 +373,48 @@ int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode) return -1; case ENAMETOOLONG: - throw SharedMemoryException(static_cast(SharedMemoryError::NameTooLong)); + sharedMemoryError = SharedMemoryError::NameTooLong; + break; case EMFILE: case ENFILE: case ENOMEM: - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); + sharedMemoryError = SharedMemoryError::OutOfMemory; + break; default: - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); + sharedMemoryError = SharedMemoryError::IO; + break; } + + if (sharedMemoryError != SharedMemoryError::NameTooLong && errors != nullptr) + { + errors->Append( + "open(\"%s\", 0x%x, 0x%x) == -1; errno == %s;", + path, + flags, + (int)mode, + GetFriendlyErrorCodeString(openErrorCode)); + } + + throw SharedMemoryException(static_cast(sharedMemoryError)); } -int SharedMemoryHelpers::OpenDirectory(LPCSTR path) +int SharedMemoryHelpers::OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path) { _ASSERTE(path != nullptr); _ASSERTE(path[0] != '\0'); - int fileDescriptor = Open(path, O_RDONLY); + int fileDescriptor = Open(errors, path, O_RDONLY); _ASSERTE(fileDescriptor != -1 || errno == ENOENT); return fileDescriptor; } -int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bool *createdRef) +int SharedMemoryHelpers::CreateOrOpenFile( + SharedMemorySystemCallErrors *errors, + LPCSTR path, + bool createIfNotExist, + bool *createdRef) { _ASSERTE(path != nullptr); _ASSERTE(path[0] != '\0'); @@ -252,7 +423,7 @@ int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bo // Try to open the file int openFlags = O_RDWR; - int fileDescriptor = Open(path, openFlags); + int fileDescriptor = Open(errors, path, openFlags); if (fileDescriptor != -1) { if (createdRef != nullptr) @@ -273,13 +444,24 @@ int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bo // File does not exist, create the file openFlags |= O_CREAT | O_EXCL; - fileDescriptor = Open(path, openFlags, PermissionsMask_AllUsers_ReadWrite); + fileDescriptor = Open(errors, path, openFlags, PermissionsMask_AllUsers_ReadWrite); _ASSERTE(fileDescriptor != -1); // The permissions mask passed to open() is filtered by the process' permissions umask, so open() may not set all of // the requested permissions. Use chmod() to set the proper permissions. - if (chmod(path, PermissionsMask_AllUsers_ReadWrite) != 0) + int operationResult = ChangeMode(path, PermissionsMask_AllUsers_ReadWrite); + if (operationResult != 0) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "chmod(\"%s\", AllUsers_ReadWrite) == %d; errno == %s;", + path, + operationResult, + GetFriendlyErrorCodeString(errorCode)); + } + CloseFile(fileDescriptor); unlink(path); throw SharedMemoryException(static_cast(SharedMemoryError::IO)); @@ -303,21 +485,54 @@ void SharedMemoryHelpers::CloseFile(int fileDescriptor) } while (closeResult != 0 && errno == EINTR); } -SIZE_T SharedMemoryHelpers::GetFileSize(int fileDescriptor) +int SharedMemoryHelpers::ChangeMode(LPCSTR path, mode_t mode) { + _ASSERTE(path != nullptr); + _ASSERTE(path[0] != '\0'); + + int chmodResult; + do + { + chmodResult = chmod(path, mode); + } while (chmodResult != 0 && errno == EINTR); + + return chmodResult; +} + +SIZE_T SharedMemoryHelpers::GetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor) +{ + _ASSERTE(filePath != nullptr); + _ASSERTE(filePath[0] != '\0'); _ASSERTE(fileDescriptor != -1); off_t endOffset = lseek(fileDescriptor, 0, SEEK_END); if (endOffset == static_cast(-1) || lseek(fileDescriptor, 0, SEEK_SET) == static_cast(-1)) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "lseek(\"%s\", 0, %s) == -1; errno == %s;", + filePath, + endOffset == (off_t)-1 ? "SEEK_END" : "SEEK_SET", + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } + return endOffset; } -void SharedMemoryHelpers::SetFileSize(int fileDescriptor, SIZE_T byteCount) +void SharedMemoryHelpers::SetFileSize( + SharedMemorySystemCallErrors *errors, + LPCSTR filePath, + int fileDescriptor, + SIZE_T byteCount) { + _ASSERTE(filePath != nullptr); + _ASSERTE(filePath[0] != '\0'); _ASSERTE(fileDescriptor != -1); _ASSERTE(static_cast(byteCount) == byteCount); @@ -328,15 +543,33 @@ void SharedMemoryHelpers::SetFileSize(int fileDescriptor, SIZE_T byteCount) { break; } - if (errno != EINTR) + + int errorCode = errno; + if (errorCode != EINTR) { + if (errors != nullptr) + { + errors->Append( + "ftruncate(\"%s\", %zu) == %d; errno == %s;", + filePath, + byteCount, + ftruncateResult, + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } } } -void *SharedMemoryHelpers::MemoryMapFile(int fileDescriptor, SIZE_T byteCount) +void *SharedMemoryHelpers::MemoryMapFile( + SharedMemorySystemCallErrors *errors, + LPCSTR filePath, + int fileDescriptor, + SIZE_T byteCount) { + _ASSERTE(filePath != nullptr); + _ASSERTE(filePath[0] != '\0'); _ASSERTE(fileDescriptor != -1); _ASSERTE(byteCount > sizeof(SharedMemorySharedDataHeader)); _ASSERTE(AlignDown(byteCount, GetVirtualPageSize()) == byteCount); @@ -346,32 +579,52 @@ void *SharedMemoryHelpers::MemoryMapFile(int fileDescriptor, SIZE_T byteCount) { return sharedMemoryBuffer; } - switch (errno) + + int errorCode = errno; + SharedMemoryError sharedMemoryError; + switch (errorCode) { + case EMFILE: case ENFILE: case ENOMEM: - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); + sharedMemoryError = SharedMemoryError::OutOfMemory; + break; default: - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); + sharedMemoryError = SharedMemoryError::IO; + break; } + + if (errors != nullptr) + { + errors->Append( + "mmap(nullptr, %zu, PROT_READ | PROT_WRITE, MAP_SHARED, \"%s\", 0) == MAP_FAILED; errno == %s;", + byteCount, + filePath, + GetFriendlyErrorCodeString(errorCode)); + } + + throw SharedMemoryException(static_cast(sharedMemoryError)); } -bool SharedMemoryHelpers::TryAcquireFileLock(int fileDescriptor, int operation) +bool SharedMemoryHelpers::TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation) { // A file lock is acquired once per file descriptor, so the caller will need to synchronize threads of this process _ASSERTE(fileDescriptor != -1); + _ASSERTE((operation & LOCK_EX) ^ (operation & LOCK_SH)); _ASSERTE(!(operation & LOCK_UN)); while (true) { - if (flock(fileDescriptor, operation) == 0) + int flockResult = flock(fileDescriptor, operation); + if (flockResult == 0) { return true; } int flockError = errno; + SharedMemoryError sharedMemoryError = SharedMemoryError::IO; switch (flockError) { case EWOULDBLOCK: @@ -380,9 +633,23 @@ bool SharedMemoryHelpers::TryAcquireFileLock(int fileDescriptor, int operation) case EINTR: continue; - default: - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); + case ENOLCK: + sharedMemoryError = SharedMemoryError::OutOfMemory; + break; + } + + if (errors != nullptr) + { + errors->Append( + "flock(%d, %s%s) == %d; errno == %s;", + fileDescriptor, + operation & LOCK_EX ? "LOCK_EX" : "LOCK_SH", + operation & LOCK_NB ? " | LOCK_NB" : "", + flockResult, + GetFriendlyErrorCodeString(flockError)); } + + throw SharedMemoryException(static_cast(sharedMemoryError)); } } @@ -558,6 +825,7 @@ void *SharedMemorySharedDataHeader::GetData() // SharedMemoryProcessDataHeader SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( + SharedMemorySystemCallErrors *errors, LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, @@ -657,14 +925,14 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( return processDataHeader; } - SharedMemoryManager::AcquireCreationDeletionFileLock(); + SharedMemoryManager::AcquireCreationDeletionFileLock(errors); autoCleanup.m_acquiredCreationDeletionFileLock = true; // Create the session directory SharedMemoryHelpers::VerifyStringOperation(SharedMemoryManager::CopySharedMemoryBasePath(filePath)); SharedMemoryHelpers::VerifyStringOperation(filePath.Append('/')); SharedMemoryHelpers::VerifyStringOperation(id.AppendSessionDirectoryName(filePath)); - if (!SharedMemoryHelpers::EnsureDirectoryExists(filePath, true /* isGlobalLockAcquired */, createIfNotExist)) + if (!SharedMemoryHelpers::EnsureDirectoryExists(errors, filePath, true /* isGlobalLockAcquired */, createIfNotExist)) { _ASSERTE(!createIfNotExist); return nullptr; @@ -677,7 +945,7 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( SharedMemoryHelpers::VerifyStringOperation(filePath.Append(id.GetName(), id.GetNameCharCount())); bool createdFile; - int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(filePath, createIfNotExist, &createdFile); + int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, filePath, createIfNotExist, &createdFile); if (fileDescriptor == -1) { _ASSERTE(!createIfNotExist); @@ -692,7 +960,7 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( // A shared file lock on the shared memory file would be held by any process that has opened the same file. Try to take // an exclusive lock on the file. Successfully acquiring an exclusive lock indicates that no process has a reference to // the shared memory file, and this process can reinitialize its contents. - if (SharedMemoryHelpers::TryAcquireFileLock(fileDescriptor, LOCK_EX | LOCK_NB)) + if (SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_EX | LOCK_NB)) { // The shared memory file is not being used, flag it as created so that its contents will be reinitialized SharedMemoryHelpers::ReleaseFileLock(fileDescriptor); @@ -711,18 +979,18 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( SIZE_T sharedDataTotalByteCount = SharedMemorySharedDataHeader::GetTotalByteCount(sharedDataByteCount); if (createdFile) { - SharedMemoryHelpers::SetFileSize(fileDescriptor, sharedDataTotalByteCount); + SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount); } else { - SIZE_T currentFileSize = SharedMemoryHelpers::GetFileSize(fileDescriptor); + SIZE_T currentFileSize = SharedMemoryHelpers::GetFileSize(errors, filePath, fileDescriptor); if (currentFileSize < sharedDataUsedByteCount) { throw SharedMemoryException(static_cast(SharedMemoryError::HeaderMismatch)); } if (currentFileSize < sharedDataTotalByteCount) { - SharedMemoryHelpers::SetFileSize(fileDescriptor, sharedDataTotalByteCount); + SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount); } } @@ -730,14 +998,23 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( // using the file. An exclusive file lock is attempted above to detect whether the file contents are valid, for the case // where a process crashes or is killed after the file is created. Since we already hold the creation/deletion locks, a // non-blocking file lock should succeed. - if (!SharedMemoryHelpers::TryAcquireFileLock(fileDescriptor, LOCK_SH | LOCK_NB)) + if (!SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_SH | LOCK_NB)) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "flock(\"%s\", LOCK_SH | LOCK_NB) == -1; errno == %s;", + (const char *)filePath, + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } autoCleanup.m_acquiredFileLock = true; // Map the file into memory, and initialize or validate the header - void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(fileDescriptor, sharedDataTotalByteCount); + void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(errors, filePath, fileDescriptor, sharedDataTotalByteCount); autoCleanup.m_mappedBuffer = mappedBuffer; autoCleanup.m_mappedBufferByteCount = sharedDataTotalByteCount; SharedMemorySharedDataHeader *sharedDataHeader; @@ -926,11 +1203,11 @@ void SharedMemoryProcessDataHeader::Close() bool releaseSharedData = false; try { - SharedMemoryManager::AcquireCreationDeletionFileLock(); + SharedMemoryManager::AcquireCreationDeletionFileLock(nullptr); autoReleaseCreationDeletionFileLock.m_acquired = true; SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor); - if (SharedMemoryHelpers::TryAcquireFileLock(m_fileDescriptor, LOCK_EX | LOCK_NB)) + if (SharedMemoryHelpers::TryAcquireFileLock(nullptr, m_fileDescriptor, LOCK_EX | LOCK_NB)) { SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor); releaseSharedData = true; @@ -1142,7 +1419,7 @@ void SharedMemoryManager::ReleaseCreationDeletionProcessLock() LeaveCriticalSection(&s_creationDeletionProcessLock); } -void SharedMemoryManager::AcquireCreationDeletionFileLock() +void SharedMemoryManager::AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors) { _ASSERTE(IsCreationDeletionProcessLockAcquired()); _ASSERTE(!IsCreationDeletionFileLockAcquired()); @@ -1150,27 +1427,48 @@ void SharedMemoryManager::AcquireCreationDeletionFileLock() if (s_creationDeletionLockFileDescriptor == -1) { if (!SharedMemoryHelpers::EnsureDirectoryExists( + errors, *gSharedFilesPath, false /* isGlobalLockAcquired */, false /* createIfNotExist */, true /* isSystemDirectory */)) { + _ASSERTE(errno == ENOENT); + if (errors != nullptr) + { + errors->Append("stat(\"%s\", ...) == -1; errno == ENOENT;", (const char *)*gSharedFilesPath); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } + SharedMemoryHelpers::EnsureDirectoryExists( + errors, *s_runtimeTempDirectoryPath, false /* isGlobalLockAcquired */); + SharedMemoryHelpers::EnsureDirectoryExists( + errors, *s_sharedMemoryDirectoryPath, false /* isGlobalLockAcquired */); - s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(*s_sharedMemoryDirectoryPath); + + s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(errors, *s_sharedMemoryDirectoryPath); if (s_creationDeletionLockFileDescriptor == -1) { + if (errors != nullptr) + { + int errorCode = errno; + errors->Append( + "open(\"%s\", O_RDONLY | O_CLOEXEC, 0) == -1; errno == %s;", + (const char *)*s_sharedMemoryDirectoryPath, + GetFriendlyErrorCodeString(errorCode)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } } - bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(s_creationDeletionLockFileDescriptor, LOCK_EX); + bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, s_creationDeletionLockFileDescriptor, LOCK_EX); _ASSERTE(acquiredFileLock); #ifdef _DEBUG s_creationDeletionFileLockOwnerThreadId = THREADSilentGetCurrentThreadId(); diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index d666d5101ba7a..5ae53759fa2be 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -439,7 +439,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( try { MutexTryAcquireLockResult tryAcquireLockResult = - static_cast(processDataHeader->GetData())->TryAcquireLock(dwMilliseconds); + static_cast(processDataHeader->GetData())->TryAcquireLock(nullptr, dwMilliseconds); switch (tryAcquireLockResult) { case MutexTryAcquireLockResult::AcquiredLock: diff --git a/src/coreclr/pal/src/synchobj/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp index 3fff2e7917fad..e3d63fe232b49 100644 --- a/src/coreclr/pal/src/synchobj/mutex.cpp +++ b/src/coreclr/pal/src/synchobj/mutex.cpp @@ -23,6 +23,7 @@ SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do t #include "pal/mutex.hpp" #include "pal/file.hpp" #include "pal/thread.hpp" +#include "pal/utils.h" #include "../synchmgr/synchmanager.hpp" @@ -92,33 +93,71 @@ static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, ARRAY_SIZE(anyMutexTypeI Function: CreateMutexW + See doc for PAL_CreateMutexW. +--*/ + +HANDLE +PALAPI +CreateMutexW( + IN LPSECURITY_ATTRIBUTES lpMutexAttributes, + IN BOOL bInitialOwner, + IN LPCWSTR lpName) +{ + return PAL_CreateMutexW(bInitialOwner, lpName, nullptr, 0); +} + +/*++ +Function: + PAL_CreateMutexW + Note: lpMutexAttributes currently ignored: -- Win32 object security not supported -- handles to mutex objects are not inheritable Parameters: - See MSDN doc. + lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information. + dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes. + + See MSDN docs on CreateMutexW for all other parameters. --*/ HANDLE PALAPI -CreateMutexW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, +PAL_CreateMutexW( IN BOOL bInitialOwner, - IN LPCWSTR lpName) + IN LPCWSTR lpName, + IN LPSTR lpSystemCallErrors, + IN DWORD dwSystemCallErrorsBufferSize) { HANDLE hMutex = NULL; PAL_ERROR palError; CPalThread *pthr = NULL; char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1]; - PERF_ENTRY(CreateMutexW); - ENTRY("CreateMutexW(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%S)\n", - lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:W16_NULLSTRING); + PERF_ENTRY(PAL_CreateMutexW); + ENTRY("PAL_CreateMutexW(bInitialOwner=%d, lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d\n", + bInitialOwner, + lpName, + lpName?lpName:W16_NULLSTRING, + lpSystemCallErrors, + dwSystemCallErrorsBufferSize); pthr = InternalGetCurrentThread(); + /* validate parameters */ + if ((int)dwSystemCallErrorsBufferSize < 0 || (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0)) + { + ERROR("One or more parameters are invalid\n"); + palError = ERROR_INVALID_PARAMETER; + goto CreateMutexWExit; + } + + if (lpSystemCallErrors != nullptr) + { + lpSystemCallErrors[0] = '\0'; + } + if (lpName != nullptr) { int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr); @@ -138,13 +177,17 @@ CreateMutexW( } } - palError = InternalCreateMutex( - pthr, - lpMutexAttributes, - bInitialOwner, - lpName == nullptr ? nullptr : utf8Name, - &hMutex - ); + { + SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize); + palError = InternalCreateMutex( + &errors, + pthr, + nullptr, + bInitialOwner, + lpName == nullptr ? nullptr : utf8Name, + &hMutex + ); + } CreateMutexWExit: // @@ -156,14 +199,14 @@ CreateMutexW( pthr->SetLastError(palError); - LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(CreateMutexW); + LOGEXIT("PAL_CreateMutexW returns HANDLE %p\n", hMutex); + PERF_EXIT(PAL_CreateMutexW); return hMutex; } /*++ Function: -CreateMutexW +CreateMutexExW Note: lpMutexAttributes currently ignored: @@ -195,14 +238,16 @@ CreateMutexExW( -- handles to mutex objects are not inheritable Parameters: + errors -- An optional wrapper for system call errors, for more detailed error information. pthr -- thread data for calling thread phEvent -- on success, receives the allocated mutex handle - See MSDN docs on CreateMutex for all other parameters + See MSDN docs on CreateMutex for all other parameters. --*/ PAL_ERROR CorUnix::InternalCreateMutex( + SharedMemorySystemCallErrors *errors, CPalThread *pthr, LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, @@ -286,7 +331,7 @@ CorUnix::InternalCreateMutex( SharedMemoryProcessDataHeader *processDataHeader; try { - processDataHeader = NamedMutexProcessData::CreateOrOpen(lpName, !!bInitialOwner, &createdNamedMutex); + processDataHeader = NamedMutexProcessData::CreateOrOpen(errors, lpName, !!bInitialOwner, &createdNamedMutex); } catch (SharedMemoryException ex) { @@ -512,7 +557,7 @@ OpenMutexA ( goto OpenMutexAExit; } - palError = InternalOpenMutex(pthr, lpName, &hMutex); + palError = InternalOpenMutex(nullptr, pthr, lpName, &hMutex); OpenMutexAExit: if (NO_ERROR != palError) @@ -529,11 +574,8 @@ OpenMutexA ( Function: OpenMutexW -Note: - dwDesiredAccess is currently ignored (no Win32 object security support) - bInheritHandle is currently ignored (handles to mutexes are not inheritable) - -See MSDN doc. +Parameters: + See doc for PAL_OpenMutexW. --*/ HANDLE @@ -542,26 +584,61 @@ OpenMutexW( IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN LPCWSTR lpName) +{ + return PAL_OpenMutexW(lpName, nullptr, 0); +} + +/*++ +Function: + PAL_OpenMutexW + +Note: + dwDesiredAccess is currently ignored (no Win32 object security support) + bInheritHandle is currently ignored (handles to mutexes are not inheritable) + +Parameters: + lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information. + dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes. + + See MSDN docs on OpenMutexW for all other parameters. +--*/ + +HANDLE +PALAPI +PAL_OpenMutexW( + IN LPCWSTR lpName, + IN LPSTR lpSystemCallErrors, + IN DWORD dwSystemCallErrorsBufferSize) { HANDLE hMutex = NULL; PAL_ERROR palError = NO_ERROR; CPalThread *pthr = NULL; char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1]; - PERF_ENTRY(OpenMutexW); - ENTRY("OpenMutexW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n", - dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING); + PERF_ENTRY(PAL_OpenMutexW); + ENTRY("PAL_OpenMutexW(lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d)\n", + lpName, + lpName?lpName:W16_NULLSTRING, + lpSystemCallErrors, + dwSystemCallErrorsBufferSize); pthr = InternalGetCurrentThread(); /* validate parameters */ - if (lpName == nullptr) + if (lpName == nullptr || + (int)dwSystemCallErrorsBufferSize < 0 || + (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0)) { - ERROR("name is NULL\n"); + ERROR("name is NULL or other parameters are invalid\n"); palError = ERROR_INVALID_PARAMETER; goto OpenMutexWExit; } + if (lpSystemCallErrors != nullptr) + { + lpSystemCallErrors[0] = '\0'; + } + { int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr); if (bytesWritten == 0) @@ -578,9 +655,10 @@ OpenMutexW( } goto OpenMutexWExit; } - } - palError = InternalOpenMutex(pthr, lpName == nullptr ? nullptr : utf8Name, &hMutex); + SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize); + palError = InternalOpenMutex(&errors, pthr, lpName == nullptr ? nullptr : utf8Name, &hMutex); + } OpenMutexWExit: if (NO_ERROR != palError) @@ -588,8 +666,8 @@ OpenMutexW( pthr->SetLastError(palError); } - LOGEXIT("OpenMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(OpenMutexW); + LOGEXIT("PAL_OpenMutexW returns HANDLE %p\n", hMutex); + PERF_EXIT(PAL_OpenMutexW); return hMutex; } @@ -599,6 +677,7 @@ OpenMutexW( InternalOpenMutex Parameters: + errors -- An optional wrapper for system call errors, for more detailed error information. pthr -- thread data for calling thread phEvent -- on success, receives the allocated mutex handle @@ -607,6 +686,7 @@ OpenMutexW( PAL_ERROR CorUnix::InternalOpenMutex( + SharedMemorySystemCallErrors *errors, CPalThread *pthr, LPCSTR lpName, HANDLE *phMutex @@ -645,7 +725,7 @@ CorUnix::InternalOpenMutex( SharedMemoryProcessDataHeader *processDataHeader; try { - processDataHeader = NamedMutexProcessData::Open(lpName); + processDataHeader = NamedMutexProcessData::Open(errors, lpName); } catch (SharedMemoryException ex) { @@ -746,7 +826,7 @@ DWORD SPINLOCKTryAcquire (LONG * lock) // MutexHelpers #if NAMED_MUTEX_USE_PTHREAD_MUTEX -void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex) +void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex) { _ASSERTE(mutex != nullptr); @@ -772,6 +852,11 @@ void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t * int error = pthread_mutexattr_init(&mutexAttributes); if (error != 0) { + if (errors != nullptr) + { + errors->Append("pthread_mutexattr_init(...) == %s;", GetFriendlyErrorCodeString(error)); + } + throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); } autoCleanup.m_mutexAttributes = &mutexAttributes; @@ -788,6 +873,11 @@ void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t * error = pthread_mutex_init(mutex, &mutexAttributes); if (error != 0) { + if (errors != nullptr) + { + errors->Append("pthread_mutex_init(...) == %s;", GetFriendlyErrorCodeString(error)); + } + throw SharedMemoryException(static_cast(error == EPERM ? SharedMemoryError::IO : SharedMemoryError::OutOfMemory)); } } @@ -800,7 +890,10 @@ void MutexHelpers::DestroyMutex(pthread_mutex_t *mutex) _ASSERTE(error == 0 || error == EBUSY); // the error will be EBUSY if the mutex is locked } -MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds) +MutexTryAcquireLockResult MutexHelpers::TryAcquireLock( + SharedMemorySystemCallErrors *errors, + pthread_mutex_t *mutex, + DWORD timeoutMilliseconds) { _ASSERTE(mutex != nullptr); @@ -850,7 +943,19 @@ MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, D throw SharedMemoryException(static_cast(NamedMutexError::MaximumRecursiveLocksReached)); default: + { + if (errors != nullptr) + { + errors->Append( + "%s(...) == %s;", + timeoutMilliseconds == (DWORD)-1 ? "pthread_mutex_lock" + : timeoutMilliseconds == 0 ? "pthread_mutex_trylock" + : "pthread_mutex_timedlock", + GetFriendlyErrorCodeString(lockResult)); + } + throw SharedMemoryException(static_cast(NamedMutexError::Unknown)); + } } } @@ -866,7 +971,7 @@ void MutexHelpers::ReleaseLock(pthread_mutex_t *mutex) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NamedMutexSharedData -NamedMutexSharedData::NamedMutexSharedData() +NamedMutexSharedData::NamedMutexSharedData(SharedMemorySystemCallErrors *errors) : #if !NAMED_MUTEX_USE_PTHREAD_MUTEX m_timedWaiterCount(0), @@ -883,7 +988,7 @@ NamedMutexSharedData::NamedMutexSharedData() _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired()); #if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(&m_lock); + MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(errors, &m_lock); #endif // NAMED_MUTEX_USE_PTHREAD_MUTEX } @@ -917,6 +1022,7 @@ void NamedMutexSharedData::IncTimedWaiterCount() ULONG newValue = InterlockedIncrement(reinterpret_cast(&m_timedWaiterCount)); if (newValue == 0) { + InterlockedDecrement(reinterpret_cast(&m_timedWaiterCount)); throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); } } @@ -979,17 +1085,22 @@ const UINT8 NamedMutexProcessData::SyncSystemVersion = 1; const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100; -SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef) +SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( + SharedMemorySystemCallErrors *errors, + LPCSTR name, + bool acquireLockIfCreated, + bool *createdRef) { - return CreateOrOpen(name, true /* createIfNotExist */, acquireLockIfCreated, createdRef); + return CreateOrOpen(errors, name, true /* createIfNotExist */, acquireLockIfCreated, createdRef); } -SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(LPCSTR name) +SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(SharedMemorySystemCallErrors *errors, LPCSTR name) { - return CreateOrOpen(name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */); + return CreateOrOpen(errors, name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */); } SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( + SharedMemorySystemCallErrors *errors, LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, @@ -1079,6 +1190,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( bool created; SharedMemoryProcessDataHeader *processDataHeader = SharedMemoryProcessDataHeader::CreateOrOpen( + errors, name, SharedMemorySharedDataHeader(SharedMemoryType::Mutex, SyncSystemVersion), sizeof(NamedMutexSharedData), @@ -1105,7 +1217,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( if (created) { // Initialize the shared data - new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData; + new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData(errors); } if (processDataHeader->GetData() == nullptr) @@ -1115,7 +1227,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( SharedMemoryHelpers::BuildSharedFilesPath(lockFilePath, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME); if (created) { - SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */); + SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, true /* isGlobalLockAcquired */); } // Create the session directory @@ -1124,7 +1236,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(lockFilePath)); if (created) { - SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */); + SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, true /* isGlobalLockAcquired */); autoCleanup.m_lockFilePath = &lockFilePath; autoCleanup.m_sessionDirectoryPathCharCount = lockFilePath.GetCount(); } @@ -1132,14 +1244,22 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( // Create or open the lock file SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/')); SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append(id->GetName(), id->GetNameCharCount())); - int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(lockFilePath, created); + int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, lockFilePath, created); if (lockFileDescriptor == -1) { _ASSERTE(!created); if (createIfNotExist) { + if (errors != nullptr) + { + errors->Append( + "open(\"%s\", O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0) == -1; errno == ENOENT;", + (const char *)lockFilePath); + } + throw SharedMemoryException(static_cast(SharedMemoryError::IO)); } + return nullptr; } autoCleanup.m_createdLockFile = created; @@ -1164,7 +1284,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( // If the mutex was created and if requested, acquire the lock initially while holding the creation/deletion locks if (created && acquireLockIfCreated) { - MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(0); + MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(errors, 0); _ASSERTE(tryAcquireLockResult == MutexTryAcquireLockResult::AcquiredLock); } } @@ -1331,12 +1451,12 @@ void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcess m_nextInThreadOwnedNamedMutexList = next; } -MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMilliseconds) +MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds) { NamedMutexSharedData *sharedData = GetSharedData(); #if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(sharedData->GetLock(), timeoutMilliseconds); + MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(errors, sharedData->GetLock(), timeoutMilliseconds); if (result == MutexTryAcquireLockResult::TimedOut) { return result; @@ -1445,7 +1565,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil bool acquiredFileLock = false; while (sharedData->HasAnyTimedWaiters()) { - if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) + if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) { acquiredFileLock = true; break; @@ -1457,13 +1577,13 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil break; } - acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX); + acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX); _ASSERTE(acquiredFileLock); break; } case 0: - if (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) + if (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) { return MutexTryAcquireLockResult::TimedOut; } @@ -1472,7 +1592,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil default: { // Try to acquire the file lock without waiting - if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) + if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) { break; } @@ -1511,7 +1631,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil ? remainingMilliseconds : PollLoopMaximumSleepMilliseconds; Sleep(sleepMilliseconds); - } while (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)); + } while (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)); break; } } diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index deccf3e1a852e..3682a4cef1691 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -308,6 +308,8 @@ static const Entry s_QCall[] = DllImportEntry(OpenMutexW) DllImportEntry(OpenSemaphoreW) DllImportEntry(OutputDebugStringW) + DllImportEntry(PAL_CreateMutexW) + DllImportEntry(PAL_OpenMutexW) DllImportEntry(ReleaseMutex) DllImportEntry(ReleaseSemaphore) DllImportEntry(ResetEvent) diff --git a/src/libraries/Common/src/System/IO/Win32Marshal.cs b/src/libraries/Common/src/System/IO/Win32Marshal.cs index ad2c81e9fc515..0c11b227e677e 100644 --- a/src/libraries/Common/src/System/IO/Win32Marshal.cs +++ b/src/libraries/Common/src/System/IO/Win32Marshal.cs @@ -22,7 +22,7 @@ internal static Exception GetExceptionForLastWin32Error(string? path = "") /// Converts the specified Win32 error into a corresponding object, optionally /// including the specified path in the error message. /// - internal static Exception GetExceptionForWin32Error(int errorCode, string? path = "") + internal static Exception GetExceptionForWin32Error(int errorCode, string? path = "", string? errorDetails = null) { // ERROR_SUCCESS gets thrown when another unexpected interop call was made before checking GetLastWin32Error(). // Errors have to get retrieved as soon as possible after P/Invoking to avoid this. @@ -57,13 +57,19 @@ internal static Exception GetExceptionForWin32Error(int errorCode, string? path case Interop.Errors.ERROR_OPERATION_ABORTED: return new OperationCanceledException(); case Interop.Errors.ERROR_INVALID_PARAMETER: + default: - string msg = string.IsNullOrEmpty(path) - ? GetPInvokeErrorMessage(errorCode) - : $"{GetPInvokeErrorMessage(errorCode)} : '{path}'"; - return new IOException( - msg, - MakeHRFromErrorCode(errorCode)); + string msg = GetPInvokeErrorMessage(errorCode); + if (!string.IsNullOrEmpty(path)) + { + msg += $" : '{path}'."; + } + if (!string.IsNullOrEmpty(errorDetails)) + { + msg += $" {errorDetails}"; + } + + return new IOException(msg, MakeHRFromErrorCode(errorCode)); } static string GetPInvokeErrorMessage(int errorCode) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 2e00d445ee47e..e1cc39aeaf4aa 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3635,6 +3635,9 @@ Unknown error "{0}". + + One or more system calls failed: {0} + Operation could destabilize the runtime. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index ebe03d7b5b71b..db2751834350a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2149,6 +2149,7 @@ + @@ -2206,7 +2207,6 @@ Common\System\Memory\FixedBufferExtensions.cs - @@ -2717,4 +2717,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs index 10a91926c3e02..07eb21b084980 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs @@ -24,11 +24,6 @@ private void CreateMutexCore(bool initiallyOwned, string? name, out bool created if (mutexHandle.IsInvalid) { mutexHandle.SetHandleAsInvalid(); -#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI - if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) - // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); -#endif if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); @@ -56,13 +51,6 @@ private static OpenExistingResult OpenExistingWorker(string name, out Mutex? res myHandle.Dispose(); -#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI - if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) - { - // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); - } -#endif if (Interop.Errors.ERROR_FILE_NOT_FOUND == errorCode || Interop.Errors.ERROR_INVALID_NAME == errorCode) return OpenExistingResult.NameNotFound; if (Interop.Errors.ERROR_PATH_NOT_FOUND == errorCode)