|
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, std::wstring* 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 |