[kernel] Add testable_user_ptr and fake_user_ptr

Add testable_user_ptr and fake_user_ptr to facilitate testing kernel
code that deal with user_ptr's.

ZX-1857 #done

Change-Id: If3e516711034cff1d94b16bdc98ae082ef149600
diff --git a/kernel/lib/user_copy/empty.c b/kernel/lib/user_copy/empty.c
deleted file mode 100644
index 161e001..0000000
--- a/kernel/lib/user_copy/empty.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2017 The Fuchsia Authors
-//
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file or at
-// https://opensource.org/licenses/MIT
-
-// Note: Currently this module is a header only library.  This file exists only
-// to give the compiler something to build in order to create a library.  It can
-// be removed if/when the module contains something which needs to be compiled
-// and linked later.
diff --git a/kernel/lib/user_copy/fake_user_ptr.cpp b/kernel/lib/user_copy/fake_user_ptr.cpp
new file mode 100644
index 0000000..76792f0
--- /dev/null
+++ b/kernel/lib/user_copy/fake_user_ptr.cpp
@@ -0,0 +1,20 @@
+// 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 <lib/user_copy/fake_user_ptr.h>
+
+#include <string.h>
+
+namespace internal {
+
+namespace testing {
+
+zx_status_t unsafe_copy(void* dst, const void* src, size_t len) {
+    memcpy(dst, src, len);
+    return ZX_OK;
+}
+
+}  // namespace testing
+
+}  // namespace internal
diff --git a/kernel/lib/user_copy/include/lib/user_copy/fake_user_ptr.h b/kernel/lib/user_copy/include/lib/user_copy/fake_user_ptr.h
new file mode 100644
index 0000000..118ffa1
--- /dev/null
+++ b/kernel/lib/user_copy/include/lib/user_copy/fake_user_ptr.h
@@ -0,0 +1,53 @@
+// 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.
+
+#pragma once
+
+#include <lib/user_copy/user_ptr.h>
+#include <lib/user_copy/testable_user_ptr.h>
+
+namespace internal {
+
+namespace testing {
+
+// fake_user_ptr is an unsafe variant of user_ptr that is designed to be used in tests.
+//
+// It works in conjunction with testable_user_ptr.  See testable_user_ptr.h for an example.
+//
+// fake_user_ptr replaces user_ptr's copy routines with relaxed (unsafe) ones that can be used to
+// access only kernel memory.
+//
+// fake_user_in_ptr should *never* be used outside of tests.
+
+zx_status_t unsafe_copy(void* dst, const void* src, size_t len);
+
+// TODO(ZX-1849): Find a way to prevent these traits from making their way into a release build. One
+// idea would to be to reference a symbol that's found only in test builds.
+class FakeUserCopyTraits {
+public:
+    constexpr static CopyFunc CopyTo = unsafe_copy;
+    constexpr static CopyFunc CopyFrom = unsafe_copy;
+};
+
+template <typename T>
+using fake_user_in_ptr = testable_user_in_ptr<T, FakeUserCopyTraits>;
+
+template <typename T>
+using fake_user_out_ptr = testable_user_out_ptr<T, FakeUserCopyTraits>;
+
+template <typename T>
+using fake_user_inout_ptr = testable_user_inout_ptr<T, FakeUserCopyTraits>;
+
+template <typename T>
+fake_user_in_ptr<T> make_fake_user_in_ptr(T* p) { return fake_user_in_ptr<T>(p); }
+
+template <typename T>
+fake_user_out_ptr<T> make_fake_user_out_ptr(T* p) { return fake_user_out_ptr<T>(p); }
+
+template <typename T>
+fake_user_inout_ptr<T> make_fake_user_inout_ptr(T* p) { return fake_user_inout_ptr<T>(p); }
+
+}  // namespace testing
+
+}  // namespace internal
diff --git a/kernel/lib/user_copy/include/lib/user_copy/testable_user_ptr.h b/kernel/lib/user_copy/include/lib/user_copy/testable_user_ptr.h
new file mode 100644
index 0000000..09f69ac
--- /dev/null
+++ b/kernel/lib/user_copy/include/lib/user_copy/testable_user_ptr.h
@@ -0,0 +1,39 @@
+// 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.
+
+#pragma once
+
+#include <lib/user_copy/user_ptr.h>
+
+// testable_user_ptr is a user_ptr that simplifies testing of code dealing with userspace addresses.
+//
+// It works in conjunction with fake_user_ptr.  The typical use case is you have a function that
+// takes a user_ptr and you want to write a test for it that runs in kernel space.  Say you have a
+// function foo:
+//
+//     bool foo(user_in_ptr<const void> in);
+//
+// As is, you can't easily test foo() from kernel space because 'in' must be a userspace address.
+// Here's where testable_user_ptr comes in.  You can change foo's signature to take a
+// testable_user_ptr instead:
+//
+//     template <typename UCT>
+//     void foo(testable_user_in_ptr<const void, UCT> in);
+//
+// All the call sites should continue to work as usual.  Now, in your test code where you call
+// foo(), use a fake_user_ptr instead:
+//
+//     EXPECT_TRUE(foo(internal::testing::make_fake_user_in_ptr(...)));
+//
+// Note: fake_user_ptr should *never* be used outside of test code because it lacks the safety
+// mechanisms of user_ptr.
+
+template <typename T, typename UCT>
+using testable_user_in_ptr = internal::user_ptr<T, internal::kIn, UCT>;
+
+template <typename T, typename UCT>
+using testable_user_out_ptr = internal::user_ptr<T, internal::kOut, UCT>;
+
+template <typename T, typename UCT>
+using testable_user_inout_ptr = internal::user_ptr<T, internal::kInOut, UCT>;
diff --git a/kernel/lib/user_copy/include/lib/user_copy/user_ptr.h b/kernel/lib/user_copy/include/lib/user_copy/user_ptr.h
index 87735e6..1786dcc 100644
--- a/kernel/lib/user_copy/include/lib/user_copy/user_ptr.h
+++ b/kernel/lib/user_copy/include/lib/user_copy/user_ptr.h
@@ -21,7 +21,15 @@
     kInOut = kIn | kOut,
 };
 
