Added support for running in a sandbox on Mac (#20735)
authorOded Hanson <odhanson@microsoft.com>
Tue, 6 Nov 2018 16:31:49 +0000 (18:31 +0200)
committerJan Vorlicek <janvorli@microsoft.com>
Tue, 6 Nov 2018 16:31:49 +0000 (17:31 +0100)
* Added support for running in a sandbox on Mac

When running in a sandbox, the Mac operating system will limit access to resources, esp. the file system. Right now both Mutex and SharedMemory in the PAL are accessing the /tmp folder for which Mac does not provide the application permissions to access.

Instead, the sandbox provides the ability to share information between applications by using a shared container folder. This is done by registering the application with an Application Group ID. Using this ID, we can access the shared folder and read/write from it.

Since the .Net runtime can be loaded in multiple ways, we decided that the easiest way to let the runtime know what the application group ID is via an environment variable. Thus, if the NETCOREAPP_SANDBOX_APPLICATION_GROUP_ID environment variable is set (on Mac), the runtime will assume we are sandboxed, and will use the value provided as the application group ID. Note that due to limitations on semaphore file lengths, we will not allow application group IDs longer than 13 characters. This gives us 10 characters for the developer ID, and 3 extra characters for the group name.

When sandbox is disabled (the environment variable is empty) then the folder for Mutex and SharedMemory will continue to be rooted in /tmp. However when the sandbox is enabled, these files will be created under /user/{loginname}/Library/Group Containers/{AppGroupId}/.

Fixes #20473

* Made  gApplicationContainerPath a pointer so it does not get automatically deleted by the c runtime

* Made s_runtimeTempDirectoryPath and s_sharedMemoryDirectoryPath pointers so they are not automatically deleted by the c runtime

* Renamed gApplicationContainerPath to gSharedFilesPath

* Renamed NETCOREAPP_SANDBOX_APPLICATION_GROUP_ID to DOTNET_SANDBOX_APPLICATION_GROUP_ID

* Fixed usage of VerifyStringOperation

* Replaced new with InternalNew

* Wrapped Apple specific code with #ifdef

* Added exception handling during close

* Moved VerifyStringOperation macro into SharedMemoryManager

* Moved PathCharString variable declarations before AutoCleanup is declared.

* Fixed initialization functions not to throw

* Renamed CopyPath to BuildSharedFilesPath

* Fixed misc nits

* Fixed implicit conversions from BOOL to bool

* Moved MAX_APPLICATION_GROUP_ID_LENGTH inside ifdef APPLE

* Removed PAL_IsApplicationSandboxed

src/pal/inc/pal.h
src/pal/src/include/pal/palinternal.h
src/pal/src/include/pal/process.h
src/pal/src/include/pal/sharedmemory.h
src/pal/src/include/pal/sharedmemory.inl
src/pal/src/include/pal/stackstring.hpp
src/pal/src/init/pal.cpp
src/pal/src/sharedmemory/sharedmemory.cpp
src/pal/src/synchobj/mutex.cpp
src/pal/src/thread/process.cpp

index a7f0ff5d74f5b4656db85d76cc431262f95b53ac..2a51d584ad566ec2e34f8036bc0ea2ccf355f479 100644 (file)
@@ -442,6 +442,13 @@ BOOL
 PALAPI
 PAL_NotifyRuntimeStarted(VOID);
 
+#ifdef __APPLE__
+PALIMPORT
+LPCSTR
+PALAPI
+PAL_GetApplicationGroupId();
+#endif // __APPLE__
+
 static const int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH;
 
 PALIMPORT
index 0e37ba84c0e4526447090f97c3357604800fb101..a66ef7e18edb44dde2c725c9af91ec7b5e0aed30 100644 (file)
@@ -622,6 +622,8 @@ function_name() to call the system's implementation
    we'll catch any definition conflicts */
 #include <sys/socket.h>
 
+#include <pal/stackstring.hpp>
+
 #if !HAVE_INFTIM
 #define INFTIM  -1
 #endif // !HAVE_INFTIM
@@ -631,6 +633,8 @@ function_name() to call the system's implementation
 #undef assert
 #define assert (Use__ASSERTE_instead_of_assert) assert
 
+#define string_countof(a) (sizeof(a) / sizeof(a[0]) - 1)
+
 #ifndef __ANDROID__
 #define TEMP_DIRECTORY_PATH "/tmp/"
 #else
@@ -641,6 +645,16 @@ function_name() to call the system's implementation
 
 #define PROCESS_PIPE_NAME_PREFIX ".dotnet-pal-processpipe"
 
+#ifdef __APPLE__
+#define APPLICATION_CONTAINER_BASE_PATH_SUFFIX "/Library/Group Containers/"
+
+// Not much to go with, but Max semaphore length on Mac is 31 characters. In a sandbox, the semaphore name
+// must be prefixed with an application group ID. This will be 10 characters for developer ID and extra 2 
+// characters for group name. For example ABCDEFGHIJ.MS. We still need some characters left
+// for the actual semaphore names.
+#define MAX_APPLICATION_GROUP_ID_LENGTH 13
+#endif // __APPLE__
+
 #ifdef __cplusplus
 extern "C"
 {
@@ -664,6 +678,11 @@ typedef enum _TimeConversionConstants
 bool
 ReadMemoryValueFromFile(const char* filename, size_t* val);
 
+#ifdef __APPLE__
+bool
+GetApplicationContainerFolder(PathCharString& buffer, const char *applicationGroupId, int applicationGroupIdLength);
+#endif // __APPLE__
+
 /* This is duplicated in utilcode.h for CLR, with cooler type-traits */
 template <typename T>
 inline
index f5d8b46de66055facdf0dd62c1366f14d623a6bf..4eccc6189f8aec18a1338b06467468346423a785 100644 (file)
@@ -24,6 +24,7 @@ Revision History:
 #define _PAL_PROCESS_H_
 
 #include "pal/palinternal.h"
+#include "pal/stackstring.hpp"
 
 #ifdef __cplusplus
 extern "C"
@@ -43,6 +44,13 @@ extern DWORD gSID;
 
 extern LPWSTR pAppDir;
 
+// The Mac sandbox application group ID (if exists) and container (shared) path
+#ifdef __APPLE__
+extern LPCSTR gApplicationGroupId;
+extern int gApplicationGroupIdLength;
+#endif // __APPLE__
+extern PathCharString *gSharedFilesPath;
+
 /*++
 Function:
   PROCGetProcessIDFromHandle
index fdc395e3c6df2a30d2a59128643f86efd3935666..3ea3b711826c1db59abfabb2398ff963058c8fa4 100644 (file)
 #define _countof(a) (sizeof(a) / sizeof(a[0]))
 #endif // !_countof
 
-// The temporary folder is used for storing shared memory files and their lock files.
-// The location of the temporary folder varies (e.g. /data/local/tmp on Android)
-// and is set in TEMP_DIRECTORY_PATH. TEMP_DIRECTORY_PATH ends with '/'
+// The folder used for storing shared memory files and their lock files is defined in 
+// the gSharedFilesPath global variable. The value of the variable depends on which 
+// OS is being used, and if the application is running in a sandbox in Mac.
+// gSharedFilesPath ends with '/'
 // - Global shared memory files go in:
-//     {tmp}/.dotnet/shm/global/<fileName>
+//     {gSharedFilesPath}/.dotnet/shm/global/<fileName>
 // - Session-scoped shared memory files go in:
-//     {tmp}/.dotnet/shm/session<sessionId>/<fileName>
+//     {gSharedFilesPath}/.dotnet/shm/session<sessionId>/<fileName>
 // - Lock files associated with global shared memory files go in:
-//     {tmp}/.dotnet/lockfiles/global/<fileName>
+//     {gSharedFilesPath}/.dotnet/lockfiles/global/<fileName>
 // - Lock files associated with session-scoped shared memory files go in:
-//     {tmp}/.dotnet/lockfiles/session<sessionId>/<fileName>
+//     {gSharedFilesPath}/.dotnet/lockfiles/session<sessionId>/<fileName>
 
 #define SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT (_MAX_FNAME - 1)
-#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (_countof("Global\\") - 1 + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
+#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (string_countof("Global\\") + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
 
-#define SHARED_MEMORY_TEMP_DIRECTORY_PATH TEMP_DIRECTORY_PATH
-#define SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_PATH TEMP_DIRECTORY_PATH ".dotnet"
-
-#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH TEMP_DIRECTORY_PATH ".dotnet/shm"
-#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH TEMP_DIRECTORY_PATH ".dotnet/lockfiles"
-static_assert_no_msg(_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH) >= _countof(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH));
+#define SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_NAME ".dotnet"
+#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME ".dotnet/shm"
+#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME ".dotnet/lockfiles"
+static_assert_no_msg(_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) >= _countof(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME));
 
 #define SHARED_MEMORY_GLOBAL_DIRECTORY_NAME "global"
 #define SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX "session"
 static_assert_no_msg(_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) >= _countof(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME));
 
-#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE TEMP_DIRECTORY_PATH ".coreclr.XXXXXX"
+#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE ".coreclr.XXXXXX"
 
 #define SHARED_MEMORY_MAX_SESSION_ID_CHAR_COUNT (10)
 
+// Note that this Max size does not include the prefix folder path size which is unknown (in the case of sandbox) until runtime
 #define SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT \
     ( \
-        _countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH) - 1 + \
+        string_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) + \
         1 /* path separator */ + \
-        _countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) - 1 + \
+        string_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) + \
         SHARED_MEMORY_MAX_SESSION_ID_CHAR_COUNT + \
         1 /* path separator */ + \
         SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT \
     )
