aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-06-26 16:47:01 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2017-06-26 16:47:01 -0400
commit8b9e04f282c76786fad1a031b38e279bfababd9c (patch)
tree7034afb9f540e0b14e3cc814c33e4eb2fa28e1b3
parent1b3c872c8342803d0fcd8042e4e007d173191db6 (diff)
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: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c248
1 files changed, 115 insertions, 133 deletions
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index f45119c5337d..7007beba918b 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -231,6 +231,102 @@ static int handle_send_req(ipmi_user_t user,
231 return rv; 231 return rv;
232} 232}
233 233
234static int handle_recv(struct ipmi_file_private *priv,
235 bool trunc, struct ipmi_recv *rsp,
236 int (*copyout)(struct ipmi_recv *, void __user *),
237 void __user *to)
238{
239 int addr_len;
240 struct list_head *entry;
241 struct ipmi_recv_msg *msg;
242 unsigned long flags;
243 int rv = 0;
244
245 /* We claim a mutex because we don't want two
246 users getting something from the queue at a time.
247 Since we have to release the spinlock before we can
248 copy the data to the user, it's possible another
249 user will grab something from the queue, too. Then
250 the messages might get out of order if something
251 fails and the message gets put back onto the
252 queue. This mutex prevents that problem. */
253 mutex_lock(&priv->recv_mutex);
254
255 /* Grab the message off the list. */
256 spin_lock_irqsave(&(priv->recv_msg_lock), flags);
257 if (list_empty(&(priv->recv_msgs))) {
258 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
259 rv = -EAGAIN;
260 goto recv_err;
261 }
262 entry = priv->recv_msgs.next;
263 msg = list_entry(entry, struct ipmi_recv_msg, link);
264 list_del(entry);
265 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
266
267 addr_len = ipmi_addr_length(msg->addr.addr_type);
268 if (rsp->addr_len < addr_len)
269 {
270 rv = -EINVAL;
271 goto recv_putback_on_err;
272 }
273
274 if (copy_to_user(rsp->addr, &(msg->addr), addr_len)) {
275 rv = -EFAULT;
276 goto recv_putback_on_err;
277 }
278 rsp->addr_len = addr_len;
279
280 rsp->recv_type = msg->recv_type;
281 rsp->msgid = msg->msgid;
282 rsp->msg.netfn = msg->msg.netfn;
283 rsp->msg.cmd = msg->msg.cmd;
284
285 if (msg->msg.data_len > 0) {
286 if (rsp->msg.data_len < msg->msg.data_len) {
287 rv = -EMSGSIZE;
288 if (trunc)
289 msg->msg.data_len = rsp->msg.data_len;
290 else
291 goto recv_putback_on_err;
292 }
293
294 if (copy_to_user(rsp->msg.data,
295 msg->msg.data,
296 msg->msg.data_len))
297 {
298 rv = -EFAULT;
299 goto recv_putback_on_err;
300 }
301 rsp->msg.data_len = msg->msg.data_len;
302 } else {
303 rsp->msg.data_len = 0;
304 }
305
306 rv = copyout(rsp, to);
307 if (rv)
308 goto recv_putback_on_err;
309
310 mutex_unlock(&priv->recv_mutex);
311 ipmi_free_recv_msg(msg);
312 return 0;
313
314recv_putback_on_err:
315 /* If we got an error, put the message back onto
316 the head of the queue. */
317 spin_lock_irqsave(&(priv->recv_msg_lock), flags);
318 list_add(entry, &(priv->recv_msgs));
319 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
320recv_err:
321 mutex_unlock(&priv->recv_mutex);
322 return rv;
323}
324
325static int copyout_recv(struct ipmi_recv *rsp, void __user *to)
326{
327 return copy_to_user(to, rsp, sizeof(struct ipmi_recv)) ? -EFAULT : 0;
328}
329
234static int ipmi_ioctl(struct file *file, 330static int ipmi_ioctl(struct file *file,
235 unsigned int cmd, 331 unsigned int cmd,
236 unsigned long data) 332 unsigned long data)
@@ -277,100 +373,12 @@ static int ipmi_ioctl(struct file *file,
277 case IPMICTL_RECEIVE_MSG_TRUNC: 373 case IPMICTL_RECEIVE_MSG_TRUNC:
278 { 374 {
279 struct ipmi_recv rsp; 375 struct ipmi_recv rsp;
280 int addr_len;
281 struct list_head *entry;
282 struct ipmi_recv_msg *msg;
283 unsigned long flags;
284
285
286 rv = 0;
287 if (copy_from_user(&rsp, arg, sizeof(rsp))) {
288 rv = -EFAULT;
289 break;
290 }
291
292 /* We claim a mutex because we don't want two
293 users getting something from the queue at a time.
294 Since we have to release the spinlock before we can
295 copy the data to the user, it's possible another
296 user will grab something from the queue, too. Then
297 the messages might get out of order if something
298 fails and the message gets put back onto the
299 queue. This mutex prevents that problem. */
300 mutex_lock(&priv->recv_mutex);
301
302 /* Grab the message off the list. */
303 spin_lock_irqsave(&(priv->recv_msg_lock), flags);
304 if (list_empty(&(priv->recv_msgs))) {
305 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
306 rv = -EAGAIN;
307 goto recv_err;
308 }
309 entry = priv->recv_msgs.next;
310 msg = list_entry(entry, struct ipmi_recv_msg, link);
311 list_del(entry);
312 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
313
314 addr_len = ipmi_addr_length(msg->addr.addr_type);
315 if (rsp.addr_len < addr_len)
316 {
317 rv = -EINVAL;
318 goto recv_putback_on_err;
319 }
320
321 if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
322 rv = -EFAULT;
323 goto recv_putback_on_err;
324 }
325 rsp.addr_len = addr_len;
326
327 rsp.recv_type = msg->recv_type;
328 rsp.msgid = msg->msgid;
329 rsp.msg.netfn = msg->msg.netfn;
330 rsp.msg.cmd = msg->msg.cmd;
331
332 if (msg->msg.data_len > 0) {
333 if (rsp.msg.data_len < msg->msg.data_len) {
334 rv = -EMSGSIZE;
335 if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
336 msg->msg.data_len = rsp.msg.data_len;
337 } else {
338 goto recv_putback_on_err;
339 }
340 }
341
342 if (copy_to_user(rsp.msg.data,
343 msg->msg.data,
344 msg->msg.data_len))
345 {
346 rv = -EFAULT;
347 goto recv_putback_on_err;
348 }
349 rsp.msg.data_len = msg->msg.data_len;
350 } else {
351 rsp.msg.data_len = 0;
352 }
353 376
354 if (copy_to_user(arg, &rsp, sizeof(rsp))) { 377 if (copy_from_user(&rsp, arg, sizeof(rsp)))
355 rv = -EFAULT; 378 rv = -EFAULT;
356 goto recv_putback_on_err; 379 else
357 } 380 rv = handle_recv(priv, cmd == IPMICTL_RECEIVE_MSG_TRUNC,
358 381 &rsp, copyout_recv, arg);
359 mutex_unlock(&priv->recv_mutex);
360 ipmi_free_recv_msg(msg);
361 break;
362
363 recv_putback_on_err:
364 /* If we got an error, put the message back onto
365 the head of the queue. */
366 spin_lock_irqsave(&(priv->recv_msg_lock), flags);
367 list_add(entry, &(priv->recv_msgs));
368 spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
369 mutex_unlock(&priv->recv_mutex);
370 break;
371
372 recv_err:
373 mutex_unlock(&priv->recv_mutex);
374 break; 382 break;
375 } 383 }
376 384
@@ -711,17 +719,6 @@ static long get_compat_ipmi_msg(struct ipmi_msg *p64,
711 return 0; 719 return 0;
712} 720}
713 721
714static long put_compat_ipmi_msg(struct ipmi_msg *p64,
715 struct compat_ipmi_msg __user *p32)
716{
717 if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
718 __put_user(p64->netfn, &p32->netfn) ||
719 __put_user(p64->cmd, &p32->cmd) ||
720 __put_user(p64->data_len, &p32->data_len))
721 return -EFAULT;
722 return 0;
723}
724
725static long get_compat_ipmi_req(struct ipmi_req *p64, 722static long get_compat_ipmi_req(struct ipmi_req *p64,
726 struct compat_ipmi_req __user *p32) 723 struct compat_ipmi_req __user *p32)
727{ 724{
@@ -765,16 +762,19 @@ static long get_compat_ipmi_recv(struct ipmi_recv *p64,
765 return 0; 762 return 0;
766} 763}
767 764
768static long put_compat_ipmi_recv(struct ipmi_recv *p64, 765static int copyout_recv32(struct ipmi_recv *p64, void __user *to)
769 struct compat_ipmi_recv __user *p32)
770{ 766{
771 if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || 767 struct compat_ipmi_recv v32;
772 __put_user(p64->recv_type, &p32->recv_type) || 768 memset(&v32, 0, sizeof(struct compat_ipmi_recv));
773 __put_user(p64->addr_len, &p32->addr_len) || 769 v32.recv_type = p64->recv_type;
774 __put_user(p64->msgid, &p32->msgid) || 770 v32.addr = ptr_to_compat(p64->addr);
775 put_compat_ipmi_msg(&p64->msg, &p32->msg)) 771 v32.addr_len = p64->addr_len;
776 return -EFAULT; 772 v32.msgid = p64->msgid;
777 return 0; 773 v32.msg.netfn = p64->msg.netfn;
774 v32.msg.cmd = p64->msg.cmd;
775 v32.msg.data_len = p64->msg.data_len;
776 v32.msg.data = ptr_to_compat(p64->msg.data);
777 return copy_to_user(to, &v32, sizeof(v32)) ? -EFAULT : 0;
778} 778}
779 779
780/* 780/*
@@ -783,7 +783,6 @@ static long put_compat_ipmi_recv(struct ipmi_recv *p64,
783static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, 783static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
784 unsigned long arg) 784 unsigned long arg)
785{ 785{
786 int rc;
787 struct ipmi_file_private *priv = filep->private_data; 786 struct ipmi_file_private *priv = filep->private_data;
788 787
789 switch(cmd) { 788 switch(cmd) {
@@ -811,32 +810,15 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
811 case COMPAT_IPMICTL_RECEIVE_MSG: 810 case COMPAT_IPMICTL_RECEIVE_MSG:
812 case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: 811 case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC:
813 { 812 {
814 struct ipmi_recv __user *precv64;
815 struct ipmi_recv recv64; 813 struct ipmi_recv recv64;
816 814
817 memset(&recv64, 0, sizeof(recv64)); 815 memset(&recv64, 0, sizeof(recv64));
818 if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) 816 if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))
819 return -EFAULT; 817 return -EFAULT;
820 818
821 precv64 = compat_alloc_user_space(sizeof(recv64)); 819 return handle_recv(priv,
822 if (copy_to_user(precv64, &recv64, sizeof(recv64))) 820 cmd == COMPAT_IPMICTL_RECEIVE_MSG_TRUNC,
823 return -EFAULT; 821 &recv64, copyout_recv32, compat_ptr(arg));
824
825 rc = ipmi_ioctl(filep,
826 ((cmd == COMPAT_IPMICTL_RECEIVE_MSG)
827 ? IPMICTL_RECEIVE_MSG
828 : IPMICTL_RECEIVE_MSG_TRUNC),
829 (unsigned long) precv64);
830 if (rc != 0)
831 return rc;
832
833 if (copy_from_user(&recv64, precv64, sizeof(recv64)))
834 return -EFAULT;
835
836 if (put_compat_ipmi_recv(&recv64, compat_ptr(arg)))
837 return -EFAULT;
838
839 return rc;
840 } 822 }
841 default: 823 default:
842 return ipmi_ioctl(filep, cmd, arg); 824 return ipmi_ioctl(filep, cmd, arg);