buffer_validation.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2019 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. #include "media/gpu/buffer_validation.h"
  5. #include <algorithm>
  6. #include <cstdint>
  7. #include "base/logging.h"
  8. #include "base/numerics/checked_math.h"
  9. #include "base/numerics/safe_conversions.h"
  10. #include "build/build_config.h"
  11. #include "media/base/video_frame.h"
  12. #include "ui/gfx/geometry/size.h"
  13. #include "ui/gfx/gpu_memory_buffer.h"
  14. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  15. #include <sys/types.h>
  16. #include <unistd.h>
  17. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  18. namespace media {
  19. bool GetFileSize(const int fd, size_t* size) {
  20. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  21. if (fd < 0) {
  22. VLOG(1) << "Invalid file descriptor";
  23. return false;
  24. }
  25. off_t fd_size = lseek(fd, 0, SEEK_END);
  26. lseek(fd, 0, SEEK_SET);
  27. if (fd_size < 0u) {
  28. VPLOG(1) << "Fail to find the size of fd";
  29. return false;
  30. }
  31. if (!base::IsValueInRangeForNumericType<size_t>(fd_size)) {
  32. VLOG(1) << "fd_size is out of range of size_t"
  33. << ", size=" << size
  34. << ", size_t max=" << std::numeric_limits<size_t>::max()
  35. << ", size_t min=" << std::numeric_limits<size_t>::min();
  36. return false;
  37. }
  38. *size = static_cast<size_t>(fd_size);
  39. return true;
  40. #else
  41. NOTIMPLEMENTED();
  42. return false;
  43. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  44. }
  45. bool VerifyGpuMemoryBufferHandle(
  46. media::VideoPixelFormat pixel_format,
  47. const gfx::Size& coded_size,
  48. const gfx::GpuMemoryBufferHandle& gmb_handle,
  49. GetFileSizeCBForTesting file_size_cb_for_testing) {
  50. if (gmb_handle.type != gfx::NATIVE_PIXMAP) {
  51. VLOG(1) << "Unexpected GpuMemoryBufferType: " << gmb_handle.type;
  52. return false;
  53. }
  54. if (!media::VideoFrame::IsValidCodedSize(coded_size)) {
  55. VLOG(1) << "Coded size is beyond allowed dimensions: "
  56. << coded_size.ToString();
  57. return false;
  58. }
  59. // YV12 is used by ARC++ on MTK8173. Consider removing it.
  60. if (pixel_format != PIXEL_FORMAT_I420 && pixel_format != PIXEL_FORMAT_YV12 &&
  61. pixel_format != PIXEL_FORMAT_NV12 &&
  62. pixel_format != PIXEL_FORMAT_P016LE) {
  63. VLOG(1) << "Unsupported: " << pixel_format;
  64. return false;
  65. }
  66. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  67. const size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
  68. if (num_planes != gmb_handle.native_pixmap_handle.planes.size() ||
  69. num_planes == 0) {
  70. VLOG(1) << "Invalid number of dmabuf planes passed: "
  71. << gmb_handle.native_pixmap_handle.planes.size()
  72. << ", expected: " << num_planes;
  73. return false;
  74. }
  75. // Strides monotonically decrease.
  76. for (size_t i = 1; i < num_planes; i++) {
  77. if (gmb_handle.native_pixmap_handle.planes[i - 1].stride <
  78. gmb_handle.native_pixmap_handle.planes[i].stride) {
  79. return false;
  80. }
  81. }
  82. for (size_t i = 0; i < num_planes; i++) {
  83. const auto& plane = gmb_handle.native_pixmap_handle.planes[i];
  84. DVLOG(4) << "Plane " << i << ", offset: " << plane.offset
  85. << ", stride: " << plane.stride;
  86. size_t file_size_in_bytes;
  87. if (file_size_cb_for_testing) {
  88. file_size_in_bytes = file_size_cb_for_testing.Run();
  89. } else if (!plane.fd.is_valid() ||
  90. !GetFileSize(plane.fd.get(), &file_size_in_bytes)) {
  91. return false;
  92. }
  93. size_t plane_height =
  94. media::VideoFrame::Rows(i, pixel_format, coded_size.height());
  95. base::CheckedNumeric<size_t> min_plane_size =
  96. base::CheckMul(base::strict_cast<size_t>(plane.stride), plane_height);
  97. size_t plane_pixel_width =
  98. media::VideoFrame::RowBytes(i, pixel_format, coded_size.width());
  99. if (!min_plane_size.IsValid<uint64_t>() ||
  100. min_plane_size.ValueOrDie<uint64_t>() > plane.size ||
  101. base::strict_cast<size_t>(plane.stride) < plane_pixel_width) {
  102. VLOG(1) << "Invalid strides/sizes";
  103. return false;
  104. }
  105. // Check |offset| + (the size of a plane) on each plane is not larger than
  106. // |file_size_in_bytes|. This ensures we don't access out of a buffer
  107. // referred by |fd|.
  108. base::CheckedNumeric<uint64_t> min_buffer_size =
  109. base::CheckAdd(plane.offset, plane.size);
  110. if (!min_buffer_size.IsValid() ||
  111. min_buffer_size.ValueOrDie() >
  112. base::strict_cast<uint64_t>(file_size_in_bytes)) {
  113. VLOG(1) << "Invalid strides/offsets";
  114. return false;
  115. }
  116. }
  117. return true;
  118. #else
  119. NOTIMPLEMENTED();
  120. return false;
  121. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  122. }
  123. } // namespace media