1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <arch/user_copy.h>
8 #include <lib/user_copy/internal.h>
9 #include <type_traits>
10 #include <vm/vm.h>
11 #include <zircon/types.h>
12 
13 // user_*_ptr<> wraps a pointer to user memory, to differentiate it from kernel
14 // memory. They can be in, out, or inout pointers.
15 
16 namespace internal {
17 
18 enum InOutPolicy {
19     kIn = 1,
20     kOut = 2,
21     kInOut = kIn | kOut,
22 };
23 
24 template <typename T, InOutPolicy Policy>
25 class user_ptr {
26 public:
27     static_assert(std::is_const<T>::value == (Policy == kIn),
28                   "In pointers must be const, and Out and InOut pointers must not be const");
29 
user_ptr(T * p)30     explicit user_ptr(T* p) : ptr_(p) {}
31 
user_ptr(const user_ptr & other)32     user_ptr(const user_ptr& other) : ptr_(other.ptr_) {}
33 
34     user_ptr& operator=(const user_ptr& other) {
35         ptr_ = other.ptr_;
36         return *this;
37     }
38 
get()39     T* get() const { return ptr_; }
40 
41     template <typename C>
reinterpret()42     user_ptr<C, Policy> reinterpret() const { return user_ptr<C, Policy>(reinterpret_cast<C*>(ptr_)); }
43 
44     // special operator to return the nullness of the pointer
45     explicit operator bool() const { return ptr_ != nullptr; }
46 
47     // Returns a user_ptr pointing to the |index|-th element from this one, or a null user_ptr if
48     // this pointer is null. Note: This does no other validation, and the behavior is undefined on
49     // overflow. (Using this will fail to compile if T is |void|.)
element_offset(size_t index)50     user_ptr element_offset(size_t index) const {
51         return ptr_ ? user_ptr(ptr_ + index) : user_ptr(nullptr);
52     }
53 
54     // Returns a user_ptr offset by |offset| bytes from this one.
byte_offset(size_t offset)55     user_ptr byte_offset(size_t offset) const {
56         return ptr_ ? user_ptr(reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr_) + offset))
57                     : user_ptr(nullptr);
58     }
59 
60     // Copies a single T to user memory. (Using this will fail to compile if T is |void|.)
61     // Note: The templatization is simply to allow the class to compile if T is |void|.
62     template <typename S = T>
copy_to_user(const S & src)63     zx_status_t copy_to_user(const S& src) const {
64         static_assert(std::is_same<S, T>::value, "Do not use the template parameter.");
65         static_assert(Policy & kOut, "can only copy to user for kOut or kInOut user_ptr");
66         return arch_copy_to_user(ptr_, &src, sizeof(S));
67     }
68 
69     // Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
copy_array_to_user(const T * src,size_t count)70     zx_status_t copy_array_to_user(const T* src, size_t count) const {
71         static_assert(Policy & kOut, "can only copy to user for kOut or kInOut user_ptr");
72         size_t len;
73         if (mul_overflow(count, internal::type_size<T>(), &len)) {
74             return ZX_ERR_INVALID_ARGS;
75         }
76         return arch_copy_to_user(ptr_, src, len);
77     }
78 
79     // Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
copy_array_to_user(const T * src,size_t count,size_t offset)80     zx_status_t copy_array_to_user(const T* src, size_t count, size_t offset) const {
81         static_assert(Policy & kOut, "can only copy to user for kOut or kInOut user_ptr");
82         size_t len;
83         if (mul_overflow(count, internal::type_size<T>(), &len)) {
84             return ZX_ERR_INVALID_ARGS;
85         }
86         return arch_copy_to_user(ptr_ + offset, src, len);
87     }
88 
89     // Copies a single T from user memory. (Using this will fail to compile if T is |void|.)
copy_from_user(typename std::remove_const<T>::type * dst)90     zx_status_t copy_from_user(typename std::remove_const<T>::type* dst) const {
91         static_assert(Policy & kIn, "can only copy from user for kIn or kInOut user_ptr");
92         // Intentionally use sizeof(T) here, so *using* this method won't compile if T is |void|.
93         return arch_copy_from_user(dst, ptr_, sizeof(T));
94     }
95 
96     // Copies an array of T from user memory. Note: This takes a count not a size, unless T is
97     // |void|.
copy_array_from_user(typename std::remove_const<T>::type * dst,size_t count)98     zx_status_t copy_array_from_user(typename std::remove_const<T>::type* dst, size_t count) const {
99         static_assert(Policy & kIn, "can only copy from user for kIn or kInOut user_ptr");
100         size_t len;
101         if (mul_overflow(count, internal::type_size<T>(), &len)) {
102             return ZX_ERR_INVALID_ARGS;
103         }
104         return arch_copy_from_user(dst, ptr_, len);
105     }
106 
107     // Copies a sub-array of T from user memory. Note: This takes a count not a size, unless T is
108     // |void|.
copy_array_from_user(typename std::remove_const<T>::type * dst,size_t count,size_t offset)109     zx_status_t copy_array_from_user(typename std::remove_const<T>::type* dst, size_t count, size_t offset) const {
110         static_assert(Policy & kIn, "can only copy from user for kIn or kInOut user_ptr");
111         size_t len;
112         if (mul_overflow(count, internal::type_size<T>(), &len)) {
113             return ZX_ERR_INVALID_ARGS;
114         }
115         return arch_copy_from_user(dst, ptr_ + offset, len);
116     }
117 
118 private:
119     // It is very important that this class only wrap the pointer type itself
120     // and not include any other members so as not to break the ABI between
121     // the kernel and user space.
122     T* ptr_;
123 };
124 
125 } // namespace internal
126 
127 template <typename T>
128 using user_in_ptr = internal::user_ptr<T, internal::kIn>;
129 
130 template <typename T>
131 using user_out_ptr = internal::user_ptr<T, internal::kOut>;
132 
133 template <typename T>
134 using user_inout_ptr = internal::user_ptr<T, internal::kInOut>;
135 
136 template <typename T>
make_user_in_ptr(T * p)137 user_in_ptr<T> make_user_in_ptr(T* p) { return user_in_ptr<T>(p); }
138 
139 template <typename T>
make_user_out_ptr(T * p)140 user_out_ptr<T> make_user_out_ptr(T* p) { return user_out_ptr<T>(p); }
141 
142 template <typename T>
make_user_inout_ptr(T * p)143 user_inout_ptr<T> make_user_inout_ptr(T* p) { return user_inout_ptr<T>(p); }
144