-static_assert_no_msg(SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
 
 class AutoFreeBuffer
 {
@@ -107,9 +106,9 @@ public:
 
     static void *Alloc(SIZE_T byteCount);
 
-    template<SIZE_T DestinationByteCount, SIZE_T SourceByteCount> static SIZE_T CopyString(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, const char (&source)[SourceByteCount]);
-    template<SIZE_T DestinationByteCount> static SIZE_T CopyString(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, LPCSTR source, SIZE_T sourceCharCount);
-    template<SIZE_T DestinationByteCount> static SIZE_T AppendUInt32String(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, UINT32 value);
+    template<SIZE_T SuffixByteCount> static void BuildSharedFilesPath(PathCharString& destination, const char (&suffix)[SuffixByteCount]);
+    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);
 private:
@@ -126,6 +125,12 @@ public:
 
     static bool TryAcquireFileLock(int fileDescriptor, int operation);
     static void ReleaseFileLock(int fileDescriptor);
+
+    static void VerifyStringOperation(bool success);
+    static void VerifyStringOperation(BOOL success)
+    {
+        VerifyStringOperation(success != FALSE); 
+    }
 };
 
 class SharedMemoryId
@@ -147,7 +152,7 @@ public:
     bool Equals(SharedMemoryId *other) const;
 
 public:
