Add thunks for MojoDuplicateHandle[WithReducedRights]().

R=azani@chromium.org

Review URL: https://codereview.chromium.org/2000253002 .
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index b500f6b..260eae0 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -31,6 +31,19 @@
   return g_core->GetRights(handle, MakeUserPointer(rights));
 }
 
+MojoResult MojoDuplicateHandleWithReducedRights(
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  return g_core->DuplicateHandleWithReducedRights(handle, rights_to_remove,
+                                                  MakeUserPointer(new_handle));
+}
+
+MojoResult MojoDuplicateHandle(MojoHandle handle, MojoHandle* new_handle) {
+  return g_core->DuplicateHandleWithReducedRights(
+      handle, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(new_handle));
+}
+
 MojoResult MojoWait(MojoHandle handle,
                     MojoHandleSignals signals,
                     MojoDeadline deadline,
diff --git a/mojo/edk/embedder/system_impl_private_entrypoints.cc b/mojo/edk/embedder/system_impl_private_entrypoints.cc
index 0f1b18d..fb705c4 100644
--- a/mojo/edk/embedder/system_impl_private_entrypoints.cc
+++ b/mojo/edk/embedder/system_impl_private_entrypoints.cc
@@ -100,6 +100,26 @@
   return core->GetRights(handle, MakeUserPointer(rights));
 }
 
+MojoResult MojoSystemImplDuplicateHandleWithReducedRights(
+    MojoSystemImpl system,
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->DuplicateHandleWithReducedRights(handle, rights_to_remove,
+                                                MakeUserPointer(new_handle));
+}
+
+MojoResult MojoSystemImplDuplicateHandle(MojoSystemImpl system,
+                                         MojoHandle handle,
+                                         MojoHandle* new_handle) {
+  mojo::system::Core* core = static_cast<mojo::system::Core*>(system);
+  DCHECK(core);
+  return core->DuplicateHandleWithReducedRights(handle, MOJO_HANDLE_RIGHT_NONE,
+                                                MakeUserPointer(new_handle));
+}
+
 MojoResult MojoSystemImplWait(MojoSystemImpl system,
                               MojoHandle handle,
                               MojoHandleSignals signals,
diff --git a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
index 7ccdc84..9d9668b 100644
--- a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
+++ b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
@@ -25,6 +25,8 @@
     MojoGetTimeTicksNow,
     MojoClose,
     MojoGetRights,
+    MojoDuplicateHandleWithReducedRights,
+    MojoDuplicateHandle,
     MojoWait,
     MojoWaitMany,
     MojoCreateMessagePipe,
diff --git a/mojo/nacl/sfi/nacl_bindings/mojo_irt.c b/mojo/nacl/sfi/nacl_bindings/mojo_irt.c
index 41c8c49..9e653ce 100644
--- a/mojo/nacl/sfi/nacl_bindings/mojo_irt.c
+++ b/mojo/nacl/sfi/nacl_bindings/mojo_irt.c
@@ -70,6 +70,34 @@
   return result;
 };
 
+static MojoResult irt_MojoDuplicateHandleWithReducedRights(
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  uint32_t params[5];
+  MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
+  params[0] = 4;
+  params[1] = (uint32_t)(&handle);
+  params[2] = (uint32_t)(&rights_to_remove);
+  params[3] = (uint32_t)(new_handle);
+  params[4] = (uint32_t)(&result);
+  DoMojoCall(params, sizeof(params));
+  return result;
+};
+
+static MojoResult irt_MojoDuplicateHandle(
+    MojoHandle handle,
+    MojoHandle* new_handle) {
+  uint32_t params[4];
+  MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
+  params[0] = 5;
+  params[1] = (uint32_t)(&handle);
+  params[2] = (uint32_t)(new_handle);
+  params[3] = (uint32_t)(&result);
+  DoMojoCall(params, sizeof(params));
+  return result;
+};
+
 static MojoResult irt_MojoWait(
     MojoHandle handle,
     MojoHandleSignals signals,
@@ -77,7 +105,7 @@
     struct MojoHandleSignalsState* signals_state) {
   uint32_t params[6];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 4;
+  params[0] = 6;
   params[1] = (uint32_t)(&handle);
   params[2] = (uint32_t)(&signals);
   params[3] = (uint32_t)(&deadline);
@@ -96,7 +124,7 @@
     struct MojoHandleSignalsState* signals_states) {
   uint32_t params[8];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 5;
+  params[0] = 7;
   params[1] = (uint32_t)(handles);
   params[2] = (uint32_t)(signals);
   params[3] = (uint32_t)(&num_handles);
@@ -114,7 +142,7 @@
     MojoHandle* message_pipe_handle1) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 6;
