1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <fcntl.h>
16 #include <unistd.h>
17
18 #include "../sandboxed.h" // NOLINT(build/include)
19 #include "helper.h" // NOLINT(build/include)
20 #include "libpng.h" // NOLINT(build/include)
21 #include "gtest/gtest.h"
22 #include "sandboxed_api/util/status_matchers.h"
23
24 namespace {
25
26 using ::sapi::IsOk;
27 using ::testing::ContainerEq;
28 using ::testing::Eq;
29 using ::testing::Ge;
30 using ::testing::Gt;
31 using ::testing::IsTrue;
32 using ::testing::NotNull;
33
34 struct Data {
35 int width;
36 int height;
37 uint8_t color_type;
38 uint8_t bit_depth;
39 int number_of_passes;
40 size_t rowbytes;
41 std::unique_ptr<sapi::v::Array<uint8_t>> row_pointers;
42 };
43
ReadPng(LibPNGApi & api,absl::string_view infile,Data & data)44 void ReadPng(LibPNGApi& api, absl::string_view infile, Data& data) {
45 sapi::v::Fd fd(open(infile.data(), O_RDONLY));
46
47 ASSERT_THAT(fd.GetValue(), Ge(0)) << "Error opening input file";
48 ASSERT_THAT((&api)->sandbox()->TransferToSandboxee(&fd), IsOk());
49
50 ASSERT_THAT(fd.GetRemoteFd(), Ge(0)) << "Error receiving remote FD";
51
52 sapi::v::ConstCStr rb_var("rb");
53 absl::StatusOr<void*> status_or_file =
54 api.png_fdopen(fd.GetRemoteFd(), rb_var.PtrBefore());
55 ASSERT_THAT(status_or_file, IsOk());
56
57 sapi::v::RemotePtr file(status_or_file.value());
58 ASSERT_THAT(file.GetValue(), NotNull()) << "Could not open " << infile;
59
60 sapi::v::Array<char> header(8);
61 ASSERT_THAT(api.png_fread(header.PtrBoth(), 1, header.GetSize(), &file),
62 IsOk());
63
64 absl::StatusOr<int> status_or_int =
65 api.png_sig_cmp(header.PtrBoth(), 0, header.GetSize());
66 ASSERT_THAT(status_or_int, IsOk());
67 ASSERT_THAT(status_or_int.value(), Eq(0)) << infile << " is not a PNG file";
68
69 sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
70 absl::StatusOr<png_structp> status_or_png_structp =
71 api.png_create_read_struct_wrapper(ver_string_var.PtrBefore(), nullptr);
72
73 ASSERT_THAT(status_or_png_structp, IsOk());
74 sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
75 ASSERT_THAT(struct_ptr.GetValue(), NotNull())
76 << "png_create_read_struct_wrapper failed";
77
78 absl::StatusOr<png_infop> status_or_png_infop =
79 api.png_create_info_struct(&struct_ptr);
80
81 ASSERT_THAT(status_or_png_infop, IsOk());
82 sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
83 ASSERT_THAT(info_ptr.GetValue(), NotNull())
84 << "png_create_info_struct failed";
85
86 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
87 ASSERT_THAT(api.png_init_io_wrapper(&struct_ptr, &file), IsOk());
88 ASSERT_THAT(api.png_set_sig_bytes(&struct_ptr, header.GetSize()), IsOk());
89 ASSERT_THAT(api.png_read_info(&struct_ptr, &info_ptr), IsOk());
90
91 status_or_int = api.png_get_image_width(&struct_ptr, &info_ptr);
92 ASSERT_THAT(status_or_int, IsOk());
93 data.width = status_or_int.value();
94 EXPECT_THAT(data.width, Gt(0));
95
96 status_or_int = api.png_get_image_height(&struct_ptr, &info_ptr);
97 ASSERT_THAT(status_or_int, IsOk());
98 data.height = status_or_int.value();
99 EXPECT_THAT(data.height, Gt(0));
100
101 absl::StatusOr<uint8_t> status_or_uchar =
102 api.png_get_color_type(&struct_ptr, &info_ptr);
103 ASSERT_THAT(status_or_uchar, IsOk());
104 data.color_type = status_or_uchar.value();
105
106 status_or_uchar = api.png_get_bit_depth(&struct_ptr, &info_ptr);
107 ASSERT_THAT(status_or_uchar, IsOk());
108 data.bit_depth = status_or_uchar.value();
109
110 status_or_int = api.png_set_interlace_handling(&struct_ptr);
111 ASSERT_THAT(status_or_int, IsOk());
112 data.number_of_passes = status_or_int.value();
113
114 ASSERT_THAT(api.png_read_update_info(&struct_ptr, &info_ptr), IsOk());
115 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
116
117 absl::StatusOr<uint32_t> status_or_uint =
118 api.png_get_rowbytes(&struct_ptr, &info_ptr);
119 ASSERT_THAT(status_or_uint, IsOk());
120 data.rowbytes = status_or_uint.value();
121 EXPECT_THAT(data.rowbytes, Ge(data.width));
122
123 data.row_pointers =
124 std::make_unique<sapi::v::Array<uint8_t>>(data.height * data.rowbytes);
125
126 ASSERT_THAT(
127 api.png_read_image_wrapper(&struct_ptr, data.row_pointers->PtrAfter(),
128 data.height, data.rowbytes),
129 IsOk());
130
131 ASSERT_THAT(api.png_fclose(&file), IsOk());
132 }
133
WritePng(LibPNGApi & api,absl::string_view outfile,Data & data)134 void WritePng(LibPNGApi& api, absl::string_view outfile, Data& data) {
135 sapi::v::Fd fd(open(outfile.data(), O_WRONLY));
136
137 ASSERT_THAT(fd.GetValue(), Ge(0)) << "Error opening output file";
138 ASSERT_THAT((&api)->sandbox()->TransferToSandboxee(&fd), IsOk());
139
140 ASSERT_THAT(fd.GetRemoteFd(), Ge(0)) << "Error receiving remote FD";
141
142 sapi::v::ConstCStr wb_var("wb");
143 absl::StatusOr<void*> status_or_file =
144 api.png_fdopen(fd.GetRemoteFd(), wb_var.PtrBefore());
145 ASSERT_THAT(status_or_file, IsOk());
146
147 sapi::v::RemotePtr file(status_or_file.value());
148 ASSERT_THAT(file.GetValue(), NotNull()) << "Could not open " << outfile;
149
150 sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
151 absl::StatusOr<png_structp> status_or_png_structp =
152 api.png_create_write_struct_wrapper(ver_string_var.PtrBefore(), nullptr);
153 ASSERT_THAT(status_or_png_structp, IsOk());
154
155 sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
156 ASSERT_THAT(struct_ptr.GetValue(), NotNull())
157 << "png_create_write_struct_wrapper failed";
158
159 absl::StatusOr<png_infop> status_or_png_infop =
160 api.png_create_info_struct(&struct_ptr);
161 ASSERT_THAT(status_or_png_infop, IsOk());
162
163 sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
164 ASSERT_THAT(info_ptr.GetValue(), NotNull())
165 << "png_create_info_struct failed";
166
167 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
168 ASSERT_THAT(api.png_init_io_wrapper(&struct_ptr, &file), IsOk());
169
170 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
171 ASSERT_THAT(
172 api.png_set_IHDR(&struct_ptr, &info_ptr, data.width, data.height,
173 data.bit_depth, data.color_type, PNG_INTERLACE_NONE,
174 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE),
175 IsOk());
176
177 ASSERT_THAT(api.png_write_info(&struct_ptr, &info_ptr), IsOk());
178
179 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
180 ASSERT_THAT(
181 api.png_write_image_wrapper(&struct_ptr, data.row_pointers->PtrBefore(),
182 data.height, data.rowbytes),
183 IsOk());
184
185 ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
186 ASSERT_THAT(api.png_write_end(&struct_ptr, nullptr), IsOk());
187
188 ASSERT_THAT(api.png_fclose(&file), IsOk());
189 }
190
TEST(SandboxTest,ReadModifyWrite)191 TEST(SandboxTest, ReadModifyWrite) {
192 std::string infile = GetFilePath("red_ball.png");
193 std::string outfile = GetFilePath("test_output.png");
194
195 LibPNGSapiSandbox sandbox;
196 ASSERT_THAT(sandbox.Init(), IsOk());
197 LibPNGApi api(&sandbox);
198
199 Data data;
200 ReadPng(api, infile, data);
201
202 ASSERT_THAT(data.color_type == PNG_COLOR_TYPE_RGBA ||
203 data.color_type == PNG_COLOR_TYPE_RGB,
204 IsTrue())
205 << infile << " has unexpected color type. Expected RGB or RGBA";
206
207 size_t channel_count = 3;
208 if (data.color_type == PNG_COLOR_TYPE_RGBA) {
209 channel_count = 4;
210 }
211
212 EXPECT_THAT(channel_count * data.width, Eq(data.rowbytes));
213
214 // RGB to BGR
215 for (size_t i = 0; i != data.height; ++i) {
216 for (size_t j = 0; j != data.width; ++j) {
217 uint8_t r = (*data.row_pointers)[i * data.rowbytes + j * channel_count];
218 uint8_t b =
219 (*data.row_pointers)[i * data.rowbytes + j * channel_count + 2];
220 (*data.row_pointers)[i * data.rowbytes + j * channel_count] = b;
221 (*data.row_pointers)[i * data.rowbytes + j * channel_count + 2] = r;
222 }
223 }
224
225 WritePng(api, outfile, data);
226
227 Data result;
228 ReadPng(api, outfile, result);
229
230 EXPECT_THAT(result.height, Eq(data.height));
231 EXPECT_THAT(result.width, Eq(data.width));
232 EXPECT_THAT(result.color_type, Eq(data.color_type));
233 EXPECT_THAT(result.rowbytes, Eq(data.rowbytes));
234 EXPECT_THAT(result.bit_depth, Eq(data.bit_depth));
235 EXPECT_THAT(result.number_of_passes, Eq(data.number_of_passes));
236 EXPECT_THAT(absl::MakeSpan(result.row_pointers->GetData(),
237 result.row_pointers->GetSize()),
238 ContainerEq(absl::MakeSpan(data.row_pointers->GetData(),
239 data.row_pointers->GetSize())));
240 }
241
242 } // namespace
243