Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* 3 : : * linux/ipc/msgutil.c 4 : : * Copyright (C) 1999, 2004 Manfred Spraul 5 : : */ 6 : : 7 : : #include <linux/spinlock.h> 8 : : #include <linux/init.h> 9 : : #include <linux/security.h> 10 : : #include <linux/slab.h> 11 : : #include <linux/ipc.h> 12 : : #include <linux/msg.h> 13 : : #include <linux/ipc_namespace.h> 14 : : #include <linux/utsname.h> 15 : : #include <linux/proc_ns.h> 16 : : #include <linux/uaccess.h> 17 : : #include <linux/sched.h> 18 : : 19 : : #include "util.h" 20 : : 21 : : DEFINE_SPINLOCK(mq_lock); 22 : : 23 : : /* 24 : : * The next 2 defines are here bc this is the only file 25 : : * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE 26 : : * and not CONFIG_IPC_NS. 27 : : */ 28 : : struct ipc_namespace init_ipc_ns = { 29 : : .count = REFCOUNT_INIT(1), 30 : : .user_ns = &init_user_ns, 31 : : .ns.inum = PROC_IPC_INIT_INO, 32 : : #ifdef CONFIG_IPC_NS 33 : : .ns.ops = &ipcns_operations, 34 : : #endif 35 : : }; 36 : : 37 : : struct msg_msgseg { 38 : : struct msg_msgseg *next; 39 : : /* the next part of the message follows immediately */ 40 : : }; 41 : : 42 : : #define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg)) 43 : : #define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg)) 44 : : 45 : : 46 : 0 : static struct msg_msg *alloc_msg(size_t len) 47 : : { 48 : : struct msg_msg *msg; 49 : : struct msg_msgseg **pseg; 50 : : size_t alen; 51 : : 52 : 0 : alen = min(len, DATALEN_MSG); 53 : 0 : msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT); 54 : 0 : if (msg == NULL) 55 : : return NULL; 56 : : 57 : 0 : msg->next = NULL; 58 : 0 : msg->security = NULL; 59 : : 60 : 0 : len -= alen; 61 : 0 : pseg = &msg->next; 62 : 0 : while (len > 0) { 63 : : struct msg_msgseg *seg; 64 : : 65 : 0 : cond_resched(); 66 : : 67 : 0 : alen = min(len, DATALEN_SEG); 68 : 0 : seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT); 69 : 0 : if (seg == NULL) 70 : : goto out_err; 71 : 0 : *pseg = seg; 72 : 0 : seg->next = NULL; 73 : 0 : pseg = &seg->next; 74 : 0 : len -= alen; 75 : : } 76 : : 77 : : return msg; 78 : : 79 : : out_err: 80 : 0 : free_msg(msg); 81 : 0 : return NULL; 82 : : } 83 : : 84 : 0 : struct msg_msg *load_msg(const void __user *src, size_t len) 85 : : { 86 : : struct msg_msg *msg; 87 : : struct msg_msgseg *seg; 88 : : int err = -EFAULT; 89 : : size_t alen; 90 : : 91 : 0 : msg = alloc_msg(len); 92 : 0 : if (msg == NULL) 93 : : return ERR_PTR(-ENOMEM); 94 : : 95 : 0 : alen = min(len, DATALEN_MSG); 96 : 0 : if (copy_from_user(msg + 1, src, alen)) 97 : : goto out_err; 98 : : 99 : 0 : for (seg = msg->next; seg != NULL; seg = seg->next) { 100 : 0 : len -= alen; 101 : 0 : src = (char __user *)src + alen; 102 : 0 : alen = min(len, DATALEN_SEG); 103 : 0 : if (copy_from_user(seg + 1, src, alen)) 104 : : goto out_err; 105 : : } 106 : : 107 : 0 : err = security_msg_msg_alloc(msg); 108 : 0 : if (err) 109 : : goto out_err; 110 : : 111 : : return msg; 112 : : 113 : : out_err: 114 : 0 : free_msg(msg); 115 : 0 : return ERR_PTR(err); 116 : : } 117 : : #ifdef CONFIG_CHECKPOINT_RESTORE 118 : : struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) 119 : : { 120 : : struct msg_msgseg *dst_pseg, *src_pseg; 121 : : size_t len = src->m_ts; 122 : : size_t alen; 123 : : 124 : : if (src->m_ts > dst->m_ts) 125 : : return ERR_PTR(-EINVAL); 126 : : 127 : : alen = min(len, DATALEN_MSG); 128 : : memcpy(dst + 1, src + 1, alen); 129 : : 130 : : for (dst_pseg = dst->next, src_pseg = src->next; 131 : : src_pseg != NULL; 132 : : dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) { 133 : : 134 : : len -= alen; 135 : : alen = min(len, DATALEN_SEG); 136 : : memcpy(dst_pseg + 1, src_pseg + 1, alen); 137 : : } 138 : : 139 : : dst->m_type = src->m_type; 140 : : dst->m_ts = src->m_ts; 141 : : 142 : : return dst; 143 : : } 144 : : #else 145 : 0 : struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) 146 : : { 147 : 0 : return ERR_PTR(-ENOSYS); 148 : : } 149 : : #endif 150 : 0 : int store_msg(void __user *dest, struct msg_msg *msg, size_t len) 151 : : { 152 : : size_t alen; 153 : : struct msg_msgseg *seg; 154 : : 155 : 0 : alen = min(len, DATALEN_MSG); 156 : 0 : if (copy_to_user(dest, msg + 1, alen)) 157 : : return -1; 158 : : 159 : 0 : for (seg = msg->next; seg != NULL; seg = seg->next) { 160 : 0 : len -= alen; 161 : 0 : dest = (char __user *)dest + alen; 162 : 0 : alen = min(len, DATALEN_SEG); 163 : 0 : if (copy_to_user(dest, seg + 1, alen)) 164 : : return -1; 165 : : } 166 : : return 0; 167 : : } 168 : : 169 : 0 : void free_msg(struct msg_msg *msg) 170 : : { 171 : : struct msg_msgseg *seg; 172 : : 173 : 0 : security_msg_msg_free(msg); 174 : : 175 : 0 : seg = msg->next; 176 : 0 : kfree(msg); 177 : 0 : while (seg != NULL) { 178 : 0 : struct msg_msgseg *tmp = seg->next; 179 : : 180 : 0 : cond_resched(); 181 : 0 : kfree(seg); 182 : : seg = tmp; 183 : : } 184 : 0 : }