Commit fa1f8c0d authored by Milan Broz's avatar Milan Broz

Limit KDF memory by available physical memory.

On some systems the requested amount of memory causes OOM killer
to kill the process (instead of returning ENOMEM).

For now, we never try to use more than half of available
physical memory.
parent e0cacef5
......@@ -146,6 +146,7 @@ ssize_t read_lseek_blockwise(int fd, size_t bsize, size_t alignment, void *buf,
size_t crypt_getpagesize(void);
unsigned crypt_cpusonline(void);
uint64_t crypt_getphysmemory_kb(void);
int init_crypto(struct crypt_device *ctx);
......
......@@ -45,6 +45,23 @@ unsigned crypt_cpusonline(void)
return r < 0 ? 1 : r;
}
uint64_t crypt_getphysmemory_kb(void)
{
long pagesize, phys_pages;
uint64_t phys_memory_kb;
pagesize = sysconf(_SC_PAGESIZE);
phys_pages = sysconf(_SC_PHYS_PAGES);
if (pagesize < 0 || phys_pages < 0)
return 0;
phys_memory_kb = pagesize / 1024;
phys_memory_kb *= phys_pages;
return phys_memory_kb;
}
ssize_t read_buffer(int fd, void *buf, size_t count)
{
size_t read_size = 0;
......
......@@ -38,6 +38,26 @@ const struct crypt_pbkdf_type default_luks1 = {
.time_ms = DEFAULT_LUKS1_ITER_TIME
};
static uint32_t adjusted_pbkdf_memory(void)
{
uint64_t memory_kb = crypt_getphysmemory_kb();
/* Ignore bogus value */
if (memory_kb < (128 * 1024))
return DEFAULT_LUKS2_MEMORY_KB;
/*
* Never use more than half of physical memory.
* OOM killer is too clever...
*/
memory_kb /= 2;
if (memory_kb < DEFAULT_LUKS2_MEMORY_KB)
return (uint32_t)memory_kb;
return DEFAULT_LUKS2_MEMORY_KB;
}
/*
* PBKDF configuration interface
*/
......@@ -100,7 +120,7 @@ int init_pbkdf_type(struct crypt_device *cd,
struct crypt_pbkdf_type *cd_pbkdf = crypt_get_pbkdf(cd);
const char *hash, *type;
unsigned cpus;
uint32_t old_flags;
uint32_t old_flags, memory_kb;
int r;
if (!pbkdf && dev_type && !strcmp(dev_type, CRYPT_LUKS2))
......@@ -164,6 +184,16 @@ int init_pbkdf_type(struct crypt_device *cd,
}
}
if (cd_pbkdf->max_memory_kb) {
memory_kb = adjusted_pbkdf_memory();
if (cd_pbkdf->max_memory_kb > memory_kb) {
log_dbg("Not enough physical memory detected, "
"PBKDF max memory decreased from %dkB to %dkB.",
cd_pbkdf->max_memory_kb, memory_kb);
cd_pbkdf->max_memory_kb = memory_kb;
}
}
log_dbg("PBKDF %s, hash %s, time_ms %u (iterations %u), max_memory_kb %u, parallel_threads %u.",
cd_pbkdf->type ?: "(none)", cd_pbkdf->hash ?: "(none)", cd_pbkdf->time_ms,
cd_pbkdf->iterations, cd_pbkdf->max_memory_kb, cd_pbkdf->parallel_threads);
......
......@@ -904,7 +904,8 @@ Specifying 0 as parameter selects the compiled-in default.
.TP
.B "\-\-pbkdf\-memory <number>"
Set the memory cost for PBKDF (for Argon2i/id the number represents kilobytes).
Note that it is maximal value, PBKDF benchmark can decrease it.
Note that it is maximal value, PBKDF benchmark or available physical memory
can decrease it.
This option is not available for PBKDF2.
.TP
.B "\-\-pbkdf\-parallel <number>"
......
......@@ -141,6 +141,23 @@ static unsigned cpus_online(void)
return r;
}
static uint32_t adjusted_pbkdf_memory(void)
{
long pagesize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
uint64_t memory_kb;
if (pagesize <= 0 || pages <= 0)
return DEFAULT_LUKS2_MEMORY_KB;
memory_kb = pagesize / 1024 * pages / 2;
if (memory_kb < DEFAULT_LUKS2_MEMORY_KB)
return (uint32_t)memory_kb;
return DEFAULT_LUKS2_MEMORY_KB;
}
static unsigned _min(unsigned a, unsigned b)
{
return a < b ? a : b;
......@@ -2125,7 +2142,7 @@ static void Pbkdf(void)
OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
EQ_(pbkdf->max_memory_kb, DEFAULT_LUKS2_MEMORY_KB);
EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
// set and verify argon2 type
OK_(crypt_set_pbkdf_type(cd, &argon2));
......@@ -2150,7 +2167,7 @@ static void Pbkdf(void)
OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
EQ_(pbkdf->max_memory_kb, DEFAULT_LUKS2_MEMORY_KB);
EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
// try to pass illegal values
argon2.parallel_threads = 0;
......@@ -2182,14 +2199,14 @@ static void Pbkdf(void)
OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
EQ_(pbkdf->max_memory_kb, DEFAULT_LUKS2_MEMORY_KB);
EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
crypt_set_iteration_time(cd, 1);
OK_(crypt_load(cd, CRYPT_LUKS, NULL));
OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
EQ_(pbkdf->time_ms, 1);
EQ_(pbkdf->max_memory_kb, DEFAULT_LUKS2_MEMORY_KB);
EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
crypt_free(cd);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment