Make SYS_chdir affect other processes
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 2 Oct 2018 19:55:13 +0000 (15:55 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 2 Oct 2018 19:55:13 +0000 (15:55 -0400)
Our chdir() takes a pid.  A controlling process (e.g. a parent) can change
the directory of a child.  This is meant to be done after proc_create and
before proc_run; the parent can set up the child before it starts running,
which avoids all sorts of nasty issues that happen in the fork() world.

Unfortunately, our chdir wasn't doing that since we got rid of the VFS.
This fixes a Go test that we had always skipped (TestStartProcess).

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

index f861d46..6570985 100644 (file)
@@ -1068,8 +1068,8 @@ struct chan *fdtochan(struct fd_table *fdt, int fd, int mode, int chkmnt,
 long kchanio(void *vc, void *buf, int n, int mode);
 int openmode(uint32_t o);
 void fdclose(struct fd_table *fdt, int fd);
-int syschdir(char *path);
-int sysfchdir(int fd);
+int syschdir(struct proc *target, char *path);
+int sysfchdir(struct proc *target, int fd);
 int grpclose(struct fd_table *fdt, int fd);
 int sysclose(int fd);
 int syscreate(char *path, int mode, uint32_t perm);
index 86c4fa9..ad13b8b 100644 (file)
@@ -144,14 +144,15 @@ void fdclose(struct fd_table *fdt, int fd)
        close_fd(fdt, fd);
 }
 
-static void set_dot(struct chan *c)
+static void set_dot(struct proc *p, struct chan *c)
 {
-       c = atomic_swap_ptr((void**)&current->dot, c);
+       c = atomic_swap_ptr((void**)&p->dot, c);
        synchronize_rcu();
        cclose(c);
 }
 
-int syschdir(char *path)
+/* Note namec() happens in the namespace of the caller. */
+int syschdir(struct proc *target, char *path)
 {
        ERRSTACK(1);
        struct chan *c;
@@ -162,11 +163,12 @@ int syschdir(char *path)
        }
        c = namec(path, Atodir, 0, 0, NULL);
        poperror();
-       set_dot(c);
+       set_dot(target, c);
        return 0;
 }
 
-int sysfchdir(int fd)
+/* Note fdtochan() happens with the FDs of the caller. */
+int sysfchdir(struct proc *target, int fd)
 {
        ERRSTACK(1);
        struct chan *c;
@@ -186,7 +188,7 @@ int sysfchdir(int fd)
         * namespace is otherwise different from when the original fd/chan was first
         * created. */
        if (c->flag & O_PATH) {
-               set_dot(c);
+               set_dot(target, c);
                return 0;
        }
        if (waserror()) {
@@ -194,7 +196,7 @@ int sysfchdir(int fd)
                poperror();
                return -1;
        }
-       syschdir(channame(c));
+       syschdir(target, channame(c));
        cclose(c);
        poperror();
 
index b3dabcb..677408a 100644 (file)
@@ -557,11 +557,11 @@ static struct proc *get_controllable_proc(struct proc *p, pid_t pid)
 {
        struct proc *target = pid2proc(pid);
        if (!target) {
-               set_errno(ESRCH);
+               set_error(ESRCH, "no proc for pid %d", pid);
                return 0;
        }
        if (!proc_controls(p, target)) {
-               set_errno(EPERM);
+               set_error(EPERM, "can't control pid %d", pid);
                proc_decref(target);
                return 0;
        }
@@ -1981,12 +1981,17 @@ static intreg_t sys_chdir(struct proc *p, pid_t pid, const char *path,
 
        if (!target)
                return -1;
+       if ((target != p) && (target->state != PROC_CREATED)) {
+               proc_decref(target);
+               set_error(EINVAL, "pid %d has already started", pid);
+               return -1;
+       }
        t_path = copy_in_path(p, path, path_l);
        if (!t_path) {
                proc_decref(target);
                return -1;
        }
-       retval = syschdir(t_path);
+       retval = syschdir(target, t_path);
        free_path(p, t_path);
        proc_decref(target);
        return retval;
@@ -1999,7 +2004,12 @@ static intreg_t sys_fchdir(struct proc *p, pid_t pid, int fd)
 
        if (!target)
                return -1;
-       retval = sysfchdir(fd);
+       if ((target != p) && (target->state != PROC_CREATED)) {
+               proc_decref(target);
+               set_error(EINVAL, "pid %d has already started", pid);
+               return -1;
+       }
+       retval = sysfchdir(target, fd);
        proc_decref(target);
        return retval;
 }