Commit message: None
CVE: CVE-2014-1710
CWE: CWE-119
Vulnerability description: The AsyncPixelTransfersCompletedQuery::End function in gpu/command_buffer/service/query_manager.cc in Google Chrome, as used in Google Chrome OS before 33.0.1750.152, does not check whether a certain position is within the bounds of a shared-memory segment, which allows remote attackers to cause a denial of service (GPU command-buffer memory corruption) or possibly have unspecified other impact via unknown vectors.

Project: Chrome

bool QueryManager::AddPendingQuery(Query* query,
                                   base::subtle::Atomic32 submit_count) {
  DCHECK(query);
  DCHECK(!query->IsDeleted());
  if (!RemovePendingQuery(query)) {
    return false;
  }
  query->MarkAsPending(submit_count);
  pending_queries_.push_back(query);
  return true;
}

Project: Chrome

bool QueryManager::AddPendingTransferQuery(
    Query* query,
    base::subtle::Atomic32 submit_count) {
  DCHECK(query);
  DCHECK(!query->IsDeleted());
  if (!RemovePendingQuery(query)) {
    return false;
  }
  query->MarkAsPending(submit_count);
  pending_transfer_queries_.push_back(query);
  return true;
}

Project: Chrome

GLenum QueryManager::AdjustTargetForEmulation(GLenum target) {
  switch (target) {
    case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
    case GL_ANY_SAMPLES_PASSED_EXT:
      if (use_arb_occlusion_query2_for_occlusion_query_boolean_) {
        target = GL_ANY_SAMPLES_PASSED_EXT;
      } else if (use_arb_occlusion_query_for_occlusion_query_boolean_) {
        target = GL_SAMPLES_PASSED_ARB;
      }
      break;
    default:
      break;
  }
  return target;
}

Project: Chrome

AllSamplesPassedQuery::AllSamplesPassedQuery(
    QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset,
    GLuint service_id)
    : Query(manager, target, shm_id, shm_offset),
      service_id_(service_id) {
}

Project: Chrome

  AsyncPixelTransferCompletionObserverImpl(base::subtle::Atomic32 submit_count)
      : submit_count_(submit_count), cancelled_(false) {}

Project: Chrome