+  params[0] = 8;
   params[1] = (uint32_t)(options);
   params[2] = (uint32_t)(message_pipe_handle0);
   params[3] = (uint32_t)(message_pipe_handle1);
@@ -132,7 +160,7 @@
     MojoWriteMessageFlags flags) {
   uint32_t params[8];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 7;
+  params[0] = 9;
   params[1] = (uint32_t)(&message_pipe_handle);
   params[2] = (uint32_t)(bytes);
   params[3] = (uint32_t)(&num_bytes);
@@ -153,7 +181,7 @@
     MojoReadMessageFlags flags) {
   uint32_t params[8];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 8;
+  params[0] = 10;
   params[1] = (uint32_t)(&message_pipe_handle);
   params[2] = (uint32_t)(bytes);
   params[3] = (uint32_t)(num_bytes);
@@ -171,7 +199,7 @@
     MojoHandle* data_pipe_consumer_handle) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 9;
+  params[0] = 11;
   params[1] = (uint32_t)(options);
   params[2] = (uint32_t)(data_pipe_producer_handle);
   params[3] = (uint32_t)(data_pipe_consumer_handle);
@@ -185,7 +213,7 @@
     const struct MojoDataPipeProducerOptions* options) {
   uint32_t params[4];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 10;
+  params[0] = 12;
   params[1] = (uint32_t)(&data_pipe_producer_handle);
   params[2] = (uint32_t)(options);
   params[3] = (uint32_t)(&result);
@@ -199,7 +227,7 @@
     uint32_t options_num_bytes) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 11;
+  params[0] = 13;
   params[1] = (uint32_t)(&data_pipe_producer_handle);
   params[2] = (uint32_t)(options);
   params[3] = (uint32_t)(&options_num_bytes);
@@ -215,7 +243,7 @@
     MojoWriteDataFlags flags) {
   uint32_t params[6];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 12;
+  params[0] = 14;
   params[1] = (uint32_t)(&data_pipe_producer_handle);
   params[2] = (uint32_t)(elements);
   params[3] = (uint32_t)(num_bytes);
@@ -232,7 +260,7 @@
     MojoWriteDataFlags flags) {
   uint32_t params[6];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 13;
+  params[0] = 15;
   params[1] = (uint32_t)(&data_pipe_producer_handle);
   params[2] = (uint32_t)(buffer);
   params[3] = (uint32_t)(buffer_num_bytes);
@@ -247,7 +275,7 @@
     uint32_t num_bytes_written) {
   uint32_t params[4];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 14;
+  params[0] = 16;
   params[1] = (uint32_t)(&data_pipe_producer_handle);
   params[2] = (uint32_t)(&num_bytes_written);
   params[3] = (uint32_t)(&result);
@@ -260,7 +288,7 @@
     const struct MojoDataPipeConsumerOptions* options) {
   uint32_t params[4];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 15;
+  params[0] = 17;
   params[1] = (uint32_t)(&data_pipe_consumer_handle);
   params[2] = (uint32_t)(options);
   params[3] = (uint32_t)(&result);
@@ -274,7 +302,7 @@
     uint32_t options_num_bytes) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 16;
+  params[0] = 18;
   params[1] = (uint32_t)(&data_pipe_consumer_handle);
   params[2] = (uint32_t)(options);
   params[3] = (uint32_t)(&options_num_bytes);
