After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 723655 - Socket source is left in the poll after the socket is closed
Socket source is left in the poll after the socket is closed
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: network
unspecified
Other All
: Normal minor
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2014-02-05 09:02 UTC by Mikhail Zabaluev
Modified: 2017-10-25 14:36 UTC
See Also:
GNOME target: ---
GNOME version: 3.11/3.12


Attachments
Remove the socket from poll if it has been closed (1.04 KB, patch)
2014-02-05 09:05 UTC, Mikhail Zabaluev
reviewed Details | Review
Test /socket/connection-close-with-pending (3.01 KB, patch)
2014-02-05 09:05 UTC, Mikhail Zabaluev
none Details | Review
Test /socket/source-postmortem (2.55 KB, patch)
2014-02-05 22:53 UTC, Mikhail Zabaluev
reviewed Details | Review
socket: Don't poll the socket fd after close (3.11 KB, patch)
2017-10-21 02:39 UTC, Michael Catanzaro
none Details | Review
Test /socket/source-postmortem (2.55 KB, patch)
2017-10-21 02:39 UTC, Michael Catanzaro
none Details | Review
Test /socket/source-postmortem (2.56 KB, patch)
2017-10-21 02:47 UTC, Michael Catanzaro
needs-work Details | Review
socket: Don't poll the socket fd after close (3.19 KB, patch)
2017-10-23 15:10 UTC, Michael Catanzaro
committed Details | Review
Add socket postmortem test (2.53 KB, patch)
2017-10-23 15:10 UTC, Michael Catanzaro
committed Details | Review
socket: actually remove fd from poll when socket is closed (1.08 KB, patch)
2017-10-25 00:54 UTC, Michael Catanzaro
none Details | Review
socket: actually remove fd from poll when socket is closed (1.10 KB, patch)
2017-10-25 00:55 UTC, Michael Catanzaro
committed Details | Review

Description Mikhail Zabaluev 2014-02-05 09:02:50 UTC
GSocketSource is used to poll on a socket, for instance, in GSocketConnection. When the socket is closed, the source should become ready in order for any operations waiting on the socket's sources to be reliably completed. Now the socket's former file descriptor is simply left in the poll, which may lead to it failing to report events if it has been reclaimed for another object.
Comment 1 Mikhail Zabaluev 2014-02-05 09:05:42 UTC
Created attachment 268133 [details] [review]
Remove the socket from poll if it has been closed

This prevents polling on file descriptors that are no longer in use
or have been reused for something else.
Comment 2 Mikhail Zabaluev 2014-02-05 09:05:47 UTC
Created attachment 268134 [details] [review]
Test /socket/connection-close-with-pending

Test that closing a GSocketConnection with a pending read on the
input substream causes the read to terminate with the correct error.
This might not be the case without the preceding GSocketSource fix,
if the file descriptor has been reclaimed by another kernel object
that does not poll ready.
Comment 3 Dan Winship 2014-02-05 10:17:44 UTC
The bug here is that g_io_stream_close() succeeds when you have a read pending. It is supposed to fail, as you note in your test case.

*** This bug has been marked as a duplicate of bug 707912 ***
Comment 4 Mikhail Zabaluev 2014-02-05 22:53:41 UTC
Created attachment 268238 [details] [review]
Test /socket/source-postmortem

Checks that after a GSocket is closed, a source created off it
with g_socket_create_source() will dispatch with G_IO_NVAL
even if the descriptor used by the socket is reclaimed for
something else.
Comment 5 Mikhail Zabaluev 2014-02-05 22:57:48 UTC
g_io_stream_close() is one thing, but this bug is really about GSocketSource polling for a descriptor that's no longer used for the socket.
I have changed the test to illustrate.
Comment 6 Michael Catanzaro 2017-10-17 21:29:10 UTC
Review of attachment 268133 [details] [review]:

This looks excellent, thanks!

Only problem is it needs rebased; we need to bring back socket_source_prepare, which was removed on non-Windows. Are you still interested in landing this and doing the rebase? If not, I'll try to take care of it.
Comment 7 Michael Catanzaro 2017-10-17 21:29:31 UTC
Review of attachment 268238 [details] [review]:

Excellent!
Comment 8 Michael Catanzaro 2017-10-21 02:39:20 UTC
Created attachment 361992 [details] [review]
socket: Don't poll the socket fd after close

This prevents polling on file descriptors that are no longer in use
or have been reused for something else.

Based on a patch by Mikhail Zabaluev
Comment 9 Michael Catanzaro 2017-10-21 02:39:23 UTC
Created attachment 361993 [details] [review]
Test /socket/source-postmortem

