thread-safe popen(3) at 4-STABLE
Dmitry Morozovsky
marck at FreeBSD.org
Sat Oct 16 09:19:57 PDT 2004
Dear colleagues,
checking for hanged and CPU-eating clamav, my colleague (oleg at rinet.ru) found
that popen(3) is not thread safe. What about commiting tjr's fix (attached) to
RELENG_4?
Sincerely,
D.Marck [DM5020, MCK-RIPE, DM3-RIPN]
---------------------------------------------------------------------------
*** Dmitry Morozovsky --- D.Marck --- Wild Woozle --- marck at FreeBSD.org ***
---------------------------------------------------------------------------
-------------- next part --------------
Index: lib/libc/gen/popen.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/gen/popen.c,v
retrieving revision 1.14
diff -u -r1.14 popen.c
--- lib/libc/gen/popen.c 27 Jan 2000 23:06:19 -0000 1.14
+++ lib/libc/gen/popen.c 16 Oct 2004 16:14:38 -0000
@@ -51,6 +51,17 @@
#include <string.h>
#include <paths.h>
+#ifndef _THREAD_SAFE
+#define THREAD_LOCK()
+#define THREAD_UNLOCK()
+#else
+#include <pthread.h>
+#include "libc_private.h"
+static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
+#define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
+#endif /* _THREAD_SAFE */
+
extern char **environ;
static struct pid {
@@ -95,8 +106,10 @@
argv[2] = (char *)command;
argv[3] = NULL;
+ THREAD_LOCK();
switch (pid = vfork()) {
case -1: /* Error. */
+ THREAD_UNLOCK();
(void)_close(pdes[0]);
(void)_close(pdes[1]);
free(cur);
@@ -134,6 +147,7 @@
_exit(127);
/* NOTREACHED */
}
+ THREAD_UNLOCK();
/* Parent; assume fdopen can't fail. */
if (*type == 'r') {
@@ -146,9 +160,11 @@
/* Link into list of file descriptors. */
cur->fp = iop;
- cur->pid = pid;
+ cur->pid = pid;
+ THREAD_LOCK();
cur->next = pidlist;
pidlist = cur;
+ THREAD_UNLOCK();
return (iop);
}
@@ -167,12 +183,23 @@
int pstat;
pid_t pid;
- /* Find the appropriate file pointer. */
+ /*
+ * Find the appropriate file pointer and remove it from the list.
+ */
+ THREAD_LOCK();
for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
if (cur->fp == iop)
break;
- if (cur == NULL)
+ if (cur == NULL) {
+ THREAD_UNLOCK();
return (-1);
+ }
+ /* Remove the entry from the linked list. */
+ if (last == NULL)
+ pidlist = cur->next;
+ else
+ last->next = cur->next;
+ THREAD_UNLOCK();
(void)fclose(iop);
@@ -180,11 +207,6 @@
pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
} while (pid == -1 && errno == EINTR);
- /* Remove the entry from the linked list. */
- if (last == NULL)
- pidlist = cur->next;
- else
- last->next = cur->next;
free(cur);
return (pid == -1 ? -1 : pstat);
More information about the freebsd-hackers
mailing list