Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 28 Nov 1996 22:54:22 -0800 (PST)
From:      vax@linkdead.paranoia.com
To:        freebsd-gnats-submit@freebsd.org
Subject:   bin/2119: mount lies to child about argv0, which causes crunch to lose
Message-ID:  <199611290654.WAA01756@freefall.freebsd.org>
Resent-Message-ID: <199611290700.XAA01988@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         2119
>Category:       bin
>Synopsis:       mount lies to child about argv0, which causes crunch to lose
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Nov 28 23:00:01 PST 1996
>Last-Modified:
>Originator:     VaX#n8
>Organization:
League of Non-aligned Wizards
>Release:        HEAD revisions
>Environment:
I'm using your CVS/WWW interface and notice you have the same problem
as NetBSD.
>Description:
The program /sbin/mount lies to child about argv[0], saying that it is "ffs"
instead of "mount_ffs".  This is wrong, doesn't really save much work
(if any), and confuses crunched executables, which branch in main() based
on argv[0].  While it might be possible to put in "ffs" as an alias for
"mount_ffs", this pollutes the namespace and conflicts with an identical
problem in NetBSD's fsck which I will submit seperately.

The code calls exit() rather than _exit() in the child if it fails
to execve().  This is wrong.  Quoth vfork(2):
     Be careful, also, to call _exit
     rather than exit if you can't execve,  since exit will flush and close
     standard I/O channels, and thereby mess up the parent processes standard
     I/O data structures.  (Even with fork it is wrong to call exit since
     buffered data would then be flushed twice.)

One of the tests for the debug flag which enabled some output was
supposed to be a test for the verbose flag --- the debug flag
is supposed to prevent execing the sub-program.

Our fsck had a special GNUC thing to protect optbuf from vfork clobbering.
I assume that is since optbuf is a dynamically allocated object.
Since I do not understand it completely, it is possible that it is
not necessary in this chunk of code.

>How-To-Repeat:
bash# mount /usr
ffs: /dev/sd1e on /usr: Device busy

That second line is generated by mount_ffs, and the label is the value
of argv[0].

bash# mount -v /mnt
/dev/fd0a on /mnt type ffs (local)
bash# mount -dv /mnt
exec: mount_ffs -o noauto /dev/fd0a /mnt

Whoops! Option "-v" doesn't really give us information on the exec call.
I was considering moving the printf loop into the child but didn't
know what the implications of writing to stdout in the child would be.

bash# cc -O  -Werror -Wall   -c /usr/src/sbin/mount/mount.c

I evaluated the FreeBSD error handling in the edirs/execve loop and
decided I didn't like it -- it never reports the error if the first
exec generates one (e.g. ENOEXEC).

>Fix:
Patch for NetBSD but should give an idea

--- sbin/mount/mount.c~ Thu Oct 24 06:13:48 1996
+++ sbin/mount/mount.c  Thu Nov 28 23:35:55 1996
@@ -243,7 +243,7 @@
         */
        if (rval == 0 && getuid() == 0 &&
            (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
-               if (fscanf(mountdfp, "%ld", &pid) == 1 &&
+               if (fscanf(mountdfp, "%ld", (long *) &pid) == 1 &&
                     pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
                        err(1, "signal mountd");
                (void)fclose(mountdfp);
@@ -288,12 +288,19 @@
                _PATH_USRSBIN,
                NULL
        };
-       const char *argv[100], **edir;
+       char execbase[MAXPATHLEN] = "mount_";
+       const char *argv[100] = { (const char *)execbase }, **edir;
        struct statfs sf;
        pid_t pid;
-       int argc, i, status;
+       int argc = 1, i, status;
        char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN];
 
+#ifdef __GNUC__
+        /* Avoid vfork clobbering */
+        (void) &optbuf;
+        (void) &name;
+#endif
+
        if (realpath(name, mntpath) == NULL) {
                warn("realpath %s", name);
                return (1);
@@ -344,16 +351,23 @@
        if (flags & MNT_UPDATE)
                optbuf = catopt(optbuf, "update");
 
-       argc = 0;
-       argv[argc++] = vfstype;
+       /* construct basename of executable and argv[0] simultaneously */
+       (void)strncat(execbase,
+                     (const char *)vfstype,
+                     sizeof(execbase) - 6);  /* strlen("mount_") + \0 */
        mangle(optbuf, &argc, argv);
        argv[argc++] = spec;
        argv[argc++] = name;
        argv[argc] = NULL;
 
-       if (debug) {
-               (void)printf("exec: mount_%s", vfstype);
-               for (i = 1; i < argc; i++)
+       /*
+        * XXX
+        * perhaps this belongs just before each exec call in the child
+        * if you move it there, move the debug-related _exit(0) too
+        */
+       if (verbose) {
+               (void)printf("exec:");
+               for (i = 0; i < argc; i++)
                        (void)printf(" %s", argv[i]);
                (void)printf("\n");
                return (0);
@@ -365,11 +379,14 @@
                free(optbuf);
                return (1);
        case 0:                                 /* Child. */
+                if (debug)
+                        _exit(0);
+
                /* Go find an executable. */
                edir = edirs;
                do {
                        (void)snprintf(execname,
-                           sizeof(execname), "%s/mount_%s", *edir, vfstype);
+                           sizeof(execname), "%s/%s", *edir, execbase);
                        execv(execname, (char * const *)argv);
                        if (errno != ENOENT)
                                warn("exec %s for %s", execname, name);
@@ -377,7 +394,7 @@
 
                if (errno == ENOENT)
                        warn("exec %s for %s", execname, name);
-               exit(1);
+               _exit(1);
                /* NOTREACHED */
        default:                                /* Parent. */
                free(optbuf);
@@ -487,14 +504,14 @@
                which = IN_LIST;
 
        /* Count the number of types. */
-       for (i = 1, nextcp = fslist; nextcp = strchr(nextcp, ','); i++)
+       for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++)
                ++nextcp;
 
        /* Build an array of that many types. */
        if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL)
-               err(1, NULL);
+               err(1, "mallocing typelist");
        av[0] = fslist;
-       for (i = 1, nextcp = fslist; nextcp = strchr(nextcp, ','); i++) {
+       for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) {
                *nextcp = '\0';
                av[i] = ++nextcp;
        }
@@ -513,7 +530,7 @@
        if (s0 && *s0) {
                i = strlen(s0) + strlen(s1) + 1 + 1;
                if ((cp = malloc(i)) == NULL)
-                       err(1, NULL);
+                       err(1, "mallocing options");
                (void)snprintf(cp, i, "%s,%s", s0, s1);
        } else
                cp = strdup(s1);

>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199611290654.WAA01756>