@@ -290,7 +318,7 @@
     MojoReadDataFlags flags) {
   uint32_t params[6];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 17;
+  params[0] = 19;
   params[1] = (uint32_t)(&data_pipe_consumer_handle);
   params[2] = (uint32_t)(elements);
   params[3] = (uint32_t)(num_bytes);
@@ -307,7 +335,7 @@
     MojoReadDataFlags flags) {
   uint32_t params[6];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 18;
+  params[0] = 20;
   params[1] = (uint32_t)(&data_pipe_consumer_handle);
   params[2] = (uint32_t)(buffer);
   params[3] = (uint32_t)(buffer_num_bytes);
@@ -322,7 +350,7 @@
     uint32_t num_bytes_read) {
   uint32_t params[4];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 19;
+  params[0] = 21;
   params[1] = (uint32_t)(&data_pipe_consumer_handle);
   params[2] = (uint32_t)(&num_bytes_read);
   params[3] = (uint32_t)(&result);
@@ -336,7 +364,7 @@
     MojoHandle* shared_buffer_handle) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 20;
+  params[0] = 22;
   params[1] = (uint32_t)(options);
   params[2] = (uint32_t)(&num_bytes);
   params[3] = (uint32_t)(shared_buffer_handle);
@@ -351,7 +379,7 @@
     MojoHandle* new_buffer_handle) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 21;
+  params[0] = 23;
   params[1] = (uint32_t)(&buffer_handle);
   params[2] = (uint32_t)(options);
   params[3] = (uint32_t)(new_buffer_handle);
