RustCrypto / password-hashes

Password hashing functions / KDFs
677 stars 83 forks source link

Argon2: unbounded (?) RAM usage on Raspberry Pis #526

Closed daladim closed 2 months ago

daladim commented 2 months ago

Hello,

I've just realized argon2 is using unexpected amounts of RAM on a Raspberry Pi.

Minimal reproducer

Using the current master branch (@ 21d80b0040e1eff0f8f4e76733fdfd3210bac84a), but I discovered this issue @ 0.5.3.

use argon2::{
    password_hash::{PasswordHash, PasswordVerifier},
    Argon2
};

const HASHED_AAAA: &str = "$argon2id$v=19$m=19456,t=2,p=1$JBGJ6MV6WMfRxt3SrRHsHg$XemLlBUVStBYVJKj/9qPYQxOSuvszcFh17NK6+t4tA8";

use memory_stats::memory_stats;
fn print_memory() {
    let stats = memory_stats().unwrap();
    println!("physical memory: {:.2} M", stats.physical_mem as f32 / 1_000_000.0);
}

#[test]
fn ram_usage() {
    for _i in 1..10 {
        check(HASHED_AAAA, "AAAA");
    }
}

fn check(hashed: &str, pass: &str) {
    print_memory();
    let ph = PasswordHash::new(hashed).unwrap();
    Argon2::default().verify_password(pass.as_bytes(), &ph).unwrap();
    print_memory();
}

Results

To run the reproducer (in the argon2 folder): cargo add memory-stats and cargo test --all-features -- --nocapture ram_usage

On a Raspberry Pi 5 (aarch64, Raspberry Pi OS 12 Bookworm, rustc 1.81.0 stable):

running 1 test
physical memory: 2.29 M
physical memory: 2.32 M
physical memory: 2.32 M
physical memory: 22.22 M
physical memory: 22.22 M
physical memory: 42.14 M
physical memory: 42.14 M
physical memory: 62.06 M
physical memory: 62.06 M
physical memory: 81.99 M
physical memory: 81.99 M
physical memory: 101.91 M
physical memory: 101.91 M
physical memory: 121.84 M
physical memory: 121.84 M
physical memory: 141.76 M
physical memory: 141.76 M
physical memory: 161.69 M
test ram_usage ... ok

This is consistent over several Rapsberry Pis I have tried.

However, on the following setups, this test always showed sensible and steady RAM usage:

daladim commented 2 months ago

I'll try to investigate myself a bit, but I'm sure you'll be quicker than me to figure out what the root cause can be.

I'll chime in here if ever I find anything relevant.

tarcieri commented 2 months ago

Dup of #478, #486

argon2 has extremely trivial use of the heap with 100% safe code. Please see #478 where we went through all of this already.

However, it does use a large amount of memory, which puts stress on the heap.

You are observing how your memory allocator works, no more.

daladim commented 2 months ago

Thanks for your reply. I could create a minimal reproducer that I filed to the rust-lang repo

tarcieri commented 2 months ago

Calling Argon2 over and over like that signals to the heap that it should hang on to the memory, rather than releasing it to the OS, because you are making similarly shaped heap allocations over and over, followed by immediately freeing it.

Also note that if you want to experiment with different memory strategies, this API lets you completely manage the memory yourself and avoids all direct heap allocations: https://docs.rs/argon2/latest/argon2/struct.Argon2.html#method.hash_password_into_with_memory