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