summaryrefslogtreecommitdiff
path: root/chromium/sandbox/win/src/crosscall_server.cc
blob: 15cfa5f4ea1895e956b69c32ea92c5950ecbb244 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sandbox/win/src/crosscall_server.h"

#include <stddef.h>
#include <stdint.h>

#include <string>
#include <vector>

#include "base/atomicops.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "sandbox/win/src/crosscall_client.h"
#include "sandbox/win/src/crosscall_params.h"

// See comment in atomicops.h. This is needed any time windows.h is included
// after atomicops.h.
#undef MemoryBarrier

// This code performs the ipc message validation. Potential security flaws
// on the ipc are likelier to be found in this code than in the rest of
// the ipc code.

namespace {

// The buffer for a message must match the max channel size.
const size_t kMaxBufferSize = sandbox::kIPCChannelSize;

}  // namespace

namespace sandbox {

// Returns the actual size for the parameters in an IPC buffer. Returns
// zero if the |param_count| is zero or too big.
uint32_t GetActualBufferSize(uint32_t param_count, void* buffer_base) {
  // The template types are used to calculate the maximum expected size.
  typedef ActualCallParams<1, kMaxBufferSize> ActualCP1;
  typedef ActualCallParams<2, kMaxBufferSize> ActualCP2;
  typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
  typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
  typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
  typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
  typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
  typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
  typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;

  // Retrieve the actual size and the maximum size of the params buffer.
  switch (param_count) {
    case 0:
      return 0;
    case 1:
      return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize();
    case 2:
      return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize();
    case 3:
      return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize();
    case 4:
      return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize();
    case 5:
      return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize();
    case 6:
      return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize();
    case 7:
      return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize();
    case 8:
      return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize();
    case 9:
      return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
    default:
      return 0;
  }
}

// Verifies that the declared sizes of an IPC buffer are within range.
bool IsSizeWithinRange(uint32_t buffer_size,
                       uint32_t min_declared_size,
                       uint32_t declared_size) {
  if ((buffer_size < min_declared_size) ||
      (sizeof(CrossCallParamsEx) > min_declared_size)) {
    // Minimal computed size bigger than existing buffer or param_count
    // integer overflow.
    return false;
  }

  if ((declared_size > buffer_size) || (declared_size < min_declared_size)) {
    // Declared size is bigger than buffer or smaller than computed size
    // or param_count is equal to 0 or bigger than 9.
    return false;
  }

  return true;
}

CrossCallParamsEx::CrossCallParamsEx() : CrossCallParams(0, 0) {}

// We override the delete operator because the object's backing memory
// is hand allocated in CreateFromBuffer. We don't override the new operator
// because the constructors are private so there is no way to mismatch
// new & delete.
void CrossCallParamsEx::operator delete(void* raw_memory) throw() {
  if (!raw_memory) {
    // C++ standard allows 'delete 0' behavior.
    return;
  }
  delete[] reinterpret_cast<char*>(raw_memory);
}

// This function uses a SEH try block so cannot use C++ objects that
// have destructors or else you get Compiler Error C2712. So no DCHECKs
// inside this function.
CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base,
                                                       uint32_t buffer_size,
                                                       uint32_t* output_size) {
  // IMPORTANT: Everything inside buffer_base and derived from it such
  // as param_count and declared_size is untrusted.
  if (!buffer_base)
    return nullptr;
  if (buffer_size < sizeof(CrossCallParams))
    return nullptr;
  if (buffer_size > kMaxBufferSize)
    return nullptr;

  char* backing_mem = nullptr;
  uint32_t param_count = 0;
  uint32_t declared_size;
  uint32_t min_declared_size;
  CrossCallParamsEx* copied_params = nullptr;

  // Touching the untrusted buffer is done under a SEH try block. This
  // will catch memory access violations so we don't crash.
  __try {
    CrossCallParams* call_params =
        reinterpret_cast<CrossCallParams*>(buffer_base);

    // Check against the minimum size given the number of stated params
    // if too small we bail out.
    param_count = call_params->GetParamsCount();
    min_declared_size =
        sizeof(CrossCallParams) + ((param_count + 1) * sizeof(ParamInfo));

    // Initial check for the buffer being big enough to determine the actual
    // buffer size.
    if (buffer_size < min_declared_size)
      return nullptr;

    // Retrieve the declared size which if it fails returns 0.
    declared_size = GetActualBufferSize(param_count, buffer_base);

    if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size))
      return nullptr;

    // Now we copy the actual amount of the message.
    *output_size = declared_size;
    backing_mem = new char[declared_size];
    copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem);
    memcpy(backing_mem, call_params, declared_size);

    // Avoid compiler optimizations across this point. Any value stored in
    // memory should be stored for real, and values previously read from memory
    // should be actually read.
    base::subtle::MemoryBarrier();

    min_declared_size =
        sizeof(CrossCallParams) + ((param_count + 1) * sizeof(ParamInfo));

    // Check that the copied buffer is still valid.
    if (copied_params->GetParamsCount() != param_count ||
        GetActualBufferSize(param_count, backing_mem) != declared_size ||
        !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) {
      delete[] backing_mem;
      return nullptr;
    }

  } __except (EXCEPTION_EXECUTE_HANDLER) {
    // In case of a windows exception we know it occurred while touching the
    // untrusted buffer so we bail out as is.
    delete[] backing_mem;
    return nullptr;
  }

  const char* last_byte = &backing_mem[declared_size];
  const char* first_byte = &backing_mem[min_declared_size];

  // Verify here that all and each parameters make sense. This is done in the
  // local copy.
  for (uint32_t ix = 0; ix != param_count; ++ix) {
    uint32_t size = 0;
    ArgType type;
    char* address = reinterpret_cast<char*>(
        copied_params->GetRawParameter(ix, &size, &type));
    if ((!address) ||                                     // No null params.
        (INVALID_TYPE >= type) || (LAST_TYPE <= type) ||  // Unknown type.
        (address < backing_mem) ||         // Start cannot point before buffer.
        (address < first_byte) ||          // Start cannot point too low.
        (address > last_byte) ||           // Start cannot point past buffer.
        ((address + size) < address) ||    // Invalid size.
        ((address + size) > last_byte)) {  // End cannot point past buffer.
      // Malformed.
      delete[] backing_mem;
      return nullptr;
    }
  }
  // The parameter buffer looks good.
  return copied_params;
}

