jedisct1 / libsodium

A modern, portable, easy to use crypto library.

Home Page:https://libsodium.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

sodium_malloc() does not check return value from sodium_mlock()

tdammers opened this issue · comments

Specific source code location:

sodium_mlock(unprotected_ptr, unprotected_size);

When sodium_mlock() runs into an OS-imposed mlock limit, it will set errno and return -1, and the memory will remain unlocked. But sodium_malloc() does not check the return value, so we still get the allocated memory back, and, in line to the documentation, we expect it to be mlocked, but it actually isn't.

And because there is no "loud error", the consequences of this may go entirely undetected (after all, the memory itself is still valid, it's just not mlocked), or they may cause spurious errors in code that depends on it being mlocked, like segfaults, assertion failures on line 627, or just silently leaking secrets to swap.

IMO, a fix would amount to this:

  • Capture the return value of sodium_mlock().
  • If it's not 0, free base_ptr, and return NULL. errno should hold the correct error at this point.

These system calls are intentionally not checked here.

We can't guarantee that sodium_malloc() can lock memory. On some environments, memory locking just doesn't exist. On others, such as WebAssembly, the function is failing right now, but may eventually work (and this will, very likely, also depend on the runtime).

On other systems, the system call may or may not work according to the system configuration and the amount of already locked memory, which is very non-deterministic.

For many application developers, sodium_malloc() would be seen as something that "randomly fails" and would discourage its use.

The reason is was introduced is to mitigate heartbleed-like attacks. It can also be used as a general bound checking mechanism, and it's widely used that way in the test suite. Locking memory is just a bonus step, but without any guarantees. If the documentation states it otherwise or is confusing, that should be clarified.

For cases where memory locking is absolutely required, there is the sodium_mlock() function.

The relevant documentation is this:

In addition, sodium_mlock() is called on the region to help avoid it being swapped to disk.

(found here)

I think clarification would be appropriate here, e.g.:

In addition, sodium_mlock() is called on the region to help avoid it being swapped to disk. Note however that sodium_mlock() may fail, in which case sodium_malloc() will return the memory regardless, but it will not be mlocked. If you need to rely on mlocking, consider calling sodium_mlock() explicitly, and checking its return value.

I would also suggest documenting this design decision in the source code, to avoid future misunderstandings of this kind.