Browse Source

[LibOS] Rework of IPC subsystem's creation of processes

Previously, Graphene incorrectly treated execve() under SGX PAL:
Graphene would emulate execve() as fork + execve, and the new forked
process didn't try to "assume" the identity of its parent (which
violated execve specification "all process attributes are preserved").

This commit reworks the implementations of clone/fork and execve. In
particular, the IPC subsystem clearly distinguishes between the two
cases: clone/fork works as before whereas execve forks new "real"
process (which starts executing the requested program) and silently
exits the now-useless "temporary" process. New "real" process assumes
the identity of "temporary" process by inheriting its VMID (ID of
process for IPC purposes) and IPC-info objects with their PAL handles.

This commit also cleans up initialization of four IPC-info objects: self
(creates process-unique server pipe for IPC), parent (holds pipe for IPC
with parent process), and two namespace leaders (hold pipes for IPC with
leader processes). To correctly identify new-process server pipe, the
implementation of create_pipe() now allows to create VMID-based pipe URI.
Dmitrii Kuvaiskii 6 years ago
parent
commit
b0619cb7a6

+ 2 - 4
LibOS/shim/include/shim_ipc.h

@@ -447,10 +447,10 @@ int ipc_sysv_semreply_callback (IPC_CALLBACK_ARGS);
 int init_ipc(void);
 int init_ipc_helper(void);
 
-struct shim_process* create_process(void);
+struct shim_process* create_process(bool dup_cur_process);
 void free_process(struct shim_process* process);
 
