Skip to content

Commit 507d780

Browse files
tiwaigitster
authored andcommitted
pager: don't use unsafe functions in signal handlers
Since the commit a3da882 (pager: do wait_for_pager on signal death), we call wait_for_pager() in the pager's signal handler. The recent bug report revealed that this causes a deadlock in glibc at aborting "git log" [*1*]. When this happens, git process is left unterminated, and it can't be killed by SIGTERM but only by SIGKILL. The problem is that wait_for_pager() function does more than waiting for pager process's termination, but it does cleanups and printing errors. Unfortunately, the functions that may be used in a signal handler are very limited [*2*]. Particularly, malloc(), free() and the variants can't be used in a signal handler because they take a mutex internally in glibc. This was the cause of the deadlock above. Other than the direct calls of malloc/free, many functions calling malloc/free can't be used. strerror() is such one, either. Also the usage of fflush() and printf() in a signal handler is bad, although it seems working so far. In a safer side, we should avoid them, too. This patch tries to reduce the calls of such functions in signal handlers. wait_for_signal() takes a flag and avoids the unsafe calls. Also, finish_command_in_signal() is introduced for the same reason. There the free() calls are removed, and only waits for the children without whining at errors. [*1*] https://bugzilla.opensuse.org/show_bug.cgi?id=942297 [*2*] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03 Signed-off-by: Takashi Iwai <[email protected]> Reviewed-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a17c56c commit 507d780

File tree

3 files changed

+34
-14
lines changed

3 files changed

+34
-14
lines changed

pager.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,29 @@
1414
static const char *pager_argv[] = { NULL, NULL };
1515
static struct child_process pager_process = CHILD_PROCESS_INIT;
1616

17-
static void wait_for_pager(void)
17+
static void wait_for_pager(int in_signal)
1818
{
19-
fflush(stdout);
20-
fflush(stderr);
19+
if (!in_signal) {
20+
fflush(stdout);
21+
fflush(stderr);
22+
}
2123
/* signal EOF to pager */
2224
close(1);
2325
close(2);
24-
finish_command(&pager_process);
26+
if (in_signal)
27+
finish_command_in_signal(&pager_process);
28+
else
29+
finish_command(&pager_process);
30+
}
31+
32+
static void wait_for_pager_atexit(void)
33+
{
34+
wait_for_pager(0);
2535
}
2636

2737
static void wait_for_pager_signal(int signo)
2838
{
29-
wait_for_pager();
39+
wait_for_pager(1);
3040
sigchain_pop(signo);
3141
raise(signo);
3242
}
@@ -90,7 +100,7 @@ void setup_pager(void)
90100

91101
/* this makes sure that the parent terminates after the pager */
92102
sigchain_push_common(wait_for_pager_signal);
93-
atexit(wait_for_pager);
103+
atexit(wait_for_pager_atexit);
94104
}
95105

96106
int pager_in_use(void)

run-command.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,27 @@ struct child_to_clean {
1818
static struct child_to_clean *children_to_clean;
1919
static int installed_child_cleanup_handler;
2020

21-
static void cleanup_children(int sig)
21+
static void cleanup_children(int sig, int in_signal)
2222
{
2323
while (children_to_clean) {
2424
struct child_to_clean *p = children_to_clean;
2525
children_to_clean = p->next;
2626
kill(p->pid, sig);
27-
free(p);
27+
if (!in_signal)
28+
free(p);
2829
}
2930
}
3031

3132
static void cleanup_children_on_signal(int sig)
3233
{
33-
cleanup_children(sig);
34+
cleanup_children(sig, 1);
3435
sigchain_pop(sig);
3536
raise(sig);
3637
}
3738

3839
static void cleanup_children_on_exit(void)
3940
{
40-
cleanup_children(SIGTERM);
41+
cleanup_children(SIGTERM, 0);
4142
}
4243

4344
static void mark_child_for_cleanup(pid_t pid)
@@ -232,14 +233,16 @@ static inline void set_cloexec(int fd)
232233
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
233234
}
234235

235-
static int wait_or_whine(pid_t pid, const char *argv0)
236+
static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
236237
{
237238
int status, code = -1;
238239
pid_t waiting;
239240
int failed_errno = 0;
240241

241242
while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
242243
; /* nothing */
244+
if (in_signal)
245+
return 0;
243246

244247
if (waiting < 0) {
245248
failed_errno = errno;
@@ -450,7 +453,7 @@ int start_command(struct child_process *cmd)
450453
* At this point we know that fork() succeeded, but execvp()
451454
* failed. Errors have been reported to our stderr.
452455
*/
453-
wait_or_whine(cmd->pid, cmd->argv[0]);
456+
wait_or_whine(cmd->pid, cmd->argv[0], 0);
454457
failed_errno = errno;
455458
cmd->pid = -1;
456459
}
@@ -549,12 +552,18 @@ int start_command(struct child_process *cmd)
549552

550553
int finish_command(struct child_process *cmd)
551554
{
552-
int ret = wait_or_whine(cmd->pid, cmd->argv[0]);
555+
int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0);
553556
argv_array_clear(&cmd->args);
554557
argv_array_clear(&cmd->env_array);
555558
return ret;
556559
}
557560

561+
int finish_command_in_signal(struct child_process *cmd)
562+
{
563+
return wait_or_whine(cmd->pid, cmd->argv[0], 1);
564+
}
565+
566+
558567
int run_command(struct child_process *cmd)
559568
{
560569
int code;
@@ -785,7 +794,7 @@ int start_async(struct async *async)
785794
int finish_async(struct async *async)
786795
{
787796
#ifdef NO_PTHREADS
788-
return wait_or_whine(async->pid, "child process");
797+
return wait_or_whine(async->pid, "child process", 0);
789798
#else
790799
void *ret = (void *)(intptr_t)(-1);
791800

run-command.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ void child_process_init(struct child_process *);
5050

5151
int start_command(struct child_process *);
5252
int finish_command(struct child_process *);
53+
int finish_command_in_signal(struct child_process *);
5354
int run_command(struct child_process *);
5455

5556
extern const char *find_hook(const char *name);

0 commit comments

Comments
 (0)