// Accessors to the parameters in the raw buffer.
void* CrossCallParamsEx::GetRawParameter(uint32_t index,
                                         uint32_t* size,
                                         ArgType* type) {
  if (index >= GetParamsCount())
    return nullptr;
  // The size is always computed from the parameter minus the next
  // parameter, this works because the message has an extra parameter slot
  *size = param_info_[index].size_;
  *type = param_info_[index].type_;

  return param_info_[index].offset_ + reinterpret_cast<char*>(this);
}

// Covers common case for 32 bit integers.
bool CrossCallParamsEx::GetParameter32(uint32_t index, uint32_t* param) {
  uint32_t size = 0;
  ArgType type;
  void* start = GetRawParameter(index, &size, &type);
  if (!start || (4 != size) || (UINT32_TYPE != type))
    return false;
  // Copy the 4 bytes.
  *(reinterpret_cast<uint32_t*>(param)) = *(reinterpret_cast<uint32_t*>(start));
  return true;
}

bool CrossCallParamsEx::GetParameterVoidPtr(uint32_t index, void** param) {
  uint32_t size = 0;
  ArgType type;
  void* start = GetRawParameter(index, &size, &type);
  if (!start || (sizeof(void*) != size) || (VOIDPTR_TYPE != type))
    return false;
  *param = *(reinterpret_cast<void**>(start));
  return true;
}

// Covers the common case of reading a string. Note that the string is not
// scanned for invalid characters.
bool CrossCallParamsEx::GetParameterStr(uint32_t index,
                                        base::string16* string) {
  DCHECK(string->empty());
  uint32_t size = 0;
  ArgType type;
  void* start = GetRawParameter(index, &size, &type);
  if (WCHAR_TYPE != type)
    return false;

  // Check if this is an empty string.
  if (size == 0) {
    *string = base::WideToUTF16(L"");
    return true;
  }

  if (!start || ((size % sizeof(wchar_t)) != 0))
    return false;
  return base::WideToUTF16(reinterpret_cast<wchar_t*>(start),
                           size / sizeof(wchar_t), string);
}

bool CrossCallParamsEx::GetParameterPtr(uint32_t index,
                                        uint32_t expected_size,
                                        void** pointer) {
  uint32_t size = 0;
  ArgType type;
  void* start = GetRawParameter(index, &size, &type);

  if ((size != expected_size) || (INOUTPTR_TYPE != type))
    return false;

  if (!start)
    return false;

  *pointer = start;
  return true;
}

void SetCallError(ResultCode error, CrossCallReturn* call_return) {
  call_return->call_outcome = error;
  call_return->extended_count = 0;
}

void SetCallSuccess(CrossCallReturn* call_return) {
  call_return->call_outcome = SBOX_ALL_OK;
}

Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc,
                                       CallbackGeneric* callback) {
  DCHECK(callback);
  std::vector<IPCCall>::iterator it = ipc_calls_.begin();
  for (; it != ipc_calls_.end(); ++it) {
    if (it->params.Matches(ipc)) {
      *callback = it->callback;
      return this;
    }
  }
  return nullptr;
}

Dispatcher::Dispatcher() {}

Dispatcher::~Dispatcher() {}

}  // namespace sandbox