[100073] trunk/base/src/darwintracelib1.0/darwintrace.c

cal at macports.org cal at macports.org
Mon Nov 26 12:24:39 PST 2012


Revision: 100073
          https://trac.macports.org/changeset/100073
Author:   cal at macports.org
Date:     2012-11-26 12:24:39 -0800 (Mon, 26 Nov 2012)
Log Message:
-----------
general (and incomplete) overhaul of the darwintrace code

 - add possibility to log to file by setting DARWINTRACE_DEBUG_LOG
 - remove code to wait for socket. This should be unneeded, since recv()
   and send() will block
 - avoid two stat(2) calls for directories by removing is_directory()
   and integrating the test into the methods that called it
 - remove hack re-trying exchange_with_port() when send() returns with
   errno = ENOTSOCK. This currently breaks trace mode and I haven't
   found out why this was necessary in the first place. Might be
   something related to libdispatch.

   The scenario usually is this: the socket is being opened, assigned to
   a FD (I can observe this using debug output). Later in the runtime of
   the process, the write (to the same FD!) fails with ENOTSOCK. o.O

   If I don't find a better solution than this, we might just re-add the
   hack.
 - simplify and comment code dealing with the filemap
 - add getdirent(2) and __getdirent64(2) overrides to prevent readdir(3)
   from returning paths outside the sandbox. A common case where not
   having this breaks stuff in trace mode is autoconf reading
   $prefix/share/aclocal.

Modified Paths:
--------------
    trunk/base/src/darwintracelib1.0/darwintrace.c

Modified: trunk/base/src/darwintracelib1.0/darwintrace.c
===================================================================
--- trunk/base/src/darwintracelib1.0/darwintrace.c	2012-11-26 20:07:50 UTC (rev 100072)
+++ trunk/base/src/darwintracelib1.0/darwintrace.c	2012-11-26 20:24:39 UTC (rev 100073)
@@ -59,19 +59,21 @@
 #include <sys/paths.h>
 #endif
 
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/param.h>
 #include <sys/syscall.h>
-#include <errno.h>
-#include <sys/socket.h>
+#include <sys/types.h>
 #include <sys/un.h>
+#include <unistd.h>
 
 #ifndef HAVE_STRLCPY
 /* Define strlcpy if it's not available. */
@@ -140,10 +142,12 @@
 inline char* const* __darwintrace_restore_env(char* const envp[]);
 inline void __darwintrace_setup();
 inline void __darwintrace_cleanup_path(char *path);
-static char * exchange_with_port(const char * buf, size_t len, int answer, char failures);
+static char * exchange_with_port(const char * buf, size_t len, int answer);
 
 #define START_FD 81
 static int __darwintrace_fd = -1;
+static FILE *__darwintrace_debug = NULL;
+static pid_t __darwintrace_pid = (pid_t) -1;
 #define BUFFER_SIZE	1024
 
 /**
@@ -160,15 +164,15 @@
 static char* __env_dyld_insert_libraries;
 static char* __env_dyld_force_flat_namespace;
 static char* __env_darwintrace_log;
+static char* __env_darwintrace_debug_log;
 
+#if DARWINTRACE_DEBUG_OUTPUT
 #if __STDC_VERSION__>=199901L
-#if DARWINTRACE_DEBUG_OUTPUT
-#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
+#define debug_printf(format, ...) fprintf(stderr, "darwintrace[%d]: " format, getpid(), __VA_ARGS__); \
+	if (__darwintrace_debug) { \
+		fprintf(__darwintrace_debug, "darwintrace: " format, __VA_ARGS__); \
+	}
 #else
-#define debug_printf(...)
-#endif
-#else
-#if DARWINTRACE_DEBUG_OUTPUT
 __attribute__ ((format (printf, 1, 2)))
 static inline
 int debug_printf(const char *format, ...) {
@@ -179,40 +183,12 @@
     va_end(args);
     return ret;
 }
+#endif
 #else
-#define debug_printf(format, param)
+#define debug_printf(...)
 #endif
-#endif
 
 /*
- * char wait_for_socket(int sock, char w)
- * Function used for read/write operation to socket...
- * Args:
- *  sock - socket 
- *  w - what should socket do in next operation. 1 for write, 0 for read
- * Return value: 
- *  1 - everything is ok, we can read/write to/from it
- *  0 - something's went wrong
- */
-static int wait_for_socket(int sock, char w)
-{
-	struct timeval tv;
-	fd_set fds;
-	
-	if(sock==-1)
-		return 0;
-	
-	tv.tv_sec=10;
-	tv.tv_usec=0;
-	FD_ZERO(&fds);
-	FD_SET(sock, &fds);
-	if(select(sock+1, (w==0 ? &fds : 0), (w==1 ? &fds : 0), 0, &tv)<1)
-		return 0;
-	return FD_ISSET(sock, &fds)!=0;
-}
-
-
-/*
  * return 0 if str doesn't begin with prefix, 1 otherwise.
  */
 inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
@@ -248,6 +224,12 @@
 	} else {
 		__env_darwintrace_log = NULL;
 	}
