-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added 'MlockManager' to prevent early 'munlock'ing #34
Conversation
6999259
to
2e6acf3
Compare
/// Round `ptr` down to the nearest page boundry (i.e returns the first address in the page that | ||
/// contains `ptr`). | ||
fn get_page_addr(ptr: *const u8) -> *const u8 { | ||
let offset = ptr as usize % *PAGE_SIZE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this calculation could be incorrect if the memory referenced by ptr
is not allocated page-aligned (by something like valloc, etc.). In other words, I'm not sure that pages always necessarily line up with 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I'm sure that this doesn't generalize at all. My thoughts were that ptr
will only ever point to an Fr
or to an [Fr]
, which are aligned because Fr
is just a wrapper around [u64; 4]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If anyone knows of a better way to get the first address in a page, I'm happy to change to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can micro-optimize that into a single bitmask instead of modulo by calculating the inverse, if we are assuming PAGE_SIZE
is always a power of 2, which the compiler cannot assume at compile. But since that's a lot harder to read, prime case for annoying bugs and not on the hot path, I suggest you do not follow my suggestions =).
Code like this can be highly prone to nasty bugs. I'd recommend some significantly more comprehensive testing. Perhaps @mbr could suggest some things. |
I was considering doing a multithreaded test, where for example, I would create 5 threads each of which creates its own |
Since we're already building a dedicated (I'm beginning to have doubts whether it's worth all that trouble and whether it even makes sense to have this enabled by default. Some of the comments here make a few good points against it.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpicks aside, I am not sure this is the way to go - "locked memory efficiency" is going to be random, right? At worst, we will lock PAGE_SIZE
memory for each secret?
A tiny allocator might be a better alternative, I think.
/// Round `ptr` down to the nearest page boundry (i.e returns the first address in the page that | ||
/// contains `ptr`). | ||
fn get_page_addr(ptr: *const u8) -> *const u8 { | ||
let offset = ptr as usize % *PAGE_SIZE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can micro-optimize that into a single bitmask instead of modulo by calculating the inverse, if we are assuming PAGE_SIZE
is always a power of 2, which the compiler cannot assume at compile. But since that's a lot harder to read, prime case for annoying bugs and not on the hot path, I suggest you do not follow my suggestions =).
} | ||
|
||
/// Returns the total number of pages currently locked into RAM. | ||
#[cfg(test)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function might be useful outside testing for logging purposes.
An inefficient allocator can be somewhat concise, here's my take. Please keep in mind that this is a 1 hour effort, so treat it like a sketch: https://gist.github.com/mbr/9d2b8481a2668dfde1829f1fa7e10a72 |
I should refresh the reviews page while writing reviews, but yes, I agree. See the gist for a proposed solution-like. Limiting to a specific size also has the advantage of being able to lock it in advance and take action immediately, reducing the likely of runtime errors ("secure memory leaks" excepted). |
So we'd have a static global (or thread-local) I guess we could add convenience methods to get whole contiguous slices, and make the |
That's one way of doing it. Two choices here, if we use a Those are the options for per-thread or per-program instances. If we can keep it to a reasonable amount of slabs, we could go a little more fine-grained (the further down we go, the higher the chance we waste valuable lockable memory though).
You can turn it into a real allocator; I just chose that particular implementation because it was fast to write, simple and hopefully readable. For slices, we'd need an extra type. Another helpful thing would probably |
Closing in favor of #42. |
MlockManager
tosecrets
module.mlock
for a page in memory, stops pages from being unlocked when they still contain secret values.page_size
.Closes issue #31