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.
This commit is contained in:
Milan Broz
2017-12-07 10:43:52 +01:00
parent e0cacef52e
commit fa1f8c0d87
5 changed files with 72 additions and 6 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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>"

View File

@@ -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);