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