From 7b08b7b712c51a99e1b76483c4c202488d987da4 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Tue, 18 Feb 2025 08:33:32 +0800 Subject: [PATCH] hooks: Fix the race condition that causes EPIPE The hooks handler in the main daemon didn't wait for the hook client to write fully, and sometimes prematurely closes the connection, causing the hook client's splice to return EPIPE (or SIGPIPE if the signal handler wasn't installed). To remedy this, we call shutdown(sock, SHUT_WR) in the client, so that attempts to read on the server side return EOF. Then we can simply use io.Copy(&buf, conn) on the server side to fetch all of the data into a buffer. --- git_hooks_client/git_hooks_client.c | 10 ++++++++++ git_hooks_handle.go | 7 +++++++ diff --git a/git_hooks_client/git_hooks_client.c b/git_hooks_client/git_hooks_client.c index e00ab3a8cca4ebfc8fd02bc7febf8fa6249df048..6471aa8990a7849c23f01c553e2c6ddb0d59d773 100644 --- a/git_hooks_client/git_hooks_client.c +++ b/git_hooks_client/git_hooks_client.c @@ -163,6 +163,16 @@ return EXIT_FAILURE; } /* + * The sending part of the UNIX socket should be shut down, to let + * io.Copy on the Go side return. + */ + if (shutdown(sock, SHUT_WR) == -1) { + perror("shutdown internal socket"); + close(sock); + return EXIT_FAILURE; + } + + /* * The first byte of the response from the UNIX domain socket is the * status code. We read it and record it as our return value. */ diff --git a/git_hooks_handle.go b/git_hooks_handle.go index 91f1894707d9e0d92aed047ec3db97266b870995..e9b163a347643d939821cb7f9da6559050407896 100644 --- a/git_hooks_handle.go +++ b/git_hooks_handle.go @@ -5,6 +5,7 @@ "bytes" "encoding/binary" "errors" "fmt" + "io" "net" "os" "path/filepath" @@ -70,6 +71,12 @@ } arg.WriteByte(b[0]) } args = append(args, arg.String()) + } + + var stdin bytes.Buffer + _, err = io.Copy(&stdin, conn) + if err != nil { + fmt.Fprintln(conn, "Failed to read to the stdin buffer:", err.Error()) } switch filepath.Base(args[0]) { -- 2.48.1