diff options
author | Greg Kurz <groug@kaod.org> | 2018-04-05 19:19:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-06 00:36:22 -0400 |
commit | a85222435bd055b2d2cedc515d810a5ea6c05432 (patch) | |
tree | 5cb9623e7cdb4229ab9bd90d559d5a6d895aa1fc /net/9p | |
parent | de37428638ec7d22b46c4c20b8536f3681559e2c (diff) |
net/9p: avoid -ERESTARTSYS leak to userspace
If it was interrupted by a signal, the 9p client may need to send some
more requests to the server for cleanup before returning to userspace.
To avoid such a last minute request to be interrupted right away, the
client memorizes if a signal is pending, clears TIF_SIGPENDING, handles
the request and calls recalc_sigpending() before returning.
Unfortunately, if the transmission of this cleanup request fails for any
reason, the transport returns an error and the client propagates it
right away, without calling recalc_sigpending().
This ends up with -ERESTARTSYS from the initially interrupted request
crawling up to syscall exit, with TIF_SIGPENDING cleared by the cleanup
request. The specific signal handling code, which is responsible for
converting -ERESTARTSYS to -EINTR is not called, and userspace receives
the confusing errno value:
open: Unknown error 512 (512)
This is really hard to hit in real life. I discovered the issue while
working on hot-unplug of a virtio-9p-pci device with an instrumented
QEMU allowing to control request completion.
Both p9_client_zc_rpc() and p9_client_rpc() functions have this buggy
error path actually. Their code flow is a bit obscure and the best
thing to do would probably be a full rewrite: to really ensure this
situation of clearing TIF_SIGPENDING and returning -ERESTARTSYS can
never happen.
But given the general lack of interest for the 9p code, I won't risk
breaking more things. So this patch simply fixes the buggy paths in
both functions with a trivial label+goto.
Thanks to Laurent Dufour for his help and suggestions on how to find the
root cause and how to fix it.
Link: http://lkml.kernel.org/r/152062809886.10599.7361006774123053312.stgit@bahia.lan
Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Yiwen Jiang <jiangyiwen@huawei.com>
Cc: Eric Van Hensbergen <ericvh@gmail.com>
Cc: Ron Minnich <rminnich@sandia.gov>
Cc: Latchesar Ionkov <lucho@ionkov.net>
Cc: David Miller <davem@davemloft.net>
Cc: Laurent Dufour <ldufour@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/client.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/net/9p/client.c b/net/9p/client.c index b433aff5ff13..e6cae8332e2e 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -769,7 +769,7 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) | |||
769 | if (err < 0) { | 769 | if (err < 0) { |
770 | if (err != -ERESTARTSYS && err != -EFAULT) | 770 | if (err != -ERESTARTSYS && err != -EFAULT) |
771 | c->status = Disconnected; | 771 | c->status = Disconnected; |
772 | goto reterr; | 772 | goto recalc_sigpending; |
773 | } | 773 | } |
774 | again: | 774 | again: |
775 | /* Wait for the response */ | 775 | /* Wait for the response */ |
@@ -804,6 +804,7 @@ again: | |||
804 | if (req->status == REQ_STATUS_RCVD) | 804 | if (req->status == REQ_STATUS_RCVD) |
805 | err = 0; | 805 | err = 0; |
806 | } | 806 | } |
807 | recalc_sigpending: | ||
807 | if (sigpending) { | 808 | if (sigpending) { |
808 | spin_lock_irqsave(¤t->sighand->siglock, flags); | 809 | spin_lock_irqsave(¤t->sighand->siglock, flags); |
809 | recalc_sigpending(); | 810 | recalc_sigpending(); |
@@ -867,7 +868,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, | |||
867 | if (err == -EIO) | 868 | if (err == -EIO) |
868 | c->status = Disconnected; | 869 | c->status = Disconnected; |
869 | if (err != -ERESTARTSYS) | 870 | if (err != -ERESTARTSYS) |
870 | goto reterr; | 871 | goto recalc_sigpending; |
871 | } | 872 | } |
872 | if (req->status == REQ_STATUS_ERROR) { | 873 | if (req->status == REQ_STATUS_ERROR) { |
873 | p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err); | 874 | p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err); |
@@ -885,6 +886,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, | |||
885 | if (req->status == REQ_STATUS_RCVD) | 886 | if (req->status == REQ_STATUS_RCVD) |
886 | err = 0; | 887 | err = 0; |
887 | } | 888 | } |
889 | recalc_sigpending: | ||
888 | if (sigpending) { | 890 | if (sigpending) { |
889 | spin_lock_irqsave(¤t->sighand->siglock, flags); | 891 | spin_lock_irqsave(¤t->sighand->siglock, flags); |
890 | recalc_sigpending(); | 892 | recalc_sigpending(); |