Commit aa606520 authored by Waiman Long's avatar Waiman Long Committed by Pablo Neira Ayuso
Browse files

ipvs: Guard access of HK_TYPE_KTHREAD cpumask with RCU



The ip_vs_ctl.c file and the associated ip_vs.h file are the only places
in the kernel where HK_TYPE_KTHREAD cpumask is being retrieved and used.
Now that HK_TYPE_KTHREAD/HK_TYPE_DOMAIN cpumask can be changed at run
time. We need to use RCU to guard access to this cpumask to avoid a
potential UAF problem as the returned cpumask may be freed before it
is being used.

We can replace HK_TYPE_KTHREAD by HK_TYPE_DOMAIN as they are aliases
of each other, but keeping the HK_TYPE_KTHREAD name can highlight the
fact that it is the kthread initiated by ipvs that is being controlled.

Fixes: 03ff7351 ("cpuset: Update HK_TYPE_DOMAIN cpumask from cpuset")
Signed-off-by: default avatarWaiman Long <longman@redhat.com>
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 4ee52b70
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -1412,7 +1412,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
	return ipvs->sysctl_run_estimation;
}

static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs)
{
	if (ipvs->est_cpulist_valid)
		return ipvs->sysctl_est_cpulist;
@@ -1530,7 +1530,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
	return 1;
}

static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs)
{
	return housekeeping_cpumask(HK_TYPE_KTHREAD);
}
@@ -1565,6 +1565,18 @@ static inline int sysctl_svc_lfactor(struct netns_ipvs *ipvs)
	return READ_ONCE(ipvs->sysctl_svc_lfactor);
}

static inline bool sysctl_est_cpulist_empty(struct netns_ipvs *ipvs)
{
	guard(rcu)();
	return cpumask_empty(__sysctl_est_cpulist(ipvs));
}

static inline unsigned int sysctl_est_cpulist_weight(struct netns_ipvs *ipvs)
{
	guard(rcu)();
	return cpumask_weight(__sysctl_est_cpulist(ipvs));
}

/* IPVS core functions
 * (from ip_vs_core.c)
 */
@@ -1904,7 +1916,7 @@ static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs)
	/* Stop tasks while cpulist is empty or if disabled with flag */
	ipvs->est_stopped = !sysctl_run_estimation(ipvs) ||
			    (ipvs->est_cpulist_valid &&
			     cpumask_empty(sysctl_est_cpulist(ipvs)));
			     sysctl_est_cpulist_empty(ipvs));
#endif
}

@@ -1920,7 +1932,7 @@ static inline bool ip_vs_est_stopped(struct netns_ipvs *ipvs)
static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs)
{
	unsigned int limit = IPVS_EST_CPU_KTHREADS *
			     cpumask_weight(sysctl_est_cpulist(ipvs));
			     sysctl_est_cpulist_weight(ipvs);

	return max(1U, limit);
}
+8 −5
Original line number Diff line number Diff line
@@ -2394,11 +2394,14 @@ static int ipvs_proc_est_cpumask_get(const struct ctl_table *table,

	mutex_lock(&ipvs->est_mutex);

	/* HK_TYPE_KTHREAD cpumask needs RCU protection */
	scoped_guard(rcu) {
		if (ipvs->est_cpulist_valid)
			mask = *valp;
		else
			mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD);
		ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask));
	}

	mutex_unlock(&ipvs->est_mutex);