• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetActualBufferSize(uint32 param_count,void * buffer_base)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.
IsSizeWithinRange(uint32 buffer_size,uint32 min_declared_size,uint32 declared_size)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 
CrossCallParamsEx()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.
operator delete(void * raw_memory)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.
CreateFromBuffer(void * buffer_base,uint32 buffer_size,uint32 * output_size)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.
GetRawParameter(uint32 index,uint32 * size,ArgType * type)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.
GetParameter32(uint32 index,uint32 * param)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 
GetParameterVoidPtr(uint32 index,void ** param)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.
GetParameterStr(uint32 index,base::string16 * string)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 
GetParameterPtr(uint32 index,uint32 expected_size,void ** pointer)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 
SetCallError(ResultCode error,CrossCallReturn * call_return)279 void SetCallError(ResultCode error, CrossCallReturn* call_return) {
280   call_return->call_outcome = error;
281   call_return->extended_count = 0;
282 }
283 
SetCallSuccess(CrossCallReturn * call_return)284 void SetCallSuccess(CrossCallReturn* call_return) {
285   call_return->call_outcome = SBOX_ALL_OK;
286 }
287 
OnMessageReady(IPCParams * ipc,CallbackGeneric * callback)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