diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-23 16:50:35 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-23 16:50:35 +0000 |
commit | ecd4b1f245a857b01bccf0b688f3efe8d9c4a2d2 (patch) | |
tree | 965b351ee04003a046a394a47c977857d972fdf8 | |
parent | e44c7ccb4b56820d5ddac708094fcf918d219c6e (diff) | |
parent | ab52bcdb3475c3c242a89fe3a0d2323e31a6cced (diff) | |
download | libcore-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.java | 24 | ||||
-rw-r--r-- | ojluni/src/main/native/canonicalize_md.c | 106 |
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 } } |