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.
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"
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
19 // The buffer for a message must match the max channel size.
20 const size_t kMaxBufferSize
= sandbox::kIPCChannelSize
;
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
;
40 // Retrieve the actual size and the maximum size of the params buffer.
41 switch (param_count
) {
45 return reinterpret_cast<ActualCP1
*>(buffer_base
)->GetSize();
47 return reinterpret_cast<ActualCP2
*>(buffer_base
)->GetSize();
49 return reinterpret_cast<ActualCP3
*>(buffer_base
)->GetSize();
51 return reinterpret_cast<ActualCP4
*>(buffer_base
)->GetSize();
53 return reinterpret_cast<ActualCP5
*>(buffer_base
)->GetSize();
55 return reinterpret_cast<ActualCP6
*>(buffer_base
)->GetSize();
57 return reinterpret_cast<ActualCP7
*>(buffer_base
)->GetSize();
59 return reinterpret_cast<ActualCP8
*>(buffer_base
)->GetSize();
61 return reinterpret_cast<ActualCP9
*>(buffer_base
)->GetSize();
67 // Verifies that the declared sizes of an IPC buffer are within range.
68 bool IsSizeWithinRange(uint32 buffer_size
, uint32 min_declared_size
,
69 uint32 declared_size
) {
70 if ((buffer_size
< min_declared_size
) ||
71 (sizeof(CrossCallParamsEx
) > min_declared_size
)) {
72 // Minimal computed size bigger than existing buffer or param_count
77 if ((declared_size
> buffer_size
) || (declared_size
< min_declared_size
)) {
78 // Declared size is bigger than buffer or smaller than computed size
79 // or param_count is equal to 0 or bigger than 9.
86 CrossCallParamsEx::CrossCallParamsEx()
87 :CrossCallParams(0, 0) {
90 // We override the delete operator because the object's backing memory
91 // is hand allocated in CreateFromBuffer. We don't override the new operator
92 // because the constructors are private so there is no way to mismatch
94 void CrossCallParamsEx::operator delete(void* raw_memory
) throw() {
95 if (NULL
== raw_memory
) {
96 // C++ standard allows 'delete 0' behavior.
99 delete[] reinterpret_cast<char*>(raw_memory
);
102 // This function uses a SEH try block so cannot use C++ objects that
103 // have destructors or else you get Compiler Error C2712. So no DCHECKs
104 // inside this function.
105 CrossCallParamsEx
* CrossCallParamsEx::CreateFromBuffer(void* buffer_base
,
107 uint32
* output_size
) {
108 // IMPORTANT: Everything inside buffer_base and derived from it such
109 // as param_count and declared_size is untrusted.
110 if (NULL
== buffer_base
) {
113 if (buffer_size
< sizeof(CrossCallParams
)) {
116 if (buffer_size
> kMaxBufferSize
) {
120 char* backing_mem
= NULL
;
121 uint32 param_count
= 0;
122 uint32 declared_size
;
123 uint32 min_declared_size
;
124 CrossCallParamsEx
* copied_params
= NULL
;
126 // Touching the untrusted buffer is done under a SEH try block. This
127 // will catch memory access violations so we don't crash.
129 CrossCallParams
* call_params
=
130 reinterpret_cast<CrossCallParams
*>(buffer_base
);
132 // Check against the minimum size given the number of stated params
133 // if too small we bail out.
134 param_count
= call_params
->GetParamsCount();
135 min_declared_size
= sizeof(CrossCallParams
) +
136 ((param_count
+ 1) * sizeof(ParamInfo
));
138 // Retrieve the declared size which if it fails returns 0.
139 declared_size
= GetActualBufferSize(param_count
, buffer_base
);
141 if (!IsSizeWithinRange(buffer_size
, min_declared_size
, declared_size
))
144 // Now we copy the actual amount of the message.
145 *output_size
= declared_size
;
146 backing_mem
= new char[declared_size
];
147 copied_params
= reinterpret_cast<CrossCallParamsEx
*>(backing_mem
);
148 memcpy(backing_mem
, call_params
, declared_size
);
150 // Avoid compiler optimizations across this point. Any value stored in
151 // memory should be stored for real, and values previously read from memory
152 // should be actually read.
155 min_declared_size
= sizeof(CrossCallParams
) +
156 ((param_count
+ 1) * sizeof(ParamInfo
));
158 // Check that the copied buffer is still valid.
159 if (copied_params
->GetParamsCount() != param_count
||
160 GetActualBufferSize(param_count
, backing_mem
) != declared_size
||
161 !IsSizeWithinRange(buffer_size
, min_declared_size
, declared_size
)) {
162 delete [] backing_mem
;
166 } __except(EXCEPTION_EXECUTE_HANDLER
) {
167 // In case of a windows exception we know it occurred while touching the
168 // untrusted buffer so we bail out as is.
169 delete [] backing_mem
;
173 const char* last_byte
= &backing_mem
[declared_size
];
174 const char* first_byte
= &backing_mem
[min_declared_size
];
176 // Verify here that all and each parameters make sense. This is done in the
178 for (uint32 ix
=0; ix
!= param_count
; ++ix
) {
181 char* address
= reinterpret_cast<char*>(
182 copied_params
->GetRawParameter(ix
, &size
, &type
));
183 if ((NULL
== address
) || // No null params.
184 (INVALID_TYPE
>= type
) || (LAST_TYPE
<= type
) || // Unknown type.
185 (address
< backing_mem
) || // Start cannot point before buffer.
186 (address
< first_byte
) || // Start cannot point too low.
187 (address
> last_byte
) || // Start cannot point past buffer.
188 ((address
+ size
) < address
) || // Invalid size.
189 ((address
+ size
) > last_byte
)) { // End cannot point past buffer.
191 delete[] backing_mem
;
195 // The parameter buffer looks good.
196 return copied_params
;
199 // Accessors to the parameters in the raw buffer.
200 void* CrossCallParamsEx::GetRawParameter(uint32 index
, uint32
* size
,
202 if (index
>= GetParamsCount()) {
205 // The size is always computed from the parameter minus the next
206 // parameter, this works because the message has an extra parameter slot
207 *size
= param_info_
[index
].size_
;
208 *type
= param_info_
[index
].type_
;
210 return param_info_
[index
].offset_
+ reinterpret_cast<char*>(this);
213 // Covers common case for 32 bit integers.
214 bool CrossCallParamsEx::GetParameter32(uint32 index
, uint32
* param
) {
217 void* start
= GetRawParameter(index
, &size
, &type
);
218 if ((NULL
== start
) || (4 != size
) || (UINT32_TYPE
!= type
)) {
222 *(reinterpret_cast<uint32
*>(param
)) = *(reinterpret_cast<uint32
*>(start
));
226 bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index
, void** param
) {
229 void* start
= GetRawParameter(index
, &size
, &type
);
230 if ((NULL
== start
) || (sizeof(void*) != size
) || (VOIDPTR_TYPE
!= type
)) {
233 *param
= *(reinterpret_cast<void**>(start
));
237 // Covers the common case of reading a string. Note that the string is not
238 // scanned for invalid characters.
239 bool CrossCallParamsEx::GetParameterStr(uint32 index
, base::string16
* string
) {
242 void* start
= GetRawParameter(index
, &size
, &type
);
243 if (WCHAR_TYPE
!= type
) {
247 // Check if this is an empty string.
253 if ((NULL
== start
) || ((size
% sizeof(wchar_t)) != 0)) {
256 string
->append(reinterpret_cast<wchar_t*>(start
), size
/(sizeof(wchar_t)));
260 bool CrossCallParamsEx::GetParameterPtr(uint32 index
, uint32 expected_size
,
264 void* start
= GetRawParameter(index
, &size
, &type
);
266 if ((size
!= expected_size
) || (INOUTPTR_TYPE
!= type
)) {
278 void SetCallError(ResultCode error
, CrossCallReturn
* call_return
) {
279 call_return
->call_outcome
= error
;
280 call_return
->extended_count
= 0;
283 void SetCallSuccess(CrossCallReturn
* call_return
) {
284 call_return
->call_outcome
= SBOX_ALL_OK
;
287 Dispatcher
* Dispatcher::OnMessageReady(IPCParams
* ipc
,
288 CallbackGeneric
* callback
) {
290 std::vector
<IPCCall
>::iterator it
= ipc_calls_
.begin();
291 for (; it
!= ipc_calls_
.end(); ++it
) {
292 if (it
->params
.Matches(ipc
)) {
293 *callback
= it
->callback
;
300 } // namespace sandbox