Memory size of `Box<S: Zeroize>`

This is a cross post, see here Reddit - Dive into anything
Just wanted to increase the audience

I'm trying to extend the secrecy crate to mlock(2) the data to offer beter ptrotection to make sure data is not moved to swap.

The new API is a wrappe over Box<S: Zeroize>. I'm trying to use memsec which has this munlock(addr: *mut u8, len: usize), the pointer I resolved it but the len is probematic, I don't know how to correctly find the memory size of that Box<S: Zeroize>, especialy if it's a Vec<_>.

Found some crates which might help but they impose you to implement some trait or derive simething. I was looking for a generic solution, I know this is kinda not clear to find.
Another approach would be to expose len param on methods used to wrap the Box but that is not ideal.

Is there a way to find the memory size of that wrapped data in this case?

Are you looking for size_of_val?

1 Like

yes, but for Vec it returns 24 bytes, as the internal representation of it is 3 fields like a pointer, len and capacity. If doesn't know to measure size for all elements. It works ok for [u8] though.

If you have a Vec you need to use as_slice or deref it:

size_of_val(v.as_slice());
size_of_val(&*v);

For Box, you would deref it.

3 Likes

Note that to mlock the whole thing, you need to mlock both the data the Vec points to, and the length and capacity fields of the Vec; otherwise, there's the risk of leaking some metadata (length, capacity) to swap.

1 Like

yes just that I have a function like this
fn new<T>(source: Box<T>)
cannot .as_slice() in source

don't think it's a problems if those leaks, what I care is not to leak the content as it keeps credentials

Well, that will only get you so far. Vec<Vec<u8>>: Zeroize, for example, and you’ll need to descend into the second level of vectors to find the memory that actually contains sensitive information— I don’t know of any way to do this generically other than defining a new trait for the purpose.

2 Likes

Use deref.

fn new<T>(source: Box<T>) -> usize {
    size_of_val(&*source)
}

same, for Box<Vec<u8>> of a vec with 4096 elements it returns 24

Okay, I see what you want now. You can't really do this without a trait to recursively apply the operation to each indirection. You said you didn't want to use a trait before, why is that? It would look basically the same as Zeroize but with the constructor and destructor instead of just destructor.

2 Likes

There is no fully generic way to determine "all memory reachable from this struct", even if you had some ability to see every struct definition. You don't know what raw pointers are valid, the types on raw pointers don't have to be accurate, and people can build their own opaque indirections to boot.

5 Likes

how to do it with another trait?

It will probably look like

trait MemLock {
    fn mlock(&self);
    fn munlock(&self);
}

and then implementing that as appropriate for the types that can be used securely to lock the secret holding memory regions.

However, note that locking/unlocking is done at page granularity without any sort of reference count, so if two secret regions are in a shared page, unlocking one will have the effect of unlocking the other as well, allowing it to be paged out again. If you need to protect secrets at this level, you'll want to have a separate allocator region for secrets instead of trying to protect whatever random spots in memory they end up in using the general purpose allocator. (So that means not using the standard containers, at least not until they eventually become generic over allocator sometime in the future.)

3 Likes

I suppose an alternative would be for them to report the memory regions that need to be locked instead of doing it themselves. That would give an opportunity for a higher-level coordinator to sort out these sorts of conflict.

2 Likes

Knowing that a given credential is of length 2048 bits is useful information to some classes of attacker; and knowing that your password is 13 characters long is hugely useful if I have acquired password dumps that give me 100 passwords you've previously used, of which 2 are 13 characters long - you've just made it very likely that I can guess your password in two guesses.

Whether this matters falls out of your threat model; but my experience of threat models where "attacker can get at swap content" is a concern is that they tend to also have concerns about secret length metadata.

Indeed there are those concerns, also there is Cold boot attack, for which this zeroizing and mlock doesn't help, but a possible help against that would be to expire and zeroize credentials on idle, when not actively used, and reload them when needed

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.