mirror of
https://github.com/termux/termux-packages.git
synced 2024-12-12 13:03:31 +00:00
226 lines
6.9 KiB
Diff
226 lines
6.9 KiB
Diff
From d7ca77f10800afc2485fe162869cf23b4d0c90f7 Mon Sep 17 00:00:00 2001
|
|
From: agnostic-apollo <agnosticapollo@gmail.com>
|
|
Date: Thu, 28 Mar 2024 18:46:02 +0100
|
|
Subject: [PATCH] Fix bugs in File.getCanonicalPath()
|
|
|
|
1. If path length is `>= PATH_MAX` (4096) characters, return error.
|
|
2. If any path component length is `> NAME_MAX` (255) characters, return
|
|
error.
|
|
3. If path has consecutive path separators e.g. `//`, remove them before
|
|
processing and returning.
|
|
4. Ensure leading double dot `..` path components are removed since a
|
|
canonical path returned by canonicalize() must not contain them.
|
|
---
|
|
.../unix/native/libjava/canonicalize_md.c | 102 +++++++++++++++---
|
|
1 file changed, 87 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/src/java.base/unix/native/libjava/canonicalize_md.c b/src/java.base/unix/native/libjava/canonicalize_md.c
|
|
index 2bb896bf32d7..7210f305282f 100644
|
|
--- a/src/java.base/unix/native/libjava/canonicalize_md.c
|
|
+++ b/src/java.base/unix/native/libjava/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
|
|
@@ -43,6 +44,38 @@
|
|
defined in the java.io.File class */
|
|
|
|
|
|
+/* Remove consecutive duplicate path separators `//` and the trailing
|
|
+ path separator if not rootfs `/`. */
|
|
+
|
|
+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;
|
|
+}
|
|
+
|
|
+
|
|
/* 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. */
|
|
|
|
@@ -62,7 +95,9 @@ collapsible(char *names)
|
|
n++;
|
|
while (*p) {
|
|
if (*p == '/') {
|
|
- p++;
|
|
+ while (*p && *p == '/') {
|
|
+ p++;
|
|
+ }
|
|
break;
|
|
}
|
|
p++;
|
|
@@ -85,7 +120,9 @@ splitNames(char *names, char **ix)
|
|
ix[i++] = p++;
|
|
while (*p) {
|
|
if (*p == '/') {
|
|
- *p++ = '\0';
|
|
+ while (*p && *p == '/') {
|
|
+ *p++ = '\0';
|
|
+ }
|
|
break;
|
|
}
|
|
p++;
|
|
@@ -128,11 +165,13 @@ joinNames(char *names, int nc, char **ix)
|
|
static void
|
|
collapse(char *path)
|
|
{
|
|
+ // Remove consecutive duplicate path separators '/' regardless of if single or double dot components exist
|
|
+ removeDupSeparator(path);
|
|
+
|
|
char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */
|
|
int nc;
|
|
char **ix;
|
|
int i, j;
|
|
- char *p, *q;
|
|
|
|
nc = collapsible(names);
|
|
if (nc < 2) return; /* Nothing to do */
|
|
@@ -145,7 +184,7 @@ collapse(char *path)
|
|
/* Find next occurrence of "." or ".." */
|
|
do {
|
|
char *p = ix[i];
|
|
- if (p[0] == '.') {
|
|
+ if (p != NULL && p[0] == '.') {
|
|
if (p[1] == '\0') {
|
|
dots = 1;
|
|
break;
|
|
@@ -166,14 +205,14 @@ collapse(char *path)
|
|
ix[i] = 0;
|
|
}
|
|
else {
|
|
- /* If there is a preceding name, remove both that name and this
|
|
- instance of ".."; otherwise, leave the ".." as is */
|
|
+ /* Remove this instance of ".." and any preceding name if one exists */
|
|
for (j = i - 1; j >= 0; j--) {
|
|
if (ix[j]) break;
|
|
}
|
|
+
|
|
+ ix[i] = 0;
|
|
if (j < 0) continue;
|
|
ix[j] = 0;
|
|
- ix[i] = 0;
|
|
}
|
|
/* i will be incremented at the top of the loop */
|
|
}
|
|
@@ -195,7 +234,7 @@ JDK_Canonicalize(const char *orig, char *out, int len)
|
|
return -1;
|
|
}
|
|
|
|
- if (strlen(orig) > PATH_MAX) {
|
|
+ if (strlen(orig) >= PATH_MAX) {
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
@@ -206,15 +245,20 @@ JDK_Canonicalize(const char *orig, char *out, int len)
|
|
collapse(out);
|
|
return 0;
|
|
} else {
|
|
+ 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];
|
|
+ char path[PATH_MAX];
|
|
|
|
- // strlen(orig) <= PATH_MAX, see above
|
|
- strncpy(path, orig, PATH_MAX);
|
|
- // append null for == case
|
|
- path[PATH_MAX] = '\0';
|
|
+ strncpy(path, orig, sizeof(path));
|
|
+ if (path[PATH_MAX - 1] != '\0') {
|
|
+ errno = ENAMETOOLONG;
|
|
+ return -1;
|
|
+ }
|
|
end = path + strlen(path);
|
|
|
|
for (p = end; p > path;) {
|
|
@@ -242,6 +286,7 @@ JDK_Canonicalize(const char *orig, char *out, int len)
|
|
}
|
|
}
|
|
|
|
+ size_t nameMax;
|
|
if (r != NULL) {
|
|
/* Append unresolved subpath to resolved subpath */
|
|
int rn = strlen(r);
|
|
@@ -250,18 +295,45 @@ JDK_Canonicalize(const char *orig, char *out, int len)
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
+ 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 */
|
|
+ nameMax = pathconf("/", _PC_NAME_MAX);
|
|
strcpy(out, path);
|
|
collapse(out);
|
|
- return 0;
|
|
}
|
|
+
|
|
+ // Ensure resolve path length is "< PATH_MAX" and collapse() did not overwrite
|
|
+ // terminating null byte
|
|
+ char resolvedPath[PATH_MAX];
|
|
+ strncpy(resolvedPath, out, 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;
|
|
}
|
|
}
|
|
--
|
|
2.44.0
|
|
|