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.

Reply via email to