[syscalls] Add ZX_POL_NEW_PROCESS

Previously, we lacked a policy bit for controlling process creation.
We'll use this policy bit in the future to force clients through
fuchsia.process.Launcher.

Also, hide ZX_POL_MAX from userspace so we can add more policy bits in
the future.

TEST=spawn_launcher_test

Change-Id: Idcba7c7db595774bd10b227da41e4c7249bb83ea
diff --git a/docs/syscalls/job_set_policy.md b/docs/syscalls/job_set_policy.md
index d3f3d6b..3c6d720 100644
--- a/docs/syscalls/job_set_policy.md
+++ b/docs/syscalls/job_set_policy.md
@@ -67,6 +67,8 @@
   a new fifo.
 + **ZX_POL_NEW_TIMER** a process under this job is attempting to create
   a new timer.
++ **ZX_POL_NEW_PROCESS** a process under this job is attempting to create
+  a new process.
 + **ZX_POL_NEW_ANY** is a special *condition* that stands for all of
   the above **ZX_NEW** condtions such as **ZX_POL_NEW_VMO**,
   **ZX_POL_NEW_CHANNEL**, **ZX_POL_NEW_EVENT**, **ZX_POL_NEW_EVENTPAIR**,
diff --git a/kernel/object/policy_manager.cpp b/kernel/object/policy_manager.cpp
index a4e9007..db5682c 100644
--- a/kernel/object/policy_manager.cpp
+++ b/kernel/object/policy_manager.cpp
@@ -43,7 +43,8 @@
         uint64_t new_socket      :  4;
         uint64_t new_fifo        :  4;
         uint64_t new_timer       :  4;
-        uint64_t unused_bits     : 19;
+        uint64_t new_process     :  4;
+        uint64_t unused_bits     : 15;
         uint64_t cookie_mode     :  1;  // see kPolicyInCookie.
     };
 
@@ -62,7 +63,7 @@
 static_assert(sizeof(Encoding) == sizeof(pol_cookie_t), "bitfield issue");
 
 // Make sure that adding new policies forces updating this file.
-static_assert(ZX_POL_MAX == 12u, "please update PolicyManager AddPolicy and QueryBasicPolicy");
+static_assert(ZX_POL_MAX == 13u, "please update PolicyManager AddPolicy and QueryBasicPolicy");
 
 PolicyManager* PolicyManager::Create(uint32_t default_action) {
     fbl::AllocChecker ac;
@@ -151,6 +152,7 @@
     case ZX_POL_NEW_SOCKET: return GetEffectiveAction(existing.new_socket);
     case ZX_POL_NEW_FIFO: return GetEffectiveAction(existing.new_fifo);
     case ZX_POL_NEW_TIMER: return GetEffectiveAction(existing.new_timer);
+    case ZX_POL_NEW_PROCESS: return GetEffectiveAction(existing.new_process);
     case ZX_POL_VMAR_WX: return GetEffectiveAction(existing.vmar_wx);
     default: return ZX_POL_ACTION_DENY;
     }
@@ -219,6 +221,9 @@
     case ZX_POL_NEW_TIMER:
         POLMAN_SET_ENTRY(mode, existing.new_timer, policy, result.new_timer);
         break;
+    case ZX_POL_NEW_PROCESS:
+        POLMAN_SET_ENTRY(mode, existing.new_process, policy, result.new_process);
+        break;
     default:
         return ZX_ERR_NOT_SUPPORTED;
     }
diff --git a/kernel/syscalls/task.cpp b/kernel/syscalls/task.cpp
index 2bba39a..d7fcd45 100644
--- a/kernel/syscalls/task.cpp
+++ b/kernel/syscalls/task.cpp
@@ -329,21 +329,26 @@
     if (options != 0)
         return ZX_ERR_INVALID_ARGS;
 
