<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[141420] trunk/base</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="https://trac.macports.org/changeset/141420">141420</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2015-10-17 17:49:32 -0700 (Sat, 17 Oct 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>base: Fix trace mode on El Capitan

OS X El Capitan introduces System Integrity Protection for files. Executables
with this flag set will be started in a sanitized environment by the kernel,
stripping all DYLD_* variables. This breaks trace mode, because tracing relies
on preloading to wrap file related system calls using DYLD_INSERT_LIBRARIES.

A trivial workaround for the problem is to make a copy of the affected
binaries, which will strip the flag, and then adjust the invocation of the
binary to execute the copy instead (but leaving argv[0] as-is to avoid giving
the program an indication of being run from a non-standard location).

This change implements this approach by copying the SIP-flagged binaries to
$prefix/var/macports/sip-workaround on demand iff
 - the system has the SF_RESTRICTED flag defined
 - a binary is started with DYLD_INSERT_LIBRARIES set
 - the file exists and has SF_RESTRICTED set
 - the file isn't SUID or SGID (which we could not reliably copy, and which
   have never preserved DYLD_* variables)
If the file to be executed is a script and has a shebang line, the checks are
run on the interpreter instead, and if necessary, the interpreter is copied.
This requires interpreting the shebang line in user space.

Copies are created on-demand and are lazy: The file modification times are
checked before overwriting an existing copy. Copies are created in a per-user
folder, which will be created on-demand in a 1777 directory (like /tmp).

Changes are also needed way before darwintrace.dylib first runs: The DYLD_*
variables are already stripped in src/pextlib1.0/system.c, where
/usr/bin/sandbox-exec and /bin/sh are run, which both have the SF_RESTRICTED
flag on 10.11 now. Consequently, the same copying approach is applied there.

