Browse Source

inet: use the direct accept4 syscall when __NR_accept is missing

On targets that lack __NR_accept but provide __NR_accept4 (e.g. m68k),
accept() fell through to the __socketcall() multiplexer stub. That stub
is generated by the _syscall2 assembly macro and carries no unwind
tables (no .eh_frame/FDE), so an asynchronous cancellation that
interrupts the blocking accept cannot be unwound out of: the forced
unwinder reaches end-of-stack at the stub frame and the cleanup handlers
never run (tst-cancelx4 reported "cleanup handler not called for
'accept'").

Route accept through the direct accept4 syscall instead. Like every
other cancellable socket wrapper it is then an INLINE_SYSCALL inlined
into __NC(accept), which is built with unwind tables, so the blocking
syscall sits in a frame the forced unwinder can traverse.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi 1 week ago
parent
commit
7f129388b6
1 changed files with 5 additions and 0 deletions
  1. 5 0
      libc/inet/socketcalls.c

+ 5 - 0
libc/inet/socketcalls.c

@@ -65,6 +65,11 @@ static int __NC(accept)(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
 {
 # ifdef __NR_accept
 	return INLINE_SYSCALL(accept, 3, sockfd, addr, addrlen);
+# elif defined(__NR_accept4)
+	/* Targets without __NR_accept (e.g. m68k) must use the direct accept4
+	   syscall, not the asm __socketcall stub: the stub carries no CFI, so
+	   asynchronous cancellation cannot unwind out of the blocking call.  */
+	return INLINE_SYSCALL(accept4, 4, sockfd, addr, addrlen, 0);
 # elif defined(__NR_socketcall)
 	unsigned long args[3];