1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/crosscall_server.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,301 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include <string> 1.9 +#include <vector> 1.10 + 1.11 +#include "sandbox/win/src/crosscall_server.h" 1.12 +#include "sandbox/win/src/crosscall_params.h" 1.13 +#include "sandbox/win/src/crosscall_client.h" 1.14 +#include "base/logging.h" 1.15 + 1.16 +// This code performs the ipc message validation. Potential security flaws 1.17 +// on the ipc are likelier to be found in this code than in the rest of 1.18 +// the ipc code. 1.19 + 1.20 +namespace { 1.21 + 1.22 +// The buffer for a message must match the max channel size. 1.23 +const size_t kMaxBufferSize = sandbox::kIPCChannelSize; 1.24 + 1.25 +} 1.26 + 1.27 +namespace sandbox { 1.28 + 1.29 +// Returns the actual size for the parameters in an IPC buffer. Returns 1.30 +// zero if the |param_count| is zero or too big. 1.31 +uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) { 1.32 + // The template types are used to calculate the maximum expected size. 1.33 + typedef ActualCallParams<1, kMaxBufferSize> ActualCP1; 1.34 + typedef ActualCallParams<2, kMaxBufferSize> ActualCP2; 1.35 + typedef ActualCallParams<3, kMaxBufferSize> ActualCP3; 1.36 + typedef ActualCallParams<4, kMaxBufferSize> ActualCP4; 1.37 + typedef ActualCallParams<5, kMaxBufferSize> ActualCP5; 1.38 + typedef ActualCallParams<6, kMaxBufferSize> ActualCP6; 1.39 + typedef ActualCallParams<7, kMaxBufferSize> ActualCP7; 1.40 + typedef ActualCallParams<8, kMaxBufferSize> ActualCP8; 1.41 + typedef ActualCallParams<9, kMaxBufferSize> ActualCP9; 1.42 + 1.43 + // Retrieve the actual size and the maximum size of the params buffer. 1.44 + switch (param_count) { 1.45 + case 0: 1.46 + return 0; 1.47 + case 1: 1.48 + return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize(); 1.49 + case 2: 1.50 + return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize(); 1.51 + case 3: 1.52 + return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize(); 1.53 + case 4: 1.54 + return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize(); 1.55 + case 5: 1.56 + return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize(); 1.57 + case 6: 1.58 + return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize(); 1.59 + case 7: 1.60 + return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize(); 1.61 + case 8: 1.62 + return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize(); 1.63 + case 9: 1.64 + return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize(); 1.65 + default: 1.66 + NOTREACHED(); 1.67 + return 0; 1.68 + } 1.69 +} 1.70 + 1.71 +// Verifies that the declared sizes of an IPC buffer are within range. 1.72 +bool IsSizeWithinRange(uint32 buffer_size, uint32 min_declared_size, 1.73 + uint32 declared_size) { 1.74 + if ((buffer_size < min_declared_size) || 1.75 + (sizeof(CrossCallParamsEx) > min_declared_size)) { 1.76 + // Minimal computed size bigger than existing buffer or param_count 1.77 + // integer overflow. 1.78 + return false; 1.79 + } 1.80 + 1.81 + if ((declared_size > buffer_size) || (declared_size < min_declared_size)) { 1.82 + // Declared size is bigger than buffer or smaller than computed size 1.83 + // or param_count is equal to 0 or bigger than 9. 1.84 + return false; 1.85 + } 1.86 + 1.87 + return true; 1.88 +} 1.89 + 1.90 +CrossCallParamsEx::CrossCallParamsEx() 1.91 + :CrossCallParams(0, 0) { 1.92 +} 1.93 + 1.94 +// We override the delete operator because the object's backing memory 1.95 +// is hand allocated in CreateFromBuffer. We don't override the new operator 1.96 +// because the constructors are private so there is no way to mismatch 1.97 +// new & delete. 1.98 +void CrossCallParamsEx::operator delete(void* raw_memory) throw() { 1.99 + if (NULL == raw_memory) { 1.100 + // C++ standard allows 'delete 0' behavior. 1.101 + return; 1.102 + } 1.103 + delete[] reinterpret_cast<char*>(raw_memory); 1.104 +} 1.105 + 1.106 +// This function uses a SEH try block so cannot use C++ objects that 1.107 +// have destructors or else you get Compiler Error C2712. So no DCHECKs 1.108 +// inside this function. 1.109 +CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, 1.110 + uint32 buffer_size, 1.111 + uint32* output_size) { 1.112 + // IMPORTANT: Everything inside buffer_base and derived from it such 1.113 + // as param_count and declared_size is untrusted. 1.114 + if (NULL == buffer_base) { 1.115 + return NULL; 1.116 + } 1.117 + if (buffer_size < sizeof(CrossCallParams)) { 1.118 + return NULL; 1.119 + } 1.120 + if (buffer_size > kMaxBufferSize) { 1.121 + return NULL; 1.122 + } 1.123 + 1.124 + char* backing_mem = NULL; 1.125 + uint32 param_count = 0; 1.126 + uint32 declared_size; 1.127 + uint32 min_declared_size; 1.128 + CrossCallParamsEx* copied_params = NULL; 1.129 + 1.130 + // Touching the untrusted buffer is done under a SEH try block. This 1.131 + // will catch memory access violations so we don't crash. 1.132 + __try { 1.133 + CrossCallParams* call_params = 1.134 + reinterpret_cast<CrossCallParams*>(buffer_base); 1.135 + 1.136 + // Check against the minimum size given the number of stated params 1.137 + // if too small we bail out. 1.138 + param_count = call_params->GetParamsCount(); 1.139 + min_declared_size = sizeof(CrossCallParams) + 1.140 + ((param_count + 1) * sizeof(ParamInfo)); 1.141 + 1.142 + // Retrieve the declared size which if it fails returns 0. 1.143 + declared_size = GetActualBufferSize(param_count, buffer_base); 1.144 + 1.145 + if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) 1.146 + return NULL; 1.147 + 1.148 + // Now we copy the actual amount of the message. 1.149 + *output_size = declared_size; 1.150 + backing_mem = new char[declared_size]; 1.151 + copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem); 1.152 + memcpy(backing_mem, call_params, declared_size); 1.153 + 1.154 + // Avoid compiler optimizations across this point. Any value stored in 1.155 + // memory should be stored for real, and values previously read from memory 1.156 + // should be actually read. 1.157 + _ReadWriteBarrier(); 1.158 + 1.159 + min_declared_size = sizeof(CrossCallParams) + 1.160 + ((param_count + 1) * sizeof(ParamInfo)); 1.161 + 1.162 + // Check that the copied buffer is still valid. 1.163 + if (copied_params->GetParamsCount() != param_count || 1.164 + GetActualBufferSize(param_count, backing_mem) != declared_size || 1.165 + !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) { 1.166 + delete [] backing_mem; 1.167 + return NULL; 1.168 + } 1.169 + 1.170 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.171 + // In case of a windows exception we know it occurred while touching the 1.172 + // untrusted buffer so we bail out as is. 1.173 + delete [] backing_mem; 1.174 + return NULL; 1.175 + } 1.176 + 1.177 + const char* last_byte = &backing_mem[declared_size]; 1.178 + const char* first_byte = &backing_mem[min_declared_size]; 1.179 + 1.180 + // Verify here that all and each parameters make sense. This is done in the 1.181 + // local copy. 1.182 + for (uint32 ix =0; ix != param_count; ++ix) { 1.183 + uint32 size = 0; 1.184 + ArgType type; 1.185 + char* address = reinterpret_cast<char*>( 1.186 + copied_params->GetRawParameter(ix, &size, &type)); 1.187 + if ((NULL == address) || // No null params. 1.188 + (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type. 1.189 + (address < backing_mem) || // Start cannot point before buffer. 1.190 + (address < first_byte) || // Start cannot point too low. 1.191 + (address > last_byte) || // Start cannot point past buffer. 1.192 + ((address + size) < address) || // Invalid size. 1.193 + ((address + size) > last_byte)) { // End cannot point past buffer. 1.194 + // Malformed. 1.195 + delete[] backing_mem; 1.196 + return NULL; 1.197 + } 1.198 + } 1.199 + // The parameter buffer looks good. 1.200 + return copied_params; 1.201 +} 1.202 + 1.203 +// Accessors to the parameters in the raw buffer. 1.204 +void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size, 1.205 + ArgType* type) { 1.206 + if (index >= GetParamsCount()) { 1.207 + return NULL; 1.208 + } 1.209 + // The size is always computed from the parameter minus the next 1.210 + // parameter, this works because the message has an extra parameter slot 1.211 + *size = param_info_[index].size_; 1.212 + *type = param_info_[index].type_; 1.213 + 1.214 + return param_info_[index].offset_ + reinterpret_cast<char*>(this); 1.215 +} 1.216 + 1.217 +// Covers common case for 32 bit integers. 1.218 +bool CrossCallParamsEx::GetParameter32(uint32 index, uint32* param) { 1.219 + uint32 size = 0; 1.220 + ArgType type; 1.221 + void* start = GetRawParameter(index, &size, &type); 1.222 + if ((NULL == start) || (4 != size) || (ULONG_TYPE != type)) { 1.223 + return false; 1.224 + } 1.225 + // Copy the 4 bytes. 1.226 + *(reinterpret_cast<uint32*>(param)) = *(reinterpret_cast<uint32*>(start)); 1.227 + return true; 1.228 +} 1.229 + 1.230 +bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { 1.231 + uint32 size = 0; 1.232 + ArgType type; 1.233 + void* start = GetRawParameter(index, &size, &type); 1.234 + if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) { 1.235 + return false; 1.236 + } 1.237 + *param = *(reinterpret_cast<void**>(start)); 1.238 + return true; 1.239 +} 1.240 + 1.241 +// Covers the common case of reading a string. Note that the string is not 1.242 +// scanned for invalid characters. 1.243 +bool CrossCallParamsEx::GetParameterStr(uint32 index, std::wstring* string) { 1.244 + uint32 size = 0; 1.245 + ArgType type; 1.246 + void* start = GetRawParameter(index, &size, &type); 1.247 + if (WCHAR_TYPE != type) { 1.248 + return false; 1.249 + } 1.250 + 1.251 + // Check if this is an empty string. 1.252 + if (size == 0) { 1.253 + *string = L""; 1.254 + return true; 1.255 + } 1.256 + 1.257 + if ((NULL == start) || ((size % sizeof(wchar_t)) != 0)) { 1.258 + return false; 1.259 + } 1.260 + string->append(reinterpret_cast<wchar_t*>(start), size/(sizeof(wchar_t))); 1.261 + return true; 1.262 +} 1.263 + 1.264 +bool CrossCallParamsEx::GetParameterPtr(uint32 index, uint32 expected_size, 1.265 + void** pointer) { 1.266 + uint32 size = 0; 1.267 + ArgType type; 1.268 + void* start = GetRawParameter(index, &size, &type); 1.269 + 1.270 + if ((size != expected_size) || (INOUTPTR_TYPE != type)) { 1.271 + return false; 1.272 + } 1.273 + 1.274 + if (NULL == start) { 1.275 + return false; 1.276 + } 1.277 + 1.278 + *pointer = start; 1.279 + return true; 1.280 +} 1.281 + 1.282 +void SetCallError(ResultCode error, CrossCallReturn* call_return) { 1.283 + call_return->call_outcome = error; 1.284 + call_return->extended_count = 0; 1.285 +} 1.286 + 1.287 +void SetCallSuccess(CrossCallReturn* call_return) { 1.288 + call_return->call_outcome = SBOX_ALL_OK; 1.289 +} 1.290 + 1.291 +Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc, 1.292 + CallbackGeneric* callback) { 1.293 + DCHECK(callback); 1.294 + std::vector<IPCCall>::iterator it = ipc_calls_.begin(); 1.295 + for (; it != ipc_calls_.end(); ++it) { 1.296 + if (it->params.Matches(ipc)) { 1.297 + *callback = it->callback; 1.298 + return this; 1.299 + } 1.300 + } 1.301 + return NULL; 1.302 +} 1.303 + 1.304 +} // namespace sandbox