+	theValue = getenv("DARWINTRACE_DEBUG_LOG");
+	if (theValue != NULL) {
+		__env_darwintrace_debug_log = strdup(theValue);
+	} else {
+		__env_darwintrace_debug_log = NULL;
+	}
 }
 
 /*
@@ -291,6 +273,10 @@
 		__darwintrace_alloc_env(
 			"DARWINTRACE_LOG",
 			__env_darwintrace_log);
+	char* darwintrace_debug_log_ptr =	
+		__darwintrace_alloc_env(
+			"DARWINTRACE_DEBUG_LOG",
+			__env_darwintrace_debug_log);
 
 	char* const * theEnvIter = envp;
 	int theEnvLength = 0;
@@ -318,6 +304,9 @@
 		} else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_LOG=")) {
 			theValue = darwintrace_log_ptr;
 			darwintrace_log_ptr = NULL;
+		} else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_DEBUG_LOG=")) {
+			theValue = darwintrace_debug_log_ptr;
+			darwintrace_debug_log_ptr = NULL;
 		}
 		
 		if (theValue) {
@@ -336,6 +325,9 @@
 	if (darwintrace_log_ptr) {
 		*theCopyIter++ = darwintrace_log_ptr;
 	}
+	if (darwintrace_debug_log_ptr) {
+		*theCopyIter++ = darwintrace_debug_log_ptr;
+	}
 
 	*theCopyIter = 0;
 	
@@ -344,7 +336,7 @@
 
 static void ask_for_filemap()
 {
-	filemap=exchange_with_port("filemap\t", sizeof("filemap\t"), 1, 0);
+	filemap=exchange_with_port("filemap\t", sizeof("filemap\t"), 1);
 	if(filemap==(char*)-1)
 		filemap=0;
 }
@@ -353,24 +345,51 @@
 inline void __darwintrace_setup() {
 #define open(x,y,z) syscall(SYS_open, (x), (y), (z))
 #define close(x) syscall(SYS_close, (x))
-	if (__darwintrace_fd == -1) {
+	pid_t oldpid = __darwintrace_pid;
+	if (__darwintrace_pid != (pid_t) -1 && __darwintrace_pid != getpid()) {
+		if (__darwintrace_fd != -1) {
+			close(__darwintrace_fd);
+			__darwintrace_fd = -1;
+		}
+		if (__darwintrace_debug) {
+			fclose(__darwintrace_debug);
+			__darwintrace_debug = NULL;
+		}
+		__darwintrace_pid = (pid_t) -1;
+	}
+	if (__darwintrace_pid == (pid_t) -1) {
+		__darwintrace_pid = getpid();
 		if (__env_darwintrace_log != NULL) {
 			int olderrno = errno;
-			int sock=socket(AF_UNIX, SOCK_STREAM, 0);
+			int sock = socket(AF_UNIX, SOCK_STREAM, 0);
 			struct sockaddr_un sun;
-			sun.sun_family=AF_UNIX;
+			sun.sun_family = AF_UNIX;
 			strncpy(sun.sun_path, __env_darwintrace_log, sizeof(sun.sun_path));
-			if(connect(sock, (struct sockaddr*)&sun, strlen(__env_darwintrace_log)+1+sizeof(sun.sun_family))!=-1)
-			{
-				debug_printf("darwintrace: connect successful. socket %d\n", sock);
-				__darwintrace_fd=sock;
-				ask_for_filemap();
+			if (connect(sock, (struct sockaddr*)&sun, strlen(__env_darwintrace_log) + 1 + sizeof(sun.sun_family)) != -1) {
+				debug_printf("connect successful, socket %d in pid %d\n", sock, __darwintrace_pid);
+				__darwintrace_fd = sock;
+				ask_for_filemap() ;
 			} else {
 				debug_printf("connect failed: %s\n", strerror(errno));
 				abort();
 			}
 			errno = olderrno;
 		}
+		if (__darwintrace_debug == NULL) {
+			if (__env_darwintrace_debug_log != NULL) {
+				char logpath[MAXPATHLEN];
+				snprintf(logpath, MAXPATHLEN, __env_darwintrace_debug_log, getpid());
+				if (NULL == (__darwintrace_debug = fopen(logpath, "w"))) {
+					fprintf(stderr, "failed to open logfile: %s\n", strerror(errno));
+					abort();
+				}
+				fprintf(__darwintrace_debug, "pid %d is process %s\n", getpid(), getenv("_"));
+				debug_printf("logging socket communication to: %s\n", logpath);
+			}
+		}
+		if (oldpid != (pid_t) -1) {
+			debug_printf("seems to have forked from %d, re-opening files\n", __darwintrace_pid);
+		}
 	}
 #undef close
 #undef open
@@ -386,7 +405,7 @@
 	int size;
 	char somepath[MAXPATHLEN];
 	char logbuffer[BUFFER_SIZE];
-	
+
 	do {
 #ifdef __APPLE__ /* Only Darwin has volfs and F_GETPATH */
 		if ((fd > 0) && (DARWINTRACE_LOG_FULL_PATH
@@ -416,9 +435,9 @@
 
 	size = snprintf(logbuffer, sizeof(logbuffer),
 		"%s\t%s",
-		op, somepath );
+		op, somepath);
 
-	exchange_with_port(logbuffer, size+1, 0, 0);
+	exchange_with_port(logbuffer, size+1, 0);
 	
 	return;
 }
