| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef MOJO_NACL_MOJO_SYSCALL_INTERNAL_H_ | 
 | #define MOJO_NACL_MOJO_SYSCALL_INTERNAL_H_ | 
 |  | 
 | #include <type_traits> | 
 |  | 
 | #include "native_client/src/trusted/service_runtime/nacl_copy.h" | 
 | #include "native_client/src/trusted/service_runtime/sel_ldr.h" | 
 |  | 
 | namespace { | 
 |  | 
 | class ScopedCopyLock { | 
 |  public: | 
 |   explicit ScopedCopyLock(struct NaClApp* nap) : nap_(nap) { | 
 |     NaClCopyTakeLock(nap_); | 
 |   } | 
 |   ~ScopedCopyLock() { NaClCopyDropLock(nap_); } | 
 |  | 
 |  private: | 
 |   struct NaClApp* nap_; | 
 | }; | 
 |  | 
 | static inline uintptr_t NaClUserToSysAddrArray(struct NaClApp* nap, | 
 |                                                uint32_t uaddr, | 
 |                                                size_t count, | 
 |                                                size_t size) { | 
 |   // TODO(ncbray): overflow checking | 
 |   size_t range = count * size; | 
 |   return NaClUserToSysAddrRange(nap, uaddr, range); | 
 | } | 
 |  | 
 | // We don't use plain-old memcpy because reads and writes to the untrusted | 
 | // address space from trusted code must be volatile.  Non-volatile memory | 
 | // operations are dangerous because a compiler would be free to materialize a | 
 | // second load from the same memory address or materialize a load from a memory | 
 | // address that was stored, and assume the materialized load would return the | 
 | // same value as the previous load or store.  Data races could cause the | 
 | // materialized load to return a different value, however, which could lead to | 
 | // time of check vs. time of use problems, or worse.  For this binding code in | 
 | // particular, where memcpy is being called with a constant size, it is entirely | 
 | // conceivable the function will be inlined, unrolled, and optimized. | 
 | static inline void memcpy_volatile_out(void volatile* dst, | 
 |                                        const void* src, | 
 |                                        size_t n) { | 
 |   char volatile* c_dst = static_cast<char volatile*>(dst); | 
 |   const char* c_src = static_cast<const char*>(src); | 
 |   for (size_t i = 0; i < n; i++) { | 
 |     c_dst[i] = c_src[i]; | 
 |   } | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertPointerInput(struct NaClApp* nap, uint32_t user_ptr, T** value) { | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddr(nap, user_ptr); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *value = reinterpret_cast<T*>(temp); | 
 |       return true; | 
 |     } | 
 |   } else { | 
 |     *value = nullptr; | 
 |     return true; | 
 |   } | 
 |   *value = nullptr;  // Paranoia. | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertPointerInOut(struct NaClApp* nap, | 
 |                          uint32_t user_ptr, | 
 |                          bool optional, | 
 |                          T** value, | 
 |                          uint32_t volatile** sys_ptr) { | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(uint32_t)); | 
 |     if (temp != kNaClBadAddress) { | 
 |       uint32_t volatile* converted_ptr = | 
 |           reinterpret_cast<uint32_t volatile*>(temp); | 
 |       uint32_t raw_value = *converted_ptr; | 
 |       if (!raw_value) { | 
 |         *sys_ptr = converted_ptr; | 
 |         *value = nullptr; | 
 |         return true; | 
 |       } | 
 |       uintptr_t temp = NaClUserToSysAddr(nap, raw_value); | 
 |       if (temp != kNaClBadAddress) { | 
 |         *sys_ptr = converted_ptr; | 
 |         *value = reinterpret_cast<T*>(temp); | 
 |         return true; | 
 |       } | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = nullptr; | 
 |     *value = nullptr;  // Paranoia. | 
 |     return true; | 
 |   } | 
 |   *sys_ptr = nullptr;  // Paranoia. | 
 |   *value = nullptr;    // Paranoia. | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | void CopyOutPointer(struct NaClApp* nap, T* value, uint32_t volatile* sys_ptr) { | 
 |   if (value) { | 
 |     // Will kill the process if value if the pointer does not lie in the | 
 |     // sandbox. | 
 |     uintptr_t temp = NaClSysToUser(nap, reinterpret_cast<uintptr_t>(value)); | 
 |     *sys_ptr = static_cast<uint32_t>(temp); | 
 |   } else { | 
 |     *sys_ptr = 0; | 
 |   } | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertScalarInput(struct NaClApp* nap, uint32_t user_ptr, T* value) { | 
 |   static_assert(!std::is_pointer<T>::value, "do not use for pointer types"); | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T)); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *value = *reinterpret_cast<T volatile*>(temp); | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertScalarOutput(struct NaClApp* nap, | 
 |                          uint32_t user_ptr, | 
 |                          bool optional, | 
 |                          T volatile** sys_ptr) { | 
 |   static_assert(!std::is_pointer<T>::value, "do not use for pointer types"); | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T)); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *sys_ptr = reinterpret_cast<T volatile*>(temp); | 
 |       return true; | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = 0; | 
 |     return true; | 
 |   } | 
 |   *sys_ptr = 0;  // Paranoia. | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertScalarInOut(struct NaClApp* nap, | 
 |                         uint32_t user_ptr, | 
 |                         bool optional, | 
 |                         T* value, | 
 |                         T volatile** sys_ptr) { | 
 |   static_assert(!std::is_pointer<T>::value, "do not use for pointer types"); | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T)); | 
 |     if (temp != kNaClBadAddress) { | 
 |       T volatile* converted = reinterpret_cast<T volatile*>(temp); | 
 |       *sys_ptr = converted; | 
 |       *value = *converted; | 
 |       return true; | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = 0; | 
 |     *value = static_cast<T>(0);  // Paranoia. | 
 |     return true; | 
 |   } | 
 |   *sys_ptr = 0;                // Paranoia. | 
 |   *value = static_cast<T>(0);  // Paranoia. | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertArray(struct NaClApp* nap, | 
 |                   uint32_t user_ptr, | 
 |                   uint32_t length, | 
 |                   size_t element_size, | 
 |                   bool optional, | 
 |                   T** sys_ptr) { | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = | 
 |         NaClUserToSysAddrArray(nap, user_ptr, length, element_size); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *sys_ptr = reinterpret_cast<T*>(temp); | 
 |       return true; | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = 0; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool ConvertBytes(struct NaClApp* nap, | 
 |                   uint32_t user_ptr, | 
 |                   uint32_t length, | 
 |                   bool optional, | 
 |                   T** sys_ptr) { | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, length); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *sys_ptr = reinterpret_cast<T*>(temp); | 
 |       return true; | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = 0; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | // TODO(ncbray): size validation and complete copy. | 
 | // TODO(ncbray): ensure non-null / missized structs are covered by a test case. | 
 | template <typename T> | 
 | bool ConvertExtensibleStructInput(struct NaClApp* nap, | 
 |                                   uint32_t user_ptr, | 
 |                                   bool optional, | 
 |                                   T** sys_ptr) { | 
 |   static_assert(!std::is_pointer<T>::value, "do not use for pointer types"); | 
 |   if (user_ptr) { | 
 |     uintptr_t temp = NaClUserToSysAddrRange(nap, user_ptr, sizeof(T)); | 
 |     if (temp != kNaClBadAddress) { | 
 |       *sys_ptr = reinterpret_cast<T*>(temp); | 
 |       return true; | 
 |     } | 
 |   } else if (optional) { | 
 |     *sys_ptr = 0; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | #endif  // MOJO_NACL_MOJO_SYSCALL_INTERNAL_H_ |