Checks that after a GSocket is closed, a source created off it
with g_socket_create_source() will dispatch with G_IO_NVAL
even if the descriptor used by the socket is reclaimed for
something else.
Comment 10 Michael Catanzaro 2017-10-21 02:46:16 UTC
I took a stab at updating the first patch, and added a few more checks inside socket_source_dispatch() that hopefully make sense. But I don't really know what I'm doing.

Philip, what do you think?
Comment 11 Michael Catanzaro 2017-10-21 02:47:11 UTC
Created attachment 361994 [details] [review]
Test /socket/source-postmortem

Checks that after a GSocket is closed, a source created off it
with g_socket_create_source() will dispatch with G_IO_NVAL
even if the descriptor used by the socket is reclaimed for
something else.
Comment 12 Philip Withnall 2017-10-23 11:03:39 UTC
Review of attachment 361992 [details] [review]:

Looks reasonable to me. Setting to needs-work for the question I have.

::: gio/gsocket.c
@@ +3810,3 @@
     {
       socket->priv->timed_out = TRUE;
+      if (!g_socket_is_closed (socket_source->socket))

Could potentially combine this condition with the (timeout >= 0 && timeout < g_source_get_time()) one above? Do we want to set timed_out but not events?
Comment 13 Philip Withnall 2017-10-23 11:11:12 UTC
Review of attachment 361994 [details] [review]:

::: gio/tests/socket.c
@@ +1402,3 @@
+
+static void
+test_source_postmortem (void)

Would be good to have a comment above the test function explaining what the test is trying to check, and how it does that.

@@ +1434,3 @@
+  g_assert_cmpint (fd, !=, -1);
+
+  g_main_context_iteration (NULL, FALSE);

Using the global default main context here is a bit suspect. The test would be a bit more robust if a custom main context were used.

Then you can also safely assert at the end of the test that there are no more pending sources on that main context.
Comment 14 Michael Catanzaro 2017-10-23 14:33:58 UTC
(In reply to Philip Withnall from comment #12)
> Review of attachment 361992 [details] [review] [review]:
> 
> Looks reasonable to me. Setting to needs-work for the question I have.

Eh, I was hoping you'd find a bug... touching this code is nerve-wracking.

> ::: gio/gsocket.c
> @@ +3810,3 @@
>      {
>        socket->priv->timed_out = TRUE;
> +      if (!g_socket_is_closed (socket_source->socket))
> 
> Could potentially combine this condition with the (timeout >= 0 && timeout <
> g_source_get_time()) one above? Do we want to set timed_out but not events?

Good point, I guess it makes sense to set neither.

(In reply to Philip Withnall from comment #13)
> Would be good to have a comment above the test function explaining what the
> test is trying to check, and how it does that.

OK.

> @@ +1434,3 @@
> +  g_assert_cmpint (fd, !=, -1);
> +
> +  g_main_context_iteration (NULL, FALSE);
> 
> Using the global default main context here is a bit suspect. The test would
> be a bit more robust if a custom main context were used.
> 
> Then you can also safely assert at the end of the test that there are no
> more pending sources on that main context.

Good point.
Comment 15 Michael Catanzaro 2017-10-23 14:57:11 UTC
I think the "Reuse the former socket's file descriptor" test isn't actually doing anything, because if the former fd was still in use, dup2 would silently close it. So the test can be simplified a bit more as well. What's important to test is that the callback gets visited, and that (as you noted) there is nothing more pending on the main context.
Comment 16 Michael Catanzaro 2017-10-23 15:02:34 UTC
(In reply to Michael Catanzaro from comment #15)
> What's important to test is that the callback gets visited, and that (as you noted)
> there is nothing more pending on the main context.

Well, ideally we would test that it really gets removed from the poll, which I guess was the intend behind the dup2 test... but that did not work as intended. I don't know if that's actually testable.
Comment 17 Michael Catanzaro 2017-10-23 15:10:35 UTC
Created attachment 362109 [details] [review]
socket: Don't poll the socket fd after close

This prevents polling on file descriptors that are no longer in use
or have been reused for something else.

Based on a patch by Mikhail Zabaluev
Comment 18 Michael Catanzaro 2017-10-23 15:10:39 UTC
Created attachment 362110 [details] [review]
Add socket postmortem test

Checks that after a GSocket is closed, a source created off it
with g_socket_create_source() will dispatch exactly once with
G_IO_NVAL.

Based on a patch by Mikhail Zabaluev
Comment 19 Philip Withnall 2017-10-23 15:40:08 UTC
Review of attachment 362110 [details] [review]:

::: gio/tests/socket.c
@@ +1402,3 @@
+
+static void
+test_source_postmortem (void)

Comment here seems to have not appeared.
Comment 20 Philip Withnall 2017-10-23 15:44:56 UTC
Review of attachment 362109 [details] [review]:

Comment 21 Philip Withnall 2017-10-23 15:45:37 UTC
(In reply to Michael Catanzaro from comment #14)
> (In reply to Philip Withnall from comment #12)
> > Review of attachment 361992 [details] [review] [review] [review]:
> > 
> > Looks reasonable to me. Setting to needs-work for the question I have.
> 
> Eh, I was hoping you'd find a bug... touching this code is nerve-wracking.

Comment 22 Philip Withnall 2017-10-23 16:01:53 UTC
(In reply to Philip Withnall from comment #21)
> (In reply to Michael Catanzaro from comment #14)
> > (In reply to Philip Withnall from comment #12)
> > > Review of attachment 361992 [details] [review] [review] [review] [review]:
> > > 
> > > Looks reasonable to me. Setting to needs-work for the question I have.
> > 
> > Eh, I was hoping you'd find a bug... touching this code is nerve-wracking.

Bah. There was a witty emoji-based reply in there, but Bugzilla ate it. Boo. :-(
Comment 23 Michael Catanzaro 2017-10-24 00:12:00 UTC
Review of attachment 362110 [details] [review]:

::: gio/tests/socket.c
@@ +1425,3 @@
+  g_object_unref (socket);
+
+  /* Test that, after a socket is closed, its source callback should be called

I put the comment here above the asserts, since none of the other test functions in this file have a comment on top... OK?
Comment 24 Philip Withnall 2017-10-24 07:23:34 UTC
(In reply to Michael Catanzaro from comment #23)
> Review of attachment 362110 [details] [review] [review]:
> 
> ::: gio/tests/socket.c
> @@ +1425,3 @@
> +  g_object_unref (socket);
> +
> +  /* Test that, after a socket is closed, its source callback should be
> called
> 
> I put the comment here above the asserts, since none of the other test
> functions in this file have a comment on top... OK?

I would prefer to start the convention that the comment is just above the test function, but it’s not worth respinning the patch for.
Comment 25 Philip Withnall 2017-10-24 07:23:51 UTC
Review of attachment 362110 [details] [review]:

Make it so.
Comment 26 Michael Catanzaro 2017-10-24 14:02:20 UTC
Attachment 362109 [details] pushed as f99045f - socket: Don't poll the socket fd after close
Attachment 362110 [details] pushed as c5202bc - Add socket postmortem test
Comment 27 Michael Catanzaro 2017-10-25 00:46:27 UTC
Sigh, the patch is incomplete. While iterating on this patch, I accidentally dropped a call to g_source_remove_unix_fd() in a refactoring, and forgot to bring it back.
Comment 28 Michael Catanzaro 2017-10-25 00:54:23 UTC
Created attachment 362233 [details] [review]
socket: actually remove fd from poll when socket is closed

I accidentally dropped a call to g_source_remove_unix_fd() in a
refactoring, and forgot to bring it back.

The test did not catch this, because the test only checks that the right
source callback is dispatched properly. I don't know how to test this.
Comment 29 Michael Catanzaro 2017-10-25 00:55:10 UTC
(In reply to Michael Catanzaro from comment #28)
> I accidentally dropped a call to g_source_remove_unix_fd() in a
> refactoring, and forgot to bring it back.

Well, a bit misleading... the call was never there before, I added it for my patch and then removed it before uploading.
Comment 30 Michael Catanzaro 2017-10-25 00:55:57 UTC
Created attachment 362234 [details] [review]
socket: actually remove fd from poll when socket is closed

In my previous patch, I failed to call g_source_remove_unix_fd() in
order to actually stop polling the fd of the closed socket.

The test did not catch this, because the test only checks that the right
source callback is dispatched properly. I don't know how to test this.
Comment 31 Philip Withnall 2017-10-25 09:14:12 UTC
Review of attachment 362234 [details] [review]:

LGTM with this cleanup.

::: gio/gsocket.c
@@ +3800,3 @@
+        g_source_remove_unix_fd (source, socket_source->fd_tag);
+        socket_source->fd_tag = NULL;
+      }

Code style:

if (socket_source->fd_tag != NULL)
  g_source_remove_unix_fd (source, socket_source->fd_tag);
socket_source->fd_tag = NULL;
Comment 32 Michael Catanzaro 2017-10-25 14:36:29 UTC
Attachment 362234 [details] pushed as 4b07869 - socket: actually remove fd from poll when socket is closed