Commit 8b9e04f2 authored by Al Viro's avatar Al Viro

ipmi: get COMPAT_IPMICTL_RECEIVE_MSG in sync with the native one

We want to know if copyout has succeeded before we commit to
freeing msg.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1b3c872c
...@@ -231,63 +231,16 @@ static int handle_send_req(ipmi_user_t user, ...@@ -231,63 +231,16 @@ static int handle_send_req(ipmi_user_t user,
return rv; return rv;
} }
static int ipmi_ioctl(struct file *file, static int handle_recv(struct ipmi_file_private *priv,
unsigned int cmd, bool trunc, struct ipmi_recv *rsp,
unsigned long data) int (*copyout)(struct ipmi_recv *, void __user *),
void __user *to)
{ {
int rv = -EINVAL;
struct ipmi_file_private *priv = file->private_data;
void __user *arg = (void __user *)data;
switch (cmd)
{
case IPMICTL_SEND_COMMAND:
{
struct ipmi_req req;
if (copy_from_user(&req, arg, sizeof(req))) {
rv = -EFAULT;
break;
}
rv = handle_send_req(priv->user,
&req,
priv->default_retries,
priv->default_retry_time_ms);
break;
}
case IPMICTL_SEND_COMMAND_SETTIME:
{
struct ipmi_req_settime req;
if (copy_from_user(&req, arg, sizeof(req))) {
rv = -EFAULT;
break;
}
rv = handle_send_req(priv->user,
&req.req,
req.retries,
req.retry_time_ms);
break;
}
case IPMICTL_RECEIVE_MSG:
case IPMICTL_RECEIVE_MSG_TRUNC:
{
struct ipmi_recv rsp;
int addr_len; int addr_len;
struct list_head *entry; struct list_head *entry;
struct ipmi_recv_msg *msg; struct ipmi_recv_msg *msg;
unsigned long flags; unsigned long flags;
int rv = 0;
rv = 0;
if (copy_from_user(&rsp, arg, sizeof(rsp))) {
rv = -EFAULT;
break;
}
/* We claim a mutex because we don't want two /* We claim a mutex because we don't want two
users getting something from the queue at a time. users getting something from the queue at a time.
...@@ -312,65 +265,120 @@ static int ipmi_ioctl(struct file *file, ...@@ -312,65 +265,120 @@ static int ipmi_ioctl(struct file *file,
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
addr_len = ipmi_addr_length(msg->addr.addr_type); addr_len = ipmi_addr_length(msg->addr.addr_type);
if (rsp.addr_len < addr_len) if (rsp->addr_len < addr_len)
{ {
rv = -EINVAL; rv = -EINVAL;
goto recv_putback_on_err; goto recv_putback_on_err;
} }
if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) { if (copy_to_user(rsp->addr, &(msg->addr), addr_len)) {
rv = -EFAULT; rv = -EFAULT;
goto recv_putback_on_err; goto recv_putback_on_err;
} }
rsp.addr_len = addr_len; rsp->addr_len = addr_len;
rsp.recv_type = msg->recv_type; rsp->recv_type = msg->recv_type;
rsp.msgid = msg->msgid; rsp->msgid = msg->msgid;
rsp.msg.netfn = msg->msg.netfn; rsp->msg.netfn = msg->msg.netfn;
rsp.msg.cmd = msg->msg.cmd; rsp->msg.cmd = msg->msg.cmd;
if (msg->msg.data_len > 0) { if (msg->msg.data_len > 0) {
if (rsp.msg.data_len < msg->msg.data_len) { if (rsp->msg.data_len < msg->msg.data_len) {
rv = -EMSGSIZE; rv = -EMSGSIZE;
if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) { if (trunc)
msg->msg.data_len = rsp.msg.data_len; msg->msg.data_len = rsp->msg.data_len;
} else { else
goto recv_putback_on_err; goto recv_putback_on_err;
} }
}
if (copy_to_user(rsp.msg.data, if (copy_to_user(rsp->msg.data,
msg->msg.data, msg->msg.data,
msg->msg.data_len)) msg->msg.data_len))
{ {
rv = -EFAULT; rv = -EFAULT;
goto recv_putback_on_err; goto recv_putback_on_err;
} }
rsp.msg.data_len = msg->msg.data_len; rsp->msg.data_len = msg->msg.data_len;
} else { } else {
rsp.msg.data_len = 0; rsp->msg.data_len = 0;
} }
if (copy_to_user(arg, &rsp, sizeof(rsp))) { rv = copyout(rsp, to);
rv = -EFAULT; if (rv)
goto recv_putback_on_err; goto recv_putback_on_err;
}
mutex_unlock(&priv->recv_mutex); mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg); ipmi_free_recv_msg(msg);
break; return 0;
recv_putback_on_err: recv_putback_on_err:
/* If we got an error, put the message back onto /* If we got an error, put the message back onto
the head of the queue. */ the head of the queue. */
spin_lock_irqsave(&(priv->recv_msg_lock), flags); spin_lock_irqsave(&(priv->recv_msg_lock), flags);
list_add(entry, &(priv->recv_msgs)); list_add(entry, &(priv->recv_msgs));
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
recv_err:
mutex_unlock(&priv->recv_mutex); mutex_unlock(&priv->recv_mutex);
return rv;
}
static int copyout_recv(struct ipmi_recv *rsp, void __user *to)
{
return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0;
}
static int ipmi_ioctl(struct file *file,
unsigned int cmd,
unsigned long data)
{
int rv = -EINVAL;
struct ipmi_file_private *priv = file->private_data;
void __user *arg = (void __user *)data;
switch (cmd)
{
case IPMICTL_SEND_COMMAND:
{
struct ipmi_req req;
if (copy_from_user(&req, arg, sizeof(req))) {
rv = -EFAULT;
break; break;
}
recv_err: rv = handle_send_req(priv->user,
mutex_unlock(&priv->recv_mutex); &req,
priv->default_retries,
priv->default_retry_time_ms);
break;
}
case IPMICTL_SEND_COMMAND_SETTIME:
{
struct ipmi_req_settime req;
if (copy_from_user(&req, arg, sizeof(req))) {
rv = -EFAULT;
break;
}
rv = handle_send_req(priv->user,
&req.req,
req.retries,
req.retry_time_ms);
break;
}
case IPMICTL_RECEIVE_MSG:
case IPMICTL_RECEIVE_MSG_TRUNC:
{
struct ipmi_recv rsp;
if (copy_from_user(&rsp, arg, sizeof(rsp)))
rv = -EFAULT;
else
rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC,
&rsp, copyout_recv, arg);
break; break;
} }
...@@ -711,17 +719,6 @@ static long get_compat_ipmi_msg(struct ipmi_msg *p64, ...@@ -711,17 +719,6 @@ static long get_compat_ipmi_msg(struct ipmi_msg *p64,
return 0; return 0;
} }
static long put_compat_ipmi_msg(struct ipmi_msg *p64,
struct compat_ipmi_msg __user *p32)
{
if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
__put_user(p64->netfn, &p32->netfn) ||
__put_user(p64->cmd, &p32->cmd) ||
__put_user(p64->data_len, &p32->data_len))
return -EFAULT;
return 0;
}
static long get_compat_ipmi_req(struct ipmi_req *p64, static long get_compat_ipmi_req(struct ipmi_req *p64,
struct compat_ipmi_req __user *p32) struct compat_ipmi_req __user *p32)
{ {
...@@ -765,16 +762,19 @@ static long get_compat_ipmi_recv(struct ipmi_recv *p64, ...@@ -765,16 +762,19 @@ static long get_compat_ipmi_recv(struct ipmi_recv *p64,
return 0; return 0;
} }
static long put_compat_ipmi_recv(struct ipmi_recv *p64, static int copyout_recv32(struct ipmi_recv *p64, void __user *to)
struct compat_ipmi_recv __user *p32)
{ {
if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || struct compat_ipmi_recv v32;
__put_user(p64->recv_type, &p32->recv_type) || memset(&v32, 0, sizeof(struct compat_ipmi_recv));
__put_user(p64->addr_len, &p32->addr_len) || v32.recv_type = p64->recv_type;
__put_user(p64->msgid, &p32->msgid) || v32.addr = ptr_to_compat(p64->addr);
put_compat_ipmi_msg(&p64->msg, &p32->msg)) v32.addr_len = p64->addr_len;
return -EFAULT; v32.msgid = p64->msgid;
return 0; v32.msg.netfn = p64->msg.netfn;
v32.msg.cmd = p64->msg.cmd;
v32.msg.data_len = p64->msg.data_len;
v32.msg.data = ptr_to_compat(p64->msg.data);
return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0;
} }
/* /*
...@@ -783,7 +783,6 @@ static long put_compat_ipmi_recv(struct ipmi_recv *p64, ...@@ -783,7 +783,6 @@ static long put_compat_ipmi_recv(struct ipmi_recv *p64,
static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
int rc;
struct ipmi_file_private *priv = filep->private_data; struct ipmi_file_private *priv = filep->private_data;
switch(cmd) { switch(cmd) {
...@@ -811,32 +810,15 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, ...@@ -811,32 +810,15 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
case COMPAT_IPMICTL_RECEIVE_MSG: case COMPAT_IPMICTL_RECEIVE_MSG:
case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
{ {
struct ipmi_recv __user *precv64;
struct ipmi_recv recv64; struct ipmi_recv recv64;
memset(&recv64, 0, sizeof(recv64)); memset(&recv64, 0, sizeof(recv64));
if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))
return -EFAULT; return -EFAULT;
precv64 = compat_alloc_user_space(sizeof(recv64)); return handle_recv(priv,
if (copy_to_user(precv64, &recv64, sizeof(recv64))) cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC,
return -EFAULT; &recv64, copyout_recv32, compat_ptr(arg));
rc = ipmi_ioctl(filep,
((cmd == COMPAT_IPMICTL_RECEIVE_MSG)
? IPMICTL_RECEIVE_MSG
: IPMICTL_RECEIVE_MSG_TRUNC),
(unsigned long) precv64);
if (rc != 0)
return rc;
if (copy_from_user(&recv64, precv64, sizeof(recv64)))
return -EFAULT;
if (put_compat_ipmi_recv(&recv64, compat_ptr(arg)))
return -EFAULT;
return rc;
} }
default: default:
return ipmi_ioctl(filep, cmd, arg); return ipmi_ioctl(filep, cmd, arg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment