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 #ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__
6 #define SANDBOX_SRC_CROSSCALL_PARAMS_H__
7
8 #include <windows.h>
9 #include <lmaccess.h>
10 #include <stddef.h>
11 #include <stdint.h>
12
13 #include <memory>
14
15 #include "base/macros.h"
16 #include "sandbox/win/src/internal_types.h"
17 #include "sandbox/win/src/sandbox_types.h"
18
19 // Increases |value| until there is no need for padding given an int64_t
20 // alignment. Returns the increased value.
Align(uint32_t value)21 inline uint32_t Align(uint32_t value) {
22 uint32_t alignment = sizeof(int64_t);
23 return ((value + alignment - 1) / alignment) * alignment;
24 }
25
26 // This header is part of CrossCall: the sandbox inter-process communication.
27 // This header defines the basic types used both in the client IPC and in the
28 // server IPC code. CrossCallParams and ActualCallParams model the input
29 // parameters of an IPC call and CrossCallReturn models the output params and
30 // the return value.
31 //
32 // An IPC call is defined by its 'tag' which is a (uint32_t) unique identifier
33 // that is used to route the IPC call to the proper server. Every tag implies
34 // a complete call signature including the order and type of each parameter.
35 //
36 // Like most IPC systems. CrossCall is designed to take as inputs 'simple'
37 // types such as integers and strings. Classes, generic arrays or pointers to
38 // them are not supported.
39 //
40 // Another limitation of CrossCall is that the return value and output
41 // parameters can only be uint32_t integers. Returning complex structures or
42 // strings is not supported.
43
44 namespace sandbox {
45
46 // max number of extended return parameters. See CrossCallReturn
47 const size_t kExtendedReturnCount = 8;
48
49 // Union of multiple types to be used as extended results
50 // in the CrossCallReturn.
51 union MultiType {
52 uint32_t unsigned_int;
53 void* pointer;
54 HANDLE handle;
55 ULONG_PTR ulong_ptr;
56 };
57
58 // Maximum number of IPC parameters currently supported.
59 // To increase this value, we have to:
60 // - Add another Callback typedef to Dispatcher.
61 // - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
62 // - Add another case to the switch in GetActualAndMaxBufferSize
63 const int kMaxIpcParams = 9;
64
65 // Contains the information about a parameter in the ipc buffer.
66 struct ParamInfo {
67 ArgType type_;
68 uint32_t offset_;
69 uint32_t size_;
70 };
71
72 // Models the return value and the return parameters of an IPC call
73 // currently limited to one status code and eight generic return values
74 // which cannot be pointers to other data. For x64 ports this structure
75 // might have to use other integer types.
76 struct CrossCallReturn {
77 // the IPC tag. It should match the original IPC tag.
78 uint32_t tag;
79 // The result of the IPC operation itself.
80 ResultCode call_outcome;
81 // the result of the IPC call as executed in the server. The interpretation
82 // of this value depends on the specific service.
83 union {
84 NTSTATUS nt_status;
85 DWORD win32_result;
86 };
87 // Number of extended return values.
88 uint32_t extended_count;
89 // for calls that should return a windows handle. It is found here.
90 HANDLE handle;
91 // The array of extended values.
92 MultiType extended[kExtendedReturnCount];
93 };
94
95 // CrossCallParams base class that models the input params all packed in a
96 // single compact memory blob. The representation can vary but in general a
97 // given child of this class is meant to represent all input parameters
98 // necessary to make a IPC call.
99 //
100 // This class cannot have virtual members because its assumed the IPC
101 // parameters start from the 'this' pointer to the end, which is defined by
102 // one of the subclasses
103 //
104 // Objects of this class cannot be constructed directly. Only derived
105 // classes have the proper knowledge to construct it.
106 class CrossCallParams {
107 public:
108 // Returns the tag (ipc unique id) associated with this IPC.
GetTag()109 uint32_t GetTag() const { return tag_; }
110
111 // Returns the beggining of the buffer where the IPC params can be stored.
112 // prior to an IPC call
GetBuffer()113 const void* GetBuffer() const {
114 return this;
115 }
116
117 // Returns how many parameter this IPC call should have.
GetParamsCount()118 uint32_t GetParamsCount() const { return params_count_; }
119
120 // Returns a pointer to the CrossCallReturn structure.
GetCallReturn()121 CrossCallReturn* GetCallReturn() {
122 return &call_return;
123 }
124
125 // Returns TRUE if this call contains InOut parameters.
IsInOut()126 bool IsInOut() const { return (1 == is_in_out_); }
127
128 // Tells the CrossCall object if it contains InOut parameters.
SetIsInOut(bool value)129 void SetIsInOut(bool value) {
130 if (value)
131 is_in_out_ = 1;
132 else
133 is_in_out_ = 0;
134 }
135
136 protected:
137 // constructs the IPC call params. Called only from the derived classes
CrossCallParams(uint32_t tag,uint32_t params_count)138 CrossCallParams(uint32_t tag, uint32_t params_count)
139 : tag_(tag), is_in_out_(0), params_count_(params_count) {}
140
141 private:
142 uint32_t tag_;
143 uint32_t is_in_out_;
144 CrossCallReturn call_return;
145 const uint32_t params_count_;
146 DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
147 };
148
149 // ActualCallParams models an specific IPC call parameters with respect to the
150 // storage allocation that the packed parameters should need.
151 // NUMBER_PARAMS: the number of parameters, valid from 1 to N
152 // BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
153 // typically the block size is defined by the channel size of the underlying
154 // ipc mechanism.
155 // In practice this class is used to levergage C++ capacity to properly
156 // calculate sizes and displacements given the possibility of the packed params
157 // blob to be complex.
158 //
159 // As is, this class assumes that the layout of the blob is as follows. Assume
160 // that NUMBER_PARAMS = 2 and a 32-bit build:
161 //
162 // [ tag 4 bytes]
163 // [ IsOnOut 4 bytes]
164 // [ call return 52 bytes]
165 // [ params count 4 bytes]
166 // [ parameter 0 type 4 bytes]
167 // [ parameter 0 offset 4 bytes] ---delta to ---\
168 // [ parameter 0 size 4 bytes] |
169 // [ parameter 1 type 4 bytes] |
170 // [ parameter 1 offset 4 bytes] ---------------|--\
171 // [ parameter 1 size 4 bytes] | |
172 // [ parameter 2 type 4 bytes] | |
173 // [ parameter 2 offset 4 bytes] ----------------------\
174 // [ parameter 2 size 4 bytes] | | |
175 // |---------------------------| | | |
176 // | value 0 (x bytes) | <--------------/ | |
177 // | value 1 (y bytes) | <-----------------/ |
178 // | | |
179 // | end of buffer | <---------------------/
180 // |---------------------------|
181 //
182 // Note that the actual number of params is NUMBER_PARAMS + 1
183 // so that the size of each actual param can be computed from the difference
184 // between one parameter and the next down. The offset of the last param
185 // points to the end of the buffer and the type and size are undefined.
186 //
187 template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
188 class ActualCallParams : public CrossCallParams {
189 public:
190 // constructor. Pass the ipc unique tag as input
ActualCallParams(uint32_t tag)191 explicit ActualCallParams(uint32_t tag)
192 : CrossCallParams(tag, NUMBER_PARAMS) {
193 param_info_[0].offset_ =
194 static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
195 }
196
197 // Testing-only constructor. Allows setting the |number_params| to a
198 // wrong value.
ActualCallParams(uint32_t tag,uint32_t number_params)199 ActualCallParams(uint32_t tag, uint32_t number_params)
200 : CrossCallParams(tag, number_params) {
201 param_info_[0].offset_ =
202 static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
203 }
204
205 // Testing-only method. Allows setting the apparent size to a wrong value.
206 // returns the previous size.
OverrideSize(uint32_t new_size)207 uint32_t OverrideSize(uint32_t new_size) {
208 uint32_t previous_size = param_info_[NUMBER_PARAMS].offset_;
209 param_info_[NUMBER_PARAMS].offset_ = new_size;
210 return previous_size;
211 }
212
213 // Copies each paramter into the internal buffer. For each you must supply:
214 // index: 0 for the first param, 1 for the next an so on
CopyParamIn(uint32_t index,const void * parameter_address,uint32_t size,bool is_in_out,ArgType type)215 bool CopyParamIn(uint32_t index,
216 const void* parameter_address,
217 uint32_t size,
218 bool is_in_out,
219 ArgType type) {
220 if (index >= NUMBER_PARAMS) {
221 return false;
222 }
223
224 if (UINT32_MAX == size) {
225 // Memory error while getting the size.
226 return false;
227 }
228
229 if (size && !parameter_address) {
230 return false;
231 }
232
233 if ((size > sizeof(*this)) ||
234 (param_info_[index].offset_ > (sizeof(*this) - size))) {
235 // It does not fit, abort copy.
236 return false;
237 }
238
239 char* dest = reinterpret_cast<char*>(this) + param_info_[index].offset_;
240
241 // We might be touching user memory, this has to be done from inside a try
242 // except.
243 __try {
244 memcpy(dest, parameter_address, size);
245 }
246 __except(EXCEPTION_EXECUTE_HANDLER) {
247 return false;
248 }
249
250 // Set the flag to tell the broker to update the buffer once the call is
251 // made.
252 if (is_in_out)
253 SetIsInOut(true);
254
255 param_info_[index + 1].offset_ = Align(param_info_[index].offset_ +
256 size);
257 param_info_[index].size_ = size;
258 param_info_[index].type_ = type;
259 return true;
260 }
261
262 // Returns a pointer to a parameter in the memory section.
GetParamPtr(size_t index)263 void* GetParamPtr(size_t index) {
264 return reinterpret_cast<char*>(this) + param_info_[index].offset_;
265 }
266
267 // Returns the total size of the buffer. Only valid once all the paramters
268 // have been copied in with CopyParamIn.
GetSize()269 uint32_t GetSize() const { return param_info_[NUMBER_PARAMS].offset_; }
270
271 protected:
ActualCallParams()272 ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { }
273
274 private:
275 ParamInfo param_info_[NUMBER_PARAMS + 1];
276 char parameters_[BLOCK_SIZE - sizeof(CrossCallParams)
277 - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
278 DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
279 };
280
281 static_assert(sizeof(ActualCallParams<1, 1024>) == 1024, "bad size buffer");
282 static_assert(sizeof(ActualCallParams<2, 1024>) == 1024, "bad size buffer");
283 static_assert(sizeof(ActualCallParams<3, 1024>) == 1024, "bad size buffer");
284
285 } // namespace sandbox
286
287 #endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__
288