-    SIZE_T AppendSessionDirectoryName(char (&path)[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1], SIZE_T pathCharCount) const;
+    bool AppendSessionDirectoryName(PathCharString& path) const;
 };
 
 enum class SharedMemoryType : UINT8
@@ -238,6 +243,9 @@ private:
     static CRITICAL_SECTION s_creationDeletionProcessLock;
     static int s_creationDeletionLockFileDescriptor;
 
+    static PathCharString* s_runtimeTempDirectoryPath;
+    static PathCharString* s_sharedMemoryDirectoryPath;
+
 private:
     static SharedMemoryProcessDataHeader *s_processDataHeaderListHead;
 
@@ -248,7 +256,7 @@ private:
 #endif // _DEBUG
 
 public:
-    static void StaticInitialize();
+    static bool StaticInitialize();
     static void StaticClose();
 
 public:
@@ -257,6 +265,9 @@ public:
     static void AcquireCreationDeletionFileLock();
     static void ReleaseCreationDeletionFileLock();
 
+public:
+    static bool CopySharedMemoryBasePath(PathCharString& destination);
+
 #ifdef _DEBUG
 public:
     static bool IsCreationDeletionProcessLockAcquired();
index 69b8704b65f4cb71fdf23c07cff8f363668f70f2..3a0c9797819cab47ac5f069a649830d5eab9f06b 100644 (file)
 
 #include <string.h>
 
-template<SIZE_T DestinationByteCount, SIZE_T SourceByteCount>
-SIZE_T SharedMemoryHelpers::CopyString(
-    char (&destination)[DestinationByteCount],
-    SIZE_T destinationStartOffset,
-    const char(&source)[SourceByteCount])
+template<SIZE_T SuffixByteCount>
+void SharedMemoryHelpers::BuildSharedFilesPath(
+    PathCharString& destination,
+    const char (&suffix)[SuffixByteCount])
 {
-    return CopyString(destination, destinationStartOffset, source, SourceByteCount - 1);
-}
-
-template<SIZE_T DestinationByteCount>
-SIZE_T SharedMemoryHelpers::CopyString(
-    char (&destination)[DestinationByteCount],
-    SIZE_T destinationStartOffset,
-    LPCSTR source,
-    SIZE_T sourceCharCount)
-{
-    _ASSERTE(destinationStartOffset < DestinationByteCount);
-    _ASSERTE(sourceCharCount < DestinationByteCount - destinationStartOffset);
-    _ASSERTE(strlen(source) == sourceCharCount);
-
-    memcpy_s(&destination[destinationStartOffset], DestinationByteCount - destinationStartOffset, source, sourceCharCount + 1);
-    return destinationStartOffset + sourceCharCount;
-}
-
-template<SIZE_T DestinationByteCount>
-SIZE_T SharedMemoryHelpers::AppendUInt32String(
-    char (&destination)[DestinationByteCount],
-    SIZE_T destinationStartOffset,
-    UINT32 value)
-{
-    _ASSERTE(destination != nullptr);
-    _ASSERTE(destinationStartOffset < DestinationByteCount);
-
-    int valueCharCount =
-        sprintf_s(&destination[destinationStartOffset], DestinationByteCount - destinationStartOffset, "%u", value);
-    _ASSERTE(valueCharCount > 0);
-    return destinationStartOffset + valueCharCount;
+    BuildSharedFilesPath(destination, suffix, SuffixByteCount - 1);
 }
 
 #endif // !_PAL_SHARED_MEMORY_INL_