+    auto up = ProcessDispatcher::GetCurrent();
+
+    // We check the policy against the process calling zx_process_create, which
+    // is the operative policy, rather than against |job_handle|. Access to
+    // |job_handle| is controlled by the rights associated with the handle.
+    zx_status_t result = up->QueryPolicy(ZX_POL_NEW_PROCESS);
+    if (result != ZX_OK)
+        return result;
+
     // copy out the name
     char buf[ZX_MAX_NAME_LEN];
     fbl::StringPiece sp;
     // Silently truncate the given name.
     if (name_len > sizeof(buf))
         name_len = sizeof(buf);
-    zx_status_t result = copy_user_string(_name, name_len,
-                                          buf, sizeof(buf), &sp);
+    result = copy_user_string(_name, name_len, buf, sizeof(buf), &sp);
     if (result != ZX_OK)
         return result;
     LTRACEF("name %s\n", buf);
 
-    // convert job handle to job dispatcher
-    auto up = ProcessDispatcher::GetCurrent();
-
     fbl::RefPtr<JobDispatcher> job;
     // TODO(ZX-968): define process creation job rights.
     auto status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_WRITE, &job);
@@ -354,11 +359,11 @@
     fbl::RefPtr<Dispatcher> proc_dispatcher;
     fbl::RefPtr<VmAddressRegionDispatcher> vmar_dispatcher;
     zx_rights_t proc_rights, vmar_rights;
-    zx_status_t res = ProcessDispatcher::Create(fbl::move(job), sp, options,
-                                                &proc_dispatcher, &proc_rights,
-                                                &vmar_dispatcher, &vmar_rights);
-    if (res != ZX_OK)
-        return res;
+    result = ProcessDispatcher::Create(fbl::move(job), sp, options,
+                                       &proc_dispatcher, &proc_rights,
+                                       &vmar_dispatcher, &vmar_rights);
+    if (result != ZX_OK)
+        return result;
 
     uint32_t koid = (uint32_t)proc_dispatcher->get_koid();
     ktrace(TAG_PROC_CREATE, koid, 0, 0, 0);
diff --git a/system/public/zircon/syscalls/policy.h b/system/public/zircon/syscalls/policy.h
index c89758a..f7061ce 100644
--- a/system/public/zircon/syscalls/policy.h
+++ b/system/public/zircon/syscalls/policy.h
@@ -39,7 +39,10 @@
 #define ZX_POL_NEW_SOCKET                    9u
 #define ZX_POL_NEW_FIFO                     10u
 #define ZX_POL_NEW_TIMER                    11u
-#define ZX_POL_MAX                          12u
+#define ZX_POL_NEW_PROCESS                  12u
+#ifdef _KERNEL
+#define ZX_POL_MAX                          13u
+#endif
 
 // Policy actions.
 // ZX_POL_ACTION_ALLOW and ZX_POL_ACTION_DENY can be ORed with ZX_POL_ACTION_EXCEPTION.
diff --git a/system/utest/spawn/launcher.c b/system/utest/spawn/launcher.c
new file mode 100644
index 0000000..4c776ae
--- /dev/null
+++ b/system/utest/spawn/launcher.c
@@ -0,0 +1,32 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <launchpad/launchpad.h>
+#include <zircon/syscalls.h>
+#include <string.h>
+
+int main(int argc, const char* const* argv) {
+    launchpad_t* lp = NULL;
+    launchpad_create(ZX_HANDLE_INVALID, "launcher-child", &lp);
+    launchpad_load_from_file(lp, argv[1]);
+    launchpad_set_args(lp, argc - 1, argv + 1);
+    launchpad_clone(lp, LP_CLONE_ALL);
+
+    zx_handle_t process = ZX_HANDLE_INVALID;
+    zx_status_t status = launchpad_go(lp, &process, NULL);
+    if (status != ZX_OK)
+        return 401;
+
+    status = zx_object_wait_one(process, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, NULL);
+    if (status != ZX_OK)
+        return status;
+
+    zx_info_process_t proc_info;
+    memset(&proc_info, 0, sizeof(proc_info));
+    status = zx_object_get_info(process, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL);
+    if (status != ZX_OK)
+        return status;
+
+    return proc_info.return_code;
+}
diff --git a/system/utest/spawn/rules.mk b/system/utest/spawn/rules.mk
index 4c9f3db..81c7341 100644
--- a/system/utest/spawn/rules.mk
+++ b/system/utest/spawn/rules.mk
@@ -21,9 +21,9 @@
     system/ulib/zx \
 
 MODULE_LIBS := \