@@ -366,7 +394,7 @@
     uint32_t info_num_bytes) {
   uint32_t params[5];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 22;
+  params[0] = 24;
   params[1] = (uint32_t)(&buffer_handle);
   params[2] = (uint32_t)(info);
   params[3] = (uint32_t)(&info_num_bytes);
@@ -383,7 +411,7 @@
     MojoMapBufferFlags flags) {
   uint32_t params[7];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 23;
+  params[0] = 25;
   params[1] = (uint32_t)(&buffer_handle);
   params[2] = (uint32_t)(&offset);
   params[3] = (uint32_t)(&num_bytes);
@@ -397,7 +425,7 @@
 static MojoResult irt_MojoUnmapBuffer(void* buffer) {
   uint32_t params[3];
   MojoResult result = MOJO_RESULT_INVALID_ARGUMENT;
-  params[0] = 24;
+  params[0] = 26;
   params[1] = (uint32_t)(&buffer);
   params[2] = (uint32_t)(&result);
   DoMojoCall(params, sizeof(params));
@@ -409,6 +437,8 @@
   &irt_MojoGetTimeTicksNow,
   &irt_MojoClose,
   &irt_MojoGetRights,
+  &irt_MojoDuplicateHandleWithReducedRights,
+  &irt_MojoDuplicateHandle,
   &irt_MojoWait,
   &irt_MojoWaitMany,
   &irt_MojoCreateMessagePipe,
diff --git a/mojo/nacl/sfi/nacl_bindings/mojo_syscall.cc b/mojo/nacl/sfi/nacl_bindings/mojo_syscall.cc
index cbc89d8..acbd8ea 100644
--- a/mojo/nacl/sfi/nacl_bindings/mojo_syscall.cc
+++ b/mojo/nacl/sfi/nacl_bindings/mojo_syscall.cc
@@ -164,6 +164,79 @@
       return 0;
     }
     case 4: {
+      if (num_params != 5) {
+        return -1;
+      }
+      MojoHandle handle_value;
+      MojoHandleRights rights_to_remove_value;
+      MojoHandle volatile* new_handle_ptr;
+      MojoHandle new_handle_value;
+      MojoResult volatile* result_ptr;
+      MojoResult result_value;
+      {
+        ScopedCopyLock copy_lock(nap);
+        if (!ConvertScalarInput(nap, params[1], &handle_value)) {
+          return -1;
+        }
+        if (!ConvertScalarInput(nap, params[2], &rights_to_remove_value)) {
+          return -1;
+        }
+        if (!ConvertScalarInOut(nap, params[3], false, &new_handle_value,
+                                &new_handle_ptr)) {
+          return -1;
+        }
+        if (!ConvertScalarOutput(nap, params[4], false, &result_ptr)) {
+          return -1;
+        }
+      }
+
+      result_value = MojoSystemImplDuplicateHandleWithReducedRights(
+          g_mojo_system, handle_value, rights_to_remove_value,
+          &new_handle_value);
+
+      {
+        ScopedCopyLock copy_lock(nap);
+        *new_handle_ptr = new_handle_value;
+        *result_ptr = result_value;
+      }
+
+      return 0;
+    }
+    case 5: {
+      if (num_params != 4) {
+        return -1;
+      }
+      MojoHandle handle_value;
+      MojoHandle volatile* new_handle_ptr;
+      MojoHandle new_handle_value;
+      MojoResult volatile* result_ptr;
+      MojoResult result_value;
+      {
+        ScopedCopyLock copy_lock(nap);
+        if (!ConvertScalarInput(nap, params[1], &handle_value)) {
+          return -1;
+        }
+        if (!ConvertScalarInOut(nap, params[2], false, &new_handle_value,
+                                &new_handle_ptr)) {
+          return -1;
+        }
+        if (!ConvertScalarOutput(nap, params[3], false, &result_ptr)) {
+          return -1;
+        }
+      }
+
+      result_value = MojoSystemImplDuplicateHandle(g_mojo_system, handle_value,
+                                                   &new_handle_value);
+
+      {
+        ScopedCopyLock copy_lock(nap);
+        *new_handle_ptr = new_handle_value;
+        *result_ptr = result_value;
+      }
+
+      return 0;
+    }
+    case 6: {
       if (num_params != 6) {
         return -1;
       }
@@ -208,7 +281,7 @@
 
       return 0;
     }
-    case 5: {
+    case 7: {
       if (num_params != 8) {
         return -1;
       }
@@ -264,7 +337,7 @@
 
       return 0;
     }
-    case 6: {
+    case 8: {
       if (num_params != 5) {
         return -1;
       }
@@ -308,7 +381,7 @@
 
       return 0;
     }
-    case 7: {
+    case 9: {
       if (num_params != 8) {
         return -1;
       }
@@ -357,7 +430,7 @@
 
       return 0;
     }
-    case 8: {
+    case 10: {
       if (num_params != 8) {
         return -1;
       }
@@ -417,7 +490,7 @@
 
       return 0;
     }
-    case 9: {
+    case 11: {
       if (num_params != 5) {
         return -1;
       }
@@ -461,7 +534,7 @@
 
       return 0;
     }
-    case 10: {
+    case 12: {
       if (num_params != 4) {
         return -1;
       }
@@ -493,7 +566,7 @@
 
       return 0;
     }
-    case 11: {
+    case 13: {
       if (num_params != 5) {
         return -1;
       }
@@ -531,7 +604,7 @@
 
       return 0;
     }
-    case 12: {
+    case 14: {
       if (num_params != 6) {
         return -1;
       }
@@ -576,10 +649,10 @@
 
       return 0;
     }
-    case 13:
+    case 15:
       fprintf(stderr, "MojoBeginWriteData not implemented\n");
       return -1;
-    case 14: {
+    case 16: {
       if (num_params != 4) {
         return -1;
       }
@@ -612,7 +685,7 @@
 
       return 0;
     }
-    case 15: {
+    case 17: {
       if (num_params != 4) {
         return -1;
       }
@@ -644,7 +717,7 @@
 
       return 0;
     }
-    case 16: {
+    case 18: {
       if (num_params != 5) {
         return -1;
       }
@@ -682,7 +755,7 @@
 
       return 0;
     }
-    case 17: {
+    case 19: {
       if (num_params != 6) {
         return -1;
       }
@@ -727,10 +800,10 @@
 
       return 0;
     }
-    case 18:
+    case 20:
       fprintf(stderr, "MojoBeginReadData not implemented\n");
       return -1;
-    case 19: {
+    case 21: {
       if (num_params != 4) {
         return -1;
       }
@@ -762,7 +835,7 @@
 
       return 0;
     }
-    case 20: {
+    case 22: {
       if (num_params != 5) {
         return -1;
       }
@@ -801,7 +874,7 @@
 
       return 0;
     }
-    case 21: {
+    case 23: {
       if (num_params != 5) {
         return -1;
       }
@@ -840,7 +913,7 @@
 
       return 0;
     }
-    case 22: {
+    case 24: {
       if (num_params != 5) {
         return -1;
       }
@@ -876,10 +949,10 @@
 
       return 0;
     }
-    case 23:
+    case 25:
       fprintf(stderr, "MojoMapBuffer not implemented\n");
       return -1;
-    case 24:
+    case 26:
       fprintf(stderr, "MojoUnmapBuffer not implemented\n");
       return -1;
   }
diff --git a/mojo/nacl/sfi/nacl_bindings_generator/interface.py b/mojo/nacl/sfi/nacl_bindings_generator/interface.py
index 725f118..84ad256 100644
--- a/mojo/nacl/sfi/nacl_bindings_generator/interface.py
+++ b/mojo/nacl/sfi/nacl_bindings_generator/interface.py
@@ -24,6 +24,15 @@
   f.Param('handle').In('MojoHandle')
   f.Param('rights').Out('MojoHandleRights')
 
+  f = mojo.Func('MojoDuplicateHandleWithReducedRights', 'MojoResult')
+  f.Param('handle').In('MojoHandle')
+  f.Param('rights_to_remove').In('MojoHandleRights')
+  f.Param('new_handle').Out('MojoHandle')
+
+  f = mojo.Func('MojoDuplicateHandle', 'MojoResult')
+  f.Param('handle').In('MojoHandle')
+  f.Param('new_handle').Out('MojoHandle')
+
   f = mojo.Func('MojoWait', 'MojoResult')
   f.Param('handle').In('MojoHandle')
   f.Param('signals').In('MojoHandleSignals')
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 4e36860..aeba1ee 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -63,6 +63,19 @@
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoGetRights(MOJO_HANDLE_INVALID, &rights));
 
+  // DuplicateHandleWithReducedRights:
+  MojoHandle new_handle = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoDuplicateHandleWithReducedRights(
+                MOJO_HANDLE_INVALID, MOJO_HANDLE_RIGHT_DUPLICATE, &new_handle));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, new_handle);
+
+  // DuplicateHandle:
+  new_handle = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoDuplicateHandle(MOJO_HANDLE_INVALID, &new_handle));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, new_handle);
+
   // Wait:
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000u,
@@ -160,6 +173,18 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoGetRights(h1, &rights));
   EXPECT_EQ(kDefaultMessagePipeHandleRights, rights);
 
+  // Shouldn't be able to duplicate either handle (just test "with reduced
+  // rights" on one, and without on the other).
+  MojoHandle handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoDuplicateHandleWithReducedRights(
+                h0, MOJO_HANDLE_RIGHT_DUPLICATE, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+  handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoDuplicateHandle(h1, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+
   // Shouldn't be readable, we haven't written anything.
   MojoHandleSignalsState state;
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
@@ -265,6 +290,18 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoGetRights(hc, &rights));
   EXPECT_EQ(kDefaultDataPipeConsumerHandleRights, rights);
 
+  // Shouldn't be able to duplicate either handle (just test "with reduced
+  // rights" on one, and without on the other).
+  MojoHandle handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoDuplicateHandleWithReducedRights(
+                hp, MOJO_HANDLE_RIGHT_DUPLICATE, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+  handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoDuplicateHandle(hc, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+
   // The consumer |hc| shouldn't be readable.
   MojoHandleSignalsState state;
   EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
@@ -614,11 +651,9 @@
 #define MAYBE_BasicSharedBuffer BasicSharedBuffer
 #endif
 TEST(CoreTest, MAYBE_BasicSharedBuffer) {
-  MojoHandle h0, h1;
-  void* pointer;
 
   // Create a shared buffer (|h0|).
-  h0 = MOJO_HANDLE_INVALID;
+  MojoHandle h0 = MOJO_HANDLE_INVALID;
   EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 100, &h0));
   EXPECT_NE(h0, MOJO_HANDLE_INVALID);
 
@@ -636,16 +671,17 @@
   EXPECT_EQ(100u, info.num_bytes);
 
   // Map everything.
-  pointer = nullptr;
+  void* pointer = nullptr;
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoMapBuffer(h0, 0, 100, &pointer, MOJO_MAP_BUFFER_FLAG_NONE));
   ASSERT_TRUE(pointer);
   static_cast<char*>(pointer)[50] = 'x';
 
   // Duplicate |h0| to |h1|.
-  h1 = MOJO_HANDLE_INVALID;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, nullptr, &h1));
+  MojoHandle h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateHandle(h0, &h1));
   EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h1, h0);
 
   // The new handle should have the correct rights.
   rights = MOJO_HANDLE_RIGHT_NONE;
@@ -681,7 +717,24 @@
   // Unmap it.
   EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
 
+  // Test duplication with reduced rights.
+  MojoHandle h2 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateHandleWithReducedRights(
+                                h1, MOJO_HANDLE_RIGHT_DUPLICATE, &h2));
+  EXPECT_NE(h2, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h2, h1);
+  // |h2| should have the correct rights.
+  rights = MOJO_HANDLE_RIGHT_NONE;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoGetRights(h2, &rights));
+  EXPECT_EQ(kDefaultSharedBufferHandleRights & ~MOJO_HANDLE_RIGHT_DUPLICATE,
+            rights);
+  // Trying to duplicate |h2| should fail.
+  MojoHandle h3 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED, MojoDuplicateHandle(h2, &h3));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, h3);
+
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2));
 }
 
 // This checks that things actually work in C (not C++).
