Commit message: None
CVE: CVE-2013-0920
CWE: CWE-399
Vulnerability description: Use-after-free vulnerability in the extension bookmarks API in Google Chrome before 26.0.1410.43 allows remote attackers to cause a denial of service or possibly have unspecified other impact via unknown vectors.

Project: Chrome

BookmarkEventRouter::BookmarkEventRouter(BookmarkModel* model) : model_(model) {
  model_->AddObserver(this);
}

Project: Chrome

void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) {
  model_ = NULL;
}

Project: Chrome

void BookmarksFunction::BookmarkModelChanged() {
}

Project: Chrome

void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model,
                                            const BookmarkNode* parent,
                                            int index) {
  scoped_ptr<ListValue> args(new ListValue());
  const BookmarkNode* node = parent->GetChild(index);
  args->Append(new StringValue(base::Int64ToString(node->id())));
  scoped_ptr<BookmarkTreeNode> tree_node(
      bookmark_api_helpers::GetBookmarkTreeNode(node, false, false));
  args->Append(tree_node->ToValue().release());

  DispatchEvent(model->profile(), keys::kOnBookmarkCreated, args.Pass());
}

Project: Chrome

void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel* model,
                                                     const BookmarkNode* node) {
}

Project: Chrome

void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model,
                                            const BookmarkNode* old_parent,
                                            int old_index,
                                            const BookmarkNode* new_parent,
                                            int new_index) {
  scoped_ptr<ListValue> args(new ListValue());
  const BookmarkNode* node = new_parent->GetChild(new_index);
  args->Append(new StringValue(base::Int64ToString(node->id())));
  DictionaryValue* object_args = new DictionaryValue();
  object_args->SetString(keys::kParentIdKey,
                         base::Int64ToString(new_parent->id()));
  object_args->SetInteger(keys::kIndexKey, new_index);
  object_args->SetString(keys::kOldParentIdKey,
                         base::Int64ToString(old_parent->id()));
  object_args->SetInteger(keys::kOldIndexKey, old_index);
  args->Append(object_args);

  DispatchEvent(model->profile(), keys::kOnBookmarkMoved, args.Pass());
}

Project: Chrome

void BookmarkEventRouter::BookmarkNodeRemoved(BookmarkModel* model,
                                              const BookmarkNode* parent,
                                              int index,
                                              const BookmarkNode* node) {
  scoped_ptr<ListValue> args(new ListValue());
  args->Append(new StringValue(base::Int64ToString(node->id())));
  DictionaryValue* object_args = new DictionaryValue();
  object_args->SetString(keys::kParentIdKey,
                         base::Int64ToString(parent->id()));
  object_args->SetInteger(keys::kIndexKey, index);
  args->Append(object_args);

  DispatchEvent(model->profile(), keys::kOnBookmarkRemoved, args.Pass());
}

Project: Chrome

BookmarksAPI::BookmarksAPI(Profile* profile) : profile_(profile) {
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkCreated);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkRemoved);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkChanged);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkMoved);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkChildrenReordered);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkImportBegan);
  ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
      this, keys::kOnBookmarkImportEnded);
}

Project: Chrome

BookmarksIOFunction::BookmarksIOFunction() {}

Project: Chrome

  static void Build(QuotaLimitHeuristics* heuristics) {
    BuildWithMappers(heuristics, new BookmarkIdMapper<FunctionType>(),
                                 new BookmarkIdMapper<FunctionType>());
  }

Project: Chrome

  static void BuildForCreate(QuotaLimitHeuristics* heuristics,
                             Profile* profile) {
    BuildWithMappers(heuristics, new CreateBookmarkBucketMapper(profile),
                                 new CreateBookmarkBucketMapper(profile));
  }

Project: Chrome

  static void BuildForRemove(QuotaLimitHeuristics* heuristics,
                             Profile* profile) {
    BuildWithMappers(heuristics, new RemoveBookmarksBucketMapper(profile),
                                 new RemoveBookmarksBucketMapper(profile));
  }