AsyncReadPixelsCompletedQuery::AsyncReadPixelsCompletedQuery(
    QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
    : Query(manager, target, shm_id, shm_offset) {
}

Project: Chrome

bool CommandsIssuedQuery::Begin() {
  begin_time_ = base::TimeTicks::HighResNow();
  return true;
}

Project: Chrome

bool CommandLatencyQuery::Begin() {
    return true;
}

Project: Chrome

bool AsyncReadPixelsCompletedQuery::Begin() {
  return true;
}

Project: Chrome

  void Cancel() {
    base::AutoLock locked(lock_);
    cancelled_ = true;
  }

Project: Chrome

CommandLatencyQuery::CommandLatencyQuery(
      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
    : Query(manager, target, shm_id, shm_offset) {
}

Project: Chrome

CommandsIssuedQuery::CommandsIssuedQuery(
      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
    : Query(manager, target, shm_id, shm_offset) {
}

Project: Chrome

void AsyncReadPixelsCompletedQuery::Complete() {
  MarkAsCompleted(1);
}

Project: Chrome

QueryManager::Query* QueryManager::CreateQuery(
    GLenum target, GLuint client_id, int32 shm_id, uint32 shm_offset) {
  scoped_refptr<Query> query;
  switch (target) {
    case GL_COMMANDS_ISSUED_CHROMIUM:
      query = new CommandsIssuedQuery(this, target, shm_id, shm_offset);
      break;
    case GL_LATENCY_QUERY_CHROMIUM:
      query = new CommandLatencyQuery(this, target, shm_id, shm_offset);
      break;
    case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
      query = new AsyncPixelTransfersCompletedQuery(
          this, target, shm_id, shm_offset);
      break;
    case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
      query = new AsyncReadPixelsCompletedQuery(
          this, target, shm_id, shm_offset);
      break;
    case GL_GET_ERROR_QUERY_CHROMIUM:
      query = new GetErrorQuery(this, target, shm_id, shm_offset);
      break;
    default: {
      GLuint service_id = 0;
      glGenQueriesARB(1, &service_id);
      DCHECK_NE(0u, service_id);
      query = new AllSamplesPassedQuery(
          this, target, shm_id, shm_offset, service_id);
      break;
    }
  }
  std::pair<QueryMap::iterator, bool> result =
      queries_.insert(std::make_pair(client_id, query));
  DCHECK(result.second);
  return query.get();
}

Project: Chrome

void AsyncPixelTransfersCompletedQuery::Destroy(bool /* have_context */) {
  if (!IsDeleted()) {
    MarkAsDeleted();
  }
}

Project: Chrome

void AllSamplesPassedQuery::Destroy(bool have_context) {
  if (have_context && !IsDeleted()) {
    glDeleteQueriesARB(1, &service_id_);
    MarkAsDeleted();
  }
}

Project: Chrome

void CommandsIssuedQuery::Destroy(bool /* have_context */) {
  if (!IsDeleted()) {
    MarkAsDeleted();
  }
}

Project: Chrome

void AsyncReadPixelsCompletedQuery::Destroy(bool /* have_context */) {
  if (!IsDeleted()) {
    MarkAsDeleted();
  }
}

Project: Chrome

void GetErrorQuery::Destroy(bool /* have_context */) {
  if (!IsDeleted()) {
    MarkAsDeleted();
  }
}

Project: Chrome

void QueryManager::Destroy(bool have_context) {
  pending_queries_.clear();
  pending_transfer_queries_.clear();
  while (!queries_.empty()) {
    Query* query = queries_.begin()->second.get();
    query->Destroy(have_context);
    queries_.erase(queries_.begin());
  }
}

Project: Chrome

bool AllSamplesPassedQuery::End(base::subtle::Atomic32 submit_count) {
  EndQueryHelper(target());
  return AddToPendingQueue(submit_count);
}

Project: Chrome

bool CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count) {
  base::TimeDelta elapsed = base::TimeTicks::HighResNow() - begin_time_;
  MarkAsPending(submit_count);
  return MarkAsCompleted(elapsed.InMicroseconds());
}

Project: Chrome

bool CommandLatencyQuery::End(base::subtle::Atomic32 submit_count) {
    base::TimeDelta now = base::TimeTicks::HighResNow() - base::TimeTicks();
    MarkAsPending(submit_count);
    return MarkAsCompleted(now.InMicroseconds());
}

Project: Chrome

bool GetErrorQuery::End(base::subtle::Atomic32 submit_count) {
  MarkAsPending(submit_count);
  return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError());
}

Project: Chrome

bool QueryManager::EndQuery(Query* query, base::subtle::Atomic32 submit_count) {
  DCHECK(query);
  if (!RemovePendingQuery(query)) {
    return false;
  }
  return query->End(submit_count);
}

Project: Chrome

void QueryManager::EndQueryHelper(GLenum target) {
  target = AdjustTargetForEmulation(target);
  glEndQueryARB(target);
}

Project: Chrome

GetErrorQuery::GetErrorQuery(
      QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
    : Query(manager, target, shm_id, shm_offset) {
}

Project: Chrome

QueryManager::Query* QueryManager::GetQuery(
    GLuint client_id) {
  QueryMap::iterator it = queries_.find(client_id);
  return it != queries_.end() ? it->second.get() : NULL;
}

Project: Chrome

bool QueryManager::HavePendingQueries() {
  return !pending_queries_.empty();
}

Project: Chrome

bool QueryManager::HavePendingTransferQueries() {
  return !pending_transfer_queries_.empty();
}

Project: Chrome

bool QueryManager::Query::MarkAsCompleted(uint64 result) {
  DCHECK(pending_);
  QuerySync* sync = manager_->decoder_->GetSharedMemoryAs<QuerySync*>(
      shm_id_, shm_offset_, sizeof(*sync));
  if (!sync) {
    return false;
  }

  pending_ = false;
  sync->result = result;
  base::subtle::Release_Store(&sync->process_count, submit_count_);

  return true;
}

Project: Chrome

bool AsyncPixelTransfersCompletedQuery::Process() {
  QuerySync* sync = manager()->decoder()->GetSharedMemoryAs<QuerySync*>(
      shm_id(), shm_offset(), sizeof(*sync));
  if (!sync)
    return false;

  if (base::subtle::Acquire_Load(&sync->process_count) != submit_count())
    return true;

  UnmarkAsPending();
  return true;
}

Project: Chrome

bool AllSamplesPassedQuery::Process() {
  GLuint available = 0;
  glGetQueryObjectuivARB(
      service_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
  if (!available) {
    return true;
  }
  GLuint result = 0;
  glGetQueryObjectuivARB(
      service_id_, GL_QUERY_RESULT_EXT, &result);

  return MarkAsCompleted(result != 0);
}

Project: Chrome

bool CommandsIssuedQuery::Process() {
  NOTREACHED();
  return true;
}

Project: Chrome

bool CommandLatencyQuery::Process() {
  NOTREACHED();
  return true;
}

Project: Chrome

bool AsyncReadPixelsCompletedQuery::Process() {
  return true;
}

Project: Chrome

bool GetErrorQuery::Process() {
  NOTREACHED();
  return true;
}

Project: Chrome

QueryManager::Query::Query(
     QueryManager* manager, GLenum target, int32 shm_id, uint32 shm_offset)
    : manager_(manager),
      target_(target),
      shm_id_(shm_id),
      shm_offset_(shm_offset),
      submit_count_(0),
      pending_(false),
      deleted_(false) {
  DCHECK(manager);
  manager_->StartTracking(this);
}

Project: Chrome

void QueryManager::RemoveQuery(GLuint client_id) {
  QueryMap::iterator it = queries_.find(client_id);
  if (it != queries_.end()) {
    Query* query = it->second.get();
    RemovePendingQuery(query);
    query->MarkAsDeleted();
    queries_.erase(it);
  }
}

Project: Chrome

void QueryManager::Query::RunCallbacks() {
  for (size_t i = 0; i < callbacks_.size(); i++) {
    callbacks_[i].Run();
  }
  callbacks_.clear();
}

Project: Chrome

void QueryManager::StartTracking(QueryManager::Query* /* query */) {
  ++query_count_;
}

Project: Chrome

AllSamplesPassedQuery::~AllSamplesPassedQuery() {
}

Project: Chrome

  virtual ~AsyncPixelTransferCompletionObserverImpl() {}

Project: Chrome

AsyncPixelTransfersCompletedQuery::~AsyncPixelTransfersCompletedQuery() {
  if (observer_)
    observer_->Cancel();
}

Project: Chrome

CommandLatencyQuery::~CommandLatencyQuery() {
}

Project: Chrome

CommandsIssuedQuery::~CommandsIssuedQuery() {
}

Project: Chrome

GetErrorQuery::~GetErrorQuery() {
}

Project: Chrome

QueryManager::Query::~Query() {
  RunCallbacks();
  if (manager_) {
    manager_->StopTracking(this);
    manager_ = NULL;
  }
}

Project: Chrome

QueryManager::~QueryManager() {
  DCHECK(queries_.empty());

  CHECK_EQ(query_count_, 0u);
}