aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-23 16:50:35 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-23 16:50:35 +0000
commitecd4b1f245a857b01bccf0b688f3efe8d9c4a2d2 (patch)
tree965b351ee04003a046a394a47c977857d972fdf8
parente44c7ccb4b56820d5ddac708094fcf918d219c6e (diff)
parentab52bcdb3475c3c242a89fe3a0d2323e31a6cced (diff)
downloadlibcore-android13-mainline-permission-release.tar.gz
Snap for 9797055 from ab52bcdb3475c3c242a89fe3a0d2323e31a6cced to mainline-permission-releaseaml_per_331913010aml_per_331812030android13-mainline-permission-release
Change-Id: I949d045fd1f299ae006e5c1a17980497ec7ffa7d
-rw-r--r--luni/src/test/java/libcore/java/io/FileTest.java24
-rw-r--r--ojluni/src/main/native/canonicalize_md.c106
2 files changed, 121 insertions, 9 deletions
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index 3705f2b3c85..e61f572e8e3 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -23,6 +23,7 @@ import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -31,6 +32,8 @@ import libcore.io.Libcore;
import static android.system.Os.stat;
+import org.junit.Assert;
+
public class FileTest extends junit.framework.TestCase {
static {
@@ -414,4 +417,25 @@ public class FileTest extends junit.framework.TestCase {
// Did we cache canonical path results? hope not!
assertEquals(symlinkFile.getCanonicalPath(), f2.getCanonicalPath());
}
+
+ public void testGetCanonicalPath_duplicatePathSeparator() throws Exception {
+ assertCanonicalPath("/a//./b", "/a/b");
+ assertCanonicalPath("//a//b", "/a/b");
+ assertCanonicalPath("//a///b", "/a/b");
+ assertCanonicalPath("//a////b", "/a/b");
+ }
+
+ private void assertCanonicalPath(String internalPathValue, String expected) throws Exception {
+ File file = new File("/");
+ Field pathField = File.class.getDeclaredField("path");
+ pathField.setAccessible(true);
+
+ pathField.set(file, internalPathValue);
+ assertEquals(expected, file.getCanonicalFile().getPath());
+ }
+
+ public void testGetCanonicalPath_longPath() {
+ String p = "/a".repeat(2048);
+ Assert.assertThrows(IOException.class, () -> new File(p).getCanonicalFile());
+ }
}
diff --git a/ojluni/src/main/native/canonicalize_md.c b/ojluni/src/main/native/canonicalize_md.c
index b62ba6d10aa..ed926191193 100644
--- a/ojluni/src/main/native/canonicalize_md.c
+++ b/ojluni/src/main/native/canonicalize_md.c
@@ -33,6 +33,7 @@
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
+#include <unistd.h>
#if !defined(_ALLBSD_SOURCE)
#include <alloca.h>
#endif
@@ -42,6 +43,37 @@
defined in the java.io.File class */
+// BEGIN Android-added: Remove consecutive duplicate path separators "//". b/267617531
+// and the trailing path separator `/` if it's not root fs.
+char* removeDupSeparator(char *path)
+{
+ if (path == NULL || *path == '\0') {
+ return NULL;
+ }
+
+ char *in = path;
+ char *out = path;
+ char prevChar = 0;
+ int n = 0;
+ for (; *in != '\0'; in++) {
+ // Remove duplicate path separators
+ if (!(*in == '/' && prevChar == '/')) {
+ *(out++) = *in;
+ n++;
+ }
+ prevChar = *in;
+ }
+ *out = '\0';
+
+ // Remove the trailing path separator, except when path equals `/`
+ if (prevChar == '/' && n > 1) {
+ *(--out) = '\0';
+ }
+
+ return path;
+}
+// END Android-added: Remove consecutive duplicate path separators "//". b/267617531
+
/* Check the given name sequence to see if it can be further collapsed.
Return zero if not, otherwise return the number of names in the sequence. */
@@ -61,7 +93,11 @@ collapsible(char *names)
n++;
while (*p) {
if (*p == '/') {
- p++;
+ // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
+ // p++
+ while (*p == '/') {
+ p++;
+ }
break;
}
p++;
@@ -84,7 +120,11 @@ splitNames(char *names, char **ix)
ix[i++] = p++;
while (*p) {
if (*p == '/') {
- *p++ = '\0';
+ // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
+ // *p++ = '\0';
+ while (*p == '/') {
+ *p++ = '\0';
+ }
break;
}
p++;
@@ -127,11 +167,15 @@ joinNames(char *names, int nc, char **ix)
static void
collapse(char *path)
{
+ // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
+ removeDupSeparator(path);
+
char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */
int nc;
char **ix;
int i, j;
- char *p, *q;
+ // Android-removed: unused variables.
+ // char *p, *q;
nc = collapsible(names);
if (nc < 2) return; /* Nothing to do */
@@ -144,7 +188,9 @@ collapse(char *path)
/* Find next occurrence of "." or ".." */
do {
char *p = ix[i];
- if (p[0] == '.') {
+ // Android-changed: null pointer check.
+ // if (p[0] == '.') {
+ if (p != NULL && p[0] == '.') {
if (p[1] == '\0') {
dots = 1;
break;
@@ -196,7 +242,9 @@ canonicalize(char *original, char *resolved, int len)
return -1;
}
- if (strlen(original) > PATH_MAX) {
+ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ // if (strlen(original) > PATH_MAX) {
+ if (strlen(original) >= PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
@@ -208,13 +256,20 @@ canonicalize(char *original, char *resolved, int len)
return 0;
}
else {
+ // Android-changed: Avoid crash in getCanonicalPath(). b/266432364
+ if (errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOMEM) {
+ return -1;
+ }
+
/* Something's bogus in the original path, so remove names from the end
until either some subpath works or we run out of names */
char *p, *end, *r = NULL;
- char path[PATH_MAX + 1];
+ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ char path[PATH_MAX];
strncpy(path, original, sizeof(path));
- if (path[PATH_MAX] != '\0') {
+ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ if (path[PATH_MAX - 1] != '\0') {
errno = ENAMETOOLONG;
return -1;
}
@@ -253,6 +308,7 @@ canonicalize(char *original, char *resolved, int len)
}
}
+ size_t nameMax;
if (r != NULL) {
/* Append unresolved subpath to resolved subpath */
int rn = strlen(r);
@@ -261,20 +317,52 @@ canonicalize(char *original, char *resolved, int len)
errno = ENAMETOOLONG;
return -1;
}
+
+ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ nameMax = pathconf(r, _PC_NAME_MAX);
+
if ((rn > 0) && (r[rn - 1] == '/') && (*p == '/')) {
/* Avoid duplicate slashes */
p++;
}
strcpy(r + rn, p);
collapse(r);
- return 0;
}
else {
/* Nothing resolved, so just return the original path */
+ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ nameMax = pathconf("/", _PC_NAME_MAX);
strcpy(resolved, path);
collapse(resolved);
- return 0;
}
+
+ // BEGIN Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364
+ // Ensure resolve path length is "< PATH_MAX" and collapse() did not overwrite
+ // terminating null byte
+ char resolvedPath[PATH_MAX];
+ strncpy(resolvedPath, resolved, sizeof(resolvedPath));
+ if (resolvedPath[PATH_MAX - 1] != '\0') {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ // Ensure resolve path does not contain any components who length is "> NAME_MAX"
+ // If pathconf call failed with -1 or returned 0 in case of permission denial
+ if (nameMax < 1) {
+ nameMax = NAME_MAX;
+ }
+
+ char *component;
+ char *rest = resolvedPath;
+ while ((component = strtok_r(rest, "/", &rest))) {
+ if (strlen(component) > nameMax) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ }
+
+ return 0;
+ // END Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364
}
}