index 1f18d5fe03829595ba57182c9d2256e3ef8a4306..89fcd009b2cfa5969a1ec06b5ba06d192e9b10bd 100644 (file)
@@ -129,6 +129,12 @@ public:
         return Set(s.m_buffer, s.m_count);
     }
 
+    template<SIZE_T bufferLength> BOOL Set(const T (&buffer)[bufferLength])
+    {
+        // bufferLength includes terminator character
+        return Set(buffer, bufferLength - 1);
+    }
+
     SIZE_T GetCount() const
     {
         return m_count;
@@ -157,6 +163,11 @@ public:
         return result;
     }
 
+    T * OpenStringBuffer()
+    {
+        return m_buffer;
+    }
+
     //count should not include the terminating null
     void CloseBuffer(SIZE_T count)
     {
@@ -198,21 +209,38 @@ public:
     {
         return Append(s.GetString(), s.GetCount());
     }
-   
-   BOOL IsEmpty()
-   {
-       return 0 == m_buffer[0];
-   }
-
-   void Clear()
-   {
-       m_count = 0;
-       NullTerminate();
-   }
-   ~StackString()
-   {
-       DeleteBuffer();
-   }
+
+    template<SIZE_T bufferLength> BOOL Append(const T (&buffer)[bufferLength])
+    {
+        // bufferLength includes terminator character
+        return Append(buffer, bufferLength - 1);
+    }
+
+    BOOL Append(T ch)
+    {
+        SIZE_T endpos = m_count;
+        if (!Resize(m_count + 1))
+            return FALSE;
+
+        m_buffer[endpos] = ch;
+        NullTerminate();
+        return TRUE;
+    }
+
+    BOOL IsEmpty()
+    {
+        return 0 == m_buffer[0];
+    }
+
+    void Clear()
+    {
+        m_count = 0;
+        NullTerminate();
+    }
+    ~StackString()
+    {
+        DeleteBuffer();
+    }
 };
 
 #if _DEBUG
index dafe43df3ae6747e89db01de4731849ccbfd4d67..294f45898133798a02d1d5a85a0c8d41195edf9e 100644 (file)
@@ -355,6 +355,52 @@ Initialize(
         gPID = getpid();
         gSID = getsid(gPID);
 
+        // The gSharedFilesPath is allocated dynamically so its destructor does not get 
+        // called unexpectedly during cleanup
+        gSharedFilesPath = InternalNew<PathCharString>();
+        if (gSharedFilesPath == nullptr)
+        {
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            goto done;
+        }
+
+#ifdef __APPLE__
+        // Store application group Id. It will be null if not set
+        gApplicationGroupId = getenv("DOTNET_SANDBOX_APPLICATION_GROUP_ID");
+
+        if (nullptr != gApplicationGroupId)
+        {
+            // Verify the length of the application group ID
+            gApplicationGroupIdLength = strlen(gApplicationGroupId);
+            if (gApplicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH)
+            {
+                SetLastError(ERROR_BAD_LENGTH);
+                goto done;
+            }
+
+            // In sandbox, all IPC files (locks, pipes) should be written to the application group
+            // container. There will be no write permissions to TEMP_DIRECTORY_PATH
+            if (!GetApplicationContainerFolder(*gSharedFilesPath, gApplicationGroupId, gApplicationGroupIdLength))
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                goto done;
+            }
+
+            // Verify the size of the path won't exceed maximum allowed size
+            if (gSharedFilesPath->GetCount() + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ > MAX_LONGPATH)
+            {
+                SetLastError(ERROR_FILENAME_EXCED_RANGE);
+            }
+        }
+        else
+#endif // __APPLE__
+        {
+            gSharedFilesPath->Set(TEMP_DIRECTORY_PATH);
+
+            // We can verify statically the non sandboxed case, since the size is known during compile time
+            static_assert_no_msg(string_countof(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
+        }
+
         fFirstTimeInit = true;
 
         InitializeDefaultStackSize();
@@ -393,7 +439,11 @@ Initialize(
             // we use large numbers of threads or have many open files.
         }
 
-        SharedMemoryManager::StaticInitialize();
+        if (!SharedMemoryManager::StaticInitialize())
+        {
+            ERROR("Shared memory static initialization failed!\n");
+            goto CLEANUP0;
+        }
 
         /* initialize the shared memory infrastructure */
         if (!SHMInitialize())
index 46c07143a19acf8d9d692a5b54fedd21eb826ef3..af3934aa6133fb1699a6c1ee0355924d4210832e 100644 (file)
@@ -9,6 +9,7 @@
 #include "pal/malloc.hpp"
 #include "pal/thread.hpp"
 #include "pal/virtual.h"
+#include "pal/process.h"
 
 #include <sys/file.h>
 #include <sys/mman.h>
@@ -135,8 +136,10 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
             return true;
         }
 
-        char tempPath[] = SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE;
-        if (mkdtemp(tempPath) == nullptr)
+        PathCharString tempPath;
+        BuildSharedFilesPath(tempPath, SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE);
+
+        if (mkdtemp(tempPath.OpenStringBuffer()) == nullptr)
         {
             throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
         }
@@ -164,7 +167,7 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
 
     if (isSystemDirectory)
     {
-        // For system directories (such as SHARED_MEMORY_TEMP_DIRECTORY_PATH), require sufficient permissions only for the
+        // For system directories (such as TEMP_DIRECTORY_PATH), require sufficient permissions only for the
         // current user. For instance, "docker run --mount ..." to mount /tmp to some directory on the host mounts the
         // destination directory with the same permissions as the source directory, which may not include some permissions for
         // other users. In the docker container, other user permissions are typically not relevant and relaxing the permissions
@@ -177,9 +180,9 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
         throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
     }
 
-    // For non-system directories (such as SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_PATH), require sufficient permissions for all
-    // users and try to update them if requested to create the directory, so that shared memory files may be shared by all
-    // processes on the system.
+    // For non-system directories (such as gSharedFilesPath/SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_NAME),
+    // require sufficient permissions for all users and try to update them if requested to create the directory, so that
+    // shared memory files may be shared by all processes on the system.
     if ((statInfo.st_mode & PermissionsMask_AllUsers_ReadWriteExecute) == PermissionsMask_AllUsers_ReadWriteExecute)
     {
         return true;
@@ -388,6 +391,34 @@ void SharedMemoryHelpers::ReleaseFileLock(int fileDescriptor)
     } while (flockResult != 0 && errno == EINTR);
 }
 
