Skip to content

Commit

Permalink
Added support for running in a sandbox on Mac (dotnet#20735)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
odhanson authored and janvorli committed Nov 6, 2018
1 parent b1b424f commit 5c566d4
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 143 deletions.
7 changes: 7 additions & 0 deletions src/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions src/pal/src/include/pal/palinternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"
{
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/pal/src/include/pal/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Revision History:
#define _PAL_PROCESS_H_

#include "pal/palinternal.h"
#include "pal/stackstring.hpp"

#ifdef __cplusplus
extern "C"
Expand All @@ -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
Expand Down
57 changes: 34 additions & 23 deletions src/pal/src/include/pal/sharedmemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,45 @@
#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
{
Expand Down Expand Up @@ -107,9 +106,9 @@ class SharedMemoryHelpers

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:
Expand All @@ -126,6 +125,12 @@ class SharedMemoryHelpers

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
Expand All @@ -147,7 +152,7 @@ class SharedMemoryId
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
Expand Down Expand Up @@ -238,6 +243,9 @@ class SharedMemoryManager
static CRITICAL_SECTION s_creationDeletionProcessLock;
static int s_creationDeletionLockFileDescriptor;

static PathCharString* s_runtimeTempDirectoryPath;
static PathCharString* s_sharedMemoryDirectoryPath;

private:
static SharedMemoryProcessDataHeader *s_processDataHeaderListHead;

Expand All @@ -248,7 +256,7 @@ class SharedMemoryManager
#endif // _DEBUG

public:
static void StaticInitialize();
static bool StaticInitialize();
static void StaticClose();

public:
Expand All @@ -257,6 +265,9 @@ class SharedMemoryManager
static void AcquireCreationDeletionFileLock();
static void ReleaseCreationDeletionFileLock();

public:
static bool CopySharedMemoryBasePath(PathCharString& destination);

#ifdef _DEBUG
public:
static bool IsCreationDeletionProcessLockAcquired();
Expand Down
41 changes: 5 additions & 36 deletions src/pal/src/include/pal/sharedmemory.inl
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,12 @@

#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_
58 changes: 43 additions & 15 deletions src/pal/src/include/pal/stackstring.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ class StackString
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;
Expand Down Expand Up @@ -157,6 +163,11 @@ class StackString
return result;
}

T * OpenStringBuffer()
{
return m_buffer;
}

//count should not include the terminating null
void CloseBuffer(SIZE_T count)
{
Expand Down Expand Up @@ -198,21 +209,38 @@ class StackString
{
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
Expand Down
Loading

0 comments on commit 5c566d4

Please sign in to comment.