diff --git a/mojo/public/platform/nacl/libmojo.cc b/mojo/public/platform/nacl/libmojo.cc
index 716877c..c25aff2 100644
--- a/mojo/public/platform/nacl/libmojo.cc
+++ b/mojo/public/platform/nacl/libmojo.cc
@@ -60,6 +60,24 @@
   return irt_mojo->MojoGetRights(handle, rights);
 }
 
+MojoResult MojoDuplicateHandleWithReducedRights(
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoDuplicateHandleWithReducedRights(
+      handle, rights_to_remove, new_handle);
+}
+
+MojoResult MojoDuplicateHandle(MojoHandle handle, MojoHandle* new_handle) {
+  struct nacl_irt_mojo* irt_mojo = get_irt_mojo();
+  if (!irt_mojo)
+    abort();
+  return irt_mojo->MojoDuplicateHandle(handle, new_handle);
+}
+
 MojoResult MojoWait(MojoHandle handle,
                     MojoHandleSignals signals,
                     MojoDeadline deadline,
diff --git a/mojo/public/platform/nacl/mojo_irt.h b/mojo/public/platform/nacl/mojo_irt.h
index 4a7edc3..90c767b 100644
--- a/mojo/public/platform/nacl/mojo_irt.h
+++ b/mojo/public/platform/nacl/mojo_irt.h
@@ -22,6 +22,11 @@
   MojoTimeTicks (*MojoGetTimeTicksNow)();
   MojoResult (*MojoClose)(MojoHandle handle);
   MojoResult (*MojoGetRights)(MojoHandle handle, MojoHandleRights* rights);
+  MojoResult (*MojoDuplicateHandleWithReducedRights)(
+      MojoHandle handle,
+      MojoHandleRights rights_to_remove,
+      MojoHandle* new_handle);
+  MojoResult (*MojoDuplicateHandle)(MojoHandle handle, MojoHandle* new_handle);
   MojoResult (*MojoWait)(MojoHandle handle,
                          MojoHandleSignals signals,
                          MojoDeadline deadline,
diff --git a/mojo/public/platform/native/system_impl_private.h b/mojo/public/platform/native/system_impl_private.h
index 58b3560..3596172 100644
--- a/mojo/public/platform/native/system_impl_private.h
+++ b/mojo/public/platform/native/system_impl_private.h
@@ -54,6 +54,14 @@
 MojoResult MojoSystemImplGetRights(MojoSystemImpl system,
                                    MojoHandle handle,
                                    MojoHandleRights* rights);
+MojoResult MojoSystemImplDuplicateHandleWithReducedRights(
+    MojoSystemImpl system,
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle);
+MojoResult MojoSystemImplDuplicateHandle(MojoSystemImpl system,
+                                         MojoHandle handle,
+                                         MojoHandle* new_handle);
 MojoResult MojoSystemImplWait(MojoSystemImpl system,
                               MojoHandle handle,
                               MojoHandleSignals signals,
diff --git a/mojo/public/platform/native/system_impl_private_thunks.c b/mojo/public/platform/native/system_impl_private_thunks.c
index 82f2ac1..fb75504 100644
--- a/mojo/public/platform/native/system_impl_private_thunks.c
+++ b/mojo/public/platform/native/system_impl_private_thunks.c
@@ -48,6 +48,23 @@
   return g_system_impl_thunks.GetRights(system, handle, rights);
 }
 
+MojoResult MojoSystemImplDuplicateHandleWithReducedRights(
+    MojoSystemImpl system,
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  assert(g_system_impl_thunks.DuplicateHandleWithReducedRights);
+  return g_system_impl_thunks.DuplicateHandleWithReducedRights(
+      system, handle, rights_to_remove, new_handle);
+}
+
+MojoResult MojoSystemImplDuplicateHandle(MojoSystemImpl system,
+                                         MojoHandle handle,
+                                         MojoHandle* new_handle) {
+  assert(g_system_impl_thunks.DuplicateHandle);
+  return g_system_impl_thunks.DuplicateHandle(system, handle, new_handle);
+}
+
 MojoResult MojoSystemImplWait(MojoSystemImpl system,
                               MojoHandle handle,
                               MojoHandleSignals signals,
diff --git a/mojo/public/platform/native/system_impl_private_thunks.h b/mojo/public/platform/native/system_impl_private_thunks.h
index e29fc8e..296cb21 100644
--- a/mojo/public/platform/native/system_impl_private_thunks.h
+++ b/mojo/public/platform/native/system_impl_private_thunks.h
@@ -37,6 +37,14 @@
   MojoResult (*GetRights)(MojoSystemImpl system,
                           MojoHandle handle,
                           MojoHandleRights* rights);
+  MojoResult (*DuplicateHandleWithReducedRights)(
+      MojoSystemImpl system,
+      MojoHandle handle,
+      MojoHandleRights rights_to_remove,
+      MojoHandle* new_handle);
+  MojoResult (*DuplicateHandle)(MojoSystemImpl system,
+                                MojoHandle handle,
+                                MojoHandle* new_handle);
   MojoResult (*Wait)(MojoSystemImpl system,
                      MojoHandle handle,
                      MojoHandleSignals signals,
@@ -157,6 +165,8 @@
       MojoSystemImplGetTimeTicksNow,
       MojoSystemImplClose,
       MojoSystemImplGetRights,
+      MojoSystemImplDuplicateHandleWithReducedRights,
+      MojoSystemImplDuplicateHandle,
       MojoSystemImplWait,
       MojoSystemImplWaitMany,
       MojoSystemImplCreateMessagePipe,
diff --git a/mojo/public/platform/native/system_impl_private_unittest.cc b/mojo/public/platform/native/system_impl_private_unittest.cc
index 215ccd4..03fcafd 100644
--- a/mojo/public/platform/native/system_impl_private_unittest.cc
+++ b/mojo/public/platform/native/system_impl_private_unittest.cc
@@ -63,6 +63,18 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplGetRights(sys0, h1, &rights));
   EXPECT_EQ(kDefaultMessagePipeHandleRights, rights);
 
+  // Shouldn't be able to duplicate either handle (just test "with reduced
+  // rights" on one, and without on the other).
+  MojoHandle handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoSystemImplDuplicateHandleWithReducedRights(
+                sys0, h0, MOJO_HANDLE_RIGHT_DUPLICATE, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+  handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoSystemImplDuplicateHandle(sys0, h1, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+
   // Move the other end of the pipe to a different SystemImpl.
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplTransferHandle(sys0, h1, sys1, &h1));
   EXPECT_NE(h1, MOJO_HANDLE_INVALID);
@@ -182,6 +194,18 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplGetRights(sys0, hc, &rights));
   EXPECT_EQ(kDefaultDataPipeConsumerHandleRights, rights);
 
+  // Shouldn't be able to duplicate either handle (just test "with reduced
+  // rights" on one, and without on the other).
+  MojoHandle handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoSystemImplDuplicateHandleWithReducedRights(
+                sys0, hp, MOJO_HANDLE_RIGHT_DUPLICATE, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+  handle_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoSystemImplDuplicateHandle(sys0, hc, &handle_denied));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, handle_denied);
+
   // Move the other end of the pipe to a different SystemImpl.
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplTransferHandle(sys0, hc, sys1, &hc));
   EXPECT_NE(hc, MOJO_HANDLE_INVALID);
@@ -599,11 +623,8 @@
   MojoSystemImpl sys1 = MojoSystemImplCreateImpl();
   EXPECT_NE(sys0, sys1);
 
-  MojoHandle h0, h1;
-  void* pointer;
-
   // Create a shared buffer (|h0|).
-  h0 = MOJO_HANDLE_INVALID;
+  MojoHandle h0 = MOJO_HANDLE_INVALID;
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoSystemImplCreateSharedBuffer(sys0, nullptr, kSize, &h0));
   EXPECT_NE(h0, MOJO_HANDLE_INVALID);
