xref: /linux-6.15/ipc/msgutil.c (revision 734bbc1c)
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. Hallyn DEFINE_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. Hallyn struct 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 Torvalds struct msg_msgseg {
381da177e4SLinus Torvalds 	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 
45*734bbc1cSKees Cook static kmem_buckets *msg_buckets __ro_after_init;
46*734bbc1cSKees Cook 
init_msg_buckets(void)47*734bbc1cSKees Cook static int __init init_msg_buckets(void)
48*734bbc1cSKees Cook {
49*734bbc1cSKees Cook 	msg_buckets = kmem_buckets_create("msg_msg", SLAB_ACCOUNT,
50*734bbc1cSKees Cook 					  sizeof(struct msg_msg),
51*734bbc1cSKees Cook 					  DATALEN_MSG, NULL);
52*734bbc1cSKees Cook 
53*734bbc1cSKees Cook 	return 0;
54*734bbc1cSKees Cook }
55*734bbc1cSKees Cook subsys_initcall(init_msg_buckets);
56be5f4b33SPeter Hurley 
alloc_msg(size_t len)574e9b45a1SMathias Krause static struct msg_msg *alloc_msg(size_t len)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	struct msg_msg *msg;
601da177e4SLinus Torvalds 	struct msg_msgseg **pseg;
614e9b45a1SMathias Krause 	size_t alen;
621da177e4SLinus Torvalds 
633d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
64*734bbc1cSKees Cook 	msg = kmem_buckets_alloc(msg_buckets, sizeof(*msg) + alen, GFP_KERNEL);
651da177e4SLinus Torvalds 	if (msg == NULL)
66be5f4b33SPeter Hurley 		return NULL;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	msg->next = NULL;
691da177e4SLinus Torvalds 	msg->security = NULL;
701da177e4SLinus Torvalds 
71be5f4b33SPeter Hurley 	len -= alen;
72be5f4b33SPeter Hurley 	pseg = &msg->next;
73be5f4b33SPeter Hurley 	while (len > 0) {
74be5f4b33SPeter Hurley 		struct msg_msgseg *seg;
75d6a2946aSLi Rongqing 
76d6a2946aSLi Rongqing 		cond_resched();
77d6a2946aSLi Rongqing 
78be5f4b33SPeter Hurley 		alen = min(len, DATALEN_SEG);
798c8d4d45SAristeu Rozanski 		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
80be5f4b33SPeter Hurley 		if (seg == NULL)
81be5f4b33SPeter Hurley 			goto out_err;
82be5f4b33SPeter Hurley 		*pseg = seg;
83be5f4b33SPeter Hurley 		seg->next = NULL;
84be5f4b33SPeter Hurley 		pseg = &seg->next;
85be5f4b33SPeter Hurley 		len -= alen;
86be5f4b33SPeter Hurley 	}
87be5f4b33SPeter Hurley 
88be5f4b33SPeter Hurley 	return msg;
89be5f4b33SPeter Hurley 
90be5f4b33SPeter Hurley out_err:
91be5f4b33SPeter Hurley 	free_msg(msg);
92be5f4b33SPeter Hurley 	return NULL;
93be5f4b33SPeter Hurley }
94be5f4b33SPeter Hurley 
load_msg(const void __user * src,size_t len)954e9b45a1SMathias Krause struct msg_msg *load_msg(const void __user *src, size_t len)
96be5f4b33SPeter Hurley {
97be5f4b33SPeter Hurley 	struct msg_msg *msg;
98be5f4b33SPeter Hurley 	struct msg_msgseg *seg;
992b3097a2SPeter Hurley 	int err = -EFAULT;
1004e9b45a1SMathias Krause 	size_t alen;
101be5f4b33SPeter Hurley 
102be5f4b33SPeter Hurley 	msg = alloc_msg(len);
103be5f4b33SPeter Hurley 	if (msg == NULL)
104be5f4b33SPeter Hurley 		return ERR_PTR(-ENOMEM);
105be5f4b33SPeter Hurley 
106be5f4b33SPeter Hurley 	alen = min(len, DATALEN_MSG);
1072b3097a2SPeter Hurley 	if (copy_from_user(msg + 1, src, alen))
1081da177e4SLinus Torvalds 		goto out_err;
1091da177e4SLinus Torvalds 
110da085d45SPeter Hurley 	for (seg = msg->next; seg != NULL; seg = seg->next) {
1111da177e4SLinus Torvalds 		len -= alen;
112da085d45SPeter Hurley 		src = (char __user *)src + alen;
1133d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1142b3097a2SPeter Hurley 		if (copy_from_user(seg + 1, src, alen))
1151da177e4SLinus Torvalds 			goto out_err;
1161da177e4SLinus Torvalds 	}
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	err = security_msg_msg_alloc(msg);
1191da177e4SLinus Torvalds 	if (err)
1201da177e4SLinus Torvalds 		goto out_err;
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 	return msg;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds out_err:
1251da177e4SLinus Torvalds 	free_msg(msg);
1261da177e4SLinus Torvalds 	return ERR_PTR(err);
1271da177e4SLinus Torvalds }
1284a674f34SStanislav Kinsbursky #ifdef CONFIG_CHECKPOINT_RESTORE
copy_msg(struct msg_msg * src,struct msg_msg * dst)1294a674f34SStanislav Kinsbursky struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
1304a674f34SStanislav Kinsbursky {
1314a674f34SStanislav Kinsbursky 	struct msg_msgseg *dst_pseg, *src_pseg;
1324e9b45a1SMathias Krause 	size_t len = src->m_ts;
1334e9b45a1SMathias Krause 	size_t alen;
1341da177e4SLinus Torvalds 
1354a674f34SStanislav Kinsbursky 	if (src->m_ts > dst->m_ts)
1364a674f34SStanislav Kinsbursky 		return ERR_PTR(-EINVAL);
1374a674f34SStanislav Kinsbursky 
1383d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
1394a674f34SStanislav Kinsbursky 	memcpy(dst + 1, src + 1, alen);
1404a674f34SStanislav Kinsbursky 
141da085d45SPeter Hurley 	for (dst_pseg = dst->next, src_pseg = src->next;
142da085d45SPeter Hurley 	     src_pseg != NULL;
143da085d45SPeter Hurley 	     dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
144da085d45SPeter Hurley 
1454a674f34SStanislav Kinsbursky 		len -= alen;
1463d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1474a674f34SStanislav Kinsbursky 		memcpy(dst_pseg + 1, src_pseg + 1, alen);
1484a674f34SStanislav Kinsbursky 	}
1494a674f34SStanislav Kinsbursky 
1504a674f34SStanislav Kinsbursky 	dst->m_type = src->m_type;
1514a674f34SStanislav Kinsbursky 	dst->m_ts = src->m_ts;
1524a674f34SStanislav Kinsbursky 
1534a674f34SStanislav Kinsbursky 	return dst;
1544a674f34SStanislav Kinsbursky }
15551eeacaaSStanislav Kinsbursky #else
copy_msg(struct msg_msg * src,struct msg_msg * dst)15651eeacaaSStanislav Kinsbursky struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
15751eeacaaSStanislav Kinsbursky {
15851eeacaaSStanislav Kinsbursky 	return ERR_PTR(-ENOSYS);
15951eeacaaSStanislav Kinsbursky }
1604a674f34SStanislav Kinsbursky #endif
store_msg(void __user * dest,struct msg_msg * msg,size_t len)1614e9b45a1SMathias Krause int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
1621da177e4SLinus Torvalds {
1634e9b45a1SMathias Krause 	size_t alen;
1641da177e4SLinus Torvalds 	struct msg_msgseg *seg;
1651da177e4SLinus Torvalds 
1663d8fa456SPeter Hurley 	alen = min(len, DATALEN_MSG);
1671da177e4SLinus Torvalds 	if (copy_to_user(dest, msg + 1, alen))
1681da177e4SLinus Torvalds 		return -1;
1691da177e4SLinus Torvalds 
170da085d45SPeter Hurley 	for (seg = msg->next; seg != NULL; seg = seg->next) {
1711da177e4SLinus Torvalds 		len -= alen;
172da085d45SPeter Hurley 		dest = (char __user *)dest + alen;
1733d8fa456SPeter Hurley 		alen = min(len, DATALEN_SEG);
1741da177e4SLinus Torvalds 		if (copy_to_user(dest, seg + 1, alen))
1751da177e4SLinus Torvalds 			return -1;
1761da177e4SLinus Torvalds 	}
1771da177e4SLinus Torvalds 	return 0;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
free_msg(struct msg_msg * msg)1801da177e4SLinus Torvalds void free_msg(struct msg_msg *msg)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	struct msg_msgseg *seg;
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	security_msg_msg_free(msg);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	seg = msg->next;
1871da177e4SLinus Torvalds 	kfree(msg);
1881da177e4SLinus Torvalds 	while (seg != NULL) {
1891da177e4SLinus Torvalds 		struct msg_msgseg *tmp = seg->next;
190d6a2946aSLi Rongqing 
191d6a2946aSLi Rongqing 		cond_resched();
1921da177e4SLinus Torvalds 		kfree(seg);
1931da177e4SLinus Torvalds 		seg = tmp;
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds }
196