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.
GetActualBufferSize(uint32_t param_count,void * buffer_base)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.
IsSizeWithinRange(uint32_t buffer_size,uint32_t min_declared_size,uint32_t declared_size)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 
CrossCallParamsEx()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.
operator delete(void * raw_memory)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.
CreateFromBuffer(void * buffer_base,uint32_t buffer_size,uint32_t * output_size)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.
GetRawParameter(uint32_t index,uint32_t * size,ArgType * type)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.
GetParameter32(uint32_t index,uint32_t * param)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 
GetParameterVoidPtr(uint32_t index,void ** param)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.
GetParameterStr(uint32_t index,base::string16 * string)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 
GetParameterPtr(uint32_t index,uint32_t expected_size,void ** pointer)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 
SetCallError(ResultCode error,CrossCallReturn * call_return)286 void SetCallError(ResultCode error, CrossCallReturn* call_return) {
287   call_return->call_outcome = error;
288   call_return->extended_count = 0;
289 }
290 
SetCallSuccess(CrossCallReturn * call_return)291 void SetCallSuccess(CrossCallReturn* call_return) {
292   call_return->call_outcome = SBOX_ALL_OK;
293 }
294 
OnMessageReady(IPCParams * ipc,CallbackGeneric * callback)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 
Dispatcher()308 Dispatcher::Dispatcher() {
309 }
310 
~Dispatcher()311 Dispatcher::~Dispatcher() {
312 }
313 
314 }  // namespace sandbox
315