X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=ipc%2Fmqueue.c;h=c93fd3faac2d288e7d87db2642df7b167e15ddc0;hb=7908a9e5fc3f9a679b1777ed231a03636c068446;hp=04985a7256c27f55c94e7c7c0afc0aa9ed959b42;hpb=4294a8eedb17bbc45e1e7447c2a4d05332943248;p=safe%2Fjmp%2Flinux-2.6 diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 04985a7..c93fd3f 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "util.h" @@ -134,7 +135,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, init_waitqueue_head(&info->wait_q); INIT_LIST_HEAD(&info->e_wait_q[0].list); INIT_LIST_HEAD(&info->e_wait_q[1].list); - info->messages = NULL; info->notify_owner = NULL; info->qsize = 0; info->user = NULL; /* set when all is ok */ @@ -146,26 +146,24 @@ static struct inode *mqueue_get_inode(struct super_block *sb, info->attr.mq_msgsize = attr->mq_msgsize; } mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *); + info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); + if (!info->messages) + goto out_inode; + mq_bytes = (mq_msg_tblsz + (info->attr.mq_maxmsg * info->attr.mq_msgsize)); spin_lock(&mq_lock); if (u->mq_bytes + mq_bytes < u->mq_bytes || u->mq_bytes + mq_bytes > - p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) { + task_rlimit(p, RLIMIT_MSGQUEUE)) { spin_unlock(&mq_lock); + /* mqueue_delete_inode() releases info->messages */ goto out_inode; } u->mq_bytes += mq_bytes; spin_unlock(&mq_lock); - info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); - if (!info->messages) { - spin_lock(&mq_lock); - u->mq_bytes -= mq_bytes; - spin_unlock(&mq_lock); - goto out_inode; - } /* all is ok */ info->user = get_uid(u); } else if (S_ISDIR(mode)) { @@ -178,7 +176,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, } return inode; out_inode: - make_bad_inode(inode); iput(inode); return NULL; } @@ -187,7 +184,7 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct ipc_namespace *ns = data; - int error = 0; + int error; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; @@ -205,7 +202,9 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent) if (!sb->s_root) { iput(inode); error = -ENOMEM; + goto out; } + error = 0; out: return error; @@ -264,8 +263,9 @@ static void mqueue_delete_inode(struct inode *inode) clear_inode(inode); - mq_bytes = (info->attr.mq_maxmsg * sizeof(struct msg_msg *) + - (info->attr.mq_maxmsg * info->attr.mq_msgsize)); + /* Total amount of bytes accounted for the mqueue */ + mq_bytes = info->attr.mq_maxmsg * (sizeof(struct msg_msg *) + + info->attr.mq_msgsize); user = info->user; if (user) { spin_lock(&mq_lock); @@ -428,7 +428,7 @@ static void wq_add(struct mqueue_inode_info *info, int sr, * sr: SEND or RECV */ static int wq_sleep(struct mqueue_inode_info *info, int sr, - long timeout, struct ext_wait_queue *ewp) + ktime_t *timeout, struct ext_wait_queue *ewp) { int retval; signed long time; @@ -439,7 +439,8 @@ static int wq_sleep(struct mqueue_inode_info *info, int sr, set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&info->lock); - time = schedule_timeout(timeout); + time = schedule_hrtimeout_range_clock(timeout, + HRTIMER_MODE_ABS, 0, CLOCK_REALTIME); while (ewp->state == STATE_PENDING) cpu_relax(); @@ -551,31 +552,16 @@ static void __do_notify(struct mqueue_inode_info *info) wake_up(&info->wait_q); } -static long prepare_timeout(struct timespec *p) +static int prepare_timeout(const struct timespec __user *u_abs_timeout, + ktime_t *expires, struct timespec *ts) { - struct timespec nowts; - long timeout; - - if (p) { - if (unlikely(p->tv_nsec < 0 || p->tv_sec < 0 - || p->tv_nsec >= NSEC_PER_SEC)) - return -EINVAL; - nowts = CURRENT_TIME; - /* first subtract as jiffies can't be too big */ - p->tv_sec -= nowts.tv_sec; - if (p->tv_nsec < nowts.tv_nsec) { - p->tv_nsec += NSEC_PER_SEC; - p->tv_sec--; - } - p->tv_nsec -= nowts.tv_nsec; - if (p->tv_sec < 0) - return 0; - - timeout = timespec_to_jiffies(p) + 1; - } else - return MAX_SCHEDULE_TIMEOUT; + if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec))) + return -EFAULT; + if (!timespec_valid(ts)) + return -EINVAL; - return timeout; + *expires = timespec_to_ktime(*ts); + return 0; } static void remove_notification(struct mqueue_inode_info *info) @@ -604,8 +590,8 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr) /* check for overflow */ if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg) return 0; - if ((unsigned long)(attr->mq_maxmsg * attr->mq_msgsize) + - (attr->mq_maxmsg * sizeof (struct msg_msg *)) < + if ((unsigned long)(attr->mq_maxmsg * (attr->mq_msgsize + + sizeof (struct msg_msg *))) < (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize)) return 0; return 1; @@ -623,9 +609,10 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir, int ret; if (attr) { - ret = -EINVAL; - if (!mq_attr_ok(ipc_ns, attr)) + if (!mq_attr_ok(ipc_ns, attr)) { + ret = -EINVAL; goto out; + } /* store for use during create */ dentry->d_fsdata = attr; } @@ -659,24 +646,28 @@ out: static struct file *do_open(struct ipc_namespace *ipc_ns, struct dentry *dentry, int oflag) { + int ret; const struct cred *cred = current_cred(); static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, MAY_READ | MAY_WRITE }; if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) { - dput(dentry); - mntput(ipc_ns->mq_mnt); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err; } if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { - dput(dentry); - mntput(ipc_ns->mq_mnt); - return ERR_PTR(-EACCES); + ret = -EACCES; + goto err; } return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred); + +err: + dput(dentry); + mntput(ipc_ns->mq_mnt); + return ERR_PTR(ret); } SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode, @@ -712,9 +703,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode, if (oflag & O_CREAT) { if (dentry->d_inode) { /* entry already exists */ audit_inode(name, dentry); - error = -EEXIST; - if (oflag & O_EXCL) + if (oflag & O_EXCL) { + error = -EEXIST; goto out; + } filp = do_open(ipc_ns, dentry, oflag); } else { filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root, @@ -722,9 +714,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode, u_attr ? &attr : NULL); } } else { - error = -ENOENT; - if (!dentry->d_inode) + if (!dentry->d_inode) { + error = -ENOENT; goto out; + } audit_inode(name, dentry); filp = do_open(ipc_ns, dentry, oflag); } @@ -854,36 +847,40 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, struct ext_wait_queue *receiver; struct msg_msg *msg_ptr; struct mqueue_inode_info *info; - struct timespec ts, *p = NULL; - long timeout; + ktime_t expires, *timeout = NULL; + struct timespec ts; int ret; if (u_abs_timeout) { - if (copy_from_user(&ts, u_abs_timeout, - sizeof(struct timespec))) - return -EFAULT; - p = &ts; + int res = prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; } if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) return -EINVAL; - audit_mq_sendrecv(mqdes, msg_len, msg_prio, p); - timeout = prepare_timeout(p); + audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); - ret = -EBADF; filp = fget(mqdes); - if (unlikely(!filp)) + if (unlikely(!filp)) { + ret = -EBADF; goto out; + } inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) + if (unlikely(filp->f_op != &mqueue_file_operations)) { + ret = -EBADF; goto out_fput; + } info = MQUEUE_I(inode); audit_inode(NULL, filp->f_path.dentry); - if (unlikely(!(filp->f_mode & FMODE_WRITE))) + if (unlikely(!(filp->f_mode & FMODE_WRITE))) { + ret = -EBADF; goto out_fput; + } if (unlikely(msg_len > info->attr.mq_msgsize)) { ret = -EMSGSIZE; @@ -906,9 +903,6 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, if (filp->f_flags & O_NONBLOCK) { spin_unlock(&info->lock); ret = -EAGAIN; - } else if (unlikely(timeout < 0)) { - spin_unlock(&info->lock); - ret = timeout; } else { wait.task = current; wait.msg = (void *) msg_ptr; @@ -941,38 +935,42 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, size_t, msg_len, unsigned int __user *, u_msg_prio, const struct timespec __user *, u_abs_timeout) { - long timeout; ssize_t ret; struct msg_msg *msg_ptr; struct file *filp; struct inode *inode; struct mqueue_inode_info *info; struct ext_wait_queue wait; - struct timespec ts, *p = NULL; + ktime_t expires, *timeout = NULL; + struct timespec ts; if (u_abs_timeout) { - if (copy_from_user(&ts, u_abs_timeout, - sizeof(struct timespec))) - return -EFAULT; - p = &ts; + int res = prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; } - audit_mq_sendrecv(mqdes, msg_len, 0, p); - timeout = prepare_timeout(p); + audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); - ret = -EBADF; filp = fget(mqdes); - if (unlikely(!filp)) + if (unlikely(!filp)) { + ret = -EBADF; goto out; + } inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) + if (unlikely(filp->f_op != &mqueue_file_operations)) { + ret = -EBADF; goto out_fput; + } info = MQUEUE_I(inode); audit_inode(NULL, filp->f_path.dentry); - if (unlikely(!(filp->f_mode & FMODE_READ))) + if (unlikely(!(filp->f_mode & FMODE_READ))) { + ret = -EBADF; goto out_fput; + } /* checks if buffer is big enough */ if (unlikely(msg_len < info->attr.mq_msgsize)) { @@ -985,11 +983,6 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, if (filp->f_flags & O_NONBLOCK) { spin_unlock(&info->lock); ret = -EAGAIN; - msg_ptr = NULL; - } else if (unlikely(timeout < 0)) { - spin_unlock(&info->lock); - ret = timeout; - msg_ptr = NULL; } else { wait.task = current; wait.state = STATE_NONE; @@ -1062,13 +1055,14 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, /* create the notify skb */ nc = alloc_skb(NOTIFY_COOKIE_LEN, GFP_KERNEL); - ret = -ENOMEM; - if (!nc) + if (!nc) { + ret = -ENOMEM; goto out; - ret = -EFAULT; + } if (copy_from_user(nc->data, notification.sigev_value.sival_ptr, NOTIFY_COOKIE_LEN)) { + ret = -EFAULT; goto out; } @@ -1077,9 +1071,10 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, /* and attach it to the socket */ retry: filp = fget(notification.sigev_signo); - ret = -EBADF; - if (!filp) + if (!filp) { + ret = -EBADF; goto out; + } sock = netlink_getsockbyfilp(filp); fput(filp); if (IS_ERR(sock)) { @@ -1091,7 +1086,7 @@ retry: timeo = MAX_SCHEDULE_TIMEOUT; ret = netlink_attachskb(sock, nc, &timeo, NULL); if (ret == 1) - goto retry; + goto retry; if (ret) { sock = NULL; nc = NULL; @@ -1100,14 +1095,17 @@ retry: } } - ret = -EBADF; filp = fget(mqdes); - if (!filp) + if (!filp) { + ret = -EBADF; goto out; + } inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) + if (unlikely(filp->f_op != &mqueue_file_operations)) { + ret = -EBADF; goto out_fput; + } info = MQUEUE_I(inode); ret = 0; @@ -1170,14 +1168,17 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes, return -EINVAL; } - ret = -EBADF; filp = fget(mqdes); - if (!filp) + if (!filp) { + ret = -EBADF; goto out; + } inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) + if (unlikely(filp->f_op != &mqueue_file_operations)) { + ret = -EBADF; goto out_fput; + } info = MQUEUE_I(inode); spin_lock(&info->lock); @@ -1271,7 +1272,7 @@ static int __init init_mqueue_fs(void) if (mqueue_inode_cachep == NULL) return -ENOMEM; - /* ignore failues - they are not fatal */ + /* ignore failures - they are not fatal */ mq_sysctl_table = mq_register_sysctl_table(); error = register_filesystem(&mqueue_fs_type);