security/sandbox/win/src/crosscall_server.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.
     5 #include <string>
     6 #include <vector>
     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
    15 // the ipc code.
    17 namespace {
    19 // The buffer for a message must match the max channel size.
    20 const size_t kMaxBufferSize = sandbox::kIPCChannelSize;
    22 }
    24 namespace sandbox {
    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) {
    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 }
    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   }
    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   }
    84   return true;
    85 }
    87 CrossCallParamsEx::CrossCallParamsEx()
    88   :CrossCallParams(0, 0) {
    89 }
    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 }
   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   }
   121   char* backing_mem = NULL;
   122   uint32 param_count = 0;
   123   uint32 declared_size;
   124   uint32 min_declared_size;
   125   CrossCallParamsEx* copied_params = NULL;
   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);
   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));
   139     // Retrieve the declared size which if it fails returns 0.
   140     declared_size = GetActualBufferSize(param_count, buffer_base);
   142     if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size))
   143       return NULL;
   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);
   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();
   156     min_declared_size = sizeof(CrossCallParams) +
   157                         ((param_count + 1) * sizeof(ParamInfo));
   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     }
   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   }
   174   const char* last_byte = &backing_mem[declared_size];
   175   const char* first_byte = &backing_mem[min_declared_size];
   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 }
   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_;
   211   return param_info_[index].offset_ + reinterpret_cast<char*>(this);
   212 }
   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 }
   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 }
   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   }
   248   // Check if this is an empty string.
   249   if (size == 0) {
   250     *string = L"";
   251     return true;
   252   }
   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 }
   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);
   267   if ((size != expected_size) || (INOUTPTR_TYPE != type)) {
   268     return false;
   269   }
   271   if (NULL == start) {
   272     return false;
   273   }
   275   *pointer = start;
   276   return true;
   277 }
   279 void SetCallError(ResultCode error, CrossCallReturn* call_return) {
   280   call_return->call_outcome = error;
   281   call_return->extended_count = 0;
   282 }
   284 void SetCallSuccess(CrossCallReturn* call_return) {
   285   call_return->call_outcome = SBOX_ALL_OK;
   286 }
   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 }
   301 }  // namespace sandbox

mercurial