-    system/ulib/c \
     system/ulib/fdio \
     system/ulib/unittest \
+    system/ulib/c \
     system/ulib/zircon \
 
 include make/module.mk
@@ -43,8 +43,30 @@
     $(LOCAL_DIR)/child.c \
 
 MODULE_LIBS := \
-    system/ulib/c \
     system/ulib/fdio \
+    system/ulib/c \
+    system/ulib/zircon \
+
+include make/module.mk
+
+#
+# spawn-launcher
+#
+
+MODULE := $(LOCAL_DIR).launcher
+
+MODULE_TYPE := userapp
+MODULE_GROUP := test
+
+MODULE_NAME := spawn-launcher
+
+MODULE_SRCS := \
+    $(LOCAL_DIR)/launcher.c \
+
+MODULE_LIBS := \
+    system/ulib/launchpad \
+    system/ulib/fdio \
+    system/ulib/c \
     system/ulib/zircon \
 
 include make/module.mk
diff --git a/system/utest/spawn/spawn.cpp b/system/utest/spawn/spawn.cpp
index 6360964..f2a5558 100644
--- a/system/utest/spawn/spawn.cpp
+++ b/system/utest/spawn/spawn.cpp
@@ -16,8 +16,10 @@
 #include <unistd.h>
 #include <zircon/limits.h>
 #include <zircon/processargs.h>
+#include <zircon/syscalls/policy.h>
 
 static constexpr char kSpawnChild[] = "/boot/bin/spawn-child";
+static constexpr char kSpawnLauncher[] = "/boot/bin/spawn-launcher";
 
 static bool has_fd(int fd) {
     zx_handle_t handles[FDIO_MAX_HANDLES];
@@ -74,6 +76,47 @@
     END_TEST;
 }
 
+static bool spawn_launcher_test(void) {
+    BEGIN_TEST;
+
+    zx_status_t status;
+    zx::process process;
+    const char* argv[] = {kSpawnLauncher, kSpawnChild, nullptr};
+
+    // Check that we can spawn the lancher process in a job and that the
+    // launcher process can launch the child.
+    {
+        zx::job job;
+        ASSERT_EQ(ZX_OK, zx::job::create(zx_job_default(), 0, &job));
+
+        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
+                            argv, process.reset_and_get_address());
+        ASSERT_EQ(ZX_OK, status);
+        EXPECT_EQ(43, join(process));
+        ASSERT_EQ(ZX_OK, job.kill());
+    }
+
+    // Check that setting |ZX_POL_NEW_PROCESS| to |ZX_POL_ACTION_DENY| prevents
+    // the launcher from launching the child.
+    {
+        zx::job job;
+        ASSERT_EQ(ZX_OK, zx::job::create(zx_job_default(), 0, &job));
+        zx_policy_basic_t policy = {
+            .condition = ZX_POL_NEW_PROCESS,
+            .policy = ZX_POL_ACTION_DENY,
+        };
+        ASSERT_EQ(ZX_OK, job.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1));
+
+        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
+                            argv, process.reset_and_get_address());
+        ASSERT_EQ(ZX_OK, status);
+        EXPECT_EQ(401, join(process));
+        ASSERT_EQ(ZX_OK, job.kill());
+    }
+
+    END_TEST;
+}
+
 static bool spawn_invalid_args_test(void) {
     BEGIN_TEST;
 
@@ -550,6 +593,7 @@
 
 BEGIN_TEST_CASE(spawn_tests)
 RUN_TEST(spawn_control_test)
+RUN_TEST(spawn_launcher_test)
 RUN_TEST(spawn_invalid_args_test)
 RUN_TEST(spawn_flags_test)
 RUN_TEST(spawn_environ_test)