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