| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium 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 #include "sandbox/win/src/crosscall_server.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <string> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/logging.h" | |
| 14 #include "sandbox/win/src/crosscall_client.h" | |
| 15 #include "sandbox/win/src/crosscall_params.h" | |
| 16 | |
| 17 // This code performs the ipc message validation. Potential security flaws | |
| 18 // on the ipc are likelier to be found in this code than in the rest of | |
| 19 // the ipc code. | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // The buffer for a message must match the max channel size. | |
| 24 const size_t kMaxBufferSize = sandbox::kIPCChannelSize; | |
| 25 | |
| 26 } | |
| 27 | |
| 28 namespace sandbox { | |
| 29 | |
| 30 // Returns the actual size for the parameters in an IPC buffer. Returns | |
| 31 // zero if the |param_count| is zero or too big. | |
| 32 uint32_t GetActualBufferSize(uint32_t param_count, void* buffer_base) { | |
| 33 // The template types are used to calculate the maximum expected size. | |
| 34 typedef ActualCallParams<1, kMaxBufferSize> ActualCP1; | |
| 35 typedef ActualCallParams<2, kMaxBufferSize> ActualCP2; | |
| 36 typedef ActualCallParams<3, kMaxBufferSize> ActualCP3; | |
| 37 typedef ActualCallParams<4, kMaxBufferSize> ActualCP4; | |
| 38 typedef ActualCallParams<5, kMaxBufferSize> ActualCP5; | |
| 39 typedef ActualCallParams<6, kMaxBufferSize> ActualCP6; | |
| 40 typedef ActualCallParams<7, kMaxBufferSize> ActualCP7; | |
| 41 typedef ActualCallParams<8, kMaxBufferSize> ActualCP8; | |
| 42 typedef ActualCallParams<9, kMaxBufferSize> ActualCP9; | |
| 43 | |
| 44 // Retrieve the actual size and the maximum size of the params buffer. | |
| 45 switch (param_count) { | |
| 46 case 0: | |
| 47 return 0; | |
| 48 case 1: | |
| 49 return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize(); | |
| 50 case 2: | |
| 51 return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize(); | |
| 52 case 3: | |
| 53 return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize(); | |
| 54 case 4: | |
| 55 return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize(); | |
| 56 case 5: | |
| 57 return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize(); | |
| 58 case 6: | |
| 59 return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize(); | |
| 60 case 7: | |
| 61 return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize(); | |
| 62 case 8: | |
| 63 return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize(); | |
| 64 case 9: | |
| 65 return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize(); | |
| 66 default: | |
| 67 return 0; | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 // Verifies that the declared sizes of an IPC buffer are within range. | |
| 72 bool IsSizeWithinRange(uint32_t buffer_size, | |
| 73 uint32_t min_declared_size, | |
| 74 uint32_t declared_size) { | |
| 75 if ((buffer_size < min_declared_size) || | |
| 76 (sizeof(CrossCallParamsEx) > min_declared_size)) { | |
| 77 // Minimal computed size bigger than existing buffer or param_count | |
| 78 // integer overflow. | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 if ((declared_size > buffer_size) || (declared_size < min_declared_size)) { | |
| 83 // Declared size is bigger than buffer or smaller than computed size | |
| 84 // or param_count is equal to 0 or bigger than 9. | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 CrossCallParamsEx::CrossCallParamsEx() | |
| 92 :CrossCallParams(0, 0) { | |
| 93 } | |
| 94 | |
| 95 // We override the delete operator because the object's backing memory | |
| 96 // is hand allocated in CreateFromBuffer. We don't override the new operator | |
| 97 // because the constructors are private so there is no way to mismatch | |
| 98 // new & delete. | |
| 99 void CrossCallParamsEx::operator delete(void* raw_memory) throw() { | |
| 100 if (NULL == raw_memory) { | |
| 101 // C++ standard allows 'delete 0' behavior. | |
| 102 return; | |
| 103 } | |
| 104 delete[] reinterpret_cast<char*>(raw_memory); | |
| 105 } | |
| 106 | |
| 107 // This function uses a SEH try block so cannot use C++ objects that | |
| 108 // have destructors or else you get Compiler Error C2712. So no DCHECKs | |
| 109 // inside this function. | |
| 110 CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, | |
| 111 uint32_t buffer_size, | |
| 112 uint32_t* output_size) { | |
| 113 // IMPORTANT: Everything inside buffer_base and derived from it such | |
| 114 // as param_count and declared_size is untrusted. | |
| 115 if (NULL == buffer_base) { | |
| 116 return NULL; | |
| 117 } | |
| 118 if (buffer_size < sizeof(CrossCallParams)) { | |
| 119 return NULL; | |
| 120 } | |
| 121 if (buffer_size > kMaxBufferSize) { | |
| 122 return NULL; | |
| 123 } | |
| 124 | |
| 125 char* backing_mem = NULL; | |
| 126 uint32_t param_count = 0; | |
| 127 uint32_t declared_size; | |
| 128 uint32_t min_declared_size; | |
| 129 CrossCallParamsEx* copied_params = NULL; | |
| 130 | |
| 131 // Touching the untrusted buffer is done under a SEH try block. This | |
| 132 // will catch memory access violations so we don't crash. | |
| 133 __try { | |
| 134 CrossCallParams* call_params = | |
| 135 reinterpret_cast<CrossCallParams*>(buffer_base); | |
| 136 | |
| 137 // Check against the minimum size given the number of stated params | |
| 138 // if too small we bail out. | |
| 139 param_count = call_params->GetParamsCount(); | |
| 140 min_declared_size = sizeof(CrossCallParams) + | |
| 141 ((param_count + 1) * sizeof(ParamInfo)); | |
| 142 | |
| 143 // Retrieve the declared size which if it fails returns 0. | |
| 144 declared_size = GetActualBufferSize(param_count, buffer_base); | |
| 145 | |
| 146 if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) | |
| 147 return NULL; | |
| 148 | |
| 149 // Now we copy the actual amount of the message. | |
| 150 *output_size = declared_size; | |
| 151 backing_mem = new char[declared_size]; | |
| 152 copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem); | |
| 153 memcpy(backing_mem, call_params, declared_size); | |
| 154 | |
| 155 // Avoid compiler optimizations across this point. Any value stored in | |
| 156 // memory should be stored for real, and values previously read from memory | |
| 157 // should be actually read. | |
| 158 _ReadWriteBarrier(); | |
| 159 | |
| 160 min_declared_size = sizeof(CrossCallParams) + | |
| 161 ((param_count + 1) * sizeof(ParamInfo)); | |
| 162 | |
| 163 // Check that the copied buffer is still valid. | |
| 164 if (copied_params->GetParamsCount() != param_count || | |
| 165 GetActualBufferSize(param_count, backing_mem) != declared_size || | |
| 166 !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) { | |
| 167 delete [] backing_mem; | |
| 168 return NULL; | |
| 169 } | |
| 170 | |
| 171 } __except(EXCEPTION_EXECUTE_HANDLER) { | |
| 172 // In case of a windows exception we know it occurred while touching the | |
| 173 // untrusted buffer so we bail out as is. | |
| 174 delete [] backing_mem; | |
| 175 return NULL; | |
| 176 } | |
| 177 | |
| 178 const char* last_byte = &backing_mem[declared_size]; | |
| 179 const char* first_byte = &backing_mem[min_declared_size]; | |
| 180 | |
| 181 // Verify here that all and each parameters make sense. This is done in the | |
| 182 // local copy. | |
| 183 for (uint32_t ix = 0; ix != param_count; ++ix) { | |
| 184 uint32_t size = 0; | |
| 185 ArgType type; | |
| 186 char* address = reinterpret_cast<char*>( | |
| 187 copied_params->GetRawParameter(ix, &size, &type)); | |
| 188 if ((NULL == address) || // No null params. | |
| 189 (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type. | |
| 190 (address < backing_mem) || // Start cannot point before buffer. | |
| 191 (address < first_byte) || // Start cannot point too low. | |
| 192 (address > last_byte) || // Start cannot point past buffer. | |
| 193 ((address + size) < address) || // Invalid size. | |
| 194 ((address + size) > last_byte)) { // End cannot point past buffer. | |
| 195 // Malformed. | |
| 196 delete[] backing_mem; | |
| 197 return NULL; | |
| 198 } | |
| 199 } | |
| 200 // The parameter buffer looks good. | |
| 201 return copied_params; | |
| 202 } | |
| 203 | |
| 204 // Accessors to the parameters in the raw buffer. | |
| 205 void* CrossCallParamsEx::GetRawParameter(uint32_t index, | |
| 206 uint32_t* size, | |
| 207 ArgType* type) { | |
| 208 if (index >= GetParamsCount()) { | |
| 209 return NULL; | |
| 210 } | |
| 211 // The size is always computed from the parameter minus the next | |
| 212 // parameter, this works because the message has an extra parameter slot | |
| 213 *size = param_info_[index].size_; | |
| 214 *type = param_info_[index].type_; | |
| 215 | |
| 216 return param_info_[index].offset_ + reinterpret_cast<char*>(this); | |
| 217 } | |
| 218 | |
| 219 // Covers common case for 32 bit integers. | |
| 220 bool CrossCallParamsEx::GetParameter32(uint32_t index, uint32_t* param) { | |
| 221 uint32_t size = 0; | |
| 222 ArgType type; | |
| 223 void* start = GetRawParameter(index, &size, &type); | |
| 224 if ((NULL == start) || (4 != size) || (UINT32_TYPE != type)) { | |
| 225 return false; | |
| 226 } | |
| 227 // Copy the 4 bytes. | |
| 228 *(reinterpret_cast<uint32_t*>(param)) = *(reinterpret_cast<uint32_t*>(start)); | |
| 229 return true; | |
| 230 } | |
| 231 | |
| 232 bool CrossCallParamsEx::GetParameterVoidPtr(uint32_t index, void** param) { | |
| 233 uint32_t size = 0; | |
| 234 ArgType type; | |
| 235 void* start = GetRawParameter(index, &size, &type); | |
| 236 if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) { | |
| 237 return false; | |
| 238 } | |
| 239 *param = *(reinterpret_cast<void**>(start)); | |
| 240 return true; | |
| 241 } | |
| 242 | |
| 243 // Covers the common case of reading a string. Note that the string is not | |
| 244 // scanned for invalid characters. | |
| 245 bool CrossCallParamsEx::GetParameterStr(uint32_t index, | |
| 246 base::string16* string) { | |
| 247 uint32_t size = 0; | |
| 248 ArgType type; | |
| 249 void* start = GetRawParameter(index, &size, &type); | |
| 250 if (WCHAR_TYPE != type) { | |
| 251 return false; | |
| 252 } | |
| 253 | |
| 254 // Check if this is an empty string. | |
| 255 if (size == 0) { | |
| 256 *string = L""; | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 if ((NULL == start) || ((size % sizeof(wchar_t)) != 0)) { | |
| 261 return false; | |
| 262 } | |
| 263 string->append(reinterpret_cast<wchar_t*>(start), size/(sizeof(wchar_t))); | |
| 264 return true; | |
| 265 } | |
| 266 | |
| 267 bool CrossCallParamsEx::GetParameterPtr(uint32_t index, | |
| 268 uint32_t expected_size, | |
| 269 void** pointer) { | |
| 270 uint32_t size = 0; | |
| 271 ArgType type; | |
| 272 void* start = GetRawParameter(index, &size, &type); | |
| 273 | |
| 274 if ((size != expected_size) || (INOUTPTR_TYPE != type)) { | |
| 275 return false; | |
| 276 } | |
| 277 | |
| 278 if (NULL == start) { | |
| 279 return false; | |
| 280 } | |
| 281 | |
| 282 *pointer = start; | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 void SetCallError(ResultCode error, CrossCallReturn* call_return) { | |
| 287 call_return->call_outcome = error; | |
| 288 call_return->extended_count = 0; | |
| 289 } | |
| 290 | |
| 291 void SetCallSuccess(CrossCallReturn* call_return) { | |
| 292 call_return->call_outcome = SBOX_ALL_OK; | |
| 293 } | |
| 294 | |
| 295 Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc, | |
| 296 CallbackGeneric* callback) { | |
| 297 DCHECK(callback); | |
| 298 std::vector<IPCCall>::iterator it = ipc_calls_.begin(); | |
| 299 for (; it != ipc_calls_.end(); ++it) { | |
| 300 if (it->params.Matches(ipc)) { | |
| 301 *callback = it->callback; | |
| 302 return this; | |
| 303 } | |
| 304 } | |
| 305 return NULL; | |
| 306 } | |
| 307 | |
| 308 Dispatcher::Dispatcher() { | |
| 309 } | |
| 310 | |
| 311 Dispatcher::~Dispatcher() { | |
| 312 } | |
| 313 | |
| 314 } // namespace sandbox | |
| OLD | NEW |