https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127759
Bug ID: 127759
Summary: GNAT.Sockets C_Accept overwrites EAGAIN with EBADF on
FreeBSD
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: ada
Assignee: unassigned at gcc dot gnu.org
Reporter: hodong at nimfsoft dot com
CC: dkm at gcc dot gnu.org
Target Milestone: ---
Created attachment 65777
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65777&action=edit
Standalone Ada reproducer for GNAT.Sockets EAGAIN overwrite on FreeBSD
Environment
-----------
Host: FreeBSD 15.1-RELEASE-p4 amd64
Compiler: GCC/GNAT 16.2.0
Target: x86_64-portbld-freebsd15.1
Runtime: POSIX/pthread GNAT runtime
Files
-----
accept_eagain.adb
gnat-sockets-preserve-syscall-errno.patch
Reproducer
----------
The Ada reproducer creates a listening TCP socket on 127.0.0.1, enables
Non_Blocking_IO, and calls Accept_Socket with no client waiting.
Build and run:
gnatmake16 -q -f -gnat2022 -O2 accept_eagain.adb
./accept_eagain
Observed output with the installed GCC/GNAT 16.2.0 runtime:
GNAT.SOCKETS.SOCKET_ERROR: [9] Bad file descriptor
System call trace
-----------------
Command:
truss -o truss.txt ./accept_eagain
Relevant calls:
setsockopt(3,SOL_SOCKET,SO_NOSIGPIPE,...) = 0
accept(3,...) ERR#35 'Resource temporarily unavailable'
setsockopt(-1,SOL_SOCKET,SO_NOSIGPIPE,...) ERR#9 'Bad file descriptor'
The listening descriptor is 3. The accept call returns -1 with errno 35.
The following setsockopt call uses descriptor -1 and fails with errno 9
(EBADF).
On FreeBSD, EWOULDBLOCK is defined as EAGAIN; both have value 35.
GCC source
----------
In gcc/ada/libgnat/g-socthi.adb, C_Accept contains:
R := Syscall_Accept (S, Addr, U_Addrlen'Unchecked_Access);
...
Disable_SIGPIPE (R);
return R;
In gcc/ada/socket.c, __gnat_disable_sigpipe contains this code when
SO_NOSIGPIPE is defined:
int val = 1;
(void) setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof val);
In gcc/ada/libgnat/g-socket.adb, Accept_Socket contains:
Res := C_Accept (C.int (Server), Sin'Address, Len'Access);
if Res = Failure then
Raise_Socket_Error (Socket_Errno);
end if;
In gcc/ada/libgnat/g-socthi.ads, Socket_Errno is defined as:
function Socket_Errno return Integer renames GNAT.OS_Lib.Errno;
The GCC 16.2.0 g-socthi.adb source also contains an unconditional
Disable_SIGPIPE (R) call in C_Socket after Syscall_Socket. No failing
Syscall_Socket runtime case was exercised in this test.
Patch
-----
The attached patch guards both Disable_SIGPIPE (R) calls with R /= Failure.
It applies with patch -p1 to the GCC 16.2.0 source used for this test.
A patch -p1 --dry-run of the same patch against GCC master source retrieved
on 2026-10-07 completed without rejects.
Patched runtime result
----------------------
The patched g-socthi.adb was compiled with the GCC/GNAT 16.2.0 runtime-unit
compiler options recorded in the installed g-socthi.ali. The resulting
g-socthi.o replaced only the g-socthi.o member in a copy of the installed
libgnat.a. The reproducer was linked against that copied archive.
Observed output:
GNAT.SOCKETS.SOCKET_ERROR: [35] Resource temporarily unavailable
Relevant calls from truss:
setsockopt(3,SOL_SOCKET,SO_NOSIGPIPE,...) = 0
accept(3,...) ERR#35 'Resource temporarily unavailable'
The patched trace contains no setsockopt call with descriptor -1.
The C_Socket hunk compiled as part of the patched g-socthi.adb runtime unit.
No failing Syscall_Socket runtime case was exercised.
AI assistance disclosure
------------------------
This report, reproducer, and patch were prepared with assistance from OpenAI
ChatGPT (GPT-5.6 Sol, xhigh).
All commands and reported results were verified on the system described above.