Rework cpu affinity parsing

The CPU_SET_S api is pretty bad. In particular, it has a parameter for the size
of the array, but operations which take two (CPU_EQUAL_S) or even three arrays
(CPU_{AND,OR,XOR}_S) still take just one size. This means that all arrays must
be of the same size, or buffer overruns will occur. This is exactly what our
code would do, if it received an array of unexpected size over the network.
("Unexpected" here means anything different from what cpu_set_malloc() detects
as the "right" size.)

Let's rework this, and store the size in bytes of the allocated storage area.

The code will now parse any number up to 8191, independently of what the current
kernel supports. This matches the kernel maximum setting for any architecture,
to make things more portable.

Fixes #12605.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2019-05-29 10:20:42 +02:00
parent b12ef71416
commit 0985c7c4e2
14 changed files with 293 additions and 254 deletions
+11 -14
View File
@@ -1266,8 +1266,7 @@ struct cpu_data {
uint64_t shares;
uint64_t quota;
uint64_t period;
cpu_set_t *cpuset;
unsigned ncpus;
CPUSet cpu_set;
};
static int oci_cgroup_cpu_shares(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
@@ -1302,21 +1301,20 @@ static int oci_cgroup_cpu_quota(const char *name, JsonVariant *v, JsonDispatchFl
static int oci_cgroup_cpu_cpus(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
struct cpu_data *data = userdata;
cpu_set_t *set;
CPUSet set;
const char *n;
int ncpus;
int r;
assert(data);
assert_se(n = json_variant_string(v));
ncpus = parse_cpu_set(n, &set);
if (ncpus < 0)
return json_log(v, flags, ncpus, "Failed to parse CPU set specification: %s", n);
r = parse_cpu_set(n, &set);
if (r < 0)
return json_log(v, flags, r, "Failed to parse CPU set specification: %s", n);
CPU_FREE(data->cpuset);
data->cpuset = set;
data->ncpus = ncpus;
cpu_set_reset(&data->cpu_set);
data->cpu_set = set;
return 0;
}
@@ -1345,13 +1343,12 @@ static int oci_cgroup_cpu(const char *name, JsonVariant *v, JsonDispatchFlags fl
r = json_dispatch(v, table, oci_unexpected, flags, &data);
if (r < 0) {
CPU_FREE(data.cpuset);
cpu_set_reset(&data.cpu_set);
return r;
}
CPU_FREE(s->cpuset);
s->cpuset = data.cpuset;
s->cpuset_ncpus = data.ncpus;
cpu_set_reset(&s->cpu_set);
s->cpu_set = data.cpu_set;
if (data.shares != UINT64_MAX) {
r = settings_allocate_properties(s);
+2 -31
View File
@@ -133,7 +133,7 @@ Settings* settings_free(Settings *s) {
strv_free(s->syscall_blacklist);
rlimit_free_all(s->rlimit);
free(s->hostname);
s->cpuset = cpu_set_mfree(s->cpuset);
cpu_set_reset(&s->cpu_set);
strv_free(s->network_interfaces);
strv_free(s->network_macvlan);
@@ -803,41 +803,12 @@ int config_parse_cpu_affinity(
void *data,
void *userdata) {
_cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
Settings *settings = data;
int ncpus;
assert(rvalue);
assert(settings);
ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
if (ncpus < 0)
return ncpus;
if (ncpus == 0) {
/* An empty assignment resets the CPU list */
settings->cpuset = cpu_set_mfree(settings->cpuset);
settings->cpuset_ncpus = 0;
return 0;
}
if (!settings->cpuset) {
settings->cpuset = TAKE_PTR(cpuset);
settings->cpuset_ncpus = (unsigned) ncpus;
return 0;
}
if (settings->cpuset_ncpus < (unsigned) ncpus) {
CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset);
CPU_FREE(settings->cpuset);
settings->cpuset = TAKE_PTR(cpuset);
settings->cpuset_ncpus = (unsigned) ncpus;
return 0;
}
CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset);
return 0;
return parse_cpu_set_extend(rvalue, &settings->cpu_set, true, unit, filename, line, lvalue);
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
+2 -2
View File
@@ -13,6 +13,7 @@
#include "capability-util.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
#include "macro.h"
#include "missing_resource.h"
#include "nspawn-expose-ports.h"
@@ -163,8 +164,7 @@ typedef struct Settings {
int no_new_privileges;
int oom_score_adjust;
bool oom_score_adjust_set;
cpu_set_t *cpuset;
unsigned cpuset_ncpus;
CPUSet cpu_set;
ResolvConfMode resolv_conf;
LinkJournal link_journal;
bool link_journal_try;
+12 -17
View File
@@ -220,8 +220,7 @@ static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {};
static bool arg_no_new_privileges = false;
static int arg_oom_score_adjust = 0;
static bool arg_oom_score_adjust_set = false;
static cpu_set_t *arg_cpuset = NULL;
static unsigned arg_cpuset_ncpus = 0;
static CPUSet arg_cpu_set = {};
static ResolvConfMode arg_resolv_conf = RESOLV_CONF_AUTO;
static TimezoneMode arg_timezone = TIMEZONE_AUTO;
static unsigned arg_console_width = (unsigned) -1, arg_console_height = (unsigned) -1;
@@ -259,7 +258,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_syscall_blacklist, strv_freep);
#if HAVE_SECCOMP
STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep);
#endif
STATIC_DESTRUCTOR_REGISTER(arg_cpuset, CPU_FREEp);
STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset);
STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
static int help(void) {
@@ -1329,17 +1328,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CPU_AFFINITY: {
_cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
CPUSet cpuset;
r = parse_cpu_set(optarg, &cpuset);
if (r < 0)
return log_error_errno(r, "Failed to parse CPU affinity mask: %s", optarg);
return log_error_errno(r, "Failed to parse CPU affinity mask %s: %m", optarg);
if (arg_cpuset)
CPU_FREE(arg_cpuset);
arg_cpuset = TAKE_PTR(cpuset);
arg_cpuset_ncpus = r;
cpu_set_reset(&arg_cpu_set);
arg_cpu_set = cpuset;
arg_settings_mask |= SETTING_CPU_AFFINITY;
break;
}
@@ -2922,8 +2918,8 @@ static int inner_child(
return log_error_errno(r, "Failed to adjust OOM score: %m");
}
if (arg_cpuset)
if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0)
if (arg_cpu_set.set)
if (sched_setaffinity(0, arg_cpu_set.allocated, arg_cpu_set.set) < 0)
return log_error_errno(errno, "Failed to set CPU affinity: %m");
(void) setup_hostname();
@@ -3869,15 +3865,14 @@ static int merge_settings(Settings *settings, const char *path) {
}
if ((arg_settings_mask & SETTING_CPU_AFFINITY) == 0 &&
settings->cpuset) {
settings->cpu_set.set) {
if (!arg_settings_trusted)
log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", path);
else {
if (arg_cpuset)
CPU_FREE(arg_cpuset);
arg_cpuset = TAKE_PTR(settings->cpuset);
arg_cpuset_ncpus = settings->cpuset_ncpus;
cpu_set_reset(&arg_cpu_set);
arg_cpu_set = settings->cpu_set;
settings->cpu_set = (CPUSet) {};
}
}