0
0
mirror of https://github.com/termux/termux-packages.git synced 2024-12-12 13:03:31 +00:00
termux-packages/packages/openjdk-17/0037-Fix-bugs-in-File.getCanonicalPath.patch

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