1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SANDBOXED_API_VAR_ARRAY_H_ 16 #define SANDBOXED_API_VAR_ARRAY_H_ 17 18 #include <algorithm> 19 #include <cstdlib> 20 #include <cstring> 21 #include <string> 22 #include <type_traits> 23 24 #include "absl/log/check.h" 25 #include "absl/log/log.h" 26 #include "absl/status/status.h" 27 #include "absl/strings/str_cat.h" 28 #include "absl/strings/string_view.h" 29 #include "sandboxed_api/rpcchannel.h" 30 #include "sandboxed_api/util/status_macros.h" 31 #include "sandboxed_api/var_abstract.h" 32 #include "sandboxed_api/var_type.h" 33 34 namespace sapi::v { 35 36 // Class representing an array. 37 template <class T> 38 class Array : public Var { 39 public: 40 // The array is not owned by this object. Array(T * arr,size_t nelem)41 Array(T* arr, size_t nelem) 42 : arr_(arr), 43 nelem_(nelem), 44 total_size_(nelem_ * sizeof(T)), 45 buffer_owned_(false) { 46 SetLocal(const_cast<std::remove_const_t<T>*>(arr_)); 47 } 48 49 // The array is allocated and owned by this object. Array(size_t nelem)50 explicit Array(size_t nelem) 51 : nelem_(nelem), total_size_(nelem_ * sizeof(T)), buffer_owned_(true) { 52 void* storage = malloc(sizeof(T) * nelem); 53 CHECK(storage != nullptr); 54 SetLocal(storage); 55 arr_ = static_cast<T*>(storage); 56 } 57 Array(Array && other)58 Array(Array&& other) { *this = std::move(other); } 59 Array& operator=(Array&& other) { 60 if (this != &other) { 61 Var::operator=(std::move(other)); 62 using std::swap; 63 swap(arr_, other.arr_); 64 swap(nelem_, other.nelem_); 65 swap(total_size_, other.total_size_); 66 swap(buffer_owned_, other.buffer_owned_); 67 other.buffer_owned_ = false; // If it was owned before, we own it now. 68 } 69 return *this; 70 } 71 ~Array()72 virtual ~Array() { 73 if (buffer_owned_) { 74 free(const_cast<std::remove_const_t<T>*>(arr_)); 75 } 76 } 77 78 T& operator[](size_t v) const { return arr_[v]; } GetData()79 T* GetData() const { return arr_; } 80 GetNElem()81 size_t GetNElem() const { return nelem_; } GetSize()82 size_t GetSize() const final { return total_size_; } GetType()83 Type GetType() const final { return Type::kArray; } GetTypeString()84 std::string GetTypeString() const final { return "Array"; } ToString()85 std::string ToString() const override { 86 return absl::StrCat("Array, elem size: ", sizeof(T), 87 " B., total size: ", total_size_, 88 " B., nelems: ", GetNElem()); 89 } 90 91 // Resizes the local and remote buffer using realloc(). Note that this will 92 // make all pointers to the current data (inside and outside of the sandbox) 93 // invalid. Resize(RPCChannel * rpc_channel,size_t nelems)94 absl::Status Resize(RPCChannel* rpc_channel, size_t nelems) { 95 size_t absolute_size = sizeof(T) * nelems; 96 // Resize local buffer. 97 SAPI_RETURN_IF_ERROR(EnsureOwnedLocalBuffer(absolute_size)); 98 99 // Resize remote buffer and update local pointer. 100 void* new_addr; 101 102 SAPI_RETURN_IF_ERROR( 103 rpc_channel->Reallocate(GetRemote(), absolute_size, &new_addr)); 104 if (!new_addr) { 105 return absl::UnavailableError("Reallocate() returned nullptr"); 106 } 107 SetRemote(new_addr); 108 return absl::OkStatus(); 109 } 110 111 private: 112 friend class LenVal; 113 114 // Resizes the internal storage. EnsureOwnedLocalBuffer(size_t size)115 absl::Status EnsureOwnedLocalBuffer(size_t size) { 116 if (size % sizeof(T)) { 117 return absl::FailedPreconditionError( 118 "Array size not a multiple of the item size"); 119 } 120 // Do not (re-)allocate memory if the new size matches our size - except 121 // when we don't own that buffer. 122 if (size == total_size_ && buffer_owned_) { 123 return absl::OkStatus(); 124 } 125 void* new_addr = nullptr; 126 if (buffer_owned_) { 127 new_addr = realloc(arr_, size); 128 } else { 129 new_addr = malloc(size); 130 if (new_addr) { 131 memcpy(new_addr, arr_, std::min(size, total_size_)); 132 buffer_owned_ = true; 133 } 134 } 135 if (!new_addr) { 136 return absl::UnavailableError("(Re-)malloc failed"); 137 } 138 139 arr_ = static_cast<T*>(new_addr); 140 total_size_ = size; 141 nelem_ = size / sizeof(T); 142 SetLocal(new_addr); 143 return absl::OkStatus(); 144 } 145 146 // Pointer to the data, owned by the object if buffer_owned_ is 'true'. 147 T* arr_ = nullptr; 148 size_t nelem_ = 0; // Number of elements 149 size_t total_size_ = 0; // Total size in bytes 150 bool buffer_owned_ = false; // Whether we own the buffer 151 }; 152 153 // Specialized Array class for representing NUL-terminated C-style strings. The 154 // buffer is owned by the class, and is mutable. 155 class CStr : public Array<char> { 156 public: CStr(absl::string_view cstr)157 explicit CStr(absl::string_view cstr) : Array<char>(cstr.size() + 1) { 158 std::copy(cstr.begin(), cstr.end(), GetData()); 159 GetData()[cstr.size()] = '\0'; 160 } 161 ToString()162 std::string ToString() const final { 163 return absl::StrCat("CStr: len(w/o NUL):", strlen(GetData()), ", ['", 164 GetData(), "']"); 165 } 166 }; 167 168 // Specialized Array class for representing NUL-terminated C-style strings. The 169 // buffer is not owned by the class and is not mutable. 170 class ConstCStr : public Array<const char> { 171 public: ConstCStr(const char * cstr)172 explicit ConstCStr(const char* cstr) 173 : Array<const char>(cstr, strlen(cstr) + 1) {} 174 ToString()175 std::string ToString() const final { 176 return absl::StrCat("ConstCStr: len(w/o NUL):", strlen(GetData()), ", ['", 177 GetData(), "']"); 178 } 179 }; 180 181 } // namespace sapi::v 182 183 #endif // SANDBOXED_API_VAR_ARRAY_H_ 184