From 6654cad3c5c32ebca045a943957777b1b3167425 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 26 Feb 2026 09:47:54 -0500 Subject: [PATCH] libthread: copy exec args into malloc'ed memory On Solaris, the child of fork does not have access to the stacks of other threads that existed at the time of the fork, as demonstrated by the C program below (fails on Solaris, works everywhere else). Work around this by making a copy of everything the child needs in malloc'ed memory, which is inherited properly. #include #include #include #include #include #include int *p; pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ready = PTHREAD_COND_INITIALIZER; pthread_cond_t never = PTHREAD_COND_INITIALIZER; void* thread1(void *v) { int local = 42; pthread_mutex_lock(&mu); p = &local; pthread_cond_signal(&ready); pthread_cond_wait(&never, &mu); return 0; } void* thread2(void *v) { pthread_mutex_lock(&mu); while(p == 0) pthread_cond_wait(&ready, &mu); pthread_mutex_unlock(&mu); printf("parent: *p = %d\n", *p); int pid = fork(); if (pid == 0) { printf("child: *p = %d\n", *p); exit(0); } printf("parent: pid = %d\n", pid); int status; waitpid(pid, &status, 0); if(WIFEXITED(status)) printf("child exit %d\n", WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("child signal %d\n", WTERMSIG(status)); else printf("unexpected child status %d\n", status); return 0; } int main(void) { pthread_t t1, t2; pthread_create(&t1, 0, thread1, 0); pthread_create(&t2, 0, thread2, 0); void *v; pthread_join(t2, &v); return 0; } --- src/libthread/exec.c | 50 +++++++++++++++++++++++++++++--------- src/libthread/threadimpl.h | 2 +- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/libthread/exec.c b/src/libthread/exec.c index fb5d521a..71032d1b 100644 --- a/src/libthread/exec.c +++ b/src/libthread/exec.c @@ -27,17 +27,45 @@ execproc(void *v) int _runthreadspawn(int *fd, char *cmd, char **argv, char *dir) { - int pid; - Execjob e; + int i, pid, argc; + Execjob *e; - e.fd = fd; - e.cmd = cmd; - e.argv = argv; - e.dir = dir; - e.c = chancreate(sizeof(void*), 0); - proccreate(execproc, &e, 65536); - pid = recvul(e.c); - chanfree(e.c); + // Copy all args into malloc'ed memory, in case the arguments + // point into a thread stack. On Solaris, the stacks of other + // threads are not inherited by the forked child. + e = mallocz(sizeof *e, 1); + memmove(e->fd, fd, 3*sizeof fd[0]); + e->cmd = strdup(cmd); + if(e->cmd == nil) + sysfatal("out of memory"); + argc = 0; + while(argv[argc] != nil) + argc++; + e->argv = mallocz((argc+1)*sizeof argv[0], 1); + if(e->argv == nil) + sysfatal("out of memory"); + for(i=0; iargv[i] = strdup(argv[i]); + if(e->argv[i] == nil) + sysfatal("out of memory"); + } + if(e->dir != nil) { + e->dir = strdup(dir); + if(e->dir == nil) + sysfatal("out of memory"); + } + e->c = chancreate(sizeof(void*), 0); + + proccreate(execproc, e, 65536); + pid = recvul(e->c); + + free(e->cmd); + for(i=0; iargv[i]); + free(e->argv); + free(e->dir); + chanfree(e->c); + free(e); return pid; } @@ -78,7 +106,7 @@ _threadspawn(int fd[3], char *cmd, char *argv[], char *dir) return -1; case 0: /* can't RFNOTEG - will lose tty */ - if(dir != nil ) + if(dir != nil) chdir(dir); /* best effort */ dup2(fd[0], 0); dup2(fd[1], 1); diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h index fd40f252..2788f99a 100644 --- a/src/libthread/threadimpl.h +++ b/src/libthread/threadimpl.h @@ -31,7 +31,7 @@ enum struct Execjob { - int *fd; + int fd[3]; char *cmd; char **argv; char *dir;