Control: tag -1 + patch

On Tue, Oct 12, 2021 at 05:40:42PM +1000, Alexander Zangerl wrote:
> sorry, but i disagree. yes, it is a bug. but no, it is not severity
> important: a 2000+ levels deep directory hierarchy has no
> practical relevance whatsoever.
> and a workaround exists: don't create ridiculously deep hierarchies ;-)
I mean, yeah, fair enough; I have this explicitly for testing coreutils.

Below you'll find a patch for 0.4b47-2, replacing the static MAXPATHLEN
path buffer in treescan() with a dynamically allocated one. This fixes
both the original bug (down to directory depths I assume no-one
has achieved before), and the 
  ./[very long path redacted]: name exceeds 4096 char
diagnostic I got when I originally bumped ulimit -s.

It does use asprintf(3) ‒ while an extension,
it's universal across the extant Berkeley and Solaris derivatives.

-x mode still panics since the paths *are* longer than PATH_MAX, but you
can't pass those to syscalls anyway and restructuring around that would
be non-trivial (in this case, your workaround should be applied
instead :P).

Best,
наб
Date: Wed, 29 Dec 2021 16:26:07 +0100
From: наб <nabijaczlew...@nabijaczleweli.xyz>
Subject: Don't trust MAXPATHLEN in treescan()

Dynamically allocate the path buffer:
this both allows paths longer than PATH_MAX,
and thins the stack frame considerably
(no longer segfaulting on a Very Deep tree
 on Debian-default 8k ulimit -s)

--- dump-0.4b47.orig/restore/dirs.c
+++ dump-0.4b47/restore/dirs.c
@@ -287,9 +287,8 @@ treescan(char *pname, dump_ino_t ino, lo
 {
        struct inotab *itp;
        struct direct *dp;
-       int namelen;
        off_t bpt;
-       char locname[MAXPATHLEN + 1];
+       char *locname;
 
        itp = inotablookup(ino);
        if (itp == NULL) {
@@ -308,9 +307,6 @@ treescan(char *pname, dump_ino_t ino, lo
         * begin search through the directory
         * skipping over "." and ".."
         */
-       namelen = snprintf(locname, sizeof(locname), "%s/", pname);
-       if (namelen >= (int)sizeof(locname))
-               namelen = sizeof(locname) - 1;
        rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
        dp = rst_readdir(dirp); /* "." */
        if (dp != NULL && strcmp(dp->d_name, ".") == 0)
@@ -328,15 +324,13 @@ treescan(char *pname, dump_ino_t ino, lo
         * a zero inode signals end of directory
         */
        while (dp != NULL) {
-               locname[namelen] = '\0';
-               if (namelen + dp->d_namlen >= (int)sizeof(locname)) {
-                       fprintf(stderr, "%s%s: name exceeds %ld char\n",
-                               locname, dp->d_name, (long)sizeof(locname) - 1);
-               } else {
-                       (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
-                       treescan(locname, dp->d_ino, todo);
-                       rst_seekdir(dirp, bpt, itp->t_seekpt);
+               if (asprintf(&locname, "%s/%.*s", pname, (int)dp->d_namlen, 
dp->d_name) == -1) {
+                       panic("Error: %s\n", strerror(errno));
+                       abort();
                }
+               treescan(locname, dp->d_ino, todo);
+               free(locname);
+               rst_seekdir(dirp, bpt, itp->t_seekpt);
                dp = rst_readdir(dirp);
                bpt = rst_telldir(dirp);
        }

Attachment: signature.asc
Description: PGP signature

Reply via email to