Home | History | Annotate | Download | only in src
      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
    302