Skip to content

Add the CurrentUserOnly and CurrentSessionOnly options for named Mutex, Semaphore, and EventWaitHandle#112213

Merged
kouvel merged 8 commits intodotnet:mainfrom
kouvel:NamedWhApis
Mar 12, 2025
Merged

Add the CurrentUserOnly and CurrentSessionOnly options for named Mutex, Semaphore, and EventWaitHandle#112213
kouvel merged 8 commits intodotnet:mainfrom
kouvel:NamedWhApis

Conversation

@kouvel
Copy link
Contributor

@kouvel kouvel commented Feb 5, 2025

  • In Mutex, Semaphore, and EventWaitHandle, added overloads for methods with a name argument to also include a NamedWaitHandleOptions options argument. The CurrentUserOnly option indicates whether the object should be limited in access to the current user. The CurrentSessionOnly option indicates whether the object object is intended to be used only within the current session (an alternative to the name prefixes, such as Global\). The default value for NamedWaitHandleOptions has CurrentUserOnly=true and CurrentSessionOnly=true.
  • On Unixes, named semaphores and events are not supported. Named mutexes are have a functional implementation on Unixes in CoreCLR. NativeAOT and Mono on Unixes use an incomplete/temporary implementation where named mutexes are just process-local mutexes stored in a dictionary keyed by the name and can't be used to synchronize between processes. This change only briefly extends the NativeAOT/Mono implementation to add another prefix to the name when CurrentUserOnly=true. Sharing the CoreCLR Unix named mutex implementation with NativeAOT/Mono is left to a separate change. Most of what is described below when CurrentUserOnly=true on Unixes refers to the CoreCLR implementation.
  • The plan is to also deprecate the APIs with a name argument but without an options argument in a separate change
  • API review: [API Proposal]: New overloads for named wait handles that enable creating/opening user-specific synchronization primitives #102682 (comment)

Windows with CurrentUserOnly=true

When creating a named mutex, a security descriptor is created and assigned to the object.

  • The owner and group are set to the current user
  • The DACL has two ACEs:
    • One ACE allows the current user the relevant access rights
    • Another ACE allows the BUILTIN\Administrators group the READ_CONTROL access right to enable reading the security info for diagnostic purposes
  • The SACL has a mandatory label ACE
    • The access policy prevents principals with an integrity level lower than the object from opening the object
    • The integrity level assigned to the object is the same as what would be assigned by the system by default
  • When opening a named mutex, the owner and DACL are verified to see if they are as expected, and if not, a WaitHandleCannotBeOpenedException is thrown
  • Access controls are set when creating an object and checked when opening an object. Once a handle to the object is obtained, typical synchronization operations may not do further access checks. It's up to the caller to be careful about how the handle to the object is passed around.

Unixes with CurrentUserOnly=true

  • Files relevant to named mutexes (shared memory and lock files) go under /tmp/.dotnet-uidN/ where N is the effective user ID
    • /tmp/ (or equivalent) was chosen mainly for the automatic cleanup, as the number of files can add up in cases where mutexes are not disposed, particularly when randomly generated names are used upon each invocation of a process. Due to the use of a global location /tmp/ or equivalent, it would be possible for a user to deny access to the .dotnet-uidN directory of a different user if the directory hadn't been created yet. An alternative considered was to use the home directory and introduce some kind of periodic cleanup strategy, but there are also other tradeoffs.
  • /tmp/ or equivalent must either have the sticky bit, or it must be owned by the current user and without write access for any other user. Otherwise, an exception is thrown.
  • Permissions of the /tmp/.dotnet-uidN/ directory and files/directories under it are limited in access to the current user
  • When opening a named mutex, the owner and permissions of relevant files/directories are verified to see if they are as expected, and if not, an exception is thrown
  • Access controls are set when creating an object and checked when opening an object. Once a handle to the object is obtained, typical synchronization operations may not do further access checks. It's up to the caller to be careful about how the handle to the object is passed around.

Namespaces

  • On Windows, there is no namespace for kernel objects for each user. CurrentSessionOnly=true is close, but it is possible for multiple users to be running code simultaneously in the same session. There is a global namespace, and a namespace per session. When CurrentUserOnly=true, callers may need to ensure that the name used is distinguished for different users.
  • On Unixes, a different directory tree is used when CurrentUserOnly=true, so each user has a separate namespace for objects, including for session-scoped and session-unscoped objects.

Loading
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants