diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-06-26 16:47:01 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-06-26 16:47:01 -0400 |
commit | 8b9e04f282c76786fad1a031b38e279bfababd9c (patch) | |
tree | 7034afb9f540e0b14e3cc814c33e4eb2fa28e1b3 | |
parent | 1b3c872c8342803d0fcd8042e4e007d173191db6 (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.c | 248 |
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 | ||
234 | static 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 | |||
314 | recv_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); | ||
320 | recv_err: | ||
321 | mutex_unlock(&priv->recv_mutex); | ||
322 | return rv; | ||
323 | } | ||
324 | |||
325 | static 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 | |||
234 | static int ipmi_ioctl(struct file *file, | 330 | static 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 | ||
714 | static 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 | |||
725 | static long get_compat_ipmi_req(struct ipmi_req *p64, | 722 | static 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 | ||
768 | static long put_compat_ipmi_recv(struct ipmi_recv *p64, | 765 | static 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, | |||
783 | static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, | 783 | static 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); |