net/unix/af_unix.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
The long-standing comment in unix_release_sock() mentioned a "FIXME" about
BSD sending ECONNRESET to connected sockets upon closure, while Linux waits
for the last reference. This behavior has existed since early UNIX socket
implementations and is intentional.
Update the comment to clarify that this is a deliberate design difference,
not a pending fix, and remove the outdated FIXME marker.
Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
---
net/unix/af_unix.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 768098dec231..c21230a69f42 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -734,14 +734,13 @@ static void unix_release_sock(struct sock *sk, int embrion)
/* ---- Socket is dead now and most probably destroyed ---- */
/*
- * Fixme: BSD difference: In BSD all sockets connected to us get
- * ECONNRESET and we die on the spot. In Linux we behave
- * like files and pipes do and wait for the last
- * dereference.
+ * Note: BSD sends ECONNREST to all sockets connected to a closing peer
+ * and terminates immediately. Linux, however, intentionally behaves more
+ * like pipes - waiting for the final dereference before destruction.
*
- * Can't we simply set sock->err?
- *
- * What the above comment does talk about? --ANK(980817)
+ * This behaviour is by design and aligns with Linux's file semantics.
+ * Historical note: this difference from BSD has been present since the
+ * early UNIX socket implementation and is not considered a bug.
*/
if (READ_ONCE(unix_tot_inflight))
---
base-commit: 7ea30958b3054f5e488fa0b33c352723f7ab3a2a
change-id: 20251017-fix-fix-me-59b369ffcfb5
Best regards,
--
Sunday Adelodun <adelodunolaoluwa@yahoo.com>
From: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
Date: Fri, 17 Oct 2025 14:30:45 +0100
> The long-standing comment in unix_release_sock() mentioned a "FIXME" about
> BSD sending ECONNRESET to connected sockets upon closure, while Linux waits
> for the last reference. This behavior has existed since early UNIX socket
> implementations and is intentional.
>
> Update the comment to clarify that this is a deliberate design difference,
> not a pending fix, and remove the outdated FIXME marker.
>
> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
> ---
> net/unix/af_unix.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index 768098dec231..c21230a69f42 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -734,14 +734,13 @@ static void unix_release_sock(struct sock *sk, int embrion)
> /* ---- Socket is dead now and most probably destroyed ---- */
>
> /*
> - * Fixme: BSD difference: In BSD all sockets connected to us get
> - * ECONNRESET and we die on the spot. In Linux we behave
> - * like files and pipes do and wait for the last
> - * dereference.
> + * Note: BSD sends ECONNREST to all sockets connected to a closing peer
> + * and terminates immediately.
I ran a script below on Mac (I hope the behvaviour does
not differ from FreeBSD), and I only see ECONNRESET on
SOCK_DGRAM test case.
Even after close()ing a SOCK_STREAM socket, its peer can
read 0, not ECONNRESET.
So, the comment looks outdated.
---8<---
$ python3 a.py
test 1
b'hello'
b''
test 2
b''
b''
test 3
[Errno 54] Connection reset by peer
---8<---
> Linux, however, intentionally behaves more
> + * like pipes - waiting for the final dereference before destruction.
Note that Linux also sets ECONNRESET if the close()d socket
has unread data or is not yet accept()ed. You can find this
a few lines above of the diff.
---8<---
$ python3 a.py
test 1
b'hello'
b''
test 2
[Errno 104] Connection reset by peer
test 3
[Errno 11] Resource temporarily unavailable
---8<---
> *
> - * Can't we simply set sock->err?
> - *
> - * What the above comment does talk about? --ANK(980817)
> + * This behaviour is by design and aligns with Linux's file semantics.
> + * Historical note: this difference from BSD has been present since the
> + * early UNIX socket implementation and is not considered a bug.
> */
So, I'd remove the entire comment, and if needed, add a
selftest and update man page.
Thanks!
---8<---
import os
from socket import *
def test1():
print("test 1")
server = socket(AF_UNIX, SOCK_STREAM)
server.bind(b'test')
server.listen()
client = socket(AF_UNIX, SOCK_STREAM)
client.connect(server.getsockname())
child, _ = server.accept()
child.send(b'hello')
child.close()
try:
client.setblocking(False)
print(client.recv(20))
print(client.recv(20))
except Exception as e:
print(e)
client.close()
server.close()
os.remove('test')
def test2():
print("test 2")
server = socket(AF_UNIX, SOCK_STREAM)
server.bind(b'test')
server.listen()
client = socket(AF_UNIX, SOCK_STREAM)
client.connect(server.getsockname())
child, _ = server.accept()
client.send(b'hello')
child.close()
try:
client.setblocking(False)
print(client.recv(20))
print(client.recv(20))
except Exception as e:
print(e)
client.close()
server.close()
os.remove('test')
def test3():
print("test 3")
server = socket(AF_UNIX, SOCK_DGRAM)
server.bind(b'test')
client = socket(AF_UNIX, SOCK_DGRAM)
client.connect(server.getsockname())
client.send(b'hello')
server.close()
try:
client.setblocking(False)
print(client.recv(20))
print(client.recv(20))
except Exception as e:
print(e)
client.close()
os.remove('test')
test1()
test2()
test3()
---8<---
On 10/19/25 00:52, Kuniyuki Iwashima wrote:
> From: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
> Date: Fri, 17 Oct 2025 14:30:45 +0100
>> The long-standing comment in unix_release_sock() mentioned a "FIXME" about
>> BSD sending ECONNRESET to connected sockets upon closure, while Linux waits
>> for the last reference. This behavior has existed since early UNIX socket
>> implementations and is intentional.
>>
>> Update the comment to clarify that this is a deliberate design difference,
>> not a pending fix, and remove the outdated FIXME marker.
>>
>> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com>
>> ---
>> net/unix/af_unix.c | 13 ++++++-------
>> 1 file changed, 6 insertions(+), 7 deletions(-)
>>
>> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
>> index 768098dec231..c21230a69f42 100644
>> --- a/net/unix/af_unix.c
>> +++ b/net/unix/af_unix.c
>> @@ -734,14 +734,13 @@ static void unix_release_sock(struct sock *sk, int embrion)
>> /* ---- Socket is dead now and most probably destroyed ---- */
>>
>> /*
>> - * Fixme: BSD difference: In BSD all sockets connected to us get
>> - * ECONNRESET and we die on the spot. In Linux we behave
>> - * like files and pipes do and wait for the last
>> - * dereference.
>> + * Note: BSD sends ECONNREST to all sockets connected to a closing peer
>> + * and terminates immediately.
> I ran a script below on Mac (I hope the behvaviour does
> not differ from FreeBSD), and I only see ECONNRESET on
> SOCK_DGRAM test case.
>
> Even after close()ing a SOCK_STREAM socket, its peer can
> read 0, not ECONNRESET.
>
> So, the comment looks outdated.
>
> ---8<---
> $ python3 a.py
> test 1
> b'hello'
> b''
> test 2
> b''
> b''
> test 3
> [Errno 54] Connection reset by peer
> ---8<---
>
>
>> Linux, however, intentionally behaves more
>> + * like pipes - waiting for the final dereference before destruction.
> Note that Linux also sets ECONNRESET if the close()d socket
> has unread data or is not yet accept()ed. You can find this
> a few lines above of the diff.
>
> ---8<---
> $ python3 a.py
> test 1
> b'hello'
> b''
> test 2
> [Errno 104] Connection reset by peer
> test 3
> [Errno 11] Resource temporarily unavailable
> ---8<---
>
>
>> *
>> - * Can't we simply set sock->err?
>> - *
>> - * What the above comment does talk about? --ANK(980817)
>> + * This behaviour is by design and aligns with Linux's file semantics.
>> + * Historical note: this difference from BSD has been present since the
>> + * early UNIX socket implementation and is not considered a bug.
>> */
> So, I'd remove the entire comment, and if needed, add a
> selftest and update man page.
>
> Thanks!
>
>
> ---8<---
> import os
> from socket import *
>
>
> def test1():
> print("test 1")
> server = socket(AF_UNIX, SOCK_STREAM)
> server.bind(b'test')
> server.listen()
>
> client = socket(AF_UNIX, SOCK_STREAM)
> client.connect(server.getsockname())
>
> child, _ = server.accept()
>
> child.send(b'hello')
> child.close()
>
> try:
> client.setblocking(False)
> print(client.recv(20))
> print(client.recv(20))
> except Exception as e:
> print(e)
>
> client.close()
> server.close()
> os.remove('test')
>
>
> def test2():
> print("test 2")
> server = socket(AF_UNIX, SOCK_STREAM)
> server.bind(b'test')
> server.listen()
>
> client = socket(AF_UNIX, SOCK_STREAM)
> client.connect(server.getsockname())
>
> child, _ = server.accept()
>
> client.send(b'hello')
> child.close()
>
> try:
> client.setblocking(False)
> print(client.recv(20))
> print(client.recv(20))
> except Exception as e:
> print(e)
>
> client.close()
> server.close()
> os.remove('test')
>
>
> def test3():
> print("test 3")
> server = socket(AF_UNIX, SOCK_DGRAM)
> server.bind(b'test')
>
> client = socket(AF_UNIX, SOCK_DGRAM)
> client.connect(server.getsockname())
>
> client.send(b'hello')
> server.close()
>
> try:
> client.setblocking(False)
> print(client.recv(20))
> print(client.recv(20))
> except Exception as e:
> print(e)
>
> client.close()
> os.remove('test')
>
>
> test1()
> test2()
> test3()
> ---8<---
Thanks Kuniyuki for the detailed explanation and example script.
I'll go ahead and send a v2 that removes the entire comment as you
suggested.
After that, I’ll work on a follow-up patch to add a selftest for this
behavior.
Thanks again!
Sunday
© 2016 - 2026 Red Hat, Inc.