Project: Chrome

  static void BuildWithMappers(QuotaLimitHeuristics* heuristics,
      BucketMapper* short_mapper, BucketMapper* long_mapper) {
    const Config kSustainedLimitConfig = {
      bookmarks::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE,
      TimeDelta::FromMinutes(1)
    };
    heuristics->push_back(new SustainedLimit(
        TimeDelta::FromMinutes(10),
        kSustainedLimitConfig,
        short_mapper,
        "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE"));

    const Config kTimedLimitConfig = {
      bookmarks::MAX_WRITE_OPERATIONS_PER_HOUR,
      TimeDelta::FromHours(1)
    };
    heuristics->push_back(new TimedLimit(
        kTimedLimitConfig,
        long_mapper,
        "MAX_WRITE_OPERATIONS_PER_HOUR"));
  }

Project: Chrome

void BookmarkEventRouter::DispatchEvent(
    Profile* profile,
    const char* event_name,
    scoped_ptr<ListValue> event_args) {
  if (extensions::ExtensionSystem::Get(profile)->event_router()) {
    extensions::ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(
        make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
  }
}

Project: Chrome

bool BookmarksFunction::EditBookmarksEnabled() {
  PrefService* prefs = PrefServiceFromBrowserContext(profile_);
  if (prefs->GetBoolean(prefs::kEditBookmarksEnabled))
    return true;
  error_ = keys::kEditBookmarksDisabled;
  return false;
}

Project: Chrome

void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning(
    BookmarkModel* model) {
  scoped_ptr<ListValue> args(new ListValue());
  DispatchEvent(model->profile(),
                keys::kOnBookmarkImportBegan,
                args.Pass());
}

Project: Chrome

void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
  scoped_ptr<ListValue> args(new ListValue());
  DispatchEvent(model->profile(),
                keys::kOnBookmarkImportEnded,
                args.Pass());
}

Project: Chrome

bool BookmarksRemoveFunction::ExtractIds(const ListValue* args,
                                        std::list<int64>* ids,
                                        bool* invalid_id) {
  std::string id_string;
  if (!args->GetString(0, &id_string))
    return false;
  int64 id;
  if (base::StringToInt64(id_string, &id))
    ids->push_back(id);
  else
    *invalid_id = true;
  return true;
}

Project: Chrome

bool BookmarksMoveFunction::ExtractIds(const ListValue* args,
                                      std::list<int64>* ids,
                                      bool* invalid_id) {
  return BookmarksUpdateFunction::ExtractIds(args, ids, invalid_id);
}

Project: Chrome

bool BookmarksUpdateFunction::ExtractIds(const ListValue* args,
                                        std::list<int64>* ids,
                                        bool* invalid_id) {
  return BookmarksRemoveFunction::ExtractIds(args, ids, invalid_id);
}

Project: Chrome

void BookmarksImportFunction::FileSelected(const base::FilePath& path,
                                           int index,
                                           void* params) {
#if !defined(OS_ANDROID)
  scoped_refptr<ImporterHost> importer_host(new ImporterHost);
  importer::SourceProfile source_profile;
  source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
  source_profile.source_path = path;
  importer_host->StartImportSettings(source_profile,
                                     profile(),
                                     importer::FAVORITES,
                                     new ProfileWriter(profile()),
                                     true);
#endif
  Release();  // Balanced in BookmarksIOFunction::SelectFile()
}

Project: Chrome

void BookmarksExportFunction::FileSelected(const base::FilePath& path,
                                           int index,
                                           void* params) {
#if !defined(OS_ANDROID)
  bookmark_html_writer::WriteBookmarks(profile(), path, NULL);
#endif
  Release();  // Balanced in BookmarksIOFunction::SelectFile()
}

Project: Chrome

void BookmarksIOFunction::FileSelectionCanceled(void* params) {
  Release();  // Balanced in BookmarksIOFunction::SelectFile()
}

Project: Chrome

bool BookmarksFunction::GetBookmarkIdAsInt64(const std::string& id_string,
                                             int64* id) {
  if (base::StringToInt64(id_string, id))
    return true;

  error_ = keys::kInvalidIdError;
  return false;
}