@@ -624,7 +645,7 @@
   }
 
   // Map everything.
-  pointer = nullptr;
+  void* pointer = nullptr;
   EXPECT_EQ(MOJO_RESULT_OK,
             MojoSystemImplMapBuffer(sys0, h0, 0u, kSize, &pointer,
                                     MOJO_MAP_BUFFER_FLAG_NONE));
@@ -632,10 +653,10 @@
   static_cast<char*>(pointer)[kSize / 2] = 'x';
 
   // Duplicate |h0| to |h1|.
-  h1 = MOJO_HANDLE_INVALID;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoSystemImplDuplicateBufferHandle(sys0, h0, nullptr, &h1));
+  MojoHandle h1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplDuplicateHandle(sys0, h0, &h1));
   EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h1, h0);
 
   // The new handle should have the correct rights.
   rights = MOJO_HANDLE_RIGHT_NONE;
@@ -679,7 +700,25 @@
   // Unmap it.
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplUnmapBuffer(sys1, pointer));
 
+  // Test duplication with reduced rights.
+  MojoHandle h2 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplDuplicateHandleWithReducedRights(
+                                sys1, h1, MOJO_HANDLE_RIGHT_DUPLICATE, &h2));
+  EXPECT_NE(h2, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h2, h1);
+  // |h2| should have the correct rights.
+  rights = MOJO_HANDLE_RIGHT_NONE;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplGetRights(sys1, h2, &rights));
+  EXPECT_EQ(kDefaultSharedBufferHandleRights & ~MOJO_HANDLE_RIGHT_DUPLICATE,
+            rights);
+  // Trying to duplicate |h2| should fail.
+  MojoHandle h3 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            MojoSystemImplDuplicateHandle(sys1, h2, &h3));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, h3);
+
   EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys1, h1));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSystemImplClose(sys1, h2));
 
   // 2 SystemImpls are leaked...
 }