+void SharedMemoryHelpers::BuildSharedFilesPath(PathCharString& destination, const char *suffix, int suffixCharCount)
+{
+    _ASSERTE(strlen(suffix) == suffixCharCount);
+
+    VerifyStringOperation(destination.Set(*gSharedFilesPath));
+    VerifyStringOperation(destination.Append(suffix, suffixCharCount));
+}
+
+bool SharedMemoryHelpers::AppendUInt32String(
+    PathCharString& destination,
+    UINT32 value)
+{
+    char int32String[16];
+
+    int valueCharCount =
+        sprintf_s(int32String, sizeof(int32String), "%u", value);
+    _ASSERTE(valueCharCount > 0);
+    return destination.Append(int32String, valueCharCount) != FALSE;
+}
+
+void SharedMemoryHelpers::VerifyStringOperation(bool success)
+{
+    if (!success)
+    {
+        throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // SharedMemoryId
 
@@ -471,22 +502,17 @@ bool SharedMemoryId::Equals(SharedMemoryId *other) const
         strcmp(GetName(), other->GetName()) == 0;
 }
 
-SIZE_T SharedMemoryId::AppendSessionDirectoryName(
-    char (&path)[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1],
-    SIZE_T pathCharCount) const
+bool SharedMemoryId::AppendSessionDirectoryName(PathCharString& path) const
 {
     if (IsSessionScope())
     {
-        pathCharCount = SharedMemoryHelpers::CopyString(path, pathCharCount, SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX);
-        pathCharCount = SharedMemoryHelpers::AppendUInt32String(path, pathCharCount, GetCurrentSessionId());
+        return path.Append(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) != FALSE
+            && SharedMemoryHelpers::AppendUInt32String(path, GetCurrentSessionId());
     }
     else
     {
-        pathCharCount = SharedMemoryHelpers::CopyString(path, pathCharCount, SHARED_MEMORY_GLOBAL_DIRECTORY_NAME);
+        return path.Append(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME) != FALSE;
     }
-
-    _ASSERTE(pathCharCount <= SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT);
-    return pathCharCount;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -538,12 +564,13 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
         *createdRef = false;
     }
 
+    PathCharString filePath;
     SharedMemoryId id(name);
 
     struct AutoCleanup
     {
         bool m_acquiredCreationDeletionFileLock;
-        char *m_filePath;
+        PathCharString *m_filePath;
         SIZE_T m_sessionDirectoryPathCharCount;
         bool m_createdFile;
         int m_fileDescriptor;
@@ -592,14 +619,14 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
             if (m_createdFile)
             {
                 _ASSERTE(m_filePath != nullptr);
-                unlink(m_filePath);
+                unlink(*m_filePath);
             }
 
             if (m_sessionDirectoryPathCharCount != 0)
             {
-                _ASSERTE(m_filePath != nullptr);
-                m_filePath[m_sessionDirectoryPathCharCount] = '\0';
-                rmdir(m_filePath);
+                _ASSERTE(*m_filePath != nullptr);
+                m_filePath->CloseBuffer(m_sessionDirectoryPathCharCount);
+                rmdir(*m_filePath);
             }
 
             if (m_acquiredCreationDeletionFileLock)
@@ -623,21 +650,21 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
     autoCleanup.m_acquiredCreationDeletionFileLock = true;
 
     // Create the session directory
-    char filePath[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
-    SIZE_T filePathCharCount = SharedMemoryHelpers::CopyString(filePath, 0, SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
-    filePath[filePathCharCount++] = '/';
-    filePathCharCount = id.AppendSessionDirectoryName(filePath, filePathCharCount);
+    SharedMemoryHelpers::VerifyStringOperation(SharedMemoryManager::CopySharedMemoryBasePath(filePath));
+    SharedMemoryHelpers::VerifyStringOperation(filePath.Append('/'));
+    SharedMemoryHelpers::VerifyStringOperation(id.AppendSessionDirectoryName(filePath));
     if (!SharedMemoryHelpers::EnsureDirectoryExists(filePath, true /* isGlobalLockAcquired */, createIfNotExist))
     {
         _ASSERTE(!createIfNotExist);
         return nullptr;
     }
-    autoCleanup.m_filePath = filePath;
-    autoCleanup.m_sessionDirectoryPathCharCount = filePathCharCount;
+    autoCleanup.m_filePath = &filePath;
+    autoCleanup.m_sessionDirectoryPathCharCount = filePath.GetCount();
 
     // Create or open the shared memory file
-    filePath[filePathCharCount++] = '/';
-    filePathCharCount = SharedMemoryHelpers::CopyString(filePath, filePathCharCount, id.GetName(), id.GetNameCharCount());
+    SharedMemoryHelpers::VerifyStringOperation(filePath.Append('/'));
+    SharedMemoryHelpers::VerifyStringOperation(filePath.Append(id.GetName(), id.GetNameCharCount()));
+
     bool createdFile;
     int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(filePath, createIfNotExist, &createdFile);
     if (fileDescriptor == -1)
@@ -919,16 +946,25 @@ void SharedMemoryProcessDataHeader::Close()
         return;
     }
 
-    // Delete the shared memory file, and the session directory if it's not empty
-    char path[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
-    SIZE_T sessionDirectoryPathCharCount = SharedMemoryHelpers::CopyString(path, 0, SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
-    path[sessionDirectoryPathCharCount++] = '/';
-    sessionDirectoryPathCharCount = m_id.AppendSessionDirectoryName(path, sessionDirectoryPathCharCount);
-    path[sessionDirectoryPathCharCount++] = '/';
-    SharedMemoryHelpers::CopyString(path, sessionDirectoryPathCharCount, m_id.GetName(), m_id.GetNameCharCount());
-    unlink(path);
-    path[sessionDirectoryPathCharCount] = '\0';
-    rmdir(path);
+    try
+    {
+        // Delete the shared memory file, and the session directory if it's not empty
+        PathCharString path;
+        SharedMemoryHelpers::VerifyStringOperation(SharedMemoryManager::CopySharedMemoryBasePath(path));
+        SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
+        SharedMemoryHelpers::VerifyStringOperation(m_id.AppendSessionDirectoryName(path));
+        SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
+
+        SIZE_T sessionDirectoryPathCharCount = path.GetCount();
+        SharedMemoryHelpers::VerifyStringOperation(path.Append(m_id.GetName(), m_id.GetNameCharCount()));
+        unlink(path);
+        path.CloseBuffer(sessionDirectoryPathCharCount);
+        rmdir(path);
+    }
+    catch (SharedMemoryException)
+    {
+        // Ignore the error, just don't release shared data
+    }
 }
 
 SharedMemoryId *SharedMemoryProcessDataHeader::GetId()
@@ -998,15 +1034,36 @@ CRITICAL_SECTION SharedMemoryManager::s_creationDeletionProcessLock;
 int SharedMemoryManager::s_creationDeletionLockFileDescriptor = -1;
 
 SharedMemoryProcessDataHeader *SharedMemoryManager::s_processDataHeaderListHead = nullptr;
+PathCharString* SharedMemoryManager::s_runtimeTempDirectoryPath;
+PathCharString* SharedMemoryManager::s_sharedMemoryDirectoryPath;
 
 #ifdef _DEBUG
 SIZE_T SharedMemoryManager::s_creationDeletionProcessLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
 SIZE_T SharedMemoryManager::s_creationDeletionFileLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
 #endif // _DEBUG
 
-void SharedMemoryManager::StaticInitialize()
+bool SharedMemoryManager::StaticInitialize()
 {
     InitializeCriticalSection(&s_creationDeletionProcessLock);
+
+    s_runtimeTempDirectoryPath = InternalNew<PathCharString>();
+    s_sharedMemoryDirectoryPath = InternalNew<PathCharString>();
+
+    if (s_runtimeTempDirectoryPath && s_sharedMemoryDirectoryPath)
+    {
+        try
+        {
+            SharedMemoryHelpers::BuildSharedFilesPath(*s_runtimeTempDirectoryPath, SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_NAME);
+            SharedMemoryHelpers::BuildSharedFilesPath(*s_sharedMemoryDirectoryPath, SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME);
+
+            return true;
+        }
+        catch (SharedMemoryException)
+        {
+            // Ignore and let function fail
+        }
+    }
+    return false;
 }
 
 void SharedMemoryManager::StaticClose()
@@ -1056,7 +1113,7 @@ void SharedMemoryManager::AcquireCreationDeletionFileLock()
     if (s_creationDeletionLockFileDescriptor == -1)
     {
         if (!SharedMemoryHelpers::EnsureDirectoryExists(
-                SHARED_MEMORY_TEMP_DIRECTORY_PATH,
+                *gSharedFilesPath,
                 false /* isGlobalLockAcquired */,
                 false /* createIfNotExist */,
                 true /* isSystemDirectory */))
@@ -1064,12 +1121,12 @@ void SharedMemoryManager::AcquireCreationDeletionFileLock()
             throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
         }
         SharedMemoryHelpers::EnsureDirectoryExists(
-            SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_PATH,
+            *s_runtimeTempDirectoryPath,
             false /* isGlobalLockAcquired */);
         SharedMemoryHelpers::EnsureDirectoryExists(
-            SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH,
+            *s_sharedMemoryDirectoryPath,
             false /* isGlobalLockAcquired */);
-        s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
+        s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(*s_sharedMemoryDirectoryPath);
         if (s_creationDeletionLockFileDescriptor == -1)
         {
             throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
@@ -1161,3 +1218,8 @@ SharedMemoryProcessDataHeader *SharedMemoryManager::FindProcessDataHeader(Shared
     }
     return nullptr;
 }
+
+bool SharedMemoryManager::CopySharedMemoryBasePath(PathCharString& destination)
+{
+    return destination.Set(*s_sharedMemoryDirectoryPath) != FALSE;
+}
index bbc3e2d0835dc9acc408e49c61ce5ca6846c8dac..a018d9db84c9b691ec72ad286dea2bc43e69293a 100644 (file)
@@ -1069,13 +1069,17 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
     _ASSERTE(name != nullptr);
     _ASSERTE(createIfNotExist || !acquireLockIfCreated);
 
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+    PathCharString lockFilePath;
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+
     struct AutoCleanup
     {
         bool m_acquiredCreationDeletionProcessLock;
         bool m_acquiredCreationDeletionFileLock;
         SharedMemoryProcessDataHeader *m_processDataHeader;
     #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
-        char *m_lockFilePath;
+        PathCharString *m_lockFilePath;
         SIZE_T m_sessionDirectoryPathCharCount;
         bool m_createdLockFile;
         int m_lockFileDescriptor;
@@ -1109,14 +1113,14 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
                 if (m_createdLockFile)
                 {
                     _ASSERTE(m_lockFilePath != nullptr);
-                    unlink(m_lockFilePath);
+                    unlink(*m_lockFilePath);
                 }
 
                 if (m_sessionDirectoryPathCharCount != 0)
                 {
                     _ASSERTE(m_lockFilePath != nullptr);
-                    m_lockFilePath[m_sessionDirectoryPathCharCount] = '\0';
-                    rmdir(m_lockFilePath);
+                    m_lockFilePath->CloseBuffer(m_sessionDirectoryPathCharCount);
+                    rmdir(*m_lockFilePath);
                 }
             }
         #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
@@ -1179,29 +1183,26 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
     {
     #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
         // Create the lock files directory
-        char lockFilePath[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
-        SIZE_T lockFilePathCharCount =
-            SharedMemoryHelpers::CopyString(lockFilePath, 0, SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH);
+        SharedMemoryHelpers::BuildSharedFilesPath(lockFilePath, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME);
         if (created)
         {
             SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
         }
 
         // Create the session directory
-        lockFilePath[lockFilePathCharCount++] = '/';
         SharedMemoryId *id = processDataHeader->GetId();
-        lockFilePathCharCount = id->AppendSessionDirectoryName(lockFilePath, lockFilePathCharCount);
+        SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/'));
+        SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(lockFilePath));
         if (created)
         {
             SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
-            autoCleanup.m_lockFilePath = lockFilePath;
-            autoCleanup.m_sessionDirectoryPathCharCount = lockFilePathCharCount;
+            autoCleanup.m_lockFilePath = &lockFilePath;
+            autoCleanup.m_sessionDirectoryPathCharCount = lockFilePath.GetCount();
         }
 
         // Create or open the lock file
-        lockFilePath[lockFilePathCharCount++] = '/';
-        lockFilePathCharCount =
-            SharedMemoryHelpers::CopyString(lockFilePath, lockFilePathCharCount, id->GetName(), id->GetNameCharCount());
+        SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/'));
+        SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append(id->GetName(), id->GetNameCharCount()));
         int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(lockFilePath, created);
         if (lockFileDescriptor == -1)
         {
@@ -1316,17 +1317,25 @@ void NamedMutexProcessData::Close(bool isAbruptShutdown, bool releaseSharedData)
         return;
     }
 
-    // Delete the lock file, and the session directory if it's not empty
-    char path[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
-    SIZE_T sessionDirectoryPathCharCount = SharedMemoryHelpers::CopyString(path, 0, SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH);
-    path[sessionDirectoryPathCharCount++] = '/';
-    SharedMemoryId *id = m_processDataHeader->GetId();
-    sessionDirectoryPathCharCount = id->AppendSessionDirectoryName(path, sessionDirectoryPathCharCount);
-    path[sessionDirectoryPathCharCount++] = '/';
-    SharedMemoryHelpers::CopyString(path, sessionDirectoryPathCharCount, id->GetName(), id->GetNameCharCount());
-    unlink(path);
-    path[sessionDirectoryPathCharCount] = '\0';
-    rmdir(path);
+    try
+    {
+        // Delete the lock file, and the session directory if it's not empty
+        PathCharString path;
+        SharedMemoryHelpers::BuildSharedFilesPath(path, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME);
+        SharedMemoryId *id = m_processDataHeader->GetId();
+        SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
+        SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(path));
+        SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
+        SIZE_T sessionDirectoryPathCharCount = path.GetCount();
+        SharedMemoryHelpers::VerifyStringOperation(path.Append(id->GetName(), id->GetNameCharCount()));
+        unlink(path);
+        path.CloseBuffer(sessionDirectoryPathCharCount);
+        rmdir(path);
+    }
+    catch (SharedMemoryException)
+    {
+        // Ignore the error, just don't release shared data
+    }
 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
 }
 
index 19d3c08fc0474969a1f7de4e3ede57ea0aeaf2da..0141b076541b5002fe34d0c7a9c4145c8fa2cf1c 100644 (file)
@@ -139,6 +139,13 @@ Volatile<LONG> terminator = 0;
 DWORD gPID = (DWORD) -1;
 DWORD gSID = (DWORD) -1;
 
+// Application group ID for this process
+#ifdef __APPLE__
+LPCSTR gApplicationGroupId = nullptr;
+int gApplicationGroupIdLength = 0;
+#endif // __APPLE__
+PathCharString* gSharedFilesPath = nullptr;
+
 // The lowest common supported semaphore length, including null character
 // NetBSD-7.99.25: 15 characters
 // MacOSX 10.11: 31 -- Core 1.0 RC2 compatibility
@@ -1983,6 +1990,15 @@ exit:
     return launched;
 }
 
+#ifdef __APPLE__
+LPCSTR
+PALAPI
+PAL_GetApplicationGroupId()
+{
+    return gApplicationGroupId;
+}
+#endif // __APPLE__
+
 /*++
  Function:
   GetProcessIdDisambiguationKey
@@ -3945,6 +3961,21 @@ PROCGetProcessStatusExit:
     return palError;
 }
 
+#ifdef __APPLE__
+bool GetApplicationContainerFolder(PathCharString& buffer, const char *applicationGroupId, int applicationGroupIdLength)
+{
+    const char *homeDir = getpwuid(getuid())->pw_dir;
+    int homeDirLength = strlen(homeDir);
+
+    // The application group container folder is defined as:
+    // /user/{loginname}/Library/Group Containers/{AppGroupId}/
+    return buffer.Set(homeDir, homeDirLength)
+        && buffer.Append(APPLICATION_CONTAINER_BASE_PATH_SUFFIX)
+        && buffer.Append(applicationGroupId, applicationGroupIdLength)
+        && buffer.Append('/');
+}
+#endif // __APPLE__
+
 #ifdef _DEBUG
 void PROCDumpThreadList()
 {