Home | History | Annotate | Download | only in clipboard
      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 "ui/base/clipboard/clipboard.h"
      6 
      7 #include <iterator>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/synchronization/lock.h"
     13 #include "ui/gfx/size.h"
     14 
     15 namespace ui {
     16 
     17 namespace {
     18 
     19 // A compromised renderer could send us bad data, so validate it.
     20 // This function only checks that the size parameter makes sense, the caller
     21 // is responsible for further validating the bitmap buffer against
     22 // |bitmap_bytes|.
     23 //
     24 // |params| - Clipboard bitmap contents to validate.
     25 // |bitmap_bytes| - On return contains the number of bytes needed to store
     26 // the bitmap data or -1 if the data is invalid.
     27 // returns: true if the bitmap size is valid, false otherwise.
     28 bool IsBitmapSafe(const Clipboard::ObjectMapParams& params,
     29                   uint32* bitmap_bytes) {
     30   *bitmap_bytes = -1;
     31   if (params[1].size() != sizeof(gfx::Size))
     32     return false;
     33   const gfx::Size* size =
     34       reinterpret_cast<const gfx::Size*>(&(params[1].front()));
     35   uint32 total_size = size->width();
     36   // Using INT_MAX not SIZE_T_MAX to put a reasonable bound on things.
     37   if (INT_MAX / size->width() <= size->height())
     38     return false;
     39   total_size *= size->height();
     40   if (INT_MAX / total_size <= 4)
     41     return false;
     42   total_size *= 4;
     43   *bitmap_bytes = total_size;
     44   return true;
     45 }
     46 
     47 // Validates a plain bitmap on the clipboard.
     48 // Returns true if the clipboard data makes sense and it's safe to access the
     49 // bitmap.
     50 bool ValidatePlainBitmap(const Clipboard::ObjectMapParams& params) {
     51   uint32 bitmap_bytes = -1;
     52   if (!IsBitmapSafe(params, &bitmap_bytes))
     53     return false;
     54   if (bitmap_bytes != params[0].size())
     55     return false;
     56   return true;
     57 }
     58 
     59 // Valides a shared bitmap on the clipboard.
     60 // Returns true if the clipboard data makes sense and it's safe to access the
     61 // bitmap.
     62 bool ValidateAndMapSharedBitmap(const Clipboard::ObjectMapParams& params,
     63                                 base::SharedMemory* bitmap_data) {
     64   using base::SharedMemory;
     65   uint32 bitmap_bytes = -1;
     66   if (!IsBitmapSafe(params, &bitmap_bytes))
     67     return false;
     68 
     69   if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
     70     return false;
     71 
     72   if (!bitmap_data->Map(bitmap_bytes)) {
     73     PLOG(ERROR) << "Failed to map bitmap memory";
     74     return false;
     75   }
     76   return true;
     77 }
     78 
     79 // A list of allowed threads. By default, this is empty and no thread checking
     80 // is done (in the unit test case), but a user (like content) can set which
     81 // threads are allowed to call this method.
     82 typedef std::vector<base::PlatformThreadId> AllowedThreadsVector;
     83 static base::LazyInstance<AllowedThreadsVector> g_allowed_threads =
     84     LAZY_INSTANCE_INITIALIZER;
     85 
     86 // Mapping from threads to clipboard objects.
     87 typedef std::map<base::PlatformThreadId, Clipboard*> ClipboardMap;
     88 static base::LazyInstance<ClipboardMap> g_clipboard_map =
     89     LAZY_INSTANCE_INITIALIZER;
     90 
     91 // Mutex that controls access to |g_clipboard_map|.
     92 static base::LazyInstance<base::Lock>::Leaky
     93     g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER;
     94 
     95 }  // namespace
     96 
     97 // static
     98 void Clipboard::SetAllowedThreads(
     99     const std::vector<base::PlatformThreadId>& allowed_threads) {
    100   base::AutoLock lock(g_clipboard_map_lock.Get());
    101 
    102   g_allowed_threads.Get().clear();
    103   std::copy(allowed_threads.begin(), allowed_threads.end(),
    104             std::back_inserter(g_allowed_threads.Get()));
    105 }
    106 
    107 // static
    108 Clipboard* Clipboard::GetForCurrentThread() {
    109   base::AutoLock lock(g_clipboard_map_lock.Get());
    110 
    111   base::PlatformThreadId id = base::PlatformThread::CurrentId();
    112 
    113   AllowedThreadsVector* allowed_threads = g_allowed_threads.Pointer();
    114   if (!allowed_threads->empty()) {
    115     bool found = false;
    116     for (AllowedThreadsVector::const_iterator it = allowed_threads->begin();
    117          it != allowed_threads->end(); ++it) {
    118       if (*it == id) {
    119         found = true;
    120         break;
    121       }
    122     }
    123 
    124     DCHECK(found);
    125   }
    126 
    127   ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
    128   ClipboardMap::iterator it = clipboard_map->find(id);
    129   if (it != clipboard_map->end())
    130     return it->second;
    131 
    132   Clipboard* clipboard = new ui::Clipboard;
    133   clipboard_map->insert(std::make_pair(id, clipboard));
    134   return clipboard;
    135 }
    136 
    137 void Clipboard::DestroyClipboardForCurrentThread() {
    138   base::AutoLock lock(g_clipboard_map_lock.Get());
    139 
    140   ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
    141   base::PlatformThreadId id = base::PlatformThread::CurrentId();
    142   ClipboardMap::iterator it = clipboard_map->find(id);
    143   if (it != clipboard_map->end()) {
    144     delete it->second;
    145     clipboard_map->erase(it);
    146   }
    147 }
    148 
    149 void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
    150   // All types apart from CBF_WEBKIT need at least 1 non-empty param.
    151   if (type != CBF_WEBKIT && (params.empty() || params[0].empty()))
    152     return;
    153   // Some other types need a non-empty 2nd param.
    154   if ((type == CBF_BOOKMARK || type == CBF_BITMAP ||
    155        type == CBF_SMBITMAP || type == CBF_DATA) &&
    156       (params.size() != 2 || params[1].empty()))
    157     return;
    158   switch (type) {
    159     case CBF_TEXT:
    160       WriteText(&(params[0].front()), params[0].size());
    161       break;
    162 
    163     case CBF_HTML:
    164       if (params.size() == 2) {
    165         if (params[1].empty())
    166           return;
    167         WriteHTML(&(params[0].front()), params[0].size(),
    168                   &(params[1].front()), params[1].size());
    169       } else if (params.size() == 1) {
    170         WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
    171       }
    172       break;
    173 
    174     case CBF_RTF:
    175       WriteRTF(&(params[0].front()), params[0].size());
    176       break;
    177 
    178     case CBF_BOOKMARK:
    179       WriteBookmark(&(params[0].front()), params[0].size(),
    180                     &(params[1].front()), params[1].size());
    181       break;
    182 
    183     case CBF_WEBKIT:
    184       WriteWebSmartPaste();
    185       break;
    186 
    187     case CBF_BITMAP:
    188       if (!ValidatePlainBitmap(params))
    189         return;
    190 
    191       WriteBitmap(&(params[0].front()), &(params[1].front()));
    192       break;
    193 
    194     case CBF_SMBITMAP: {
    195       using base::SharedMemory;
    196       using base::SharedMemoryHandle;
    197 
    198       if (params[0].size() != sizeof(SharedMemory*))
    199         return;
    200 
    201       // It's OK to cast away constness here since we map the handle as
    202       // read-only.
    203       const char* raw_bitmap_data_const =
    204           reinterpret_cast<const char*>(&(params[0].front()));
    205       char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
    206       scoped_ptr<SharedMemory> bitmap_data(
    207           *reinterpret_cast<SharedMemory**>(raw_bitmap_data));
    208 
    209       if (!ValidateAndMapSharedBitmap(params, bitmap_data.get()))
    210         return;
    211       WriteBitmap(static_cast<const char*>(bitmap_data->memory()),
    212                   &(params[1].front()));
    213       break;
    214     }
    215 
    216     case CBF_DATA:
    217       WriteData(
    218           FormatType::Deserialize(
    219               std::string(&(params[0].front()), params[0].size())),
    220           &(params[1].front()),
    221           params[1].size());
    222       break;
    223 
    224     default:
    225       NOTREACHED();
    226   }
    227 }
    228 
    229 // static
    230 void Clipboard::ReplaceSharedMemHandle(ObjectMap* objects,
    231                                        base::SharedMemoryHandle bitmap_handle,
    232                                        base::ProcessHandle process) {
    233   using base::SharedMemory;
    234   bool has_shared_bitmap = false;
    235 
    236   for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
    237        ++iter) {
    238     if (iter->first == CBF_SMBITMAP) {
    239       // The code currently only accepts sending a single bitmap over this way.
    240       // Fail hard if we ever encounter more than one shared bitmap structure to
    241       // fill.
    242       CHECK(!has_shared_bitmap);
    243 
    244 #if defined(OS_WIN)
    245       SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
    246 #else
    247       SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
    248 #endif
    249 
    250       // We store the shared memory object pointer so it can be retrieved by the
    251       // UI thread (see DispatchObject()).
    252       iter->second[0].clear();
    253       for (size_t i = 0; i < sizeof(SharedMemory*); ++i)
    254         iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
    255       has_shared_bitmap = true;
    256     }
    257   }
    258 }
    259 
    260 }  // namespace ui
    261