<!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>[114096] trunk/base/src/darwintracelib1.0/darwintrace.c</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/114096">114096</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2013-11-28 18:11:12 -0800 (Thu, 28 Nov 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>darwintrace: fix expansion of multiple symlinks

Expanding a series of symlinks longer than a single symlink would previously
lead to re-use of a buffer that was used to store the result of readlink(2)
while the contents of the buffer were still in use in the pathComponent array.

This change refactors the path parsing and handling completely such that the
components of the normalized path will always be stored in the same buffer (and
other components will be copied there when needed), avoiding this problem.

Fixes the configure phase of stellarium and automoc (if you don't have
DARWINTRACE_DEBUG enabled).</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbasesrcdarwintracelib10darwintracec">trunk/base/src/darwintracelib1.0/darwintrace.c</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbasesrcdarwintracelib10darwintracec"></a>
<div class="modfile"><h4>Modified: trunk/base/src/darwintracelib1.0/darwintrace.c (114095 => 114096)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/darwintracelib1.0/darwintrace.c        2013-11-29 02:06:55 UTC (rev 114095)
+++ trunk/base/src/darwintracelib1.0/darwintrace.c        2013-11-29 02:11:12 UTC (rev 114096)
</span><span class="lines">@@ -707,90 +707,140 @@
</span><span class="cx">                 return true;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        char *pathComponents[MAXPATHLEN / 2 + 2];
-        size_t componentIdx = 0;
</del><ins>+        typedef struct {
+                char *start;
+                size_t len;
+        } path_component_t;
</ins><span class="cx"> 
</span><ins>+        char normPath[MAXPATHLEN];
+        normPath[0] = '/';
+        normPath[1] = '\0';
+
+        path_component_t pathComponents[MAXPATHLEN / 2 + 2];
+        size_t numComponents = 0;
+
</ins><span class="cx">         // Make sure the path is absolute.
</span><del>-        char cwd[MAXPATHLEN];
-        if (path == NULL) {
</del><ins>+        if (path == NULL || *path == '\0') {
</ins><span class="cx">                 // this is most certainly invalid, let the syscall deal with it
</span><span class="cx">                 return true;
</span><span class="cx">         }
</span><ins>+
+        char *dst = NULL;
+        const char *token = NULL;
+        size_t idx;
</ins><span class="cx">         if (*path != '/') {
</span><span class="cx">                 // The path isn't absolute, start by populating pathcomponents with the
</span><span class="cx">                 // current working directory
</span><del>-                if (getcwd(cwd, sizeof(cwd)) == NULL) {
</del><ins>+                if (getcwd(normPath, sizeof(normPath)) == NULL) {
</ins><span class="cx">                         perror(&quot;darwintrace: getcwd&quot;);
</span><span class="cx">                         abort();
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                char *lastToken = cwd + 1;
-                char *token = NULL;
-                while (NULL != (token = strsep(&amp;lastToken, &quot;/&quot;))) {
-                        pathComponents[componentIdx++] = token;
</del><ins>+                char *writableToken = normPath + 1;
+                while ((idx = strcspn(writableToken, &quot;/&quot;)) &gt; 0) {
+                        // found a token, tokenize and store it
+                        pathComponents[numComponents].start = writableToken;
+                        pathComponents[numComponents].len   = idx;
+                        numComponents++;
+
+                        bool final = writableToken[idx] == '\0';
+                        writableToken[idx] = '\0';
+                        if (final) {
+                                break;
+                        }
+                        // advance token
+                        writableToken += idx + 1;
</ins><span class="cx">                 }
</span><ins>+
+                // copy path after the CWD into the buffer and normalize it
+                if (numComponents &gt; 0) {
+                        path_component_t *lastComponent = pathComponents + (numComponents - 1);
+                        dst = lastComponent-&gt;start + lastComponent-&gt;len + 1;
+                } else {
+                        dst = normPath + 1;
+                }
+
+                // continue parsing at the begin of path
+                token = path;
+        } else {
+                // skip leading '/'
+                dst = normPath + 1;
+                *dst = '\0';
+                token = path + 1;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        // Copy path to a writable buffer
-        char lpath[strlen(path) + 1];
-        strcpy(lpath, path);
-
</del><span class="cx">         /* Make sure the path is normalized. NOTE: Do _not_ use realpath(3) here.
</span><span class="cx">          * Doing so _will_ lead to problems. This is essentially a very simple
</span><span class="cx">          * re-implementation of realpath(3). */
</span><del>-        char *lastToken = lpath;
-        char *token = NULL;
-        while (NULL != (token = strsep(&amp;lastToken, &quot;/&quot;))) {
-                if (token[0] == '\0') {
</del><ins>+        while ((idx = strcspn(token, &quot;/&quot;)) &gt; 0) {
+                // found a token, process it
+
+                if (token[0] == '\0' || token[0] == '/') {
</ins><span class="cx">                         // empty entry, ignore
</span><del>-                } else if (token[0] == '.' &amp;&amp; token[1] == '\0') {
</del><ins>+                } else if (token[0] == '.' &amp;&amp; (token[1] == '\0' || token[1] == '/')) {
</ins><span class="cx">                         // reference to current directory, ignore
</span><del>-                } else if (token[0] == '.' &amp;&amp; token[1] == '.' &amp;&amp; token[2] == '\0') {
</del><ins>+                } else if (token[0] == '.' &amp;&amp; token[1] == '.' &amp;&amp; (token[2] == '\0' || token[2] == '/')) {
</ins><span class="cx">                         // walk up one directory, but not if it's the last one, because /.. -&gt; /
</span><del>-                        if (componentIdx &gt; 0) {
-                                componentIdx--;
</del><ins>+                        if (numComponents &gt; 0) {
+                                numComponents--;
+                                if (numComponents &gt; 0) {
+                                        // move dst back to the previous entry
+                                        path_component_t *lastComponent = pathComponents + (numComponents - 1);
+                                        dst = lastComponent-&gt;start + lastComponent-&gt;len + 1;
+                                } else {
+                                        // we're at the top, move dst back to the beginning
+                                        dst = normPath + 1;
+                                }
</ins><span class="cx">                         }
</span><span class="cx">                 } else {
</span><del>-                        // default case: standard path
-                        pathComponents[componentIdx++] = token;
</del><ins>+                        // copy token to normPath buffer (and null-terminate it)
+                        strlcpy(dst, token, idx + 1);
+                        dst[idx] = '\0';
+                        // add descriptor entry for new token
+                        pathComponents[numComponents].start = dst;
+                        pathComponents[numComponents].len   = idx;
+                        numComponents++;
+
+                        // advance destination
+                        dst += idx + 1;
</ins><span class="cx">                 }
</span><ins>+
+                if (token[idx] == '\0') {
+                        break;
+                }
+                token += idx + 1;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        char link[MAXPATHLEN];
-        char normPath[MAXPATHLEN];
</del><span class="cx">         bool pathIsSymlink;
</span><span class="cx">         size_t loopCount = 0;
</span><span class="cx">         do {
</span><span class="cx">                 pathIsSymlink = false;
</span><span class="cx"> 
</span><ins>+                // Add the slashes and the terminating \0
+                for (size_t i = 0; i &lt; numComponents; ++i) {
+                        if (i == numComponents - 1) {
+                                pathComponents[i].start[pathComponents[i].len] = '\0';
+                        } else {
+                                pathComponents[i].start[pathComponents[i].len] = '/';
+                        }
+                }
+
</ins><span class="cx">                 if (++loopCount &gt;= 10) {
</span><span class="cx">                         // assume cylce and let the OS deal with that (yes, this actually
</span><span class="cx">                         // happens in software!)
</span><span class="cx">                         break;
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                char *normPathPos = normPath;
-                *normPathPos = '\0';
-
-                // Build a canonical representation of the path
-                for (size_t i = 0; i &lt; componentIdx; ++i) {
-                        *normPathPos++ = '/';
-                        normPathPos = stpcpy(normPathPos, pathComponents[i]);
-                }
-                if (componentIdx == 0) {
-                        // path is &quot;/&quot;
-                        *normPathPos++ = '/';
-                        *normPathPos = '\0';
-                }
-
</del><span class="cx">                 // Check whether the last component is a symlink; if it is, check
</span><span class="cx">                 // whether it is in the sandbox, expand it and do the same thing again.
</span><span class="cx">                 struct stat st;
</span><ins>+                //debug_printf(&quot;checking for symlink: %s\n&quot;, normPath);
</ins><span class="cx">                 if (lstat(normPath, &amp;st) != -1 &amp;&amp; S_ISLNK(st.st_mode)) {
</span><span class="cx">                         if (!__darwintrace_sandbox_check(normPath, flags)) {
</span><span class="cx">                                 return false;
</span><span class="cx">                         }
</span><span class="cx"> 
</span><ins>+                        char link[MAXPATHLEN];
</ins><span class="cx">                         pathIsSymlink = true;
</span><span class="cx"> 
</span><span class="cx">                         ssize_t linksize;
</span><span class="lines">@@ -799,33 +849,66 @@
</span><span class="cx">                                 abort();
</span><span class="cx">                         }
</span><span class="cx">                         link[linksize] = '\0';
</span><ins>+                        //debug_printf(&quot;readlink(%s) = %s\n&quot;, normPath, link);
</ins><span class="cx"> 
</span><span class="cx">                         if (*link == '/') {
</span><span class="cx">                                 // symlink is absolute, start fresh
</span><del>-                                componentIdx = 0;
</del><ins>+                                numComponents = 0;
+                                token = link + 1;
+                                dst = normPath + 1;
</ins><span class="cx">                         } else {
</span><span class="cx">                                 // symlink is relative, remove last component
</span><del>-                                if (componentIdx &gt; 0) {
-                                        componentIdx--;
</del><ins>+                                token = link;
+                                if (numComponents &gt; 0) {
+                                        numComponents--;
+                                        if (numComponents &gt; 0) {
+                                                // move dst back to the previous entry
+                                                path_component_t *lastComponent = pathComponents + (numComponents - 1);
+                                                dst = lastComponent-&gt;start + lastComponent-&gt;len + 1;
+                                        } else {
+                                                // we're at the top, move dst back to the beginning
+                                                dst = normPath + 1;
+                                        }
</ins><span class="cx">                                 }
</span><span class="cx">                         }
</span><span class="cx"> 
</span><del>-                        lastToken = link;
-                        token = NULL;
-                        while (NULL != (token = strsep(&amp;lastToken, &quot;/&quot;))) {
-                                if (token[0] == '\0') {
</del><ins>+                        while ((idx = strcspn(token, &quot;/&quot;)) &gt; 0) {
+                                // found a token, process it
+
+                                if (token[0] == '\0' || token[0] == '/') {
</ins><span class="cx">                                         // empty entry, ignore
</span><del>-                                } else if (token[0] == '.' &amp;&amp; token[1] == '\0') {
</del><ins>+                                } else if (token[0] == '.' &amp;&amp; (token[1] == '\0' || token[1] == '/')) {
</ins><span class="cx">                                         // reference to current directory, ignore
</span><del>-                                } else if (token[0] == '.' &amp;&amp; token[1] == '.' &amp;&amp; token[2] == '\0') {
</del><ins>+                                } else if (token[0] == '.' &amp;&amp; token[1] == '.' &amp;&amp; (token[2] == '\0' || token[2] == '/')) {
</ins><span class="cx">                                         // walk up one directory, but not if it's the last one, because /.. -&gt; /
</span><del>-                                        if (componentIdx &gt; 0) {
-                                                componentIdx--;
</del><ins>+                                        if (numComponents &gt; 0) {
+                                                numComponents--;
+                                                if (numComponents &gt; 0) {
+                                                        // move dst back to the previous entry
+                                                        path_component_t *lastComponent = pathComponents + (numComponents - 1);
+                                                        dst = lastComponent-&gt;start + lastComponent-&gt;len + 1;
+                                                } else {
+                                                        // we're at the top, move dst back to the beginning
+                                                        dst = normPath + 1;
+                                                }
</ins><span class="cx">                                         }
</span><span class="cx">                                 } else {
</span><del>-                                        // default case: standard path
-                                        pathComponents[componentIdx++] = token;
</del><ins>+                                        // copy token to normPath buffer
+                                        strlcpy(dst, token, idx + 1);
+                                        dst[idx] = '\0';
+                                        // add descriptor entry for new token
+                                        pathComponents[numComponents].start = dst;
+                                        pathComponents[numComponents].len   = idx;
+                                        numComponents++;
+
+                                        // advance destination
+                                        dst += idx + 1;
</ins><span class="cx">                                 }
</span><ins>+
+                                if (token[idx] == '\0') {
+                                        break;
+                                }
+                                token += idx + 1;
</ins><span class="cx">                         }
</span><span class="cx">                 }
</span><span class="cx">         } while (pathIsSymlink);
</span></span></pre>
</div>
</div>

</body>
</html>