Exemplo n.º 1
0
        protected void Lock(byte *address, long sizeToLock, TransactionState state)
        {
            var lockTaken = false;

            try
            {
                if (Sodium.Lock(address, (UIntPtr)sizeToLock) == 0)
                {
                    return;
                }

                if (DoNotConsiderMemoryLockFailureAsCatastrophicError)
                {
                    return;
                }

                if (PlatformDetails.RunningOnPosix == false)
                {
                    // when running on linux we can't do anything from within the process, so let's avoid the locking entirely
                    Monitor.Enter(WorkingSetIncreaseLocker, ref lockTaken);
                }

                TryHandleFailureToLockMemory(address, sizeToLock);
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(WorkingSetIncreaseLocker);
                }
            }
        }
Exemplo n.º 2
0
        protected void TryHandleFailureToLockMemory(byte *addressToLock, long sizeToLock)
        {
            using var currentProcess = Process.GetCurrentProcess();

            if (PlatformDetails.RunningOnPosix == false)
            {
                var retries = 10;
                while (retries > 0)
                {
                    // From: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686234(v=vs.85).aspx
                    // "The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead"
                    // let's increase the max size of memory we can lock by increasing the MinWorkingSet. On Windows, that is available for all users
                    var nextWorkingSetSize = GetNearestFileSize(currentProcess.MinWorkingSet.ToInt64() + sizeToLock);

                    if (nextWorkingSetSize > int.MaxValue && PlatformDetails.Is32Bits)
                    {
                        nextWorkingSetSize = int.MaxValue;
                    }

                    // Minimum working set size must be less than or equal to the maximum working set size.
                    // Let's increase the max as well.
                    if (nextWorkingSetSize > (long)currentProcess.MaxWorkingSet)
                    {
                        try
                        {
                            currentProcess.MaxWorkingSet = new IntPtr(nextWorkingSetSize);
                        }
                        catch (Exception e)
                        {
                            throw new InsufficientMemoryException(
                                      $"Need to increase the min working set size from {(long)currentProcess.MinWorkingSet:#,#;;0} bytes to {nextWorkingSetSize:#,#;;0} bytes but the max working set size was too small: {(long)currentProcess.MaxWorkingSet:#,#;;0}. " +
                                      $"Failed to increase the max working set size so we can lock {sizeToLock:#,#;;0} bytes for {FileName}. With encrypted " +
                                      "databases we lock some memory in order to avoid leaking secrets to disk. Treating this as a catastrophic error " +
                                      "and aborting the current operation.", e);
                        }
                    }

                    try
                    {
                        currentProcess.MinWorkingSet = new IntPtr(nextWorkingSetSize);
                    }
                    catch (Exception e)
                    {
                        throw new InsufficientMemoryException(
                                  $"Failed to increase the min working set size to {nextWorkingSetSize:#,#;;0} bytes so we can lock {sizeToLock:#,#;;0} bytes for {FileName}. With encrypted " +
                                  "databases we lock some memory in order to avoid leaking secrets to disk. Treating this as a catastrophic error " +
                                  "and aborting the current operation.", e);
                    }

                    if (Sodium.Lock(addressToLock, (UIntPtr)sizeToLock) == 0)
                    {
                        return;
                    }

                    // let's retry, since we increased the WS, but other thread might have locked the memory
                    retries--;
                }
            }

            var msg =
                $"Unable to lock memory for {FileName} with size {sizeToLock:#,#;;0} bytes), with encrypted databases we lock some memory in order to avoid leaking secrets to disk. Treating this as a catastrophic error and aborting the current operation.{Environment.NewLine}";

            if (PlatformDetails.RunningOnPosix)
            {
                msg +=
                    $"The admin may configure higher limits using: 'sudo prlimit --pid {currentProcess.Id} --memlock={sizeToLock}' to increase the limit. (It's recommended to do that as part of the startup script){Environment.NewLine}";
            }
            else
            {
                msg +=
                    $"Already tried to raise the the process min working set to {currentProcess.MinWorkingSet.ToInt64():#,#;;0} bytes but still got a failure.{Environment.NewLine}";
            }

            msg += "This behavior is controlled by the 'Security.DoNotConsiderMemoryLockFailureAsCatastrophicError' setting (expert only, modifications of this setting is not recommended).";

            throw new InsufficientMemoryException(msg);
        }