Because macports build run in a sandbox, the sandbox boundaries are extended to
allow access to $prefix/var/macports/sip-workaround.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbaseconfigureac">trunk/base/configure.ac</a></li>
<li><a href="#trunkbasedocbasemtreein">trunk/base/doc/base.mtree.in</a></li>
<li><a href="#trunkbasesrcdarwintracelib10Makefilein">trunk/base/src/darwintracelib1.0/Makefile.in</a></li>
<li><a href="#trunkbasesrcdarwintracelib10procc">trunk/base/src/darwintracelib1.0/proc.c</a></li>
<li><a href="#trunkbasesrcpextlib10Makefilein">trunk/base/src/pextlib1.0/Makefile.in</a></li>
<li><a href="#trunkbasesrcpextlib10systemc">trunk/base/src/pextlib1.0/system.c</a></li>
<li><a href="#trunkbasesrcport10portsandboxtcl">trunk/base/src/port1.0/portsandbox.tcl</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkbasesrcpextlib10sip_copy_procc">trunk/base/src/pextlib1.0/sip_copy_proc.c</a></li>
<li><a href="#trunkbasesrcpextlib10sip_copy_proch">trunk/base/src/pextlib1.0/sip_copy_proc.h</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#trunkbasesrcdarwintracelib10">trunk/base/src/darwintracelib1.0/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbaseconfigureac"></a>
<div class="modfile"><h4>Modified: trunk/base/configure.ac (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/configure.ac        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/configure.ac        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -312,6 +312,14 @@
</span><span class="cx"> eval &quot;prefix_expanded=$prefix&quot;
</span><span class="cx"> eval &quot;exec_prefix_expanded=$exec_prefix&quot;
</span><span class="cx"> AC_SUBST(prefix_expanded)
</span><ins>+
+# Define a path where our trace mode workaround for SIP-protected binaries on
+# &gt;= 10.11 puts copies
+AC_DEFINE_UNQUOTED(
+        [DARWINTRACE_SIP_WORKAROUND_PATH],
+        [&quot;${prefix}/var/macports/sip-workaround&quot;],
+        [Absolute path to a directory used by darwintrace to copy SIP-protected files before executing])
+
</ins><span class="cx"> # do this twice, since there is a nested variable of
</span><span class="cx"> # ${prefix} inside of ${sysconfdir}
</span><span class="cx"> eval &quot;MPCONFIGDIR_EXPANDED=$MPCONFIGDIR&quot;
</span></span></pre></div>
<a id="trunkbasedocbasemtreein"></a>
<div class="modfile"><h4>Modified: trunk/base/doc/base.mtree.in (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/doc/base.mtree.in        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/doc/base.mtree.in        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -23,6 +23,8 @@
</span><span class="cx">             ..
</span><span class="cx">             registry
</span><span class="cx">             ..
</span><ins>+            sip-workaround mode=01777
+            ..
</ins><span class="cx">             software
</span><span class="cx">             ..
</span><span class="cx">         ..
</span></span></pre></div>
<a id="trunkbasesrcdarwintracelib10"></a>
<div class="propset"><h4>Property changes: trunk/base/src/darwintracelib1.0</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnignore"></a>
<div class="modfile"><h4>Modified: svn:ignore</h4></div>
<span class="cx">darwintrace.dylib
</span><span class="cx">   + *.d
</span><span class="cx">Makefile
</span><span class="cx">darwintrace.dylib
</span><span class="cx">sip_copy_proc.c
</span><span class="cx">sip_copy_proc.h
</span><a id="trunkbasesrcdarwintracelib10Makefilein"></a>
<div class="modfile"><h4>Modified: trunk/base/src/darwintracelib1.0/Makefile.in (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/darwintracelib1.0/Makefile.in        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/darwintracelib1.0/Makefile.in        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -5,30 +5,62 @@
</span><span class="cx"> 
</span><span class="cx"> # This Makefile will only be run on Darwin systems; we can safely use
</span><span class="cx"> # Apple-specifics here
</span><del>-SRCS=                $(wildcard *.c)
-OBJS=                $(SRCS:%.c=%.o)
-SHLIB_NAME=        darwintrace${SHLIB_SUFFIX}
-INSTALLDIR=        ${DESTDIR}${TCL_PACKAGE_PATH}/darwintrace1.0
</del><ins>+SRCS = \
+        access.c \
+        close.c \
+        darwintrace.c \
+        dup2.c \
+        mkdir.c \
+        open.c \
+        proc.c \
+        readdir.c \
+        readlink.c \
+        rename.c \
+        rmdir.c \
+        sip_copy_proc.c \
+        stat.c \
+        unlink.c
</ins><span class="cx"> 
</span><ins>+OBJS = $(SRCS:%.c=%.o)
+SHLIB_NAME = darwintrace$(SHLIB_SUFFIX)
+INSTALLDIR = $(DESTDIR)$(TCL_PACKAGE_PATH)/darwintrace1.0
+
</ins><span class="cx"> # Yes, we know having $ signs in identifiers is not a very good idea; in the
</span><span class="cx"> # case of darwintrace we still need them, though.
</span><del>-CFLAGS_PEDANTIC=
-CFLAGS+= -fPIC ${UNIVERSAL_ARCHFLAGS}
-SHLIB_LDFLAGS+=${UNIVERSAL_ARCHFLAGS}
</del><ins>+CFLAGS_PEDANTIC =
+CFLAGS += -fPIC $(UNIVERSAL_ARCHFLAGS)
+SHLIB_LDFLAGS += $(UNIVERSAL_ARCHFLAGS)
</ins><span class="cx"> 
</span><del>-all:: ${SHLIB_NAME}
</del><ins>+# Generate dependency information
+CPPFLAGS += -MMD -MP
</ins><span class="cx"> 
</span><del>-$(SHLIB_NAME):: ${OBJS}
-        ${SHLIB_LD} ${OBJS} -o ${SHLIB_NAME} ${SHLIB_LDFLAGS} ${LIBS}
</del><ins>+all:: $(SHLIB_NAME)
</ins><span class="cx"> 
</span><ins>+# Copy sip_copy_proc.{c,h} from pextlib1.0 where they are also needed
+sip_copy_proc.c: ../pextlib1.0/sip_copy_proc.c sip_copy_proc.h 
+        cp $&lt; $@
+
+sip_copy_proc.h: ../pextlib1.0/sip_copy_proc.h
+        cp $&lt; $@
+
+# This won't be automatically detected during the first run of make, where the
+# .d files do not exist yet
+proc.c: sip_copy_proc.h
+
+$(SHLIB_NAME):: $(OBJS)
+        $(SHLIB_LD) $(OBJS) -o $(SHLIB_NAME) $(SHLIB_LDFLAGS) $(LIBS)
+
</ins><span class="cx"> clean::
</span><del>-        rm -f ${OBJS} ${SHLIB_NAME} so_locations
</del><ins>+        rm -f $(OBJS) $(SHLIB_NAME) so_locations sip_copy_proc.c sip_copy_proc.h $(SRCS:%.c=%.d)
</ins><span class="cx"> 
</span><span class="cx"> distclean:: clean
</span><span class="cx">         rm -f Makefile
</span><span class="cx"> 
</span><span class="cx"> install:: all
</span><del>-        $(INSTALL) -d -o &quot;${DSTUSR}&quot; -g &quot;${DSTGRP}&quot; -m &quot;${DSTMODE}&quot; &quot;${INSTALLDIR}&quot;
-        $(INSTALL)    -o &quot;${DSTUSR}&quot; -g &quot;${DSTGRP}&quot; -m 444 &quot;${SHLIB_NAME}&quot; &quot;${INSTALLDIR}&quot;
</del><ins>+        $(INSTALL) -d -o &quot;$(DSTUSR)&quot; -g &quot;$(DSTGRP)&quot; -m &quot;$(DSTMODE)&quot; &quot;$(INSTALLDIR)&quot;
+        $(INSTALL)    -o &quot;$(DSTUSR)&quot; -g &quot;$(DSTGRP)&quot; -m 444 &quot;$(SHLIB_NAME)&quot; &quot;$(INSTALLDIR)&quot;
</ins><span class="cx"> 
</span><span class="cx"> test::
</span><ins>+
+# Include dependency information
+-include $(SRCS:%.c=%.d)
</ins></span></pre></div>
<a id="trunkbasesrcdarwintracelib10procc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/darwintracelib1.0/proc.c (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/darwintracelib1.0/proc.c        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/darwintracelib1.0/proc.c        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> 
</span><span class="cx"> #define DARWINTRACE_USE_PRIVATE_API 1
</span><span class="cx"> #include &quot;darwintrace.h&quot;
</span><ins>+#include &quot;sip_copy_proc.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> #include &lt;ctype.h&gt;
</span><span class="cx"> #include &lt;dlfcn.h&gt;
</span><span class="lines">@@ -278,7 +279,7 @@
</span><span class="cx"> 
</span><span class="cx">                         // Call the original execve function, but restore environment
</span><span class="cx">                         char **newenv = restore_env(envp);
</span><del>-                        result = execve(path, argv, newenv);
</del><ins>+                        result = sip_copy_execve(path, argv, newenv);
</ins><span class="cx">                         free(newenv);
</span><span class="cx">                 }
</span><span class="cx">         }
</span><span class="lines">@@ -334,7 +335,7 @@
</span><span class="cx">                          * we need to call the original posix_spawn from here. */
</span><span class="cx">                         // call the original posix_spawn function, but restore environment
</span><span class="cx">                         char **newenv = restore_env(envp);
</span><del>-                        result = posix_spawn(pid, path, file_actions, attrp, argv, newenv);
</del><ins>+                        result = sip_copy_posix_spawn(pid, path, file_actions, attrp, argv, newenv);
</ins><span class="cx">                         free(newenv);
</span><span class="cx">                 }
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkbasesrcpextlib10Makefilein"></a>
<div class="modfile"><h4>Modified: trunk/base/src/pextlib1.0/Makefile.in (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/Makefile.in        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/pextlib1.0/Makefile.in        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -4,11 +4,32 @@
</span><span class="cx"> include ../../Mk/macports.autoconf.mk
</span><span class="cx"> 
</span><span class="cx"> OBJS= \
</span><del>-        Pextlib.o strsed.o fgetln.o md5cmd.o setmode.o xinstall.o \
-        fs-traverse.o strcasecmp.o vercomp.o filemap.o base32cmd.o \
-        sha1cmd.o curl.o rmd160cmd.o sha256cmd.o readline.o uid.o \
-        tracelib.o tty.o readdir.o pipe.o adv-flock.o system.o \
-        mktemp.o realpath.o
</del><ins>+        Pextlib.o \
+        adv-flock.o \
+        base32cmd.o \
+        curl.o \
+        fgetln.o \
+        filemap.o \
+        fs-traverse.o \
+        md5cmd.o \
+        mktemp.o \
+        pipe.o \
+        readdir.o \
+        readline.o \
+        realpath.o \
+        rmd160cmd.o \
+        setmode.o \
+        sha1cmd.o \
+        sha256cmd.o \
+        sip_copy_proc.o \
+        strcasecmp.o \
+        strsed.o \
+        system.o \
+        tracelib.o \
+        tty.o \
+        uid.o \
+        vercomp.o \
+        xinstall.o
</ins><span class="cx"> ifneq (@ac_cv_func_strlcat@,yes)
</span><span class="cx"> OBJS+=strlcat.o
</span><span class="cx"> endif
</span></span></pre></div>
<a id="trunkbasesrcpextlib10sip_copy_procc"></a>
<div class="addfile"><h4>Added: trunk/base/src/pextlib1.0/sip_copy_proc.c (0 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/sip_copy_proc.c                                (rev 0)
+++ trunk/base/src/pextlib1.0/sip_copy_proc.c        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -0,0 +1,503 @@
</span><ins>+/* vim: set et sw=4 ts=4 sts=4: */
+/*
+ * sip_copy_proc.c
+ * $Id$
+ *
+ * Copyright (c) 2015 Clemens Lang &lt;cal@macports.org&gt;
+ * Copyright (c) 2015 The MacPorts Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The MacPorts Project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _DARWIN_FEATURE_64_BIT_INODE
+
+#include &lt;errno.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;stdbool.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sys/socket.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;sys/time.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;sys/uio.h&gt;
+#include &lt;unistd.h&gt;
+
+#include &quot;sip_copy_proc.h&quot;
+
+#include &lt;config.h&gt;
+#ifndef DARWINTRACE_SIP_WORKAROUND_PATH
+#warning No value for DARWINTRACE_SIP_WORKAROUND_PATH found in config.h, using default of /tmp/macports-sip, which will fail unless you create it with mode 01777
+#define DARWINTRACE_SIP_WORKAROUND_PATH &quot;/tmp/macports-sip&quot;
+#endif
+
+/**
+ * Frees an array of strings and the array itself.
+ */
+static void free_argv(char *argv[]) {
+        char **arg = argv;
+        while (arg &amp;&amp; *arg) {
+                free(*arg);
+                *arg = NULL;
+                arg++;
+        }
+
+        free(argv);
+}
+
+typedef enum _copy_needed_return_t {
+    copy_needed_error,
+    copy_not_needed,
+    copy_is_needed
+} copy_needed_return_t;
+
+/**
+ * Helper function to determine whether the binary indicated by \a path
+ * supports library injection using DYLD_INSERT_LIBRARIES directly or needs to
+ * be copied to a temporary path to support it.
+ *
+ * The following conditions must be fulfilled for the copy to be necessary:
+ *  - \a environ needs to contain a variable that starts with
+ *    DYLD_INSERT_LIBRARIES
+ *  - If the file at \a path has a shebang, its shebang line will be read and
+ *    the following checks will be done against the interpreter binary.
+ *    Additionally, if the copy is necessary, the arguments given in \a argc
+ *    (the number of arguments) and \a argv (the arguments itself) will be
+ *    prefixed with the command and arguments from the shebang line. The
+ *    original first argument will be replaced with \a path to make sure it is
+ *    absolute.
+ *  - \a path (or the interpreter given in the shebang of path) must have the
+ *    \c SF_RESTRICTED flag set.
+ *  - \a path (or the interpreter given in the shebang of path) must not be
+ *    SUID or SGID.
+ *
+ * @param path The absolute path of the binary to be executed
+ * @param argc The number of arguments passed in \a argv
+ * @param argv The arguments to be passed to the file to be executed
+ * @param outargv Pointer to a modified array of arguments. Only valid if \c
+ *                copy_is_needed is returned. May be \c NULL, in which case no
+ *                modifications to the original \c argv were necessary. If
+ *                non-null, a dynamically allocated array of dynamically
+ *                allocated elements. The last element of the array is \c NULL,
+ *                which makes \c *outargv suitable for passing to execve(2).
+ *                Note that instead of the given \c path, you should pass \c
+ *                (*outargv)[0] to execve(2) as first argument.
+ * @param environ The environment for the program to be started. Will be
+ *                checked for the presence of a DYLD_INSERT_LIBRARIES variable.
+ * @param st Pointer to struct stat that will contain information about \c
+ *           path, or of \c outargv isn't \c NULL, about \c (*outargv)[0]. This
+ *           can be used to determine metadata of the file such as modification
+ *           time and size to avoid unnecessary copies.
+ * @return \c copy_isneeded iff a copy is required. \c copy_not_needed if a copy
+ *         is not needed. \c copy_needed_error on error, where errno will be
+ *         set.
+ */
+static copy_needed_return_t copy_needed(const char *path, char *const argv[],
+        char **outargv[], char *const environ[], struct stat *st) {
+#ifndef SF_RESTRICTED /* no system integrity protection */
+        return copy_not_needed;
+#else /* defined(SF_RESTRICTED) */
+        // check whether DYLD_INSERT_LIBRARIES is set
+        bool dyld_insert_libraries_present = false;
+        char *const *env = environ;
+        while (env &amp;&amp; *env) {
+                if (strncmp(&quot;DYLD_INSERT_LIBRARIES=&quot;, *env, strlen(&quot;DYLD_INSERT_LIBRARIES=&quot;)) == 0) {
+                        dyld_insert_libraries_present = true;
+                        break;
+                }
+                env++;
+        }
+        // if we didn't find DYLD_INSERT_LIBRARIES, a copy isn't needed
+        if (!dyld_insert_libraries_present) {
+                return copy_not_needed;
+        }
+
+        // open file to check for shebangs
+        const char *realpath = path;
+        size_t new_argc = 0;
+        char **new_argv = NULL;
+        FILE *f = fopen(path, &quot;r&quot;);
+        if (!f) {
+                // if opening fails we won't be able to copy anyway
+                return copy_not_needed;
+        }
+
+        /* no error checking for fgetc(3) here, because this isn't a shebang if an
+         * error occurs */
+        if (fgetc(f) == '#' &amp;&amp; fgetc(f) == '!') {
+                /* This is an interpreted script. The interpreter's flags are what
+                 * affects whether DYLD_* is stripped, so read the interpreter's path
+                 * from the file to check that instead. Additionally, read any flags
+                 * that may be passed to the interpreter, since we'll have to do the
+                 * shebang expansion in user space if we move the interpreter. */
+                char *linep = NULL;
+                size_t linecapp = 0;
+                // read first line to get the interpreter and its arguments
+                if (getline(&amp;linep, &amp;linecapp, f) &gt; 0) {
+                        char *ctxt;
+            char *word;
+            size_t idx;
+                        // do word splitting on the interpreter line and store it in new_argv
+                        for (idx = 0, word = strtok_r(linep, &quot; \t\n&quot;, &amp;ctxt);
+                                        word != NULL;
+                                        idx++, word = strtok_r(NULL, &quot; \t\n&quot;, &amp;ctxt)) {
+                                // make sure we have enough space allocated
+                                if (new_argv == NULL) {
+                                        if ((new_argv = malloc(2 * sizeof(*new_argv))) == NULL) {
+                        free(linep);
+                        return copy_needed_error;
+                                        }
+                                        new_argc = 1;
+
+                                        // new_argv[0] will be overwritten in a second
+                                        // new_argv[1] is the terminating NULL
+                                        new_argv[0] = NULL;
+                                        new_argv[1] = NULL;
+                                } else if (idx &gt;= new_argc) {
+                                        // realloc to increase the size
+                                        char **oldargv = new_argv;
+                                        if ((new_argv = realloc(oldargv, (idx + 2) * sizeof(*new_argv))) == NULL) {
+                                                free_argv(oldargv);
+                        free(linep);
+                        return copy_needed_error;
+                                        }
+                                        new_argc = idx + 1;
+                                }
+
+                                // store a copy of the word in new_argv
+                                new_argv[idx] = strdup(word);
+                                if (!new_argv[idx]) {
+                                        free_argv(new_argv);
+                    free(linep);
+                    return copy_needed_error;
+                                }
+                new_argv[idx + 1] = NULL;
+                        }
+
+                        free(linep);
+
+                        if (new_argv &amp;&amp; *new_argv) {
+                                // interpreter found, check that instead of given path
+                                realpath = *new_argv;
+                        }
+                }
+        }
+
+        // check whether the binary has SF_RESTRICTED and isn't SUID/SGID
+        if (-1 == stat(realpath, st)) {
+                // on error, return and let execve(2) deal with it
+                free_argv(new_argv);
+                return copy_not_needed;
+        } else {
+                if (!(st-&gt;st_flags &amp; SF_RESTRICTED)) {
+                        // no SIP on this binary
+                        free_argv(new_argv);
+                        return copy_not_needed;
+                }
+                if ((st-&gt;st_flags &amp; (S_ISUID | S_ISGID)) &gt; 0) {
+                        // the binary is SUID/SGID, which would get lost when copying;
+                        // DYLD_ variables are stripped for SUID/SGID binaries anyway
+                        free_argv(new_argv);
+                        return copy_not_needed;
+                }
+        }
+
+        // prefix the shebang line to the original argv
+        if (new_argv != NULL) {
+        size_t argc = 0;
+        for (char *const *argvwalk = argv; argvwalk &amp;&amp; *argvwalk; ++argvwalk) {
+            argc++;
+        }
+
+                // realloc to increase the size
+                char **oldargv = new_argv;
+                if ((new_argv = realloc(oldargv, (new_argc + argc + 1) * sizeof(*new_argv))) == NULL) {
+                        free_argv(oldargv);
+            return copy_needed_error;
+                }
+
+                new_argv[new_argc] = strdup(path);
+                if (!new_argv[new_argc]) {
+            free_argv(new_argv);
+            return copy_needed_error;
+                }
+        new_argv[new_argc + 1] = NULL;
+
+                for (size_t idx = 1; idx &lt; argc; ++idx) {
+                        new_argv[new_argc + idx] = strdup(argv[idx]);
+                        if (!new_argv[new_argc + idx]) {
+                free_argv(new_argv);
+                return copy_needed_error;
+                        }
+            new_argv[new_argc + idx + 1] = NULL;
+                }
+
+                new_argc = new_argc + argc;
+
+                *outargv = new_argv;
+        }
+
+        return copy_is_needed;
+#endif /* defined(SF_RESTRICTED) */
+}
+
+static char *lazy_copy(const char *path, struct stat *in_st) {
+    char *retval = NULL;
+    uid_t euid = geteuid();
+    int outfd = -1;
+    int infd = -1;
+
+    char *target_folder = NULL;
+    char *target_path = NULL;
+    char *target_path_temp = NULL;
+    char *dir = strdup(path);
+    if (!dir) {
+        goto lazy_copy_out;
+    }
+    char *endslash = strrchr(dir, '/');
+    if (endslash) {
+        *endslash = '\0';
+    }
+
+    if (-1 == asprintf(&amp;target_folder, &quot;%s/%lu%s&quot;, DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid, dir)) {
+        goto lazy_copy_out;
+    }
+
+    if (-1 == asprintf(&amp;target_path, &quot;%s/%lu%s&quot;, DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid, path)) {
+        goto lazy_copy_out;
+    }
+
+    if (-1 == asprintf(&amp;target_path_temp, &quot;%s/%lu/.XXXXXXXXXXXXXX&quot;, DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid)) {
+        goto lazy_copy_out;
+    }
+
+    // ensure directory exists
+    char *pos = target_folder + strlen(DARWINTRACE_SIP_WORKAROUND_PATH);
+    while (pos &amp;&amp; *pos) {
+        *pos = '\0';
+        if (-1 == mkdir(target_folder, 0755) &amp;&amp; errno != EEXIST) {
+            fprintf(stderr, &quot;sip_copy_proc: mkdir(%s): %s\n&quot;, target_folder, strerror(errno));
+            goto lazy_copy_out;
+        }
+        *pos = '/';
+        pos++;
+        pos = strchr(pos, '/');
+    }
+    if (-1 == mkdir(target_folder, 0755) &amp;&amp; errno != EEXIST) {
+        fprintf(stderr, &quot;sip_copy_proc: mkdir(%s): %s\n&quot;, target_folder, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    // check whether copying is needed; it isn't if the file exists and the
+    // modification times match
+    struct stat out_st;
+    if (   -1 != stat(target_path, &amp;out_st)
+        &amp;&amp; in_st-&gt;st_mtimespec.tv_sec == out_st.st_mtimespec.tv_sec
+        &amp;&amp; in_st-&gt;st_mtimespec.tv_nsec == out_st.st_mtimespec.tv_nsec) {
+        // copying not needed
+        retval = target_path;
+        goto lazy_copy_out;
+    }
+
+    // create temporary file to copy into and then later atomically replace
+    // target file
+    if (-1 == (outfd = mkstemp(target_path_temp))) {
+        fprintf(stderr, &quot;sip_copy_proc: mkstemp(%s): %s\n&quot;, target_path_temp, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    if (-1 == (infd = open(path, O_RDONLY | O_CLOEXEC))) {
+        fprintf(stderr, &quot;sip_copy_proc: open(%s, O_RDONLY | O_CLOEXEC): %s\n&quot;, path, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    // ensure mode is copied
+    if (-1 == fchmod(outfd, in_st-&gt;st_mode)) {
+        fprintf(stderr, &quot;sip_copy_proc: fchmod(%s, %o): %s\n&quot;, target_path_temp, in_st-&gt;st_mode, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    char *buf = malloc(in_st-&gt;st_blksize);
+    ssize_t bytes_read = 0;
+    ssize_t bytes_written = 0;
+    bool error = false;
+    do {
+        bytes_read = read(infd, buf, in_st-&gt;st_blksize);
+        if (bytes_read &lt; 0) {
+            if (errno == EINTR || errno == EAGAIN) {
+                continue;
+            } else {
+                error = true;
+                break;
+            }
+        }
+        if (bytes_read == 0) {
+            // EOF
+            break;
+        }
+
+        bytes_written = 0;
+        while (bytes_written &lt; bytes_read) {
+            ssize_t written = write(outfd, buf + bytes_written, bytes_read - bytes_written);
+            if (written &lt; 0) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    continue;
+                }
+                error = true;
+                break;
+            }
+
+            bytes_written += written;
+        }
+    } while (!error);
+    if (bytes_read &lt; 0 || bytes_written &lt; 0) {
+        goto lazy_copy_out;
+    }
+
+    struct timeval times[2];
+    TIMESPEC_TO_TIMEVAL(&amp;times[0], &amp;in_st-&gt;st_mtimespec);
+    TIMESPEC_TO_TIMEVAL(&amp;times[1], &amp;in_st-&gt;st_mtimespec);
+    if (-1 == futimes(outfd, times)) {
+        fprintf(stderr, &quot;sip_copy_proc: futimes(%s): %s\n&quot;, target_path_temp, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    if (-1 == rename(target_path_temp, target_path)) {
+        fprintf(stderr, &quot;sip_copy_proc: rename(%s, %s): %s\n&quot;, target_path_temp, target_path, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    retval = target_path;
+
+lazy_copy_out:
+    {
+        int errno_save = errno;
+        close(outfd);
+        close(infd);
+        if (target_path_temp != NULL &amp;&amp; -1 == unlink(target_path_temp) &amp;&amp; errno != ENOENT) {
+            fprintf(stderr, &quot;sip_copy_proc: unlink(%s): %s\n&quot;, target_path_temp, strerror(errno));
+            retval = NULL;
+        } else {
+            errno = errno_save;
+        }
+    }
+    free(dir);
+    free(target_path_temp);
+    free(target_folder);
+    if (retval != target_path) {
+        free(target_path);
+    }
+    return retval;
+}
+
+/**
+ * Behaves like execve(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_execve(const char *path, char *const argv[], char *const envp[]) {
+    char **outargv = NULL;
+    struct stat st;
+
+    copy_needed_return_t need_copy = copy_needed(path, argv, &amp;outargv, envp, &amp;st);
+    switch (need_copy) {
+        case copy_needed_error:
+            return -1;
+            break;
+        case copy_not_needed:
+            return execve(path, argv, envp);
+            break;
+        case copy_is_needed: {
+                const char *to_be_copied = path;
+                char *const *to_be_argv = argv;
+                if (outargv) {
+                    to_be_copied = outargv[0];
+                    to_be_argv = outargv;
+                }
+
+                char *new_path = lazy_copy(to_be_copied, &amp;st);
+                if (!new_path) {
+                    return -1;
+                }
+
+                int ret = execve(new_path, to_be_argv, envp);
+                free_argv(outargv);
+                free(new_path);
+                return ret;
+            }
+            break;
+    }
+}
+
+/**
+ * Behaves like posix_spawn(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_posix_spawn(
+        pid_t *restrict pid,
+        const char *restrict path,
+        const posix_spawn_file_actions_t *file_actions,
+        const posix_spawnattr_t *restrict attrp,
+        char *const argv[restrict],
+        char *const envp[restrict]) {
+    char **outargv = NULL;
+    struct stat st;
+
+    copy_needed_return_t need_copy = copy_needed(path, argv, &amp;outargv, envp, &amp;st);
+    switch (need_copy) {
+        case copy_needed_error:
+            return -1;
+            break;
+        case copy_not_needed:
+            return posix_spawn(pid, path, file_actions, attrp, argv, envp);
+            break;
+        case copy_is_needed: {
+                const char *to_be_copied = path;
+                char *const *to_be_argv = argv;
+                if (outargv) {
+                    to_be_copied = outargv[0];
+                    to_be_argv = outargv;
+                }
+
+                char *new_path = lazy_copy(to_be_copied, &amp;st);
+                if (!new_path) {
+                    return -1;
+                }
+
+                int ret = posix_spawn(pid, new_path, file_actions, attrp, to_be_argv, envp);
+                free_argv(outargv);
+                free(new_path);
+                return ret;
+            }
+            break;
+    }
+}
</ins></span></pre></div>
<a id="trunkbasesrcpextlib10sip_copy_proch"></a>
<div class="addfile"><h4>Added: trunk/base/src/pextlib1.0/sip_copy_proc.h (0 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/sip_copy_proc.h                                (rev 0)
+++ trunk/base/src/pextlib1.0/sip_copy_proc.h        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -0,0 +1,60 @@
</span><ins>+/* vim: set et sw=4 ts=4 sts=4: */
+/*
+ * system.c
+ * $Id: system.c 138943 2015-07-24 20:35:45Z raimue@macports.org $
+ *
+ * Copyright (c) 2015 Clemens Lang &lt;cal@macports.org&gt;
+ * Copyright (c) 2015 The MacPorts Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The MacPorts Project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &lt;spawn.h&gt;
+
+/**
+ * Behaves like execve(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_execve(const char *path, char *const argv[], char *const envp[]);
+
+
+/**
+ * Behaves like posix_spawn(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_posix_spawn(
+        pid_t *restrict pid,
+        const char *restrict path,
+        const posix_spawn_file_actions_t *file_actions,
+        const posix_spawnattr_t *restrict attrp,
+        char *const argv[restrict],
+        char *const envp[restrict]);
</ins></span></pre></div>
<a id="trunkbasesrcpextlib10systemc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/pextlib1.0/system.c (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/system.c        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/pextlib1.0/system.c        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -58,6 +58,7 @@
</span><span class="cx"> #include &lt;errno.h&gt;
</span><span class="cx"> 
</span><span class="cx"> #include &quot;system.h&quot;
</span><ins>+#include &quot;sip_copy_proc.h&quot;
</ins><span class="cx"> #include &quot;Pextlib.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #if HAVE_CRT_EXTERNS_H
</span><span class="lines">@@ -237,13 +238,13 @@
</span><span class="cx">             args[4] = &quot;-c&quot;;
</span><span class="cx">             args[5] = cmdstring;
</span><span class="cx">             args[6] = NULL;
</span><del>-            execve(sandbox_exec_path, args, environ);
</del><ins>+            sip_copy_execve(sandbox_exec_path, args, environ);
</ins><span class="cx">         } else {
</span><span class="cx">             args[0] = &quot;sh&quot;;
</span><span class="cx">             args[1] = &quot;-c&quot;;
</span><span class="cx">             args[2] = cmdstring;
</span><span class="cx">             args[3] = NULL;
</span><del>-            execve(&quot;/bin/sh&quot;, args, environ);
</del><ins>+            sip_copy_execve(&quot;/bin/sh&quot;, args, environ);
</ins><span class="cx">         }
</span><span class="cx">         exit(128);
</span><span class="cx">         /*NOTREACHED*/
</span></span></pre></div>
<a id="trunkbasesrcport10portsandboxtcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/port1.0/portsandbox.tcl (141419 => 141420)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/port1.0/portsandbox.tcl        2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/port1.0/portsandbox.tcl        2015-10-18 00:49:32 UTC (rev 141420)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> # command line usage would be:
</span><span class="cx"> # sandbox-exec -p '(version 1) (allow default) (deny file-write*) (allow file-write* &lt;filter&gt;)' some-command
</span><span class="cx"> proc portsandbox::set_profile {target} {
</span><del>-    global os.major portsandbox_profile workpath distpath altprefix \
</del><ins>+    global os.major portsandbox_profile workpath distpath prefix altprefix \
</ins><span class="cx">         package.destpath configure.ccache ccache_dir
</span><span class="cx"> 
</span><span class="cx">     switch $target {
</span><span class="lines">@@ -78,6 +78,7 @@
</span><span class="cx"> 
</span><span class="cx">     # TODO: remove altprefix support
</span><span class="cx">     lappend allow_dirs $workpath $altprefix
</span><ins>+    lappend allow_dirs $prefix/var/macports/sip-workaround
</ins><span class="cx">     if {${configure.ccache}} {
</span><span class="cx">         lappend allow_dirs $ccache_dir
</span><span class="cx">     }
</span></span></pre>
</div>
</div>

</body>
</html>