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