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