-template <typename T, InOutPolicy Policy>
+typedef zx_status_t (*CopyFunc)(void*, const void*, size_t);
+
+class RealUserCopyTraits {
+public:
+    constexpr static CopyFunc CopyTo = arch_copy_to_user;
+    constexpr static CopyFunc CopyFrom = arch_copy_from_user;
+};
+
+template <typename T, InOutPolicy Policy, typename UserCopyTraits>
 class user_ptr {
 public:
     static_assert(fbl::is_const<T>::value == (Policy == kIn),
@@ -39,7 +47,9 @@
     T* get() const { return ptr_; }
 
     template <typename C>
-    user_ptr<C, Policy> reinterpret() const { return user_ptr<C, Policy>(reinterpret_cast<C*>(ptr_)); }
+    user_ptr<C, Policy, UserCopyTraits> reinterpret() const {
+        return user_ptr<C, Policy, UserCopyTraits>(reinterpret_cast<C*>(ptr_));
+    }
 
     // special operator to return the nullness of the pointer
     explicit operator bool() const { return ptr_ != nullptr; }
@@ -63,7 +73,7 @@
     zx_status_t copy_to_user(const S& src) const {
         static_assert(fbl::is_same<S, T>::value, "Do not use the template parameter.");
         static_assert(Policy & kOut, "can only copy to user for kOut or kInOut user_ptr");
-        return arch_copy_to_user(ptr_, &src, sizeof(S));
+        return UserCopyTraits::CopyTo(ptr_, &src, sizeof(S));
     }
 
     // Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
@@ -73,7 +83,7 @@
         if (mul_overflow(count, internal::type_size<T>(), &len)) {
             return ZX_ERR_INVALID_ARGS;
         }
-        return arch_copy_to_user(ptr_, src, len);
+        return UserCopyTraits::CopyTo(ptr_, src, len);
     }
 
     // Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
@@ -83,14 +93,14 @@
         if (mul_overflow(count, internal::type_size<T>(), &len)) {
             return ZX_ERR_INVALID_ARGS;
         }
-        return arch_copy_to_user(ptr_ + offset, src, len);
+        return UserCopyTraits::CopyTo(ptr_ + offset, src, len);
     }
 
     // Copies a single T from user memory. (Using this will fail to compile if T is |void|.)
     zx_status_t copy_from_user(typename fbl::remove_const<T>::type* dst) const {
         static_assert(Policy & kIn, "can only copy from user for kIn or kInOut user_ptr");
         // Intentionally use sizeof(T) here, so *using* this method won't compile if T is |void|.
-        return arch_copy_from_user(dst, ptr_, sizeof(T));
+        return UserCopyTraits::CopyFrom(dst, ptr_, sizeof(T));
     }
 
     // Copies an array of T from user memory. Note: This takes a count not a size, unless T is
@@ -101,7 +111,7 @@
         if (mul_overflow(count, internal::type_size<T>(), &len)) {
             return ZX_ERR_INVALID_ARGS;
         }
-        return arch_copy_from_user(dst, ptr_, len);
+        return UserCopyTraits::CopyFrom(dst, ptr_, len);
     }
 
     // Copies a sub-array of T from user memory. Note: This takes a count not a size, unless T is
@@ -112,7 +122,7 @@
         if (mul_overflow(count, internal::type_size<T>(), &len)) {
             return ZX_ERR_INVALID_ARGS;
         }
-        return arch_copy_from_user(dst, ptr_ + offset, len);
+        return UserCopyTraits::CopyFrom(dst, ptr_ + offset, len);
     }
 
 private:
@@ -125,13 +135,13 @@
 } // namespace internal
 
 template <typename T>
-using user_in_ptr = internal::user_ptr<T, internal::kIn>;
+using user_in_ptr = internal::user_ptr<T, internal::kIn, internal::RealUserCopyTraits>;
 
 template <typename T>
-using user_out_ptr = internal::user_ptr<T, internal::kOut>;
+using user_out_ptr = internal::user_ptr<T, internal::kOut, internal::RealUserCopyTraits>;
 
 template <typename T>
-using user_inout_ptr = internal::user_ptr<T, internal::kInOut>;
+using user_inout_ptr = internal::user_ptr<T, internal::kInOut, internal::RealUserCopyTraits>;
 
 template <typename T>
 user_in_ptr<T> make_user_in_ptr(T* p) { return user_in_ptr<T>(p); }
diff --git a/kernel/lib/user_copy/rules.mk b/kernel/lib/user_copy/rules.mk
index ba9e8f7..0de507c 100644
--- a/kernel/lib/user_copy/rules.mk
+++ b/kernel/lib/user_copy/rules.mk
@@ -13,6 +13,6 @@
 # arch_copy_to_user functions to use the higher-level functionality
 # present in this module.
 
-MODULE_SRCS := $(LOCAL_DIR)/empty.c
+MODULE_SRCS := $(LOCAL_DIR)/fake_user_ptr.cpp
 
 include make/module.mk