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