On Sunday 02 December 2007 02:44, Denys Vlasenko wrote:
> Proposed patch uses user-supplied buffer directly,
> without intermediate on-stack copy.
> This can only make a difference if user supplied
> a buffer which is too small - thus user breaks API.
>
> Failure scenario:
>
> realpath("/link_name", user_buffer)
>
> /link_name -> /very_long_name_which_fits_into_PATH_MAX_and_is_also_a_link
> -> -> /shorter_name
>
> If user will give e.g. 40-char user_buffer, current implementation
> will work, patched one will overflow user_buffer by intermediate name.
>
> This should not be a problem - user must supply PATH_MAX sized buffer,
> and in this case patched version also works correctly.

And the following patch on top of previous one reuses copy_buf[]
for readlink, eliminating link_buf[]. In order to make it work,
"source" pathname is kept at the end of copy_buf, not at the
beginning (so that last NUL byte is the last byte of the copy_buf[]).

The situation when readlink returns link name which is too long
(so that it overwrites pathname), was resulting in ENAMETOOLONG
error return. This patch does the same - the fact the we now trash
pathname does not matter, as we are not returning it to the user.

Run tested.

Can somebody review these patches please?
--
vda
--- uClibc.t8/libc/stdlib/realpath.c	2007-12-02 02:18:09.000000000 -0800
+++ uClibc.t9/libc/stdlib/realpath.c	2007-12-02 02:59:11.000000000 -0800
@@ -53,11 +53,9 @@ char got_path[];
 	char copy_path[PATH_MAX];
 	/* use user supplied buffer directly - reduces stack usage */
 	/* char got_path[PATH_MAX]; */
-#ifdef S_IFLNK
-	char link_path[PATH_MAX];
-#endif
 	const char *max_path;
 	char *new_path = got_path;
+	size_t path_len;
 	int readlinks = 0;
 	int n;
 
@@ -70,13 +68,15 @@ char got_path[];
 		return NULL;
 	}
 	/* Make a copy of the source path since we may need to modify it. */
-	if (strlen(path) >= PATH_MAX - 2) {
+	path_len = strlen(path);
+	if (path_len >= PATH_MAX - 2) {
 		__set_errno(ENAMETOOLONG);
 		return NULL;
 	}
-	strcpy(copy_path, path);
-	path = copy_path;
-	max_path = copy_path + PATH_MAX - 2;
+	/* Copy so that data is at the end of copy_path[] */
+	strcpy(copy_path + (PATH_MAX-1) - path_len, path);
+	path = copy_path + (PATH_MAX-1) - path_len;
+	max_path = copy_path + PATH_MAX - 2; /* points to last non-NUL char */
 	/* If it's a relative pathname use getcwd for starters. */
 	if (*path != '/') {
 		/* Ohoo... */
@@ -127,9 +127,10 @@ char got_path[];
 			__set_errno(ELOOP);
 			return NULL;
 		}
+		path_len = strlen(path);
 		/* See if last (so far) pathname component is a symlink. */
 		*new_path = '\0';
-		n = readlink(got_path, link_path, PATH_MAX - 1);
+		n = readlink(got_path, copy_path, PATH_MAX - 1);
 		if (n < 0) {
 			/* EINVAL means the file exists but isn't a symlink. */
 			if (errno != EINVAL) {
@@ -138,22 +139,21 @@ char got_path[];
 			}
 		} else {
 			/* Safe sex check. */
-			if (strlen(path) + n >= PATH_MAX - 2) {
+			if (path_len + n >= PATH_MAX - 2) {
 				__set_errno(ENAMETOOLONG);
 				return NULL;
 			}
 			/* Note: readlink doesn't add the null byte. */
-			link_path[n] = '\0';
-			if (*link_path == '/')
+			copy_path[n] = '\0';
+			if (*copy_path == '/')
 				/* Start over for an absolute symlink. */
 				new_path = got_path;
 			else
 				/* Otherwise back up over this component. */
 				while (*(--new_path) != '/');
-			/* Insert symlink contents into path. */
-			strcpy(link_path + n, path);
-			strcpy(copy_path, link_path);
-			path = copy_path;
+			/* Prepend symlink contents to path. */
+			memmove(copy_path + (PATH_MAX-1) - n - path_len, copy_path, n);
+			path = copy_path + (PATH_MAX-1) - n - path_len;
 		}
 #endif							/* S_IFLNK */
 		*new_path++ = '/';
_______________________________________________
uClibc mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/uclibc

Reply via email to