@@ -427,82 +446,63 @@
  * do a partial realpath(3) to fix "foo//bar" to "foo/bar"
  */
 inline void __darwintrace_cleanup_path(char *path) {
-  size_t pathlen;
+	size_t pathlen;
 #ifdef __APPLE__
-  size_t rsrclen;
+	size_t rsrclen;
 #endif
-  size_t i, shiftamount;
-  enum { SAWSLASH, NOTHING } state = NOTHING;
+	size_t i, shiftamount;
+	enum { SAWSLASH, NOTHING } state = NOTHING;
 
-  /* if this is a foo/..namedfork/rsrc, strip it off */
-  pathlen = strlen(path);
-  /* ..namedfork/rsrc is only on OS X */
-#ifdef __APPLE__ 
-  rsrclen = strlen(_PATH_RSRCFORKSPEC);
-  if(pathlen > rsrclen
-     && 0 == strcmp(path + pathlen - rsrclen,
-		    _PATH_RSRCFORKSPEC)) {
-    path[pathlen - rsrclen] = '\0';
-    pathlen -= rsrclen;
-  }
+	/* if this is a foo/..namedfork/rsrc, strip it off */
+	pathlen = strlen(path);
+	/* ..namedfork/rsrc is only on OS X */
+#ifdef __APPLE__
+	rsrclen = strlen(_PATH_RSRCFORKSPEC);
+	if (pathlen > rsrclen && 0 == strcmp(path + pathlen - rsrclen, _PATH_RSRCFORKSPEC)) {
+		path[pathlen - rsrclen] = '\0';
+		pathlen -= rsrclen;
+	}
 #endif
 
-  /* for each position in string (including
-     terminal \0), check if we're in a run of
-     multiple slashes, and only emit the
-     first one
-  */
-  for(i=0, shiftamount=0; i <= pathlen; i++) {
-    if(state == SAWSLASH) {
-      if(path[i] == '/') {
-	/* consume it */
-	shiftamount++;
-      } else {
-	state = NOTHING;
-	path[i - shiftamount] = path[i];
-      }
-    } else {
-      if(path[i] == '/') {
-	state = SAWSLASH;
-      }
-      path[i - shiftamount] = path[i];
-    }
-  }
-
-  debug_printf("darwintrace: cleanup resulted in %s\n", path);
+	/* for each position in string (including terminal \0), check if we're in
+	 * a run of multiple slashes, and only emit the first one */
+	for(i = 0, shiftamount = 0; i <= pathlen; i++) {
+		if (state == SAWSLASH) {
+			if (path[i] == '/') {
+				/* consume it */
+				shiftamount++;
+				continue;
+			} else {
+				state = NOTHING;
+			}
+		} else {
+			if (path[i] == '/') {
+				state = SAWSLASH;
+			}
+		}
+		path[i - shiftamount] = path[i];
+	}
 }
 
 /*
- * return 1 if path is directory or not exists
- * return 0 otherwise
- */
-static int is_directory(const char * path)
-{
-#define stat(path, sb) syscall(SYS_stat, path, sb)
-	struct stat s;
-	if(stat(path, &s)==-1)
-		/* Actually is not directory, but anyway, we shouldn't test a dependency unless file exists */
-		return 1;
-	
-	return S_ISDIR(s.st_mode);
-#undef stat
-}
-
-
-/*
  * return 1 if path allowed, 0 otherwise
  */
