From 0dcc74a8a7eebe816f855ad0375714c52f8b785a Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Fri, 22 Apr 2022 16:20:04 +0100
Subject: [PATCH] Prevent dangling cat-file calls (goroutine alternative)
 (#19454)

If an `os/exec.Command` is passed non `*os.File` as an input/output, go
will create `os.Pipe`s and wait for their closure in `cmd.Wait()`.  If
the code following this is responsible for closing `io.Pipe`s or other
handlers then on process death from context cancellation the `Wait` can
hang.

There are two possible solutions:

1. use `os.Pipe` as the input/output as `cmd.Wait` does not wait for these.
2. create a goroutine waiting on the context cancellation that will close the inputs.

This PR provides the second option - which is a simpler change that can
be more easily backported.

Closes #19448

Signed-off-by: Andrew Thornton <art27@cantab.net>
---
 modules/git/batch_reader.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go
index 5a0a82b13a..902fa89718 100644
--- a/modules/git/batch_reader.go
+++ b/modules/git/batch_reader.go
@@ -57,6 +57,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
 		<-closed
 	}
 
+	// Ensure cancel is called as soon as the provided context is cancelled
+	go func() {
+		<-ctx.Done()
+		cancel()
+	}()
+
 	_, filename, line, _ := runtime.Caller(2)
 	filename = strings.TrimPrefix(filename, callerPrefix)
 
@@ -101,6 +107,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
 		<-closed
 	}
 
+	// Ensure cancel is called as soon as the provided context is cancelled
+	go func() {
+		<-ctx.Done()
+		cancel()
+	}()
+
 	_, filename, line, _ := runtime.Caller(2)
 	filename = strings.TrimPrefix(filename, callerPrefix)