diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index 12faca29bbb9c..c06568ccfca88 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -4,6 +4,7 @@
#include <linux/err.h>
#include <linux/idr.h>
+#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/notifier.h>
#include <linux/nsproxy.h>
@@ -31,6 +32,8 @@ struct ipc_ids {
struct ipc_namespace {
struct ipc_ids ids[3];
+ /* Serialize kernel.sem writers. */
+ struct mutex sem_ctls_mutex;
int sem_ctls[4];
int used_sems;
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index d038d944257f6..a05e28e67c305 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -51,21 +51,38 @@ static int proc_ipc_auto_msgmni(const struct ctl_table *table, int write,
static int proc_ipc_sem_dointvec(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
+ struct ctl_table ipc_table;
struct ipc_namespace *ns =
container_of(table->data, struct ipc_namespace, sem_ctls);
- int ret, semmni;
+ int sem_ctls[ARRAY_SIZE(ns->sem_ctls)];
+ size_t i;
+ int ret;
+
+ if (!write)
+ return proc_dointvec(table, write, buffer, lenp, ppos);
- semmni = ns->sem_ctls[3];
- ret = proc_dointvec(table, write, buffer, lenp, ppos);
+ mutex_lock(&ns->sem_ctls_mutex);
+ for (i = 0; i < ARRAY_SIZE(sem_ctls); i++)
+ sem_ctls[i] = READ_ONCE(ns->sem_ctls[i]);
- if (!ret)
- ret = sem_check_semmni(ns);
+ memcpy(&ipc_table, table, sizeof(ipc_table));
+ ipc_table.data = sem_ctls;
- /*
- * Reset the semmni value if an error happens.
- */
+ ret = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
if (ret)
- ns->sem_ctls[3] = semmni;
+ goto out_unlock;
+
+ if (sem_ctls[2] < 0 ||
+ sem_ctls[3] < 0 || sem_ctls[3] > ipc_mni) {
+ ret = -ERANGE;
+ goto out_unlock;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sem_ctls); i++)
+ WRITE_ONCE(ns->sem_ctls[i], sem_ctls[i]);
+
+out_unlock:
+ mutex_unlock(&ns->sem_ctls_mutex);
return ret;
}
diff --git a/ipc/sem.c b/ipc/sem.c
index a8c9807175520..0f7d32c12993f 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -249,6 +249,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
void sem_init_ns(struct ipc_namespace *ns)
{
+ mutex_init(&ns->sem_ctls_mutex);
ns->sc_semmsl = SEMMSL;
ns->sc_semmns = SEMMNS;
ns->sc_semopm = SEMOPM;diff --git a/ipc/util.h b/ipc/util.h
index a55d6cebe6d3b..51b4efa3d6db0 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -245,15 +245,6 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
void (*free)(struct ipc_namespace *, struct kern_ipc_perm *));
-static inline int sem_check_semmni(struct ipc_namespace *ns) {
- /*
- * Check semmni range [0, ipc_mni]
- * semmni is the last element of sem_ctls[4] array
- */
- return ((ns->sem_ctls[3] < 0) || (ns->sem_ctls[3] > ipc_mni))
- ? -ERANGE : 0;
-}
-
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
struct compat_ipc_perm {--
2.43.0