memory_locker 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +12 -27
- data/lib/memory_locker/version.rb +1 -1
- data/lib/memory_locker.rb +29 -20
- metadata +3 -18
- data/lib/memory_locker/libc.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4bc016f2294342753deae3eda56b89e55b5345474f12603919b5063dc441045
|
4
|
+
data.tar.gz: ac399821325952ee575fcc226de9edc41c14139a078ae33eb1c7584f24033f54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a9ad9fc290ccbe7ce4fc0fe6a52d1bce11b0fb0899721c5e4e6492f14f54811f4f630faf632dc9e7b6afc944cc22a7b188d90b3dbd6065d79b98f17e5805944
|
7
|
+
data.tar.gz: d92c5be22a0b00041829053f32bc61a6c46ce63987062584956413da81832b44046dbe50b0d14e6c8bdc9b3a1c9b121b0f76612a9741c1ec65a985fa731a760a
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# MemoryLocker
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/memory_locker.svg)](https://badge.fury.io/rb/memory_locker)
|
3
4
|
|
4
5
|
Lock memory containing sensitive data (such as passwords or cryptographic keys) to prevent it from being swapped
|
@@ -7,7 +8,7 @@ by the kernel, which allows the attacker with access to swap space to recover se
|
|
7
8
|
## How it works
|
8
9
|
|
9
10
|
Ruby doesn't allow granular memory management, therefore the approach is to lock the entire memory of a current
|
10
|
-
process using
|
11
|
+
process using [mlockall()](https://linux.die.net/man/2/mlockall).
|
11
12
|
|
12
13
|
The memory will stay locked until the process terminates. Although unlocking memory is technically possible,
|
13
14
|
the gem doesn't allow it. The reason is Ruby doesn't support reliable removal of secrets from memory,
|
@@ -19,47 +20,31 @@ Subprocesses don't inherit memory locking. Make sure to lock memory in each one
|
|
19
20
|
|
20
21
|
## Requirements
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
Refer to gem documentation for instructions.
|
25
|
-
|
26
|
-
Also, an OS supporting `mlockall()` is required. Those include most Unixes except macOS. Windows is not supported.
|
23
|
+
OS support for [mlockall()](https://linux.die.net/man/2/mlockall) is required.
|
24
|
+
Will work on most Unixes except macOS. Windows is not supported.
|
27
25
|
|
28
26
|
## Installation
|
29
27
|
|
30
|
-
|
28
|
+
gem install memory_locker
|
31
29
|
|
32
30
|
## Usage
|
33
31
|
|
34
|
-
### Enforced installation
|
35
|
-
|
36
32
|
To lock the memory of your process, add the following code early in the app lifetime:
|
37
33
|
|
38
34
|
require 'memory_locker'
|
39
|
-
MemoryLocker.call
|
40
|
-
|
41
|
-
### Optional installation
|
42
|
-
|
43
|
-
If you don't want to force the user to install `memory_locker` gem, you can make it optional.
|
44
|
-
If the user doesn't have it installed, the warning will appear, but your app will run.
|
45
|
-
|
46
|
-
begin
|
47
|
-
require 'memory_locker'
|
48
|
-
rescue LoadError
|
49
|
-
warn 'Failed to lock memory. To fix install `memory_locker` gem.'
|
50
|
-
else
|
51
|
-
MemoryLocker.call
|
52
|
-
end
|
35
|
+
MemoryLocker.call # short syntax
|
36
|
+
MemoryLocker.new.call # if you prefer to use instance explicitly
|
53
37
|
|
54
38
|
## Exceptions
|
55
39
|
|
56
|
-
If your OS is unsupported or there
|
40
|
+
If your OS is unsupported or there is a locking error, you will get an exception descending from `MemoryLocker::Error`.
|
57
41
|
|
58
42
|
## Testing
|
59
43
|
|
60
|
-
Locking the memory of your app when testing is not needed, and if you use an unsupported OS will
|
44
|
+
Locking the memory of your app when testing is not needed, and if you use an unsupported OS will break your app.
|
61
45
|
|
62
|
-
As only the `#call` method is being used, you can easily replace `MemoryLocker` with empty
|
46
|
+
As only the `#call` method is being used, you can easily replace `MemoryLocker` class or instance with an empty
|
47
|
+
lambda `->{}` in your tests.
|
63
48
|
|
64
49
|
## Development
|
65
50
|
|
@@ -72,7 +57,7 @@ push git commits and the created tag, and push the `.gem` file to [rubygems.org]
|
|
72
57
|
|
73
58
|
## Contributing
|
74
59
|
|
75
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/phantom-node/memory_locker
|
60
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/phantom-node/memory_locker>.
|
76
61
|
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
|
77
62
|
the [code of conduct](https://github.com/phantom-node/memory_locker/blob/master/CODE_OF_CONDUCT.md).
|
78
63
|
|
data/lib/memory_locker.rb
CHANGED
@@ -1,39 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require
|
3
|
+
require_relative "memory_locker/version"
|
4
|
+
require "fiddle"
|
5
5
|
|
6
6
|
# Lock process memory, so it won't be swapped by the kernel.
|
7
7
|
# It is implemented as a one-way operation: there is no unlock.
|
8
8
|
# That's because it's hard to properly clean memory in Ruby.
|
9
9
|
class MemoryLocker
|
10
|
-
Error
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
Error = Class.new StandardError
|
11
|
+
LockingError = Class.new Error
|
12
|
+
UnsupportedError = Class.new Error
|
13
|
+
|
14
|
+
# Those values should remain the same on all POSIX systems
|
15
|
+
MCL_CURRENT = 1
|
16
|
+
MCL_FUTURE = 2
|
17
|
+
private_constant :MCL_CURRENT, :MCL_FUTURE
|
14
18
|
|
15
19
|
def call
|
16
|
-
|
17
|
-
raise LockingError, "Locking of memory failed with errno #{errno}" unless result.zero?
|
20
|
+
raise LockingError, "Locking of memory failed" unless function.call(MCL_CURRENT | MCL_FUTURE).zero?
|
18
21
|
end
|
19
22
|
|
20
23
|
def self.call
|
21
|
-
new.call
|
24
|
+
new.send :call
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
def initialize(libc_loader: -> { require_relative 'memory_locker/libc' },
|
29
|
-
libc_fetcher: -> { Libc },
|
30
|
-
unsupported_error: FFI::NotFoundError,
|
31
|
-
libc_not_found_error: LoadError)
|
32
|
-
libc_loader.call
|
33
|
-
@libc = libc_fetcher.call
|
29
|
+
def function
|
30
|
+
lazy_function.call
|
34
31
|
rescue unsupported_error => e
|
35
|
-
raise UnsupportedError,
|
36
|
-
|
37
|
-
|
32
|
+
raise UnsupportedError, "Memory locking not supported: #{e.message}", cause: e
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :lazy_function, :unsupported_error
|
36
|
+
|
37
|
+
def initialize(
|
38
|
+
libc_path: nil,
|
39
|
+
function_name: "mlockall",
|
40
|
+
lazy_handle: -> { Fiddle.dlopen(libc_path)[function_name] },
|
41
|
+
lazy_function: -> { Fiddle::Function.new(lazy_handle.call, [Fiddle::TYPE_INT], Fiddle::TYPE_INT) },
|
42
|
+
unsupported_error: Fiddle::DLError
|
43
|
+
)
|
44
|
+
|
45
|
+
@lazy_function = lazy_function
|
46
|
+
@unsupported_error = unsupported_error
|
38
47
|
end
|
39
48
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memory_locker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paweł Pokrywka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: ffi
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.0.0
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.0.0
|
11
|
+
date: 2023-11-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
27
13
|
description:
|
28
14
|
email:
|
29
15
|
- pepawel@users.noreply.github.com
|
@@ -35,7 +21,6 @@ files:
|
|
35
21
|
- LICENSE.txt
|
36
22
|
- README.md
|
37
23
|
- lib/memory_locker.rb
|
38
|
-
- lib/memory_locker/libc.rb
|
39
24
|
- lib/memory_locker/version.rb
|
40
25
|
homepage: https://github.com/phantom-node/memory_locker
|
41
26
|
licenses:
|
data/lib/memory_locker/libc.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class MemoryLocker
|
4
|
-
# Low level interface to libc
|
5
|
-
module Libc
|
6
|
-
# Those values should remain the same on all POSIX systems
|
7
|
-
MCL_CURRENT = 1
|
8
|
-
MCL_FUTURE = 2
|
9
|
-
|
10
|
-
extend FFI::Library
|
11
|
-
# Try to load already loaded libc from the current process, use system libc as a fallback
|
12
|
-
ffi_lib [FFI::CURRENT_PROCESS, FFI::Library::LIBC], FFI::Library::LIBC
|
13
|
-
attach_function :real_mlockall, :mlockall, [:int], :int
|
14
|
-
|
15
|
-
def self.mlockall
|
16
|
-
result = real_mlockall(MCL_CURRENT | MCL_FUTURE)
|
17
|
-
[result, FFI.errno]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private_constant :Libc
|
22
|
-
end
|