Project: Chrome

  Bucket* GetBucket(const BucketIdType& id) {
    Bucket* b = buckets_[id];
    if (b == NULL) {
      b = new Bucket();
      buckets_[id] = b;
    }
    return b;
  }

Project: Chrome

  virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) {
    IdList ids;
    bool invalid_id = false;
    if (!FunctionType::ExtractIds(args, &ids, &invalid_id) || invalid_id)
      return;
    for (IdList::iterator it = ids.begin(); it != ids.end(); ++it)
      buckets->push_back(GetBucket(*it));
  }

Project: Chrome

base::FilePath GetDefaultFilepathForBookmarkExport() {
  base::Time time = base::Time::Now();

#if defined(OS_POSIX)
  base::FilePath::StringType filename =
      l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
                                base::TimeFormatShortDateNumeric(time));
#elif defined(OS_WIN)
  base::FilePath::StringType filename =
      l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
                                 base::TimeFormatShortDateNumeric(time));
#endif

  file_util::ReplaceIllegalCharactersInPath(&filename, '_');

  base::FilePath default_path;
  PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path);
  return default_path.Append(filename);
}

Project: Chrome

ProfileKeyedAPIFactory<BookmarksAPI>* BookmarksAPI::GetFactoryInstance() {
  return &g_factory.Get();
}

Project: Chrome

void BookmarksRemoveFunction::GetQuotaLimitHeuristics(
    QuotaLimitHeuristics* heuristics) const {
  BookmarksQuotaLimitFactory::BuildForRemove(heuristics, profile());
}

Project: Chrome

void BookmarksMoveFunction::GetQuotaLimitHeuristics(
    QuotaLimitHeuristics* heuristics) const {
  BookmarksQuotaLimitFactory::Build<BookmarksMoveFunction>(heuristics);
}

Project: Chrome

void BookmarksCreateFunction::GetQuotaLimitHeuristics(
    QuotaLimitHeuristics* heuristics) const {
  BookmarksQuotaLimitFactory::BuildForCreate(heuristics, profile());
}

Project: Chrome

void BookmarksFunction::Loaded(BookmarkModel* model, bool ids_reassigned) {
  model->RemoveObserver(this);
  Run();
  Release();  // Balanced in Run().
}

Project: Chrome

void BookmarkEventRouter::Loaded(BookmarkModel* model, bool ids_reassigned) {
}

Project: Chrome

void BookmarksIOFunction::MultiFilesSelected(
    const std::vector<base::FilePath>& files, void* params) {
  Release();  // Balanced in BookmarsIOFunction::SelectFile()
  NOTREACHED() << "Should not be able to select multiple files";
}

Project: Chrome

void BookmarksAPI::OnListenerAdded(const EventListenerInfo& details) {
  bookmark_event_router_.reset(new BookmarkEventRouter(
      BookmarkModelFactory::GetForProfile(profile_)));
  ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
}

Project: Chrome

  explicit RemoveBookmarksBucketMapper(Profile* profile) : profile_(profile) {}

Project: Chrome

