fix(config): validate received counts before publishing them
The config_receive_{basis,skip,idmap}_count helpers wrote the
peer-controlled int through the Config member before range-checking it.
An over-cap basis_count therefore left config->basis_count huge while
config->basis_dirs was still NULL; config_receive()'s error path then
called config_delete(), whose basis loop dereferenced NULL and crashed
the daemon before authentication.
Read each count into a local, validate, and only then assign, leaving the
member untouched on failure. config_delete() also guards the basis loop
with the array pointer as defense in depth.
Add a regression test that feeds over-cap basis/idmap/skip counts and
asserts rejection without crashing, plus a direct config_delete() check
on the partial (count set, array NULL) state.
This commit is contained in:
+21
-7
@@ -687,11 +687,13 @@ void config_delete(Config* config) {
|
||||
}
|
||||
config->remote_options = NULL;
|
||||
config->remote_option_count = 0;
|
||||
for (int i = 0; i < config->basis_count; i++) {
|
||||
free(config->basis_dirs[i].path);
|
||||
config->basis_dirs[i].path = NULL;
|
||||
if (config->basis_dirs) {
|
||||
for (int i = 0; i < config->basis_count; i++) {
|
||||
free(config->basis_dirs[i].path);
|
||||
config->basis_dirs[i].path = NULL;
|
||||
}
|
||||
free(config->basis_dirs);
|
||||
}
|
||||
free(config->basis_dirs);
|
||||
config->basis_dirs = NULL;
|
||||
config->basis_count = 0;
|
||||
free(config->partial_dir);
|
||||
@@ -839,21 +841,33 @@ static bool config_receive_identity_id(int fd, int32_t* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Read a peer-controlled count into a LOCAL, validate the range, and only then
|
||||
* publish it through `*value`. Writing through `*value` before validating
|
||||
* leaves the Config holding an over-cap count (e.g. 999999999) whose backing
|
||||
* array is still NULL; the receive error path then runs config_delete(), which
|
||||
* walks the array and dereferences NULL. Leaving `*value` untouched on failure
|
||||
* also keeps the failed Config in a coherent, safely-deletable state. */
|
||||
static bool config_receive_skip_count(int fd, int* value) {
|
||||
if (!receive_int(fd, value) || *value < 0 || *value > MAX_SKIP_COMPRESS_SUFFIXES)
|
||||
int v;
|
||||
if (!receive_int(fd, &v) || v < 0 || v > MAX_SKIP_COMPRESS_SUFFIXES)
|
||||
return false;
|
||||
*value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_receive_basis_count(int fd, int* value) {
|
||||
if (!receive_int(fd, value) || *value < 0 || *value > MAX_BASIS_DIRS)
|
||||
int v;
|
||||
if (!receive_int(fd, &v) || v < 0 || v > MAX_BASIS_DIRS)
|
||||
return false;
|
||||
*value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_receive_idmap_count(int fd, int* value) {
|
||||
if (!receive_int(fd, value) || *value < 0 || *value > MAX_IDENTITY_MAP)
|
||||
int v;
|
||||
if (!receive_int(fd, &v) || v < 0 || v > MAX_IDENTITY_MAP)
|
||||
return false;
|
||||
*value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user