Accept valid relative symlinks in TreeArtifacts.

--
MOS_MIGRATED_REVID=137072310
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
index b523c92..0f86ad1 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
@@ -190,8 +190,33 @@
         explodeDirectory(treeArtifact,
             pathToExplode.getChild(subpath.getBaseName()), valuesBuilder);
       } else if (subpath.isSymbolicLink()) {
-        throw new IOException(
-            "A TreeArtifact may not contain a symlink, found " + subpath);
+        PathFragment linkTarget = subpath.readSymbolicLinkUnchecked();
+        if (linkTarget.isAbsolute()) {
+          String errorMessage = String.format(
+              "A TreeArtifact may not contain absolute symlinks, found %s pointing to %s.",
+              subpath,
+              linkTarget);
+          throw new IOException(errorMessage);
+        }
+
+        // We visit each path segment of the link target to catch any path traversal outside of the
+        // TreeArtifact root directory. For example, for TreeArtifact a/b/c, it is possible to have
+        // a symlink, a/b/c/sym_link that points to ../outside_dir/../c/link_target. Although this
+        // symlink points to a file under the TreeArtifact, the link target traverses outside of the
+        // TreeArtifact into a/b/outside_dir.
+        PathFragment intermediatePath = canonicalSubpathFragment.getParentDirectory();
+        for (String pathSegment : linkTarget.getSegments()) {
+          intermediatePath = intermediatePath.getRelative(pathSegment).normalize();
+          if (intermediatePath.containsUplevelReferences()) {
+            String errorMessage = String.format(
+                "A TreeArtifact may not contain relative symlinks whose target paths traverse "
+                + "outside of the TreeArtifact, found %s pointing to %s.",
+                subpath,
+                linkTarget);
+            throw new IOException(errorMessage);
+          }
+        }
+        valuesBuilder.add(canonicalSubpathFragment);
       } else if (subpath.isFile()) {
         valuesBuilder.add(canonicalSubpathFragment);
       } else {