diff --git a/mojo/public/platform/native/system_thunks.c b/mojo/public/platform/native/system_thunks.c
index 9ed890f..6cff1cb 100644
--- a/mojo/public/platform/native/system_thunks.c
+++ b/mojo/public/platform/native/system_thunks.c
@@ -25,6 +25,20 @@
   return g_thunks.GetRights(handle, rights);
 }
 
+MojoResult MojoDuplicateHandleWithReducedRights(
+    MojoHandle handle,
+    MojoHandleRights rights_to_remove,
+    MojoHandle* new_handle) {
+  assert(g_thunks.DuplicateHandleWithReducedRights);
+  return g_thunks.DuplicateHandleWithReducedRights(handle, rights_to_remove,
+                                                   new_handle);
+}
+
+MojoResult MojoDuplicateHandle(MojoHandle handle, MojoHandle* new_handle) {
+  assert(g_thunks.DuplicateHandle);
+  return g_thunks.DuplicateHandle(handle, new_handle);
+}
+
 MojoResult MojoWait(MojoHandle handle,
                     MojoHandleSignals signals,
                     MojoDeadline deadline,
diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h
index 23f7d69..3650d1f 100644
--- a/mojo/public/platform/native/system_thunks.h
+++ b/mojo/public/platform/native/system_thunks.h
@@ -127,6 +127,11 @@
       struct MojoDataPipeProducerOptions* options,
       uint32_t options_num_bytes);
   MojoResult (*GetRights)(MojoHandle handle, MojoHandleRights* rights);
+  MojoResult (*DuplicateHandleWithReducedRights)(
+      MojoHandle handle,
+      MojoHandleRights rights_to_remove,
+      MojoHandle* new_handle);
+  MojoResult (*DuplicateHandle)(MojoHandle handle, MojoHandle* new_handle);
 };
 #pragma pack(pop)
 
@@ -161,6 +166,8 @@
       MojoSetDataPipeProducerOptions,
       MojoGetDataPipeProducerOptions,
       MojoGetRights,
+      MojoDuplicateHandleWithReducedRights,
+      MojoDuplicateHandle,
   };
   return system_thunks;
 }