-static int ask_for_dependency(char * path)
-{
+static int ask_for_dependency(char * path) {
+#define stat(y, z) syscall(SYS_stat, (y), (z))
 	char buffer[BUFFER_SIZE], *p;
-	int result=0;
-	
-	if(is_directory(path))
+	int result = 0;
+	struct stat st;
+
+	if (-1 == stat(path, &st)) {
 		return 1;
+	}
+	if (S_ISDIR(st.st_mode)) {
+		return 1;
+	}
 	
 	strncpy(buffer, "dep_check\t", sizeof(buffer));
 	strncpy(buffer+10, path, sizeof(buffer)-10);
-	p=exchange_with_port(buffer, strlen(buffer)+1, 1, 0);
+	p=exchange_with_port(buffer, strlen(buffer)+1, 1);
 	if(p==(char*)-1||!p)
 		return 0;
 	
@@ -511,6 +511,7 @@
 	
 	free(p);
 	return result;
+#undef stat
 }
 
 /*
@@ -525,155 +526,180 @@
  *    0      -- data successfully sent
  *    string -- answer (caller shoud free it)
  */
-static char * exchange_with_port(const char * buf, size_t len, int answer, char failures)
-{
-	wait_for_socket(__darwintrace_fd, 1);
-	if(send(__darwintrace_fd, buf, len, 0)==-1)
-	{
-		if(errno==ENOTSOCK && failures<3)
-		{
-			__darwintrace_fd=-1;
-			__darwintrace_setup();
-			return exchange_with_port(buf, len, answer, failures+1);
+static char * exchange_with_port(const char * buf, size_t len, int answer) {
+	size_t sent = 0;
+
+	if (__darwintrace_debug) {
+		fprintf(__darwintrace_debug, "> %s\n", buf);
+	}
+	while (sent < len) {
+		ssize_t local_sent = send(__darwintrace_fd, buf + sent, len - sent, 0);
+		if (local_sent == -1) {
+			debug_printf("error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
+			if (__darwintrace_debug)
+				fprintf(__darwintrace_debug, "darwintrace: error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
+			abort();
 		}
-		return (char*)-1;
+		sent += local_sent;
 	}
-	if(!answer)
+	if (!answer) {
 		return 0;
-	{
-		size_t l=0;
-		char * b;
+	} else {
+		size_t recv_len = 0, received;
+		char *recv_buf;
 		
-		wait_for_socket(__darwintrace_fd, 0);
-		recv(__darwintrace_fd, &l, sizeof(l),0);
-		if(!l)
+		ssize_t t;
+		if (-1 == (t = recv(__darwintrace_fd, &recv_len, sizeof(recv_len), 0))) {
+			debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
+			if (__darwintrace_debug)
+				fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
+			abort();
+		}
+		if (t != sizeof(recv_len)) {
+			debug_printf("!!! received %zd, but should have been %zd\n", t, sizeof(recv_len));
+		}
+		if (recv_len == 0) {
 			return 0;
-		b=(char*)malloc(l+1);
-		b[l]=0;
-		recv(__darwintrace_fd, b, l, 0);
-		return b;
+		}
+		recv_buf = malloc(recv_len + 1);
+		recv_buf[recv_len] = '\0';
+
+		received = 0;
+		while (received < recv_len) {
+			ssize_t local_received = recv(__darwintrace_fd, recv_buf + received, recv_len - received, 0);
+			if (local_received == -1) {
+				debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
+				if (__darwintrace_debug)
+					fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
+				abort();
+			}
+			received += local_received;
+		}
+		if (__darwintrace_debug) {
+			fprintf(__darwintrace_debug, "< %s\n", recv_buf);
+		}
+		return recv_buf;
 	}
 }
 
+#define DARWINTRACE_STATUS_PATH    ((char) 0)
+#define DARWINTRACE_STATUS_COMMAND ((char) 1)
+#define DARWINTRACE_STATUS_DONE    ((char) 2)
+
 /*
  * return 1 if path (once normalized) is in sandbox or redirected, 0 otherwise.
  */
 __attribute__((always_inline))
 inline int __darwintrace_is_in_sandbox(const char* path, char * newpath) {
-	char * t, * p, * _;
-	int result=-1;
+	char *t, *p, *_;
+	char lpath[MAXPATHLEN];
 	
 	__darwintrace_setup();
 	
-	if(!filemap)
+	if (!filemap)
 		return 1;
 	
-	if(*path=='/')
-		p=strdup(path);
-	else
-	{
-		p=(char*)malloc(MAXPATHLEN);
-		if (getcwd(p, MAXPATHLEN-1) == NULL) {
+	if (*path=='/') {
+		p = strcpy(lpath, path);
+	} else {
+		if (getcwd(lpath, MAXPATHLEN - 1) == NULL) {
 			fprintf(stderr, "darwintrace: getcwd: %s, path was: %s\n", strerror(errno), path);
 			abort();
 		}
-		if (p[strlen(p)-1] != '/')
-			strcat(p, "/");
-		strcat(p, path);
+		strcat(lpath, "/");
+		strcat(lpath, path);
 	}
-	__darwintrace_cleanup_path(p);
-			
-	do
-	{
-		for(t=filemap; *t;)
-		{
-			if(__darwintrace_strbeginswith(p, t))
-			{
-				t+=strlen(t)+1;
-				switch(*t)
-				{
+
+	p = lpath;
+
+	for (t = filemap; *t;) {
+		char state;
+		
+		if (__darwintrace_strbeginswith(p, t)) {
+			/* move t to the integer describing how to handle this match */
+			t += strlen(t) + 1;
+			switch (*t) {
 				case 0:
-					result=1;
-					break;
+					return 1;
 				case 1:
-					if(!newpath)
-					{
-						result=0;
-						break;
+					if (!newpath) {
+						return 0;
 					}
-					strcpy(newpath, t+1);
-					_=newpath+strlen(newpath);
-					if(_[-1]!='/')
-						*_++='/';
+					/* the redirected path starts right after the byte telling
+					 * us we should redirect */
+					strcpy(newpath, t + 1);
+					_ = newpath + strlen(newpath);
+					/* append '/' if it's missing */
+					if (_[-1] != '/') {
+						*_ = '/';
+					}
 					strcpy(_, p);
-					result=1;
-					break;
+					return 1;
 				case 2:
-					result=ask_for_dependency(p);
+					/* ask the socket whether this file is OK */
+					return ask_for_dependency(p);
+				default:
+					fprintf(stderr, "darwintrace: error: unexpected byte in file map: `%x'\n", *t);
+					abort();
+			}
+		}
+
+		/* advance the cursor: if the number after the string is not 1, there's
+		 * no path behind it and we can advance by strlen(t) + 3. If it is 1,
+		 * make sure to skip the path, too.
+		 */
+		state = DARWINTRACE_STATUS_PATH;
+		while (state != DARWINTRACE_STATUS_DONE) {
+			switch (state) {
+				case DARWINTRACE_STATUS_PATH:
+					if (!*t) {
+						state = DARWINTRACE_STATUS_COMMAND;
+					}
 					break;
-				}
+				case DARWINTRACE_STATUS_COMMAND:
+					if (*t == 1) {
+						state = DARWINTRACE_STATUS_PATH;
+						t++;
+					} else {
+						state = DARWINTRACE_STATUS_DONE;
+					}
+					break;
 			}
-			if(result!=-1)
-				break;
-			t+=strlen(t)+1;
-			if(*t==1)
-				t+=strlen(t)+1;
-			else
-				t+=2;
+			t++;
 		}
-		if(result!=-1)
-			break;
-		__darwintrace_log_op("sandbox_violation", path, 0);
-		result=0;
+		t++;
 	}
-	while(0);
-	free(p);
-	return result;
+
+	__darwintrace_log_op("sandbox_violation", path, 0);
+	return 0;
 }
 
-/* Log calls to open(2) into the file specified by DARWINTRACE_LOG.
-   Only logs if the DARWINTRACE_LOG environment variable is set.
-   Only logs files (or rather, do not logs directories)
-   Only logs files where the open succeeds.
-   Only logs files opened for read access, without the O_CREAT flag set
-   	(unless DARWINTRACE_LOG_CREATE is set).
-   The assumption is that any file that can be created isn't necessary
-   to build the project.
-*/
-
+/* wrapper for open(2) preventing opening files outside the sandbox */
 int open(const char* path, int flags, ...) {
 #define open(x,y,z) syscall(SYS_open, (x), (y), (z))
 	mode_t mode;
-	int result;
 	va_list args;
-	struct stat sb;
 	char newpath[MAXPATHLEN];
-	int isInSandbox;	
 
+	debug_printf("open(%s)\n", path);
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		debug_printf("open %s was forbidden\n", path);
+		errno = EACCES;
+		return -1;
+	}
+
+	if (*newpath) {
+		path = newpath;
+	}
+
 	/* Why mode here ? */
 	va_start(args, flags);
 	mode = va_arg(args, int);
 	va_end(args);
-	
-	result = 0;
-	
-	if((stat(path, &sb)!=-1 && !(sb.st_mode&S_IFDIR)) || flags & O_CREAT )
-	{
-		*newpath=0;
-		__darwintrace_setup();
-		isInSandbox = __darwintrace_is_in_sandbox(path, newpath);
-		if (isInSandbox == 0) {
-			debug_printf("darwintrace: creation/writing was forbidden at %s\n", path);
-			errno = EACCES;
-			result = -1;
-		}
-		if(*newpath)
-			path=newpath;
-	}
-	if (result == 0) {
-		result = open(path, flags, mode);
-	}
-	return result;
+
+	return open(path, flags, mode);
 #undef open
 }
 
@@ -682,25 +708,26 @@
    Only logs files where the readlink succeeds.
 */
 #ifdef READLINK_IS_NOT_P1003_1A
-int  readlink(const char * path, char * buf, int bufsiz) {
+int readlink(const char * path, char * buf, int bufsiz) {
 #else
-ssize_t  readlink(const char * path, char * buf, size_t bufsiz) {
+ssize_t readlink(const char * path, char * buf, size_t bufsiz) {
 #endif
 #define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z))
-	ssize_t result;
-	int isInSandbox;
+	char newpath[MAXPATHLEN];
 
-	result = readlink(path, buf, bufsiz);
-	if (result >= 0) {
-		__darwintrace_setup();
-		isInSandbox = __darwintrace_is_in_sandbox(path, 0);
-		if (!isInSandbox)
-		{
-			errno=EACCES;
-			result=-1;
-		}
+	debug_printf("readlink(%s)\n", path);
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		errno = EACCES;
+		return -1;
 	}
-	return result;
+
+	if (*newpath) {
+		path = newpath;
+	}
+
+	return readlink(path, buf, bufsiz);
 #undef readlink
 }
 
@@ -709,314 +736,417 @@
 #define open(x,y,z) syscall(SYS_open, (x), (y), (z))
 #define close(x) syscall(SYS_close, (x))
 #define lstat(x, y) syscall(SYS_lstat, (x), (y))
-	int result;
+	debug_printf("execve(%s)\n", path);
 	__darwintrace_setup();
 	if (__darwintrace_fd >= 0) {
-	  struct stat sb;
-	  /* for symlinks, we want to capture
-	   * both the original path and the modified one,
-	   * since for /usr/bin/gcc -> gcc-4.0,
-	   * both "gcc_select" and "gcc" are contributors
-	   */
-	  if (lstat(path, &sb) == 0) {
-	  	int fd;
+		struct stat sb;
+		/* for symlinks, we want to capture both the original path and the
+		 * modified one, since for /usr/bin/gcc -> gcc-4.0, both "gcc_select"
+		 * and "gcc" are contributors
+		 */
+		if (lstat(path, &sb) == 0) {
+			int fd;
+			if (S_ISLNK(sb.st_mode)) {
+				/* for symlinks, print both */
+				__darwintrace_log_op("execve", path, 0);
+			}
 
-	    if(S_ISLNK(sb.st_mode)) {
-	      /* for symlinks, print both */
-		  __darwintrace_log_op("execve", path, 0);
-	    }
-		
-		fd = open(path, O_RDONLY, 0);
-		if (fd > 0) {
-		  char buffer[MAXPATHLEN+1], newpath[MAXPATHLEN+1];
-		  ssize_t bytes_read;
-		
-		  *newpath=0;
-		  if(__darwintrace_is_in_sandbox(path, newpath)==0)
-		  {
-			close(fd);
-			errno=ENOENT;
-		    return -1;
-		  }
-		  if(*newpath)
-		    path=newpath;
+			fd = open(path, O_RDONLY, 0);
+			if (fd > 0) {
+				char buffer[MAXPATHLEN+1];
+				ssize_t bytes_read;
+
+				if(!__darwintrace_is_in_sandbox(path, NULL)) {
+					close(fd);
+					errno = ENOENT;
+					return -1;
+				}
 	
-		  /* once we have an open fd, if a full path was requested, do it */
-		  __darwintrace_log_op("execve", path, fd);
-
-		  /* read the file for the interpreter */
-		  bytes_read = read(fd, buffer, MAXPATHLEN);
-		  buffer[bytes_read] = 0;
-		  if (bytes_read > 2 &&
-			buffer[0] == '#' && buffer[1] == '!') {
-			const char* interp = &buffer[2];
-			int i;
-			/* skip past leading whitespace */
-			for (i = 2; i < bytes_read; ++i) {
-			  if (buffer[i] != ' ' && buffer[i] != '\t') {
-				interp = &buffer[i];
-				break;
-			  }
+				/* once we have an open fd, if a full path was requested, do it */
+				__darwintrace_log_op("execve", path, fd);
+	
+				/* read the file for the interpreter */
+				bytes_read = read(fd, buffer, MAXPATHLEN);
+				buffer[bytes_read] = 0;
+				if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
+					const char* interp = &buffer[2];
+					int i;
+					/* skip past leading whitespace */
+					for (i = 2; i < bytes_read; ++i) {
+						if (buffer[i] != ' ' && buffer[i] != '\t') {
+							interp = &buffer[i];
+							break;
+						}
+					}
+					/* found interpreter (or ran out of data); skip until next
+					 * whitespace, then terminate the string */
+					for (; i < bytes_read; ++i) {
+						if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') {
+							buffer[i] = 0;
+							break;
+						}
+					}
+					/* we have liftoff */
+					if (interp && interp[0] != '\0') {
+						__darwintrace_log_op("execve", interp, 0);
+					}
+				}
+				close(fd);
 			}
-			/* found interpreter (or ran out of data)
-			   skip until next whitespace, then terminate the string */
-			for (; i < bytes_read; ++i) {
-			  if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') {
-				buffer[i] = 0;
-				break;
-			  }
-			}
-			/* we have liftoff */
-			if (interp && interp[0] != '\0') {
-			  __darwintrace_log_op("execve", interp, 0);
-			}
-		  }
-		  close(fd);
 		}
-	  }
-	close(__darwintrace_fd);
-	__darwintrace_fd=-1;
 	}
-	
+	/* our variables won't survive exec, clean up */
+	if (__darwintrace_fd != -1) {
+		close(__darwintrace_fd);
+		__darwintrace_fd = -1;
+	}
+	if (__darwintrace_debug) {
+		fclose(__darwintrace_debug);
+		__darwintrace_debug = NULL;
+	}
+	__darwintrace_pid = (pid_t) -1;
+
 	/* call the original execve function, but fix the environment if required. */
-	result = __execve(path, argv, __darwintrace_restore_env(envp));
-	return result;
+	return __execve(path, argv, __darwintrace_restore_env(envp));
 #undef lstat
 #undef close
 #undef open
 #undef execve
 }
 
-/* if darwintrace has been initialized, trap
-   attempts to close our file descriptor
-*/
+/* if darwintrace has been initialized, trap attempts to close our file
+ * descriptor */
 int close(int fd) {
 #define close(x) syscall(SYS_close, (x))
+	if (__darwintrace_fd != -2 && fd == __darwintrace_fd) {
+		errno = EBADF;
+		return -1;
+	}
 
-  if(__darwintrace_fd != -2 && fd == __darwintrace_fd) {
-    errno = EBADF;
-    return -1;
-  }
-
-  return close(fd);
+	return close(fd);
 #undef close
 }
 
-/* Trap attempts to unlink a file outside the sandbox.
- */
+/* Trap attempts to unlink a file outside the sandbox. */
 int unlink(const char* path) {
 #define __unlink(x) syscall(SYS_unlink, (x))
-	int result = 0;
-	int isInSandbox = __darwintrace_is_in_sandbox(path, 0);
-	if (isInSandbox == 1) {
-		debug_printf("darwintrace: unlink was allowed at %s\n", path);
-	} else if (isInSandbox == 0) {
-		/* outside sandbox, but sandbox is defined: forbid */
-		debug_printf("darwintrace: unlink was forbidden at %s\n", path);
+	char newpath[MAXPATHLEN];
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		debug_printf("unlink %s was forbidden\n", path);
 		errno = EACCES;
-		result = -1;
+		return -1;
 	}
-	
-	if (result == 0) {
-		result = __unlink(path);
+
+	if (*newpath) {
+		path = newpath;
 	}
-	
-	return result;
+
+	debug_printf("unlink %s was allowed\n", path);
+
+	return __unlink(path);
 }
 
 /* Trap attempts to create directories outside the sandbox.
  */
 int mkdir(const char* path, mode_t mode) {
 #define __mkdir(x,y) syscall(SYS_mkdir, (x), (y))
-	int result = 0;
-	int isInSandbox = __darwintrace_is_in_sandbox(path, 0);
-	if (isInSandbox == 1) {
-		debug_printf("darwintrace: mkdir was allowed at %s\n", path);
-	} else if (isInSandbox == 0) {
-		/* outside sandbox, but sandbox is defined: forbid */
-		/* only consider directories that do not exist. */
-		struct stat theInfo;
-		int err;
-		err = lstat(path, &theInfo);
-		if ((err == -1) && (errno == ENOENT))
-		{
-			debug_printf("darwintrace: mkdir was forbidden at %s\n", path);
-			errno = EACCES;
-			result = -1;
-		} /* otherwise, mkdir will do nothing (directory exists) or fail
-		     (another error) */
+	char newpath[MAXPATHLEN];
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		struct stat st;
+		if (-1 == lstat(path, &st)) {
+			if (errno == ENOENT) {
+				/* directory doesn't exist yet */
+				debug_printf("mkdir was forbidden at %s\n", path);
+				errno = EACCES;
+				return -1;
+			}
+		}
+		/* otherwise, mkdir will do nothing or fail with a hopefully meaningful
+		 * error */
+	} else {
+		if (*newpath) {
+			path = newpath;
+		}
+
+		debug_printf("mkdir was allowed at %s\n", path);
 	}
-	
-	if (result == 0) {
-		result = __mkdir(path, mode);
-	}
-	
-	return result;
+
+	return __mkdir(path, mode);
 }
 
 /* Trap attempts to remove directories outside the sandbox.
  */
 int rmdir(const char* path) {
 #define __rmdir(x) syscall(SYS_rmdir, (x))
-	int result = 0;
-	int isInSandbox = __darwintrace_is_in_sandbox(path, 0);
-	if (isInSandbox == 1) {
-		debug_printf("darwintrace: rmdir was allowed at %s\n", path);
-	} else if (isInSandbox == 0) {
-		/* outside sandbox, but sandbox is defined: forbid */
-		debug_printf("darwintrace: removing directory %s was forbidden\n", path);
+	if (!__darwintrace_is_in_sandbox(path, NULL)) {
+		debug_printf("removing directory %s was forbidden\n", path);
 		errno = EACCES;
-		result = -1;
+		return -1;
 	}
-	
-	if (result == 0) {
-		result = __rmdir(path);
-	}
-	
-	return result;
+
+	debug_printf("rmdir %s was allowed\n", path);
+
+	return __rmdir(path);
 }
 
 /* Trap attempts to rename files/directories outside the sandbox.
  */
 int rename(const char* from, const char* to) {
 #define __rename(x,y) syscall(SYS_rename, (x), (y))
-	int result = 0;
-	int isInSandbox = __darwintrace_is_in_sandbox(from, 0);
-	if (isInSandbox == 1) {
-		debug_printf("darwintrace: rename was allowed at %s\n", from);
-	} else if (isInSandbox == 0) {
-		/* outside sandbox, but sandbox is defined: forbid */
-		debug_printf("darwintrace: renaming from %s was forbidden\n", from);
+	if (!__darwintrace_is_in_sandbox(from, NULL)) {
+		/* outside sandbox, forbid */
+		debug_printf("renaming from %s was forbidden\n", from);
 		errno = EACCES;
-		result = -1;
+		return -1;
 	}
+	if (!__darwintrace_is_in_sandbox(to, NULL)) {
+		debug_printf("renaming to %s was forbidden\n", to);
+		errno = EACCES;
+		return -1;
+	}
 
-	if (result == 0) {
-		isInSandbox = __darwintrace_is_in_sandbox(to, 0);
-		if (isInSandbox == 1) {
-			debug_printf("darwintrace: rename was allowed at %s\n", to);
-		} else if (isInSandbox == 0) {
-			/* outside sandbox, but sandbox is defined: forbid */
-			debug_printf("darwintrace: renaming to %s was forbidden\n", to);
-			errno = EACCES;
-			result = -1;
-		}
-	}
-	
-	if (result == 0) {
-		result = __rename(from, to);
-	}
-	
-	return result;
+	debug_printf("renaming from %s to %s was allowed\n", from, to);
+
+	return __rename(from, to);
 }
 
-int stat(const char * path, struct stat * sb)
-{
+int stat(const char * path, struct stat * sb) {
 #define stat(path, sb) syscall(SYS_stat, path, sb)
-	int result=0;
-	char newpath[260];
-		
-	*newpath=0;
-	if(!is_directory(path)&&__darwintrace_is_in_sandbox(path, newpath)==0)
-	{
-		errno=ENOENT;
-		result=-1;
-	}else
-	{
-		if(*newpath)
-			path=newpath;
-			
-		result=stat(path, sb);
+	int result = 0;
+	char newpath[MAXPATHLEN];
+
+	debug_printf("stat(%s)\n", path);
+	if (-1 == (result = stat(path, sb))) {
+		return -1;
 	}
-	
+
+	if (S_ISDIR(sb->st_mode)) {
+		return result;
+	}
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	if (*newpath) {
+		result = stat(newpath, sb);
+	}
+
 	return result;
 #undef stat
 }
 
 #if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
 
-int stat64(const char * path, struct stat64 * sb)
-{
+int stat64(const char * path, struct stat64 * sb) {
 #define stat64(path, sb) syscall(SYS_stat64, path, sb)
-	int result=0;
-	char newpath[260];
-		
-	*newpath=0;
-	if(!is_directory(path)&&__darwintrace_is_in_sandbox(path, newpath)==0)
-	{
-		errno=ENOENT;
-		result=-1;
-	}else
-	{
-		if(*newpath)
-			path=newpath;
-			
-		result=stat64(path, sb);
+	int result = 0;
+	char newpath[MAXPATHLEN];
+
+	debug_printf("stat64(%s)\n", path);
+	if (-1 == (result = stat64(path, sb))) {
+		return -1;
 	}
-	
+
+	if (S_ISDIR(sb->st_mode)) {
+		return result;
+	}
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	if (*newpath) {
+		result = stat64(newpath, sb);
+	}
+
 	return result;
 #undef stat64
 }
 
-int stat$INODE64(const char * path, struct stat64 * sb)
-{
+int stat$INODE64(const char * path, struct stat64 * sb) {
     return stat64(path, sb);
 }
 
 #endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
 
 
-int lstat(const char * path, struct stat * sb)
-{
+int lstat(const char * path, struct stat * sb) {
 #define lstat(path, sb) syscall(SYS_lstat, path, sb)
-	int result=0;
-	char newpath[260];
-	
-	*newpath=0;
-	if(!is_directory(path)&&__darwintrace_is_in_sandbox(path, newpath)==0)
-	{
-		errno=ENOENT;
-		result=-1;
-	}else
-	{
-		if(*newpath)
-			path=newpath;
-			
-		result=lstat(path, sb);
+	int result = 0;
+	char newpath[MAXPATHLEN];
+
+	debug_printf("lstat(%s)\n", path);
+	if (-1 == (result = lstat(path, sb))) {
+		return -1;
 	}
-	
+
+	if (S_ISDIR(sb->st_mode)) {
+		return result;
+	}
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	if (*newpath) {
+		result = lstat(newpath, sb);
+	}
+
 	return result;
 #undef lstat
 }
 
 #if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
 
-int lstat64(const char * path, struct stat64 * sb)
-{
+int lstat64(const char * path, struct stat64 * sb) {
 #define lstat64(path, sb) syscall(SYS_lstat64, path, sb)
-	int result=0;
-	char newpath[260];
-	
-	*newpath=0;
-	if(!is_directory(path)&&__darwintrace_is_in_sandbox(path, newpath)==0)
-	{
-		errno=ENOENT;
-		result=-1;
-	}else
-	{
-		if(*newpath)
-			path=newpath;
-			
-		result=lstat64(path, sb);
+	int result = 0;
+	char newpath[MAXPATHLEN];
+
+	debug_printf("lstat64(%s)\n", path);
+	if (-1 == (result = lstat64(path, sb))) {
+		return -1;
 	}
-	
+
+	if (S_ISDIR(sb->st_mode)) {
+		return result;
+	}
+
+	*newpath = '\0';
+	if (!__darwintrace_is_in_sandbox(path, newpath)) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	if (*newpath) {
+		result = lstat64(newpath, sb);
+	}
+
 	return result;
 #undef lstat64
 }
 
-int lstat$INODE64(const char * path, struct stat64 * sb)
-{
+int lstat$INODE64(const char * path, struct stat64 * sb) {
     return lstat64(path, sb);
 }
 
 #endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
+
+/**
+ * re-implementation of getdirent(2) and __getdirent64(2) preventing paths
+ * outside the sandbox to show up when reading the contents of a directory.
+ * Unfortunately, since we need to access the contents of the buffer, but the
+ * contents differ by architecture, we can not rely on the dirent structure
+ * defined by the header included by this program, because we don't know
+ * whether darwintrace.dylib has been compiled for 64bit or 32bit inodes. We
+ * thus copy both structs and decide at runtime.
+ */
+
+#ifdef __APPLE__
+/* only do this on mac, because fcntl(fd, F_GETPATH) might not be available on
+ * other systems, and because other system's syscall names are probably
+ * different anyway */
+
+#pragma pack(4)
+struct dirent32 {
+	ino_t d_ino;			/* file number of entry */
+	__uint16_t d_reclen;		/* length of this record */
+	__uint8_t  d_type; 		/* file type, see below */
+	__uint8_t  d_namlen;		/* length of string in d_name */
+	char d_name[__DARWIN_MAXNAMLEN + 1];	/* name must be no longer than this */
+};
+#pragma pack()
+
+struct dirent64  {
+	__uint64_t  d_ino;      /* file number of entry */
+	__uint64_t  d_seekoff;  /* seek offset (optional, used by servers) */
+	__uint16_t  d_reclen;   /* length of this record */
+	__uint16_t  d_namlen;   /* length of string in d_name */
+	__uint8_t   d_type;     /* file type, see below */
+	char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */
+};
+
+size_t __getdirentries64(int fd, void *buf, size_t bufsize, __darwin_off_t *basep) {
+#define __getdirentries64(w,x,y,z) syscall(SYS_getdirentries64, (w), (x), (y), (z))
+	size_t sz = __getdirentries64(fd, buf, bufsize, basep);
+	char dirname[MAXPATHLEN];
+	size_t dnamelen;
+
+	if (-1 == fcntl(fd, F_GETPATH, dirname)) {
+		errno = EBADF;
+		return 0;
+	}
+
+	dnamelen = strlen(dirname);
+	if (dirname[dnamelen - 1] != '/') {
+		dirname[dnamelen] = '/';
+		dirname[dnamelen + 1] = '\0';
+		dnamelen++;
+	}
+
+	dnamelen = strlen(dirname);
+	size_t offset;
+	for (offset = 0; offset < sz;) {
+		struct dirent64 *dent = (struct dirent64 *)(((char *) buf) + offset);
+		dirname[dnamelen] = '\0';
+		strcat(dirname, dent->d_name);
+		if (!__darwintrace_is_in_sandbox(dirname, NULL)) {
+			debug_printf("__getdirentries64: filtered %s\n", dirname);
+			dent->d_ino = 0;
+		} else {
+			debug_printf("__getdirentries64:  allowed %s\n", dirname);
+		}
+		offset += dent->d_reclen;
+	}
+
+	return sz;
+#undef __getdirentries64
+}
+
+int getdirentries(int fd, char *buf, int nbytes, long *basep) {
+#define getdirentries(w,x,y,z) syscall(SYS_getdirentries, (w), (x), (y), (z))
+	size_t sz = getdirentries(fd, buf, nbytes, basep);
+	char dirname[MAXPATHLEN];
+	size_t dnamelen;
+
+	if (-1 == fcntl(fd, F_GETPATH, dirname)) {
+		errno = EBADF;
+		return 0;
+	}
+
+	dnamelen = strlen(dirname);
+	if (dirname[dnamelen - 1] != '/') {
+		dirname[dnamelen] = '/';
+		dirname[dnamelen + 1] = '\0';
+		dnamelen++;
+	}
+
+	size_t offset;
+	for (offset = 0; offset < sz;) {
+		struct dirent32 *dent = (struct dirent32 *)(buf + offset);
+		dirname[dnamelen] = '\0';
+		strcat(dirname, dent->d_name);
+		if (!__darwintrace_is_in_sandbox(dirname, NULL)) {
+			debug_printf("getdirentries: filtered %s\n", dirname);
+			dent->d_ino = 0;
+		} else {
+			debug_printf("getdirentries:  allowed %s\n", dirname);
+		}
+		offset += dent->d_reclen;
+	}
+
+	return sz;
+#undef getdirentries
+}
+#endif /* __APPLE__ */
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20121126/9a9b40ae/attachment-0001.html>


More information about the macports-changes mailing list