bool BookmarksGetTreeFunction::RunImpl() {
  scoped_ptr<bookmarks::Get::Params> params(
      bookmarks::Get::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  std::vector<linked_ptr<BookmarkTreeNode> > nodes;
  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
  if (params->id_or_id_list.as_array) {
    std::vector<std::string>* ids = params->id_or_id_list.as_array.get();
    size_t count = ids->size();
    EXTENSION_FUNCTION_VALIDATE(count > 0);
    for (size_t i = 0; i < count; ++i) {
      int64 id;
      if (!GetBookmarkIdAsInt64(ids->at(i), &id))
        return false;
      const BookmarkNode* node = model->GetNodeByID(id);
      if (!node) {
        error_ = keys::kNoNodeError;
        return false;
      } else {
        bookmark_api_helpers::AddNode(node, &nodes, false);
      }
    }
  } else {
    int64 id;
    if (!GetBookmarkIdAsInt64(*params->id_or_id_list.as_string, &id))
      return false;
    const BookmarkNode* node = model->GetNodeByID(id);
    if (!node) {
      error_ = keys::kNoNodeError;
      return false;
    }
    bookmark_api_helpers::AddNode(node, &nodes, false);
  }

  results_ = bookmarks::Get::Results::Create(nodes);
  return true;
}

Project: Chrome

bool BookmarksGetChildrenFunction::RunImpl() {
  scoped_ptr<bookmarks::GetChildren::Params> params(
      bookmarks::GetChildren::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  int64 id;
  if (!GetBookmarkIdAsInt64(params->id, &id))
    return false;

  std::vector<linked_ptr<BookmarkTreeNode> > nodes;
  const BookmarkNode* node =
      BookmarkModelFactory::GetForProfile(profile())->GetNodeByID(id);
  if (!node) {
    error_ = keys::kNoNodeError;
    return false;
  }
  int child_count = node->child_count();
  for (int i = 0; i < child_count; ++i) {
    const BookmarkNode* child = node->GetChild(i);
    bookmark_api_helpers::AddNode(child, &nodes, false);
  }

  results_ = bookmarks::GetChildren::Results::Create(nodes);
  return true;
}

Project: Chrome

bool BookmarksGetFunction::RunImpl() {
  scoped_ptr<bookmarks::GetRecent::Params> params(
      bookmarks::GetRecent::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());
  if (params->number_of_items < 1)
    return false;

  std::vector<const BookmarkNode*> nodes;
  bookmark_utils::GetMostRecentlyAddedEntries(
      BookmarkModelFactory::GetForProfile(profile()),
      params->number_of_items,
      &nodes);

  std::vector<linked_ptr<BookmarkTreeNode> > tree_nodes;
  std::vector<const BookmarkNode*>::iterator i = nodes.begin();
  for (; i != nodes.end(); ++i) {
    const BookmarkNode* node = *i;
    bookmark_api_helpers::AddNode(node, &tree_nodes, false);
  }

  results_ = bookmarks::GetRecent::Results::Create(tree_nodes);
  return true;
}

Project: Chrome

bool BookmarksGetSubTreeFunction::RunImpl() {
  std::vector<linked_ptr<BookmarkTreeNode> > nodes;
  const BookmarkNode* node =
      BookmarkModelFactory::GetForProfile(profile())->root_node();
  bookmark_api_helpers::AddNode(node, &nodes, true);
  results_ = bookmarks::GetTree::Results::Create(nodes);
  return true;
}

Project: Chrome

bool BookmarksSearchFunction::RunImpl() {
  scoped_ptr<bookmarks::Search::Params> params(
      bookmarks::Search::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  PrefService* prefs = PrefServiceFromBrowserContext(profile_);
  std::string lang = prefs->GetString(prefs::kAcceptLanguages);
  std::vector<const BookmarkNode*> nodes;
  bookmark_utils::GetBookmarksContainingText(
      BookmarkModelFactory::GetForProfile(profile()),
      UTF8ToUTF16(params->query),
      std::numeric_limits<int>::max(),
      lang,
      &nodes);

  std::vector<linked_ptr<BookmarkTreeNode> > tree_nodes;
  for (std::vector<const BookmarkNode*>::iterator node_iter = nodes.begin();
       node_iter != nodes.end(); ++node_iter) {
    bookmark_api_helpers::AddNode(*node_iter, &tree_nodes, false);
  }

  results_ = bookmarks::Search::Results::Create(tree_nodes);
  return true;
}

Project: Chrome

bool BookmarksCreateFunction::RunImpl() {
  if (!EditBookmarksEnabled())
    return false;

  scoped_ptr<bookmarks::Create::Params> params(
      bookmarks::Create::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
  int64 parentId;

  if (!params->bookmark.parent_id.get()) {
    parentId = model->other_node()->id();
  } else {
    if (!GetBookmarkIdAsInt64(*params->bookmark.parent_id, &parentId))
      return false;
  }
  const BookmarkNode* parent = model->GetNodeByID(parentId);
  if (!parent) {
    error_ = keys::kNoParentError;
    return false;
  }
  if (parent->is_root()) {  // Can't create children of the root.
    error_ = keys::kModifySpecialError;
    return false;
  }

  int index;
  if (!params->bookmark.index.get()) {  // Optional (defaults to end).
    index = parent->child_count();
  } else {
    index = *params->bookmark.index;
    if (index > parent->child_count() || index < 0) {
      error_ = keys::kInvalidIndexError;
      return false;
    }
  }

  string16 title;  // Optional.
  if (params->bookmark.title.get())
    title = UTF8ToUTF16(*params->bookmark.title.get());

  std::string url_string;  // Optional.
  if (params->bookmark.url.get())
    url_string = *params->bookmark.url.get();

  GURL url(url_string);
  if (!url_string.empty() && !url.is_valid()) {
    error_ = keys::kInvalidUrlError;
    return false;
  }

  const BookmarkNode* node;
  if (url_string.length())
    node = model->AddURL(parent, index, title, url);
  else
    node = model->AddFolder(parent, index, title);
  DCHECK(node);
  if (!node) {
    error_ = keys::kNoNodeError;
    return false;
  }

  scoped_ptr<BookmarkTreeNode> ret(
      bookmark_api_helpers::GetBookmarkTreeNode(node, false, false));
  results_ = bookmarks::Create::Results::Create(*ret);

  return true;
}

Project: Chrome

bool BookmarksMoveFunction::RunImpl() {
  if (!EditBookmarksEnabled())
    return false;

  scoped_ptr<bookmarks::Move::Params> params(
      bookmarks::Move::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  int64 id;
  if (!base::StringToInt64(params->id, &id)) {
    error_ = keys::kInvalidIdError;
    return false;
  }

  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
  const BookmarkNode* node = model->GetNodeByID(id);
  if (!node) {
    error_ = keys::kNoNodeError;
    return false;
  }
  if (model->is_permanent_node(node)) {
    error_ = keys::kModifySpecialError;
    return false;
  }

  const BookmarkNode* parent = NULL;
  if (!params->destination.parent_id.get()) {
    parent = node->parent();
  } else {
    int64 parentId;
    if (!GetBookmarkIdAsInt64(*params->destination.parent_id, &parentId))
      return false;

    parent = model->GetNodeByID(parentId);
  }
  if (!parent) {
    error_ = keys::kNoParentError;
    return false;
  }
  if (parent == model->root_node()) {
    error_ = keys::kModifySpecialError;
    return false;
  }

  int index;
  if (params->destination.index.get()) {  // Optional (defaults to end).
    index = *params->destination.index;
    if (index > parent->child_count() || index < 0) {
      error_ = keys::kInvalidIndexError;
      return false;
    }
  } else {
    index = parent->child_count();
  }

  model->Move(node, parent, index);

  scoped_ptr<BookmarkTreeNode> tree_node(
      bookmark_api_helpers::GetBookmarkTreeNode(node, false, false));
  results_ = bookmarks::Move::Results::Create(*tree_node);

  return true;
}

Project: Chrome

bool BookmarksImportFunction::RunImpl() {
  if (!EditBookmarksEnabled())
    return false;
  SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE);
  return true;
}

Project: Chrome

void BookmarksIOFunction::SelectFile(ui::SelectFileDialog::Type type) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&BookmarksIOFunction::SelectFile, this, type));
    return;
  }

  base::FilePath default_path;
  if (type == ui::SelectFileDialog::SELECT_SAVEAS_FILE)
    default_path = GetDefaultFilepathForBookmarkExport();
  else
    DCHECK(type == ui::SelectFileDialog::SELECT_OPEN_FILE);

  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      base::Bind(&BookmarksIOFunction::ShowSelectFileDialog, this,
                 type, default_path));
}

Project: Chrome

void BookmarksAPI::Shutdown() {
  ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
}

Project: Chrome

  virtual ~BookmarkBucketMapper() { STLDeleteValues(&buckets_); }

Project: Chrome

BookmarksAPI::~BookmarksAPI() {
}

Project: Chrome

BookmarksIOFunction::~BookmarksIOFunction() {
  if (select_file_dialog_.get())
    select_file_dialog_->ListenerDestroyed();
}