diff options
author | Zhenyao Mo <zmo@chromium.org> | 2019-07-18 22:40:55 +0000 |
---|---|---|
committer | Jüri Valdmann <juri.valdmann@qt.io> | 2019-08-08 10:34:31 +0000 |
commit | debc89cea4a5c61a53c1544e83f090552507b3dc (patch) | |
tree | 19adffcd2c61bbedfe34332ea32c023e7c571776 | |
parent | 79d32f4cf1d730fdf623a6bec2bca8e63efdca86 (diff) | |
download | qtwebengine-chromium-debc89cea4a5c61a53c1544e83f090552507b3dc.tar.gz |
[Backport] Security bug 983938
Fix a base_level/max_level bug in validating command buffer
Currently we didn't clamp level values when computing which level actually
have images, therefore leading to out-of-bounds access.
BUG=983938
TEST=gpu_unittests
R=piman@chromium.org
Change-Id: Ia4c143f271cfc5142a34ade5f3163e29d9be7033
Reviewed-by: Antoine Labour <piman@chromium.org>
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678877}
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
3 files changed, 66 insertions, 14 deletions
diff --git a/chromium/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/chromium/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc index b94cd64e5a4..36662a93b71 100644 --- a/chromium/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc +++ b/chromium/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc @@ -5044,6 +5044,30 @@ TEST_P(GLES3DecoderTest, ImmutableTextureBaseLevelMaxLevelClamping) { } } +TEST_P(GLES3DecoderTest, ClearRenderableLevelsWithOutOfRangeBaseLevel) { + // Regression test for https://crbug.com/983938 + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, + 0); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + TextureManager* manager = group().texture_manager(); + TextureRef* texture_ref = manager->GetTexture(client_texture_id_); + ASSERT_TRUE(texture_ref != nullptr); + + { + EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 55)); + TexParameteri cmd; + cmd.Init(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 55); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + } + + // The following call will trigger out-of-bounds access in asan build + // without fixing the bug. + manager->ClearRenderableLevels(GetDecoder(), texture_ref); +} + // TODO(gman): Complete this test. // TEST_P(GLES2DecoderTest, CompressedTexImage2DGLError) { // } diff --git a/chromium/gpu/command_buffer/service/texture_manager.cc b/chromium/gpu/command_buffer/service/texture_manager.cc index abe7b8a39a5..315dc9eeb9a 100644 --- a/chromium/gpu/command_buffer/service/texture_manager.cc +++ b/chromium/gpu/command_buffer/service/texture_manager.cc @@ -1148,6 +1148,40 @@ void Texture::UpdateMaxLevel(GLint max_level) { UpdateNumMipLevels(); } +void Texture::UpdateFaceNumMipLevels(size_t face_index, + GLint width, + GLint height, + GLint depth) { + DCHECK_LT(face_index, face_infos_.size()); + DCHECK_LE(0, base_level_); + Texture::FaceInfo& face_info = face_infos_[face_index]; + if (static_cast<size_t>(base_level_) >= face_info.level_infos.size()) { + face_info.num_mip_levels = 0; + } else { + DCHECK_LE(1u, face_info.level_infos.size()); + GLint safe_max_level = std::min( + max_level_, static_cast<GLint>(face_info.level_infos.size() - 1)); + GLint max_num_mip_levels = std::max(0, safe_max_level - base_level_ + 1); + face_info.num_mip_levels = std::min( + max_num_mip_levels, + TextureManager::ComputeMipMapCount(target_, width, height, depth)); + } +} + +void Texture::UpdateFaceNumMipLevels(size_t face_index) { + DCHECK_LT(face_index, face_infos_.size()); + DCHECK_LE(0, base_level_); + Texture::FaceInfo& face_info = face_infos_[face_index]; + GLint width = 0, height = 0, depth = 0; + if (static_cast<size_t>(base_level_) < face_info.level_infos.size()) { + const Texture::LevelInfo& info = face_info.level_infos[base_level_]; + width = info.width; + height = info.height; + depth = info.depth; + } + UpdateFaceNumMipLevels(face_index, width, height, depth); +} + void Texture::UpdateNumMipLevels() { if (face_infos_.empty()) return; @@ -1164,16 +1198,8 @@ void Texture::UpdateNumMipLevels() { base_level_ = unclamped_base_level_; max_level_ = unclamped_max_level_; } - GLint max_num_mip_levels = std::max(0, max_level_ - base_level_ + 1); - for (size_t ii = 0; ii < face_infos_.size(); ++ii) { - Texture::FaceInfo& face_info = face_infos_[ii]; - if (static_cast<size_t>(base_level_) >= face_info.level_infos.size()) - continue; - const Texture::LevelInfo& info = face_info.level_infos[base_level_]; - face_info.num_mip_levels = std::min( - max_num_mip_levels, TextureManager::ComputeMipMapCount( - target_, info.width, info.height, info.depth)); - } + for (size_t ii = 0; ii < face_infos_.size(); ++ii) + UpdateFaceNumMipLevels(ii); // mipmap-completeness needs to be re-evaluated. completeness_dirty_ = true; @@ -1218,10 +1244,7 @@ void Texture::SetLevelInfo(GLenum target, info.width != width || info.height != height || info.depth != depth || info.format != format || info.type != type || info.internal_workaround) { if (level == base_level_) { - // Calculate the mip level count. - face_infos_[face_index].num_mip_levels = std::min( - std::max(0, max_level_ - base_level_ + 1), - TextureManager::ComputeMipMapCount(target_, width, height, depth)); + UpdateFaceNumMipLevels(face_index, width, height, depth); // Update NPOT face count for the first level. bool prev_npot = TextureIsNPOT(info.width, info.height, info.depth); diff --git a/chromium/gpu/command_buffer/service/texture_manager.h b/chromium/gpu/command_buffer/service/texture_manager.h index d42699ee403..1b6915336c6 100644 --- a/chromium/gpu/command_buffer/service/texture_manager.h +++ b/chromium/gpu/command_buffer/service/texture_manager.h @@ -585,6 +585,11 @@ class GPU_GLES2_EXPORT Texture final : public TextureBase { void UpdateBaseLevel(GLint base_level, const FeatureInfo* feature_info); void UpdateMaxLevel(GLint max_level); + void UpdateFaceNumMipLevels(size_t face_index, + GLint width, + GLint height, + GLint depth); + void UpdateFaceNumMipLevels(size_t face_index); void UpdateNumMipLevels(); // Increment the generation counter for all managers that have a reference to |