Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /* * linux/ipc/util.c * Copyright (C) 1999, 2004 Manfred Spraul * * This file is released under GNU General Public Licence version 2 or * (at your option) any later version. * * See the file COPYING for more details. */ #include <linux/spinlock.h> #include <linux/init.h> #include <linux/security.h> #include <linux/slab.h> #include <linux/ipc.h> #include <asm/uaccess.h> #include "util.h" struct msg_msgseg { struct msg_msgseg* next; /* the next part of the message follows immediately */ }; #define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg)) #define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg)) struct msg_msg *load_msg(const void __user *src, int len) { struct msg_msg *msg; struct msg_msgseg **pseg; int err; int alen; alen = len; if (alen > DATALEN_MSG) alen = DATALEN_MSG; msg = (struct msg_msg *)kmalloc(sizeof(*msg) + alen, GFP_KERNEL); if (msg == NULL) return ERR_PTR(-ENOMEM); msg->next = NULL; msg->security = NULL; if (copy_from_user(msg + 1, src, alen)) { err = -EFAULT; goto out_err; } len -= alen; src = ((char __user *)src) + alen; pseg = &msg->next; while (len > 0) { struct msg_msgseg *seg; alen = len; if (alen > DATALEN_SEG) alen = DATALEN_SEG; seg = (struct msg_msgseg *)kmalloc(sizeof(*seg) + alen, GFP_KERNEL); if (seg == NULL) { err = -ENOMEM; goto out_err; } *pseg = seg; seg->next = NULL; if (copy_from_user(seg + 1, src, alen)) { err = -EFAULT; goto out_err; } pseg = &seg->next; len -= alen; src = ((char __user *)src) + alen; } err = security_msg_msg_alloc(msg); if (err) goto out_err; return msg; out_err: free_msg(msg); return ERR_PTR(err); } int store_msg(void __user *dest, struct msg_msg *msg, int len) { int alen; struct msg_msgseg *seg; alen = len; if (alen > DATALEN_MSG) alen = DATALEN_MSG; if (copy_to_user(dest, msg + 1, alen)) return -1; len -= alen; dest = ((char __user *)dest) + alen; seg = msg->next; while (len > 0) { alen = len; if (alen > DATALEN_SEG) alen = DATALEN_SEG; if (copy_to_user(dest, seg + 1, alen)) return -1; len -= alen; dest = ((char __user *)dest) + alen; seg = seg->next; } return 0; } void free_msg(struct msg_msg *msg) { struct msg_msgseg *seg; security_msg_msg_free(msg); seg = msg->next; kfree(msg); while (seg != NULL) { struct msg_msgseg *tmp = seg->next; kfree(seg); seg = tmp; } } |