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