18e8ccf43SThomas Gleixner// SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds/*
3f30c2269SUwe Zeisberger * linux/ipc/msgutil.c
41da177e4SLinus Torvalds * Copyright (C) 1999, 2004 Manfred Spraul
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds#include <linux/spinlock.h>
81da177e4SLinus Torvalds#include <linux/init.h>
91da177e4SLinus Torvalds#include <linux/security.h>
101da177e4SLinus Torvalds#include <linux/slab.h>
111da177e4SLinus Torvalds#include <linux/ipc.h>
1240401530SAl Viro#include <linux/msg.h>
13614b84cfSSerge E. Hallyn#include <linux/ipc_namespace.h>
1440401530SAl Viro#include <linux/utsname.h>
150bb80f24SDavid Howells#include <linux/proc_ns.h>
161e3c941cSHoSung Jung#include <linux/uaccess.h>
17d6a2946aSLi Rongqing#include <linux/sched.h>
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds#include "util.h"
201da177e4SLinus Torvalds
217eafd7c7SSerge E. HallynDEFINE_SPINLOCK(mq_lock);
227eafd7c7SSerge E. Hallyn
23614b84cfSSerge E. Hallyn/*
24614b84cfSSerge E. Hallyn * The next 2 defines are here bc this is the only file
25614b84cfSSerge E. Hallyn * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
26614b84cfSSerge E. Hallyn * and not CONFIG_IPC_NS.
27614b84cfSSerge E. Hallyn */
28614b84cfSSerge E. Hallynstruct ipc_namespace init_ipc_ns = {
29137ec390SKirill Tkhai	.ns.count = REFCOUNT_INIT(1),
30b515498fSSerge E. Hallyn	.user_ns = &init_user_ns,
31435d5f4bSAl Viro	.ns.inum = PROC_IPC_INIT_INO,
3233c42940SAl Viro#ifdef CONFIG_IPC_NS
3333c42940SAl Viro	.ns.ops = &ipcns_operations,
3433c42940SAl Viro#endif
35614b84cfSSerge E. Hallyn};
36614b84cfSSerge E. Hallyn
371da177e4SLinus Torvaldsstruct msg_msgseg {
381e3c941cSHoSung Jung	struct msg_msgseg *next;
391da177e4SLinus Torvalds	/* the next part of the message follows immediately */
401da177e4SLinus Torvalds};
411da177e4SLinus Torvalds
424e9b45a1SMathias Krause#define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
434e9b45a1SMathias Krause#define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
441da177e4SLinus Torvalds
45be5f4b33SPeter Hurley
464e9b45a1SMathias Krausestatic struct msg_msg *alloc_msg(size_t len)
471da177e4SLinus Torvalds{
481da177e4SLinus Torvalds	struct msg_msg *msg;
491da177e4SLinus Torvalds	struct msg_msgseg **pseg;
504e9b45a1SMathias Krause	size_t alen;
511da177e4SLinus Torvalds
523d8fa456SPeter Hurley	alen = min(len, DATALEN_MSG);
538c8d4d45SAristeu Rozanski	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);
541da177e4SLinus Torvalds	if (msg == NULL)
55be5f4b33SPeter Hurley		return NULL;
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds	msg->next = NULL;
581da177e4SLinus Torvalds	msg->security = NULL;
591da177e4SLinus Torvalds
60be5f4b33SPeter Hurley	len -= alen;
61be5f4b33SPeter Hurley	pseg = &msg->next;
62be5f4b33SPeter Hurley	while (len > 0) {
63be5f4b33SPeter Hurley		struct msg_msgseg *seg;
64d6a2946aSLi Rongqing
65d6a2946aSLi Rongqing		cond_resched();
66d6a2946aSLi Rongqing
67be5f4b33SPeter Hurley		alen = min(len, DATALEN_SEG);
688c8d4d45SAristeu Rozanski		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
69be5f4b33SPeter Hurley		if (seg == NULL)
70be5f4b33SPeter Hurley			goto out_err;
71be5f4b33SPeter Hurley		*pseg = seg;
72be5f4b33SPeter Hurley		seg->next = NULL;
73be5f4b33SPeter Hurley		pseg = &seg->next;
74be5f4b33SPeter Hurley		len -= alen;
75be5f4b33SPeter Hurley	}
76be5f4b33SPeter Hurley
77be5f4b33SPeter Hurley	return msg;
78be5f4b33SPeter Hurley
79be5f4b33SPeter Hurleyout_err:
80be5f4b33SPeter Hurley	free_msg(msg);
81be5f4b33SPeter Hurley	return NULL;
82be5f4b33SPeter Hurley}
83be5f4b33SPeter Hurley
844e9b45a1SMathias Krausestruct msg_msg *load_msg(const void __user *src, size_t len)
85be5f4b33SPeter Hurley{
86be5f4b33SPeter Hurley	struct msg_msg *msg;
87be5f4b33SPeter Hurley	struct msg_msgseg *seg;
882b3097a2SPeter Hurley	int err = -EFAULT;
894e9b45a1SMathias Krause	size_t alen;
90be5f4b33SPeter Hurley
91be5f4b33SPeter Hurley	msg = alloc_msg(len);
92be5f4b33SPeter Hurley	if (msg == NULL)
93be5f4b33SPeter Hurley		return ERR_PTR(-ENOMEM);
94be5f4b33SPeter Hurley
95be5f4b33SPeter Hurley	alen = min(len, DATALEN_MSG);
962b3097a2SPeter Hurley	if (copy_from_user(msg + 1, src, alen))
971da177e4SLinus Torvalds		goto out_err;
981da177e4SLinus Torvalds
99da085d45SPeter Hurley	for (seg = msg->next; seg != NULL; seg = seg->next) {
100da085d45SPeter Hurley		len -= alen;
101da085d45SPeter Hurley		src = (char __user *)src + alen;
1023d8fa456SPeter Hurley		alen = min(len, DATALEN_SEG);
1032b3097a2SPeter Hurley		if (copy_from_user(seg + 1, src, alen))
1041da177e4SLinus Torvalds			goto out_err;
1051da177e4SLinus Torvalds	}
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds	err = security_msg_msg_alloc(msg);
1081da177e4SLinus Torvalds	if (err)
1091da177e4SLinus Torvalds		goto out_err;
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds	return msg;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvaldsout_err:
1141da177e4SLinus Torvalds	free_msg(msg);
1151da177e4SLinus Torvalds	return ERR_PTR(err);
1161da177e4SLinus Torvalds}
1174a674f34SStanislav Kinsbursky#ifdef CONFIG_CHECKPOINT_RESTORE
1184a674f34SStanislav Kinsburskystruct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
1194a674f34SStanislav Kinsbursky{
1204a674f34SStanislav Kinsbursky	struct msg_msgseg *dst_pseg, *src_pseg;
1214e9b45a1SMathias Krause	size_t len = src->m_ts;
1224e9b45a1SMathias Krause	size_t alen;
1234a674f34SStanislav Kinsbursky
1244a674f34SStanislav Kinsbursky	if (src->m_ts > dst->m_ts)
1254a674f34SStanislav Kinsbursky		return ERR_PTR(-EINVAL);
1264a674f34SStanislav Kinsbursky
1273d8fa456SPeter Hurley	alen = min(len, DATALEN_MSG);
1284a674f34SStanislav Kinsbursky	memcpy(dst + 1, src + 1, alen);
1294a674f34SStanislav Kinsbursky
130da085d45SPeter Hurley	for (dst_pseg = dst->next, src_pseg = src->next;
131da085d45SPeter Hurley	     src_pseg != NULL;
132da085d45SPeter Hurley	     dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
133da085d45SPeter Hurley
134da085d45SPeter Hurley		len -= alen;
1353d8fa456SPeter Hurley		alen = min(len, DATALEN_SEG);
1364a674f34SStanislav Kinsbursky		memcpy(dst_pseg + 1, src_pseg + 1, alen);
1374a674f34SStanislav Kinsbursky	}
1384a674f34SStanislav Kinsbursky
1394a674f34SStanislav Kinsbursky	dst->m_type = src->m_type;
1404a674f34SStanislav Kinsbursky	dst->m_ts = src->m_ts;
1414a674f34SStanislav Kinsbursky
1424a674f34SStanislav Kinsbursky	return dst;
1434a674f34SStanislav Kinsbursky}
14451eeacaaSStanislav Kinsbursky#else
14551eeacaaSStanislav Kinsburskystruct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
14651eeacaaSStanislav Kinsbursky{
14751eeacaaSStanislav Kinsbursky	return ERR_PTR(-ENOSYS);
14851eeacaaSStanislav Kinsbursky}
1494a674f34SStanislav Kinsbursky#endif
1504e9b45a1SMathias Krauseint store_msg(void __user *dest, struct msg_msg *msg, size_t len)
1511da177e4SLinus Torvalds{
1524e9b45a1SMathias Krause	size_t alen;
1531da177e4SLinus Torvalds	struct msg_msgseg *seg;
1541da177e4SLinus Torvalds
1553d8fa456SPeter Hurley	alen = min(len, DATALEN_MSG);
1561da177e4SLinus Torvalds	if (copy_to_user(dest, msg + 1, alen))
1571da177e4SLinus Torvalds		return -1;
1581da177e4SLinus Torvalds
159da085d45SPeter Hurley	for (seg = msg->next; seg != NULL; seg = seg->next) {
160da085d45SPeter Hurley		len -= alen;
161da085d45SPeter Hurley		dest = (char __user *)dest + alen;
1623d8fa456SPeter Hurley		alen = min(len, DATALEN_SEG);
1631da177e4SLinus Torvalds		if (copy_to_user(dest, seg + 1, alen))
1641da177e4SLinus Torvalds			return -1;
1651da177e4SLinus Torvalds	}
1661da177e4SLinus Torvalds	return 0;
1671da177e4SLinus Torvalds}
1681da177e4SLinus Torvalds
1691da177e4SLinus Torvaldsvoid free_msg(struct msg_msg *msg)
1701da177e4SLinus Torvalds{
1711da177e4SLinus Torvalds	struct msg_msgseg *seg;
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds	security_msg_msg_free(msg);
1741da177e4SLinus Torvalds
1751da177e4SLinus Torvalds	seg = msg->next;
1761da177e4SLinus Torvalds	kfree(msg);
1771da177e4SLinus Torvalds	while (seg != NULL) {
1781da177e4SLinus Torvalds		struct msg_msgseg *tmp = seg->next;
179d6a2946aSLi Rongqing
180d6a2946aSLi Rongqing		cond_resched();
1811da177e4SLinus Torvalds		kfree(seg);
1821da177e4SLinus Torvalds		seg = tmp;
1831da177e4SLinus Torvalds	}
1841da177e4SLinus Torvalds}