security/sandbox/win/src/crosscall_server.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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

mercurial