summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2012-07-16 22:40:10 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2012-07-16 22:40:10 (GMT)
commitcea9be24359eda18e04e8b95f9674a376a016675 (patch)
treea346d4bb32d85b2705491892b86bb9615bdf7430
parent589aece81b85ef43d31d0172816dc721986d423f (diff)
downloadtcl-cea9be24359eda18e04e8b95f9674a376a016675.zip
tcl-cea9be24359eda18e04e8b95f9674a376a016675.tar.gz
tcl-cea9be24359eda18e04e8b95f9674a376a016675.tar.bz2
[Bug 3544683]: Backport of reentrancy fix for super-POSIX correctness of the
passwd/group access functions.
-rw-r--r--ChangeLog9
-rw-r--r--unix/tclUnixCompat.c270
2 files changed, 225 insertions, 54 deletions
diff --git a/ChangeLog b/ChangeLog
index aecbf9c..ae28d4f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-07-16 Donal K. Fellows <dkf@users.sf.net>
+
+ * unix/tclUnixCompat.c (TclpGetPwNam, TclpGetPwUid, TclpGetGrNam)
+ (TclpGetGrGid): [Bug 3544683]: Use the elaborate memory management
+ scheme outlined on http://www.opengroup.org/austin/docs/austin_328.txt
+ to handle Tcl's use of standard reentrant versions of the passwd/group
+ access functions so that everything can work on all BSDs. Problem
+ identified by Stuart Cassoff.
+
2012-07-11 Jan Nijtmans <nijtmans@users.sf.net>
* win/tclWinReg.c: [Bug #3362446]: registry keys command fails
diff --git a/unix/tclUnixCompat.c b/unix/tclUnixCompat.c
index f582c0c..8b067af 100644
--- a/unix/tclUnixCompat.c
+++ b/unix/tclUnixCompat.c
@@ -13,8 +13,10 @@
#include <errno.h>
#include <string.h>
-/* See also: SC_BLOCKING_STYLE in unix/tcl.m4
+/*
+ * See also: SC_BLOCKING_STYLE in unix/tcl.m4
*/
+
#ifdef USE_FIONBIO
# ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h> /* For FIONBIO. */
@@ -23,39 +25,6 @@
# include <sys/ioctl.h>
# endif
#endif /* USE_FIONBIO */
-
-/*
- *---------------------------------------------------------------------------
- *
- * TclUnixSetBlockingMode --
- *
- * Set the blocking mode of a file descriptor.
- *
- * Results:
- *
- * 0 on success, -1 (with errno set) on error.
- *
- *---------------------------------------------------------------------------
- */
-int
-TclUnixSetBlockingMode(
- int fd, /* File descriptor */
- int mode) /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
-{
-#ifndef USE_FIONBIO
- int flags = fcntl(fd, F_GETFL);
-
- if (mode == TCL_MODE_BLOCKING) {
- flags &= ~O_NONBLOCK;
- } else {
- flags |= O_NONBLOCK;
- }
- return fcntl(fd, F_SETFL, flags);
-#else /* USE_FIONBIO */
- int state = (mode == TCL_MODE_NONBLOCKING);
- return ioctl(fd, FIONBIO, &state);
-#endif /* !USE_FIONBIO */
-}
/*
* Used to pad structures at size'd boundaries
@@ -82,10 +51,22 @@ TclUnixSetBlockingMode(
typedef struct ThreadSpecificData {
struct passwd pwd;
+#if defined(HAVE_GETPWNAM_R_5) || defined(HAVE_GETPWUID_R_5)
+#define NEED_PW_CLEANER 1
+ char *pbuf;
+ int pbuflen;
+#else
char pbuf[2048];
+#endif
struct group grp;
+#if defined(HAVE_GETGRNAM_R_5) || defined(HAVE_GETGRGID_R_5)
+#define NEED_GR_CLEANER 1
+ char *gbuf;
+ int gbuflen;
+#else
char gbuf[2048];
+#endif
#if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)
struct hostent hent;
@@ -124,14 +105,57 @@ static int CopyGrp(struct group *tgtPtr, char *buf, int buflen);
static int CopyHostent(struct hostent *tgtPtr, char *buf,
int buflen);
static int CopyPwd(struct passwd *tgtPtr, char *buf, int buflen);
-static int CopyString(CONST char *src, char *buf, int buflen);
+static int CopyString(const char *src, char *buf, int buflen);
#endif
+
+#ifdef NEED_PW_CLEANER
+static void FreePwBuf(ClientData ignored);
+#endif
+#ifdef NEED_GR_CLEANER
+static void FreeGrBuf(ClientData ignored);
+#endif
#endif /* TCL_THREADS */
/*
*---------------------------------------------------------------------------
*
+ * TclUnixSetBlockingMode --
+ *
+ * Set the blocking mode of a file descriptor.
+ *
+ * Results:
+ *
+ * 0 on success, -1 (with errno set) on error.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclUnixSetBlockingMode(
+ int fd, /* File descriptor */
+ int mode) /* Either TCL_MODE_BLOCKING or
+ * TCL_MODE_NONBLOCKING. */
+{
+#ifndef USE_FIONBIO
+ int flags = fcntl(fd, F_GETFL);
+
+ if (mode == TCL_MODE_BLOCKING) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+ return fcntl(fd, F_SETFL, flags);
+#else /* USE_FIONBIO */
+ int state = (mode == TCL_MODE_NONBLOCKING);
+
+ return ioctl(fd, FIONBIO, &state);
+#endif /* !USE_FIONBIO */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TclpGetPwNam --
*
* Thread-safe wrappers for getpwnam(). See "man getpwnam" for more
@@ -158,8 +182,33 @@ TclpGetPwNam(
#if defined(HAVE_GETPWNAM_R_5)
struct passwd *pwPtr = NULL;
- return (getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
- &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->pbuf == NULL) {
+ tsdPtr->pbuflen = (int) sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (tsdPtr->pbuflen < 1) {
+ tsdPtr->pbuflen = 1024;
+ }
+ tsdPtr->pbuf = ckalloc(tsdPtr->pbuflen);
+ Tcl_CreateThreadExitHandler(FreePwBuf, NULL);
+ }
+ while (1) {
+ int e = getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, tsdPtr->pbuflen,
+ &pwPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->pbuflen *= 2;
+ tsdPtr->pbuf = ckrealloc(tsdPtr->pbuf, tsdPtr->pbuflen);
+ }
+ return (pwPtr != NULL ? &tsdPtr->pwd : NULL);
#elif defined(HAVE_GETPWNAM_R_4)
return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
@@ -214,8 +263,33 @@ TclpGetPwUid(
#if defined(HAVE_GETPWUID_R_5)
struct passwd *pwPtr = NULL;
- return (getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
- &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->pbuf == NULL) {
+ tsdPtr->pbuflen = (int) sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (tsdPtr->pbuflen < 1) {
+ tsdPtr->pbuflen = 1024;
+ }
+ tsdPtr->pbuf = ckalloc(tsdPtr->pbuflen);
+ Tcl_CreateThreadExitHandler(FreePwBuf, NULL);
+ }
+ while (1) {
+ int e = getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, tsdPtr->pbuflen,
+ &pwPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->pbuflen *= 2;
+ tsdPtr->pbuf = ckrealloc(tsdPtr->pbuf, tsdPtr->pbuflen);
+ }
+ return (pwPtr != NULL ? &tsdPtr->pwd : NULL);
#elif defined(HAVE_GETPWUID_R_4)
return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
@@ -244,6 +318,29 @@ TclpGetPwUid(
/*
*---------------------------------------------------------------------------
*
+ * FreePwBuf --
+ *
+ * Helper that is used to dispose of space allocated and referenced from
+ * the ThreadSpecificData for user entries. (Darn that baroque POSIX
+ * reentrant interface.)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_PW_CLEANER
+static void
+FreePwBuf(
+ ClientData ignored)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ ckfree(tsdPtr->pbuf);
+}
+#endif /* NEED_PW_CLEANER */
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TclpGetGrNam --
*
* Thread-safe wrappers for getgrnam(). See "man getgrnam" for more
@@ -267,11 +364,36 @@ TclpGetGrNam(
#else
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-#if defined(HAVE_GETGRNAM_R_5)
+#ifdef HAVE_GETGRNAM_R_5
struct group *grPtr = NULL;
- return (getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
- &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->gbuf == NULL) {
+ tsdPtr->gbuflen = (int) sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (tsdPtr->gbuflen < 1) {
+ tsdPtr->gbuflen = 1024;
+ }
+ tsdPtr->gbuf = ckalloc(tsdPtr->gbuflen);
+ Tcl_CreateThreadExitHandler(FreeGrBuf, NULL);
+ }
+ while (1) {
+ int e = getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, tsdPtr->gbuflen,
+ &grPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->gbuflen *= 2;
+ tsdPtr->gbuf = ckrealloc(tsdPtr->gbuf, tsdPtr->gbuflen);
+ }
+ return (grPtr != NULL ? &tsdPtr->grp : NULL);
#elif defined(HAVE_GETGRNAM_R_4)
return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
@@ -326,8 +448,33 @@ TclpGetGrGid(
#if defined(HAVE_GETGRGID_R_5)
struct group *grPtr = NULL;
- return (getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
- &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->gbuf == NULL) {
+ tsdPtr->gbuflen = (int) sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (tsdPtr->gbuflen < 1) {
+ tsdPtr->gbuflen = 1024;
+ }
+ tsdPtr->gbuf = ckalloc(tsdPtr->gbuflen);
+ Tcl_CreateThreadExitHandler(FreeGrBuf, NULL);
+ }
+ while (1) {
+ int e = getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, tsdPtr->gbuflen,
+ &grPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->gbuflen *= 2;
+ tsdPtr->gbuf = ckrealloc(tsdPtr->gbuf, tsdPtr->gbuflen);
+ }
+ return (grPtr != NULL ? &tsdPtr->grp : NULL);
#elif defined(HAVE_GETGRGID_R_4)
return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
@@ -356,6 +503,29 @@ TclpGetGrGid(
/*
*---------------------------------------------------------------------------
*
+ * FreeGrBuf --
+ *
+ * Helper that is used to dispose of space allocated and referenced from
+ * the ThreadSpecificData for group entries. (Darn that baroque POSIX
+ * reentrant interface.)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_GR_CLEANER
+static void
+FreeGrBuf(
+ ClientData ignored)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ ckfree(tsdPtr->gbuf);
+}
+#endif /* NEED_GR_CLEANER */
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TclpGetHostByName --
*
* Thread-safe wrappers for gethostbyname(). See "man gethostbyname" for
@@ -769,7 +939,7 @@ CopyArray(
#ifdef NEED_COPYSTRING
static int
CopyString(
- CONST char *src, /* String to copy. */
+ const char *src, /* String to copy. */
char *buf, /* Buffer to copy into. */
int buflen) /* Size of buffer. */
{
@@ -788,14 +958,6 @@ CopyString(
#endif /* NEED_COPYSTRING */
/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */
-
-/*
*------------------------------------------------------------------------
*
* TclWinCPUID --
@@ -831,7 +993,7 @@ TclWinCPUID(
#endif
return status;
}
-
+
/*
* Local Variables:
* mode: c