Allow fchdir() from non-O_PATH fds
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 19 Jun 2018 17:28:04 +0000 (13:28 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 19 Jun 2018 17:28:04 +0000 (13:28 -0400)
If you chdir to an open FD, later when you did something like create a
file from DOT, we'd panic.  The FD in current->dot was opened and not
O_PATH, which you can't walk from (see devclone()).

The regular sys_chdir does a namec(Atodir), which does not call the
device's open, so that chan is a kernel-internal, non-open chan (to use
the parlance of devclone()).

The current fix works for most cases.  If you do something like
dfd = open(some_dir, O_READ); unlink(some_dir); fchdir(dfd);, the lookup
will fail.  Using O_PATH instead of O_READ should work.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/src/ns/sysfile.c

index c1a6780..dc6cf1f 100644 (file)
@@ -177,7 +177,27 @@ int sysfchdir(int fd)
        }
        c = fdtochan(&current->open_files, fd, -1, 0, 1);
        poperror();
-       set_dot(c);
+
+       /* This is a little hokey.  Ideally, we'd only allow O_PATH fds to be
+        * fchdir'd.  Linux/POSIX lets you do arbitrary FDs.  Luckily, we stored the
+        * name when we walked (__namec_from), so we should be able to recreate the
+        * chan.  Using namec() with channame() is a more heavy-weight cclone(), but
+        * also might have issues if the chan has since been removed or the
+        * namespace is otherwise different from when the original fd/chan was first
+        * created. */
+       if (c->flag & O_PATH) {
+               set_dot(c);
+               return 0;
+       }
+       if (waserror()) {
+               cclose(c);
+               poperror();
+               return -1;
+       }
+       syschdir(channame(c));
+       cclose(c);
+       poperror();
+
        return 0;
 }