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