-struct shim_ipc_info* create_ipc_info_cur_process(void);
+struct shim_ipc_info* create_ipc_info_cur_process(bool is_self_ipc_info);
 int get_ipc_info_cur_process(struct shim_ipc_info** pinfo);
 
 enum {
@@ -516,8 +516,6 @@ void ipc_port_with_child_fini(struct shim_ipc_port* port, IDTYPE vmid, unsigned
 
 struct shim_thread* terminate_ipc_helper(void);
 
-#define IPC_FORCE_RECONNECT     ((void*)-1)
-
 int prepare_ns_leaders(void);
 
 #endif /* _SHIM_IPC_H_ */

+ 1 - 1
LibOS/shim/include/shim_utils.h

@@ -223,7 +223,7 @@ void clean_link_map_list (void);
 /* create unique files/pipes */
 #define PIPE_URI_SIZE   40
 int create_pipe (IDTYPE * pipeid, char * uri, size_t size, PAL_HANDLE * hdl,
-                 struct shim_qstr * qstr);
+                 struct shim_qstr * qstr, bool use_vmid_for_name);
 int create_dir (const char * prefix, char * path, size_t size,
                 struct shim_handle ** hdl);
 int create_file (const char * prefix, char * path, size_t size,

+ 4 - 0
LibOS/shim/src/bookkeep/shim_thread.c

@@ -656,6 +656,8 @@ BEGIN_RS_FUNC(thread)
     struct shim_thread * thread = (void *) (base + GET_CP_FUNC_ENTRY());
     __UNUSED(offset);
 
+    thread->vmid = cur_process.vmid;
+
     CP_REBASE(thread->children);
     CP_REBASE(thread->siblings);
     CP_REBASE(thread->exited_children);
@@ -739,6 +741,8 @@ BEGIN_RS_FUNC(running_thread)
     struct shim_thread * cur_thread = get_cur_thread();
     thread->in_vm = true;
 
+    thread->vmid = cur_process.vmid;
+
     if (!thread->user_tcb)
         CP_REBASE(thread->tcb);
 

+ 72 - 14
LibOS/shim/src/ipc/shim_ipc.c

@@ -202,22 +202,76 @@ struct shim_ipc_info* lookup_ipc_info(IDTYPE vmid) {
     return NULL;
 }
 
-struct shim_process* create_process(void) {
+struct shim_process* create_process(bool dup_cur_process) {
     struct shim_process* new_process = calloc(1, sizeof(struct shim_process));
     if (!new_process)
         return NULL;
 
-    new_process->parent = create_ipc_info(cur_process.vmid, NULL, 0);
-
     lock(&cur_process.lock);
 
-    if (cur_process.self)
-        qstrcopy(&new_process->parent->uri, &cur_process.self->uri);
+    /* current process must have been initialized with info on its own IPC info */
+    assert(cur_process.self);
+    assert(cur_process.self->pal_handle && !qstrempty(&cur_process.self->uri));
+
+    if (dup_cur_process) {
+        /* execve case, new process assumes identity of current process and thus has
+         * - same vmid as current process
+         * - same self IPC info as current process
+         * - same parent IPC info as current process
+         */
+        new_process->vmid = cur_process.vmid;
+
+        new_process->self = create_ipc_info(cur_process.self->vmid,
+                                            qstrgetstr(&cur_process.self->uri),
+                                            cur_process.self->uri.len);
+        new_process->self->pal_handle = cur_process.self->pal_handle;
+        if (!new_process->self) {
+            unlock(&cur_process.lock);
+            return NULL;
+        }
+
+        /* there is a corner case of execve in very first process; such process does
+         * not have parent process, so cannot copy parent IPC info */
+        if (cur_process.parent) {
+            new_process->parent = create_ipc_info(cur_process.parent->vmid,
+                                                  qstrgetstr(&cur_process.parent->uri),
+                                                  cur_process.parent->uri.len);
+            new_process->parent->pal_handle = cur_process.parent->pal_handle;
+        }
+    }
+    else {
+        /* fork/clone case, new process has new identity but inherits parent  */
+        new_process->vmid = 0;
+        new_process->self = NULL;
+        new_process->parent = create_ipc_info(cur_process.self->vmid,
+                                              qstrgetstr(&cur_process.self->uri),
+                                              cur_process.self->uri.len);
+    }
+
+    if (cur_process.parent && !new_process->parent) {
+        if (new_process->self)
+            put_ipc_info(new_process->self);
+        unlock(&cur_process.lock);
+        return NULL;
+    }
+
+    /* new process inherits the same namespace leaders */
     for (int i = 0; i < TOTAL_NS; i++) {
-        if (cur_process.ns[i])
+        if (cur_process.ns[i]) {
             new_process->ns[i] = create_ipc_info(cur_process.ns[i]->vmid,
                                                  qstrgetstr(&cur_process.ns[i]->uri),
                                                  cur_process.ns[i]->uri.len);
+            if (!new_process->ns[i]) {
+                if (new_process->self)
+                    put_ipc_info(new_process->self);
+                if (new_process->parent)
+                    put_ipc_info(new_process->parent);
+                for (int j = 0; j < i; j++)
+                    put_ipc_info(new_process->ns[j]);
+                unlock(&cur_process.lock);
+                return NULL;
+            }
+        }
     }
 
     unlock(&cur_process.lock);
@@ -352,19 +406,19 @@ out:
 }
 
 /* must be called with cur_process.lock taken */
-struct shim_ipc_info* create_ipc_info_cur_process(void) {
+struct shim_ipc_info* create_ipc_info_cur_process(bool is_self_ipc_info) {
     struct shim_ipc_info* info = create_ipc_info(cur_process.vmid, NULL, 0);
     if (!info)
         return NULL;
 
+    /* pipe for cur_process.self is of format "pipe:<cur_process.vmid>", others with random name */
     char uri[PIPE_URI_SIZE];
-    if (create_pipe(NULL, uri, PIPE_URI_SIZE, &info->pal_handle,
-                    &info->uri) < 0) {
+    if (create_pipe(NULL, uri, PIPE_URI_SIZE, &info->pal_handle, &info->uri, is_self_ipc_info) < 0) {
         put_ipc_info(info);
         return NULL;
     }
 
-    add_ipc_port_by_id(0, info->pal_handle, IPC_PORT_SERVER,
+    add_ipc_port_by_id(cur_process.vmid, info->pal_handle, IPC_PORT_SERVER,
             NULL, &info->port);
 
     return info;
@@ -374,7 +428,7 @@ int get_ipc_info_cur_process(struct shim_ipc_info** info) {
     lock(&cur_process.lock);
 
     if (!cur_process.self) {
-        cur_process.self = create_ipc_info_cur_process();
+        cur_process.self = create_ipc_info_cur_process(true);
         if (!cur_process.self) {
             unlock(&cur_process.lock);
             return -EACCES;
@@ -457,7 +511,7 @@ BEGIN_CP_FUNC(ipc_info) {
         /* call qstr-specific checkpointing function for new_info->uri */
         DO_CP_IN_MEMBER(qstr, new_info, uri);
 
-        if (info->pal_handle && info->pal_handle != IPC_FORCE_RECONNECT) {
+        if (info->pal_handle) {
             struct shim_palhdl_entry* entry;
             /* call palhdl-specific checkpointing function to checkpoint
              * info->pal_handle and return created object in entry */
@@ -517,12 +571,17 @@ BEGIN_RS_FUNC(process) {
     __UNUSED(offset);
     struct shim_process* process = (void *) (base + GET_CP_FUNC_ENTRY());
 
+    /* process vmid  = 0: fork/clone case, forces to pick up new host-OS vmid
+     * process vmid != 0: execve case, forces to re-use vmid of parent */
+    if (!process->vmid)
+        process->vmid = cur_process.vmid;
+
     CP_REBASE(process->self);
     CP_REBASE(process->parent);
     CP_REBASE(process->ns);
 
     if (process->self) {
-        process->self->vmid = cur_process.vmid;
+        process->self->vmid = process->vmid;
         get_ipc_info(process->self);
     }
     if (process->parent)
@@ -531,7 +590,6 @@ BEGIN_RS_FUNC(process) {
         if (process->ns[i])
             get_ipc_info(process->ns[i]);
 
-    process->vmid = cur_process.vmid;
     memcpy(&cur_process, process, sizeof(struct shim_process));
     create_lock(&cur_process.lock);
 

+ 83 - 27
LibOS/shim/src/ipc/shim_ipc_helper.c

@@ -90,47 +90,105 @@ static ipc_callback ipc_callbacks[IPC_CODE_NUM] = {
     /* SYSV_SEMMOV      */  &ipc_sysv_semmov_callback,
 };
 
-static int init_ipc_port(struct shim_ipc_info* info, PAL_HANDLE hdl, IDTYPE type) {
-    if (!info)
+static int init_self_ipc_port(void) {
+    lock(&cur_process.lock);
+
+    if (!cur_process.self) {
+        /* very first process or clone/fork case: create IPC port from scratch */
+        cur_process.self = create_ipc_info_cur_process(/*is_self_ipc_info*/ true);
+        if (!cur_process.self) {
+            unlock(&cur_process.lock);
+            return -EACCES;
+        }
+    } else {
+        /* execve case: inherited IPC port from parent process */
+        assert(cur_process.self->pal_handle && !qstrempty(&cur_process.self->uri));
+
+        add_ipc_port_by_id(cur_process.self->vmid,
+                           cur_process.self->pal_handle,
+                           IPC_PORT_SERVER,
+                           /* fini callback */ NULL,
+                           &cur_process.self->port);
+    }
+
+    unlock(&cur_process.lock);
+    return 0;
+}
+
+static int init_parent_ipc_port(void) {
+    if (!PAL_CB(parent_process) || !cur_process.parent) {
+        /* no parent process, no sense in creating parent IPC port */
+        return 0;
+    }
+
+    lock(&cur_process.lock);
+    assert(cur_process.parent && cur_process.parent->vmid);
+
+    /* for execve case, my parent is the parent of my parent (current
+     * process transparently inherits the "real" parent through already
+     * opened pal_handle on "temporary" parent's cur_process.parent) */
+    if (!cur_process.parent->pal_handle) {
+        /* for clone/fork case, parent is connected on parent_process */
+        cur_process.parent->pal_handle = PAL_CB(parent_process);
+    }
+
+    add_ipc_port_by_id(cur_process.parent->vmid,
+                       cur_process.parent->pal_handle,
+                       IPC_PORT_DIRPRT | IPC_PORT_LISTEN,
+                       /* fini callback */ NULL,
+                       &cur_process.parent->port);
+
+    unlock(&cur_process.lock);
+    return 0;
+}
+
+static int init_ns_ipc_port(int ns_idx) {
+    if (!cur_process.ns[ns_idx]) {
+        /* no NS info from parent process, no sense in creating NS IPC port */
+        return 0;
+    }
+
+    if (!cur_process.ns[ns_idx]->pal_handle && qstrempty(&cur_process.ns[ns_idx]->uri)) {
+        /* there is no connection to NS leader via PAL handle and there is no
+         * URI to find NS leader: do not create NS IPC port now, it will be
+         * created on-demand during NS leader lookup */
         return 0;
+    }
+
+    lock(&cur_process.lock);
 
-    if (info->pal_handle == IPC_FORCE_RECONNECT) {
-        info->pal_handle = NULL;
-        if (!hdl && !qstrempty(&info->uri)) {
-            debug("Reconnecting IPC port %s\n", qstrgetstr(&info->uri));
-            hdl = DkStreamOpen(qstrgetstr(&info->uri), 0, 0, 0, 0);
-            if (!hdl)
-                return -PAL_ERRNO;
+    if (!cur_process.ns[ns_idx]->pal_handle) {
+        debug("Reconnecting IPC port %s\n", qstrgetstr(&cur_process.ns[ns_idx]->uri));
+        cur_process.ns[ns_idx]->pal_handle = DkStreamOpen(qstrgetstr(&cur_process.ns[ns_idx]->uri), 0, 0, 0, 0);
+        if (!cur_process.ns[ns_idx]->pal_handle) {
+            unlock(&cur_process.lock);
+            return -PAL_ERRNO;
         }
-        info->pal_handle = hdl;
     }
 
-    if (!info->pal_handle)
-        info->pal_handle = hdl;
+    IDTYPE type = (ns_idx == PID_NS) ? IPC_PORT_PIDLDR : IPC_PORT_SYSVLDR;
+    add_ipc_port_by_id(cur_process.ns[ns_idx]->vmid,
+                       cur_process.ns[ns_idx]->pal_handle,
+                       type | IPC_PORT_LISTEN,
+                       /* fini callback */ NULL,
+                       &cur_process.ns[ns_idx]->port);
 
-    if (info->pal_handle)
-        add_ipc_port_by_id(info->vmid == cur_process.vmid ? 0 : info->vmid,
-                           info->pal_handle, type, NULL, &info->port);
+    unlock(&cur_process.lock);
     return 0;
 }
 
 int init_ipc_ports(void) {
-    int ret = 0;
-
     if (!(port_mgr = create_mem_mgr(init_align_up(PORT_MGR_ALLOC))))
         return -ENOMEM;
 
-    if ((ret = init_ipc_port(cur_process.self, NULL, IPC_PORT_SERVER)) < 0)
+    int ret;
+    if ((ret = init_self_ipc_port()) < 0)
         return ret;
-    if (PAL_CB(parent_process) &&
-        (ret = init_ipc_port(cur_process.parent, PAL_CB(parent_process),
-                             IPC_PORT_DIRPRT|IPC_PORT_LISTEN)) < 0)
+    if ((ret = init_parent_ipc_port()) < 0)
         return ret;
-    if ((ret = init_ipc_port(cur_process.ns[PID_NS], NULL,
-                             IPC_PORT_PIDLDR|IPC_PORT_LISTEN)) < 0)
+    if ((ret = init_ns_ipc_port(PID_NS)) < 0)
         return ret;
-    if ((ret = init_ipc_port(cur_process.ns[SYSV_NS], NULL,
-                             IPC_PORT_SYSVLDR|IPC_PORT_LISTEN)) < 0)
+    if ((ret = init_ns_ipc_port(SYSV_NS)) < 0)
         return ret;
 
     return 0;
@@ -207,8 +265,6 @@ void put_ipc_port(struct shim_ipc_port* port) {
 
 static void __add_ipc_port(struct shim_ipc_port* port, IDTYPE vmid,
                            IDTYPE type, port_fini fini) {
-    assert(vmid != cur_process.vmid); /* no sense in IPCing myself */
-
     port->type |= type;
     if (vmid && !port->vmid)
         port->vmid = vmid;

+ 4 - 2
LibOS/shim/src/ipc/shim_ipc_nsimpl.h

@@ -770,7 +770,8 @@ static void __discover_ns (bool block, bool need_locate)
     if (NS_LEADER) {
         if (NS_LEADER->vmid == cur_process.vmid) {
             if (need_locate && qstrempty(&NS_LEADER->uri)) {
-                struct shim_ipc_info * info = create_ipc_info_cur_process();
+                bool is_self_ipc_info = false; /* not cur_process.self but cur_process.ns */
+                struct shim_ipc_info * info = create_ipc_info_cur_process(is_self_ipc_info);
                 if (info) {
                     put_ipc_info(NS_LEADER);
                     NS_LEADER = info;
@@ -816,7 +817,8 @@ static void __discover_ns (bool block, bool need_locate)
         goto out;
     }
 
-    if (!(NS_LEADER = create_ipc_info_cur_process()))
+    bool is_self_ipc_info = false; /* not cur_process.self but cur_process.ns */
+    if (!(NS_LEADER = create_ipc_info_cur_process(is_self_ipc_info)))
         goto out;
 
     // Finally, set the IPC port as a leadership port

+ 42 - 42
LibOS/shim/src/shim_checkpoint.c

@@ -949,7 +949,7 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
 
     if (!proc) {
         ret = -PAL_ERRNO;
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_create_process);
@@ -976,21 +976,10 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
     }
 
     /* Create process and IPC bookkeepings */
-    if (!(new_process = create_process())) {
-        ret = -ENOMEM;
-        goto err;
-    }
-
-    if (!(new_process->self = create_ipc_info(0, NULL, 0))) {
-        ret = -EACCES;
-        goto err;
-    }
-
-    char pipe_uri[PIPE_URI_SIZE];
-    if (create_pipe(NULL, pipe_uri, PIPE_URI_SIZE, &new_process->self->pal_handle,
-                    &new_process->self->uri) < 0) {
+    new_process = create_process(exec ? /*execve case*/ true : /*fork case*/ false);
+    if (!new_process) {
         ret = -EACCES;
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_connect_ipc);
@@ -1019,19 +1008,20 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
     if (!cpstore.base) {
         ret = -ENOMEM;
         debug("failed creating checkpoint store\n");
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_init_checkpoint);
 
-    /* Calling the migration function defined by the caller. */
+    /* Calling the migration function defined by caller. The thread argument
+     * is new thread in case of fork/clone and cur_thread in case of execve. */
     va_list ap;
     va_start(ap, thread);
     ret = (*migrate) (&cpstore, thread, new_process, ap);
     va_end(ap);
     if (ret < 0) {
         debug("failed creating checkpoint (ret = %d)\n", ret);
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_save_checkpoint);
@@ -1083,10 +1073,10 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
     if (!bytes) {
         ret = -PAL_ERRNO;
         debug("failed writing to process stream (ret = %d)\n", ret);
-        goto err;
+        goto out;
     } else if (bytes < sizeof(struct newproc_header)) {
         ret = -EACCES;
-        goto err;
+        goto out;
     }
 
     ADD_PROFILE_OCCURENCE(migrate_send_on_stream, bytes);
@@ -1098,7 +1088,7 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
 
     if (ret < 0) {
         debug("failed sending checkpoint (ret = %d)\n", ret);
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_send_checkpoint);
@@ -1108,7 +1098,7 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
      * to the new process using PAL calls.
      */
     if ((ret = send_handles_on_stream(proc, &cpstore)) < 0)
-        goto err;
+        goto out;
 
     SAVE_PROFILE_INTERVAL(migrate_send_pal_handles);
 
@@ -1116,7 +1106,7 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
     if ((ret = bkeep_munmap((void *) cpstore.base, cpstore.bound,
                             CP_VMA_FLAGS)) < 0) {
         debug("failed unmaping checkpoint (ret = %d)\n", ret);
-        goto err;
+        goto out;
     }
 
     DkVirtualMemoryFree((PAL_PTR) cpstore.base, cpstore.bound);
@@ -1129,36 +1119,46 @@ int do_migrate_process (int (*migrate) (struct shim_cp_store *,
                          NULL, 0);
     if (bytes == 0) {
         ret = -PAL_ERRNO;
-        goto err;
+        goto out;
     }
 
     SAVE_PROFILE_INTERVAL(migrate_wait_response);
 
-    if (gipc_hdl)
-        DkObjectClose(gipc_hdl);
-
-    /* Notify the namespace manager regarding the subleasing of TID */
-    ipc_pid_sublease_send(res.child_vmid, thread->tid,
-                          qstrgetstr(&new_process->self->uri),
-                          NULL);
+    /* exec != NULL implies the execve case so the new process "replaces"
+     * this current process: no need to notify the leader or establish IPC */
+    if (!exec) {
+        /* fork/clone case: new process is an actual child process for this
+         * current process, so notify the leader regarding subleasing of TID
+         * (child must create self-pipe with convention of pipe:child-vmid) */
+        char new_process_self_uri[256];
+        snprintf(new_process_self_uri, 256, "pipe:%u", res.child_vmid);
+        ipc_pid_sublease_send(res.child_vmid, thread->tid, new_process_self_uri, NULL);
+
+        /* listen on the new IPC port to the new child process */
+        add_ipc_port_by_id(res.child_vmid, proc,
+                IPC_PORT_DIRCLD|IPC_PORT_LISTEN|IPC_PORT_KEEPALIVE,
+                &ipc_port_with_child_fini,
+                NULL);
+    }
 
-    /* Listen on the RPC stream to the new process */
-    add_ipc_port_by_id(res.child_vmid, proc,
-                       IPC_PORT_DIRCLD|IPC_PORT_LISTEN|IPC_PORT_KEEPALIVE,
-                       &ipc_port_with_child_fini,
-                       NULL);
+    /* remote child thread has VMID of the child process (note that we don't
+     * care about execve case because the parent "intermediate" process will
+     * die right after this anyway) */
+    thread->vmid = res.child_vmid;
 
-    free_process(new_process);
-    return 0;
-err:
+    ret = 0;
+out:
     if (gipc_hdl)
         DkObjectClose(gipc_hdl);
-    if (proc)
-        DkObjectClose(proc);
     if (new_process)
         free_process(new_process);
 
-    SYS_PRINTF("process creation failed\n");
+    if (ret < 0) {
+        if (proc)
+            DkObjectClose(proc);
+        SYS_PRINTF("process creation failed\n");
+    }
+
     return ret;
 }
 

+ 19 - 3
LibOS/shim/src/shim_init.c

@@ -836,7 +836,7 @@ static int create_unique (int (*mkname) (char *, size_t, void *),
     }
 }
 
-static int name_pipe (char * uri, size_t size, void * id)
+static int name_pipe_rand (char * uri, size_t size, void * id)
 {
     IDTYPE pipeid;
     size_t len;
@@ -850,6 +850,17 @@ static int name_pipe (char * uri, size_t size, void * id)
     return len;
 }
 
+static int name_pipe_vmid (char * uri, size_t size, void * id)
+{
+    IDTYPE pipeid = cur_process.vmid;
+    size_t len;
+    debug("creating pipe: pipe.srv:%u\n", pipeid);
+    if ((len = snprintf(uri, size, "pipe.srv:%u", pipeid)) == size)
+        return -ERANGE;
+    *((IDTYPE *) id) = pipeid;
+    return len;
+}
+
 static int open_pipe (const char * uri, void * obj)
 {
     PAL_HANDLE pipe = DkStreamOpen(uri, 0, 0, 0, 0);
@@ -876,10 +887,15 @@ static int pipe_addr (char * uri, size_t size, const void * id,
 }
 
 int create_pipe (IDTYPE * id, char * uri, size_t size, PAL_HANDLE * hdl,
-                 struct shim_qstr * qstr)
+                 struct shim_qstr * qstr, bool use_vmid_for_name)
 {
     IDTYPE pipeid;
-    int ret = create_unique(&name_pipe, &open_pipe, &pipe_addr,
+    int ret;
+    if (use_vmid_for_name)
+        ret = create_unique(&name_pipe_vmid, &open_pipe, &pipe_addr,
+                            uri, size, &pipeid, hdl, qstr);
+    else
+        ret = create_unique(&name_pipe_rand, &open_pipe, &pipe_addr,
                             uri, size, &pipeid, hdl, qstr);
     if (ret > 0 && id)
         *id = pipeid;

+ 10 - 10
LibOS/shim/src/sys/shim_exec.c

@@ -15,7 +15,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /*
- * shim_epoll.c
+ * shim_exec.c
  *
  * Implementation of system call "execve".
  */
@@ -240,6 +240,8 @@ DEFINE_PROFILE_INTERVAL(search_and_check_file_for_exec, exec);
 DEFINE_PROFILE_INTERVAL(open_file_for_exec, exec);
 DEFINE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec, exec);
 
+/* thread is cur_thread stripped off stack & tcb (see below func);
+ * process is new process which is forked and waits for checkpoint. */
 static int migrate_execve (struct shim_cp_store * cpstore,
                            struct shim_thread * thread,
                            struct shim_process * process, va_list ap)
@@ -512,20 +514,18 @@ err:
     cur_thread->user_tcb    = user_tcb;
 
     if (ret < 0) {
+        /* execve failed, so reanimate this thread as if nothing happened */
         cur_thread->in_vm = true;
         unlock(&cur_thread->lock);
         return ret;
     }
 
-    struct shim_handle_map * handle_map = cur_thread->handle_map;
-    cur_thread->handle_map = NULL;
-    unlock(&cur_thread->lock);
-    if (handle_map)
-        put_handle_map(handle_map);
-
-    if (cur_thread->dummy)
-        switch_dummy_thread(cur_thread);
+    /* This "temporary" process must die quietly, not sending any messages
+     * to not confuse the parent and the execve'ed child */
+    debug("Temporary process %u exited after emulating execve (by forking new process to replace this one)\n",
+          cur_process.vmid & 0xFFFF);
+    MASTER_LOCK();
+    DkProcessExit(0);
 
-    try_process_exit(0, 0);
     return 0;
 }

+ 1 - 1
LibOS/shim/src/sys/shim_pipe.c

@@ -41,7 +41,7 @@ int create_pipes (IDTYPE * pipeid, PAL_HANDLE * srv, PAL_HANDLE * cli,
     char uri[PIPE_URI_SIZE];
 
     if ((ret = create_pipe(pipeid, uri, PIPE_URI_SIZE, &hdl0,
-                           qstr)) < 0) {
+                           qstr, /*use_vmid_for_name*/ false)) < 0) {
         debug("pipe creation failure\n");
         return ret;
     }

+ 10 - 0
LibOS/shim/test/regression/00_bootstrap.py

@@ -40,6 +40,16 @@ regression.add_check(name="2 page child binary",
 rv = regression.run_checks()
 if rv: sys.exit(rv)
 
+# Running Fork and Exec
+regression = Regression(loader, "fork_and_exec")
+
+regression.add_check(name="Fork and exec 2 page child binary",
+    check=lambda res: "child exited with status: 0" in res[0].out and \
+                      "test completed successfully" in res[0].out)
+
+rv = regression.run_checks()
+if rv: sys.exit(rv)
+
 # Running execve with invalid pointers in arguments
 regression = Regression(loader, "exec_invalid_args")
 

+ 46 - 0
LibOS/shim/test/regression/fork_and_exec.c

@@ -0,0 +1,46 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int main(int argc, const char** argv, const char** envp) {
+    pid_t child_pid;
+
+    /* duplicate STDOUT into newfd and pass it as exec_victim argument
+     * (it will be inherited by exec_victim) */
+    int newfd = dup(1);
+    char fd_argv[4];
+    snprintf(fd_argv, 4, "%d", newfd);
+    char* const new_argv[] = {"./exec_victim", fd_argv, NULL};
+
+    /* set environment variable to test that it is inherited by exec_victim */
+    setenv("IN_EXECVE", "1", 1);
+
+    child_pid = fork();
+
+    if (child_pid == 0) {
+        /* child performs execve(exec_victim) */
+        execv(new_argv[0], new_argv);
+        perror("execve failed");
+        return 1;
+    } else if (child_pid > 0) {
+        /* parent waits for child termination */
+        int status;
+        pid_t pid = wait(&status);
+        if (pid < 0) {
+            perror("wait failed");
+            return 1;
+        }
+        if (WIFEXITED(status))
+            printf("child exited with status: %d\n", WEXITSTATUS(status));
+    } else {
+        /* error */
+        perror("fork failed");
+        return 1;
+    }
+
+    puts("test completed successfully");
+    return 0;
+}