Merge pull request #69 from alexelex:libpng-master

PiperOrigin-RevId: 346072038
Change-Id: I23a9e6704106e2834a5900522a1be06341c6421a
This commit is contained in:
Copybara-Service 2020-12-07 05:34:46 -08:00
commit 08bb2f80d1
20 changed files with 1231 additions and 0 deletions

View File

@ -0,0 +1,112 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.12)
project(sandboxed_libpng CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Set this on the command-line
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
# To obtain a full SAPI_ROOT check out its source separately:
# git clone https://github.com/google/sandboxed-api.git /path/to/sapi_root
# Then configure:
# mkdir -p build && cd build
# cmake .. -G Ninja -DSAPI_ROOT=/path/to/sapi_root
option(LIBPNG_SAPI_ENABLE_EXAMPLES "" OFF)
option(LIBPNG_SAPI_ENABLE_TESTS "" OFF)
set(SAPI_ENABLE_EXAMPLES ${LIBPNG_SAPI_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${LIBPNG_SAPI_ENABLE_TESTS} CACHE BOOL "" FORCE)
set (CMAKE_FIND_LIBRARY_SUFFIXES .a $ {CMAKE_FIND_LIBRARY_SUFFIXES})
find_package(PNG REQUIRED)
list(GET PNG_INCLUDE_DIRS 0 PNG_INCLUDE_DIR)
add_subdirectory(wrapper)
add_subdirectory(
"${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
# Omit this to have the full Sandboxed API in IDE
EXCLUDE_FROM_ALL
)
add_sapi_library(libpng_sapi
# List of functions that we want to include in the
# generated sandboxed API class
FUNCTIONS png_image_free
png_image_finish_read
png_image_write_to_file
png_image_begin_read_from_file
png_get_rowbytes
png_get_bit_depth
png_get_color_type
png_get_image_width
png_get_image_height
png_set_IHDR
png_set_sig_bytes
png_set_interlace_handling
png_read_info
png_read_image_wrapper
png_read_update_info
png_write_end
png_write_info
png_write_image_wrapper
png_create_info_struct
png_create_read_struct_wrapper
png_create_write_struct_wrapper
png_init_io_wrapper
png_sig_cmp
png_fread
png_fdopen
png_rewind
png_fclose
png_setjmp
INPUTS "${PNG_INCLUDE_DIR}/png.h"
wrapper/func.h
# Header files or .cc files that should be parsed
LIBRARY wrapper
# Library dependency from the add_library() above
LIBRARY_NAME LibPNG # Name prefix for the generated header. Will be
# suffixed with "Api" and "Sandbox" as needed.
NAMESPACE "" # Optional C++ namespace to wrap the generated code
)
target_include_directories(libpng_sapi INTERFACE
"${PROJECT_BINARY_DIR}" # To find the generated SAPI header
)
if (LIBPNG_SAPI_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
if (LIBPNG_SAPI_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -0,0 +1,45 @@
# sandboxed LibPNG
Copyright 2020 Google LLC.
## Start use
You should make sure the libtiff submodule is cloned.
`git clone --recursive https://github.com/google/sandboxed-api`
## Usage
#### Build:
```
mkdir -p build && cd build
cmake .. -DSAPI_ROOT=/path/to/sapi_root
make -j8
```
#### Example:
You should add `-DLIBPNG_SAPI_ENABLE_EXAMPLES=ON` to use the example.\
run PNG to PNG:
```
./examples/pngtopng /absolute/path/to/input/image.png /absolute/path/to/output/image.png
```
run RGB to BGR:
```
./examples/rgbtobgr /absolute/path/to/input/image.png /absolute/path/to/output/image.png
```
Examples of input and output can be found in `images`.
PNG to PNG: \
input: `images/pngtest.png`\
output:` images/pngtopng_pngtest.png`
RGB to BGR: \
input: `images/red_ball.png`\
output: `images/rgbtobgr_red_ball.png`
#### Tests:
You should add `-DLIBPNG_SAPI_ENABLE_TESTS=ON` to use tests and do:
```
cd tests
ctest .
```

View File

@ -0,0 +1,51 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_executable(pngtopng
example1.cc
../tests/libpng.h
../sandboxed.h
)
find_package(PNG REQUIRED)
target_link_libraries(pngtopng PRIVATE
sapi::sapi
sandbox2::temp_file
libpng_sapi
"${PNG_LIBRARY}"
)
target_include_directories(pngtopng INTERFACE
"${PNG_INCLUDE_DIR}"
)
add_executable(rgbtobgr
example2.cc
../tests/libpng.h
../sandboxed.h
)
target_link_libraries(rgbtobgr PRIVATE
sapi::sapi
sandbox2::temp_file
libpng_sapi
"${PNG_LIBRARY}"
)
target_include_directories(rgbtobgr INTERFACE
"${PNG_INCLUDE_DIR}"
)

View File

@ -0,0 +1,80 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include "../sandboxed.h" // NOLINT(build/include)
#include "../tests/libpng.h" // NOLINT(build/include)
#include "sandboxed_api/vars.h"
absl::Status LibPNGMain(const std::string& infile, const std::string& outfile) {
LibPNGSapiSandbox sandbox;
sandbox.AddFile(infile);
sandbox.AddFile(outfile);
SAPI_RETURN_IF_ERROR(sandbox.Init());
LibPNGApi api(&sandbox);
sapi::v::Struct<png_image> image;
sapi::v::ConstCStr infile_var(infile.c_str());
sapi::v::ConstCStr outfile_var(outfile.c_str());
image.mutable_data()->version = PNG_IMAGE_VERSION;
SAPI_ASSIGN_OR_RETURN(int result, api.png_image_begin_read_from_file(
image.PtrBoth(), infile_var.PtrBefore()));
if (!result) {
return absl::InternalError(
absl::StrCat("begin read error: ", image.mutable_data()->message));
}
image.mutable_data()->format = PNG_FORMAT_RGBA;
sapi::v::Array<uint8_t> buffer(PNG_IMAGE_SIZE(*image.mutable_data()));
sapi::v::NullPtr null = sapi::v::NullPtr();
SAPI_ASSIGN_OR_RETURN(result,
api.png_image_finish_read(image.PtrBoth(), &null,
buffer.PtrBoth(), 0, &null));
if (!result) {
return absl::InternalError(
absl::StrCat("finish read error: ", image.mutable_data()->message));
}
SAPI_ASSIGN_OR_RETURN(result, api.png_image_write_to_file(
image.PtrBoth(), outfile_var.PtrBefore(), 0,
buffer.PtrBoth(), 0, &null));
if (!result) {
return absl::InternalError(
absl::StrCat("write error: ", image.mutable_data()->message));
}
return absl::OkStatus();
}
int main(int argc, const char** argv) {
if (argc != 3) {
LOG(ERROR) << "usage: example input-file output-file";
return EXIT_FAILURE;
}
auto status = LibPNGMain(argv[1], argv[2]);
if (!status.ok()) {
LOG(ERROR) << "LibPNGMain failed with error:\n"
<< status.ToString() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,234 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <unistd.h>
#include "../sandboxed.h" // NOLINT(build/include)
#include "../tests/libpng.h" // NOLINT(build/include)
struct Data {
int width;
int height;
uint8_t color_type;
uint8_t bit_depth;
int number_of_passes;
size_t rowbytes;
std::unique_ptr<sapi::v::Array<uint8_t>> row_pointers;
};
absl::StatusOr<Data> ReadPng(LibPNGApi& api, absl::string_view infile) {
sapi::v::Fd fd(open(infile.data(), O_RDONLY));
if (fd.GetValue() < 0) {
return absl::InternalError("Error opening input file");
}
SAPI_RETURN_IF_ERROR((&api)->sandbox()->TransferToSandboxee(&fd));
if (fd.GetRemoteFd() < 0) {
return absl::InternalError("Error receiving remote FD");
}
absl::StatusOr<void*> status_or_file;
sapi::v::ConstCStr rb_var("rb");
SAPI_ASSIGN_OR_RETURN(status_or_file,
api.png_fdopen(fd.GetRemoteFd(), rb_var.PtrBefore()));
sapi::v::RemotePtr file(status_or_file.value());
if (!file.GetValue()) {
return absl::InternalError(absl::StrCat("Could not open ", infile));
}
sapi::v::Array<char> header(8);
SAPI_RETURN_IF_ERROR(api.png_fread(header.PtrBoth(), 1, header.GetSize(), &file));
SAPI_ASSIGN_OR_RETURN(int return_value,
api.png_sig_cmp(header.PtrBoth(), 0, header.GetSize()));
if (return_value != 0) {
return absl::InternalError(absl::StrCat(infile, " is not a PNG file"));
}
absl::StatusOr<png_structp> status_or_png_structp;
sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
sapi::v::NullPtr null = sapi::v::NullPtr();
SAPI_ASSIGN_OR_RETURN(
status_or_png_structp,
api.png_create_read_struct_wrapper(ver_string_var.PtrBefore(), &null));
sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
if (!struct_ptr.GetValue()) {
return absl::InternalError("png_create_read_struct_wrapper failed");
}
absl::StatusOr<png_infop> status_or_png_infop;
SAPI_ASSIGN_OR_RETURN(status_or_png_infop,
api.png_create_info_struct(&struct_ptr));
sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
if (!info_ptr.GetValue()) {
return absl::InternalError("png_create_info_struct failed");
}
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_RETURN_IF_ERROR(api.png_init_io_wrapper(&struct_ptr, &file));
SAPI_RETURN_IF_ERROR(api.png_set_sig_bytes(&struct_ptr, header.GetSize()));
SAPI_RETURN_IF_ERROR(api.png_read_info(&struct_ptr, &info_ptr));
Data data;
SAPI_ASSIGN_OR_RETURN(data.width, api.png_get_image_width(&struct_ptr, &info_ptr));
SAPI_ASSIGN_OR_RETURN(data.height,
api.png_get_image_height(&struct_ptr, &info_ptr));
SAPI_ASSIGN_OR_RETURN(data.color_type,
api.png_get_color_type(&struct_ptr, &info_ptr));
SAPI_ASSIGN_OR_RETURN(data.bit_depth,
api.png_get_bit_depth(&struct_ptr, &info_ptr));
SAPI_ASSIGN_OR_RETURN(data.number_of_passes,
api.png_set_interlace_handling(&struct_ptr));
SAPI_RETURN_IF_ERROR(api.png_read_update_info(&struct_ptr, &info_ptr));
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_ASSIGN_OR_RETURN(data.rowbytes, api.png_get_rowbytes(&struct_ptr, &info_ptr));
data.row_pointers =
std::make_unique<sapi::v::Array<uint8_t>>(data.height * data.rowbytes);
SAPI_RETURN_IF_ERROR(api.png_read_image_wrapper(
&struct_ptr, data.row_pointers->PtrAfter(), data.height, data.rowbytes));
SAPI_RETURN_IF_ERROR(api.png_fclose(&file));
return data;
}
absl::Status WritePng(LibPNGApi& api, absl::string_view outfile, Data& data) {
sapi::v::Fd fd(open(outfile.data(), O_WRONLY));
if (fd.GetValue() < 0) {
return absl::InternalError("Error opening output file");
}
SAPI_RETURN_IF_ERROR((&api)->sandbox()->TransferToSandboxee(&fd));
if (fd.GetRemoteFd() < 0) {
return absl::InternalError("Error receiving remote FD");
}
absl::StatusOr<void*> status_or_file;
sapi::v::ConstCStr wb_var("wb");
SAPI_ASSIGN_OR_RETURN(status_or_file,
api.png_fdopen(fd.GetRemoteFd(), wb_var.PtrBefore()));
sapi::v::RemotePtr file(status_or_file.value());
if (!file.GetValue()) {
return absl::InternalError(absl::StrCat("Could not open ", outfile));
}
absl::StatusOr<png_structp> status_or_png_structp;
sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
sapi::v::NullPtr null = sapi::v::NullPtr();
SAPI_ASSIGN_OR_RETURN(
status_or_png_structp,
api.png_create_write_struct_wrapper(ver_string_var.PtrBefore(), &null));
sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
if (!struct_ptr.GetValue()) {
return absl::InternalError("png_create_write_struct_wrapper failed");
}
absl::StatusOr<png_infop> status_or_png_infop;
SAPI_ASSIGN_OR_RETURN(status_or_png_infop,
api.png_create_info_struct(&struct_ptr));
sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
if (!info_ptr.GetValue()) {
return absl::InternalError("png_create_info_struct failed");
}
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_RETURN_IF_ERROR(api.png_init_io_wrapper(&struct_ptr, &file));
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_RETURN_IF_ERROR(
api.png_set_IHDR(&struct_ptr, &info_ptr, data.width, data.height,
data.bit_depth, data.color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE));
SAPI_RETURN_IF_ERROR(api.png_write_info(&struct_ptr, &info_ptr));
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_RETURN_IF_ERROR(api.png_write_image_wrapper(
&struct_ptr, data.row_pointers->PtrBefore(), data.height, data.rowbytes));
SAPI_RETURN_IF_ERROR(api.png_setjmp(&struct_ptr));
SAPI_RETURN_IF_ERROR(api.png_write_end(&struct_ptr, &null));
SAPI_RETURN_IF_ERROR(api.png_fclose(&file));
return absl::OkStatus();
}
absl::Status LibPNGMain(const std::string& infile, const std::string& outfile) {
LibPNGSapiSandbox sandbox;
sandbox.AddFile(infile);
sandbox.AddFile(outfile);
SAPI_RETURN_IF_ERROR(sandbox.Init());
LibPNGApi api(&sandbox);
SAPI_ASSIGN_OR_RETURN(Data data, ReadPng(api, infile));
if (data.color_type != PNG_COLOR_TYPE_RGBA &&
data.color_type != PNG_COLOR_TYPE_RGB) {
return absl::InternalError(absl::StrCat(
infile, " has unexpected color type. Expected RGB or RGBA"));
}
size_t channel_count = 3;
if (data.color_type == PNG_COLOR_TYPE_RGBA) {
channel_count = 4;
}
// RGB to BGR
for (size_t i = 0; i != data.height; ++i) {
for (size_t j = 0; j != data.width; ++j) {
uint8_t r = (*data.row_pointers)[i * data.rowbytes + j * channel_count];
uint8_t g =
(*data.row_pointers)[i * data.rowbytes + j * channel_count + 1];
uint8_t b =
(*data.row_pointers)[i * data.rowbytes + j * channel_count + 2];
(*data.row_pointers)[i * data.rowbytes + j * channel_count] = b;
(*data.row_pointers)[i * data.rowbytes + j * channel_count + 2] = r;
}
}
SAPI_RETURN_IF_ERROR(WritePng(api, outfile, data));
return absl::OkStatus();
}
int main(int argc, const char** argv) {
if (argc != 3) {
LOG(ERROR) << "Usage: example5 infile outfile";
return EXIT_FAILURE;
}
auto status = LibPNGMain(argv[1], argv[2]);
if (!status.ok()) {
LOG(ERROR) << "LibPNGMain failed with error:\n"
<< status.ToString() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,64 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBPNG_SANDBOXED_H_
#define LIBPNG_SANDBOXED_H_
#include <linux/futex.h>
#include <syscall.h>
#include <string>
#include <utility>
#include <vector>
#include "libpng_sapi.sapi.h" // NOLINT(build/include)
class LibPNGSapiSandbox : public LibPNGSandbox {
public:
void AddFile(const std::string& file) { files_.push_back(file); }
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
sandbox2::PolicyBuilder builder;
builder.AllowRead()
.AllowStaticStartup()
.AllowWrite()
.AllowOpen()
.AllowExit()
.AllowStat()
.AllowMmap()
.AllowSystemMalloc()
.AllowSyscalls({
__NR_futex,
__NR_close,
__NR_lseek,
__NR_gettid,
__NR_sysinfo,
__NR_munmap,
__NR_recvmsg,
__NR_fcntl,
});
for (const auto& file : files_) {
builder->AddFile(file, /*is_ro=*/false);
}
return builder.BuildOrDie();
}
std::vector<std::string> files_;
};
#endif // LIBPNG_SANDBOXED_H_

View File

@ -0,0 +1,35 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(GoogleTest)
enable_testing()
add_executable(tests
basic_test.cc
extended_test.cc
helper.h
helper.cc
libpng.h
)
target_link_libraries(tests PRIVATE
gmock
gtest
gtest_main
libpng_sapi
sandbox2::temp_file
sapi::sapi
)
gtest_discover_tests(tests PROPERTIES ENVIRONMENT "TEST_SRCDIR=${PROJECT_SOURCE_DIR}")

View File

@ -0,0 +1,91 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "../sandboxed.h" // NOLINT(build/include)
#include "helper.h" // NOLINT(build/include)
#include "libpng.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/sandbox2/util/temp_file.h"
#include "sandboxed_api/util/status_matchers.h"
namespace {
using ::sapi::IsOk;
using ::testing::Eq;
using ::testing::IsTrue;
TEST(SandboxTest, ReadWrite) {
std::string infile = GetFilePath("pngtest.png");
absl::StatusOr<std::string> status_or_path =
sandbox2::CreateNamedTempFileAndClose("output.png");
ASSERT_THAT(status_or_path, IsOk()) << "Could not create temp output file";
std::string outfile = sandbox2::file::JoinPath(
sandbox2::file_util::fileops::GetCWD(), status_or_path.value());
LibPNGSapiSandbox sandbox;
sandbox.AddFile(infile);
sandbox.AddFile(outfile);
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
LibPNGApi api(&sandbox);
sapi::v::Struct<png_image> image;
sapi::v::ConstCStr infile_var(infile.c_str());
sapi::v::ConstCStr outfile_var(outfile.c_str());
image.mutable_data()->version = PNG_IMAGE_VERSION;
absl::StatusOr<int> status_or_int = api.png_image_begin_read_from_file(
image.PtrBoth(), infile_var.PtrBefore());
ASSERT_THAT(status_or_int, IsOk())
<< "fatal error when invoking png_image_begin_read_from_file";
ASSERT_THAT(status_or_int.value(), IsTrue())
<< "png_image_begin_read_from_file failed: "
<< image.mutable_data()->message;
image.mutable_data()->format = PNG_FORMAT_RGBA;
ASSERT_THAT(image.mutable_data()->version, Eq(PNG_IMAGE_VERSION))
<< "image version changed";
sapi::v::Array<uint8_t> buffer(PNG_IMAGE_SIZE(*image.mutable_data()));
sapi::v::NullPtr null = sapi::v::NullPtr();
status_or_int = api.png_image_finish_read(image.PtrBoth(), &null,
buffer.PtrBoth(), 0, &null);
ASSERT_THAT(status_or_int, IsOk())
<< "fatal error when invoking png_image_finish_read";
ASSERT_THAT(status_or_int.value(), IsTrue())
<< "png_image_finish_read failed: " << image.mutable_data()->message;
ASSERT_THAT(image.mutable_data()->version, Eq(PNG_IMAGE_VERSION))
<< "image version changed";
ASSERT_THAT(image.mutable_data()->format, Eq(PNG_FORMAT_RGBA))
<< "image format changed";
status_or_int = api.png_image_write_to_file(
image.PtrBoth(), outfile_var.PtrBefore(), 0, buffer.PtrBoth(), 0, &null);
ASSERT_THAT(status_or_int, IsOk())
<< "fatal error when invoking png_image_write_to_file";
ASSERT_THAT(status_or_int.value(), IsTrue())
<< "png_image_finish_read failed: " << image.mutable_data()->message;
ASSERT_THAT(image.mutable_data()->version, Eq(PNG_IMAGE_VERSION))
<< "image version changed";
ASSERT_THAT(image.mutable_data()->format, Eq(PNG_FORMAT_RGBA))
<< "image format changed";
}
} // namespace

View File

@ -0,0 +1,244 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <unistd.h>
#include "../sandboxed.h" // NOLINT(build/include)
#include "helper.h" // NOLINT(build/include)
#include "libpng.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/util/status_matchers.h"
namespace {
using ::sapi::IsOk;
using ::testing::ContainerEq;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Gt;
using ::testing::IsTrue;
using ::testing::NotNull;
struct Data {
int width;
int height;
uint8_t color_type;
uint8_t bit_depth;
int number_of_passes;
size_t rowbytes;
std::unique_ptr<sapi::v::Array<uint8_t>> row_pointers;
};
void ReadPng(LibPNGApi& api, absl::string_view infile, Data& data) {
sapi::v::Fd fd(open(infile.data(), O_RDONLY));
ASSERT_THAT(fd.GetValue(), Ge(0)) << "Error opening input file";
ASSERT_THAT((&api)->sandbox()->TransferToSandboxee(&fd), IsOk());
ASSERT_THAT(fd.GetRemoteFd(), Ge(0)) << "Error receiving remote FD";
sapi::v::ConstCStr rb_var("rb");
absl::StatusOr<void*> status_or_file =
api.png_fdopen(fd.GetRemoteFd(), rb_var.PtrBefore());
ASSERT_THAT(status_or_file, IsOk());
sapi::v::RemotePtr file(status_or_file.value());
ASSERT_THAT(file.GetValue(), NotNull()) << "Could not open " << infile;
sapi::v::Array<char> header(8);
ASSERT_THAT(api.png_fread(header.PtrBoth(), 1, header.GetSize(), &file),
IsOk());
absl::StatusOr<int> status_or_int =
api.png_sig_cmp(header.PtrBoth(), 0, header.GetSize());
ASSERT_THAT(status_or_int, IsOk());
ASSERT_THAT(status_or_int.value(), Eq(0)) << infile << " is not a PNG file";
sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
sapi::v::NullPtr null = sapi::v::NullPtr();
absl::StatusOr<png_structp> status_or_png_structp =
api.png_create_read_struct_wrapper(ver_string_var.PtrBefore(), &null);
ASSERT_THAT(status_or_png_structp, IsOk());
sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
ASSERT_THAT(struct_ptr.GetValue(), NotNull())
<< "png_create_read_struct_wrapper failed";
absl::StatusOr<png_infop> status_or_png_infop =
api.png_create_info_struct(&struct_ptr);
ASSERT_THAT(status_or_png_infop, IsOk());
sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
ASSERT_THAT(info_ptr.GetValue(), NotNull())
<< "png_create_info_struct failed";
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
ASSERT_THAT(api.png_init_io_wrapper(&struct_ptr, &file), IsOk());
ASSERT_THAT(api.png_set_sig_bytes(&struct_ptr, header.GetSize()), IsOk());
ASSERT_THAT(api.png_read_info(&struct_ptr, &info_ptr), IsOk());
status_or_int = api.png_get_image_width(&struct_ptr, &info_ptr);
ASSERT_THAT(status_or_int, IsOk());
data.width = status_or_int.value();
EXPECT_THAT(data.width, Gt(0));
status_or_int = api.png_get_image_height(&struct_ptr, &info_ptr);
ASSERT_THAT(status_or_int, IsOk());
data.height = status_or_int.value();
EXPECT_THAT(data.height, Gt(0));
absl::StatusOr<uint8_t> status_or_uchar =
api.png_get_color_type(&struct_ptr, &info_ptr);
ASSERT_THAT(status_or_uchar, IsOk());
data.color_type = status_or_uchar.value();
status_or_uchar = api.png_get_bit_depth(&struct_ptr, &info_ptr);
ASSERT_THAT(status_or_uchar, IsOk());
data.bit_depth = status_or_uchar.value();
status_or_int = api.png_set_interlace_handling(&struct_ptr);
ASSERT_THAT(status_or_int, IsOk());
data.number_of_passes = status_or_int.value();
ASSERT_THAT(api.png_read_update_info(&struct_ptr, &info_ptr), IsOk());
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
absl::StatusOr<uint32_t> status_or_uint =
api.png_get_rowbytes(&struct_ptr, &info_ptr);
ASSERT_THAT(status_or_uint, IsOk());
data.rowbytes = status_or_uint.value();
EXPECT_THAT(data.rowbytes, Ge(data.width));
data.row_pointers =
std::make_unique<sapi::v::Array<uint8_t>>(data.height * data.rowbytes);
ASSERT_THAT(
api.png_read_image_wrapper(&struct_ptr, data.row_pointers->PtrAfter(),
data.height, data.rowbytes),
IsOk());
ASSERT_THAT(api.png_fclose(&file), IsOk());
}
void WritePng(LibPNGApi& api, absl::string_view outfile, Data& data) {
sapi::v::Fd fd(open(outfile.data(), O_WRONLY));
ASSERT_THAT(fd.GetValue(), Ge(0)) << "Error opening output file";
ASSERT_THAT((&api)->sandbox()->TransferToSandboxee(&fd), IsOk());
ASSERT_THAT(fd.GetRemoteFd(), Ge(0)) << "Error receiving remote FD";
sapi::v::ConstCStr wb_var("wb");
absl::StatusOr<void*> status_or_file =
api.png_fdopen(fd.GetRemoteFd(), wb_var.PtrBefore());
ASSERT_THAT(status_or_file, IsOk());
sapi::v::RemotePtr file(status_or_file.value());
ASSERT_THAT(file.GetValue(), NotNull()) << "Could not open " << outfile;
sapi::v::ConstCStr ver_string_var(PNG_LIBPNG_VER_STRING);
sapi::v::NullPtr null = sapi::v::NullPtr();
absl::StatusOr<png_structp> status_or_png_structp =
api.png_create_write_struct_wrapper(ver_string_var.PtrBefore(), &null);
ASSERT_THAT(status_or_png_structp, IsOk());
sapi::v::RemotePtr struct_ptr(status_or_png_structp.value());
ASSERT_THAT(struct_ptr.GetValue(), NotNull())
<< "png_create_write_struct_wrapper failed";
absl::StatusOr<png_infop> status_or_png_infop =
api.png_create_info_struct(&struct_ptr);
ASSERT_THAT(status_or_png_infop, IsOk());
sapi::v::RemotePtr info_ptr(status_or_png_infop.value());
ASSERT_THAT(info_ptr.GetValue(), NotNull())
<< "png_create_info_struct failed";
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
ASSERT_THAT(api.png_init_io_wrapper(&struct_ptr, &file), IsOk());
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
ASSERT_THAT(
api.png_set_IHDR(&struct_ptr, &info_ptr, data.width, data.height,
data.bit_depth, data.color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE),
IsOk());
ASSERT_THAT(api.png_write_info(&struct_ptr, &info_ptr), IsOk());
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
ASSERT_THAT(
api.png_write_image_wrapper(&struct_ptr, data.row_pointers->PtrBefore(),
data.height, data.rowbytes),
IsOk());
ASSERT_THAT(api.png_setjmp(&struct_ptr), IsOk());
ASSERT_THAT(api.png_write_end(&struct_ptr, &null), IsOk());
ASSERT_THAT(api.png_fclose(&file), IsOk());
}
TEST(SandboxTest, ReadModifyWrite) {
std::string infile = GetFilePath("red_ball.png");
std::string outfile = GetFilePath("test_output.png");
LibPNGSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk());
LibPNGApi api(&sandbox);
Data data;
ReadPng(api, infile, data);
ASSERT_THAT(data.color_type == PNG_COLOR_TYPE_RGBA ||
data.color_type == PNG_COLOR_TYPE_RGB,
IsTrue())
<< infile << " has unexpected color type. Expected RGB or RGBA";
size_t channel_count = 3;
if (data.color_type == PNG_COLOR_TYPE_RGBA) {
channel_count = 4;
}
EXPECT_THAT(channel_count * data.width, Eq(data.rowbytes));
// RGB to BGR
for (size_t i = 0; i != data.height; ++i) {
for (size_t j = 0; j != data.width; ++j) {
uint8_t r = (*data.row_pointers)[i * data.rowbytes + j * channel_count];
uint8_t b =
(*data.row_pointers)[i * data.rowbytes + j * channel_count + 2];
(*data.row_pointers)[i * data.rowbytes + j * channel_count] = b;
(*data.row_pointers)[i * data.rowbytes + j * channel_count + 2] = r;
}
}
WritePng(api, outfile, data);
Data result;
ReadPng(api, outfile, result);
EXPECT_THAT(result.height, Eq(data.height));
EXPECT_THAT(result.width, Eq(data.width));
EXPECT_THAT(result.color_type, Eq(data.color_type));
EXPECT_THAT(result.rowbytes, Eq(data.rowbytes));
EXPECT_THAT(result.bit_depth, Eq(data.bit_depth));
EXPECT_THAT(result.number_of_passes, Eq(data.number_of_passes));
EXPECT_THAT(absl::MakeSpan(result.row_pointers->GetData(),
result.row_pointers->GetSize()),
ContainerEq(absl::MakeSpan(data.row_pointers->GetData(),
data.row_pointers->GetSize())));
}
} // namespace

View File

@ -0,0 +1,24 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "helper.h" // NOLINT(build/include)
#include "../sandboxed.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/path.h"
std::string GetSourcePath() { return getenv("TEST_SRCDIR"); }
std::string GetFilePath(absl::string_view filename) {
return sandbox2::file::JoinPath(GetSourcePath(), "images", filename);
}

View File

@ -0,0 +1,24 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBPNG_TESTS_HELPER_H_
#define LIBPNG_TESTS_HELPER_H_
#include <string>
#include "absl/strings/str_view.h"
std::string GetFilePath(absl::string_view filename);
#endif // LIBPNG_TESTS_HELPER_H_

View File

@ -0,0 +1,96 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBPNG_TESTS_LIBPNG_H_
#define LIBPNG_TESTS_LIBPNG_H_
// Defines from libpng library. The problem is that the build throws the error
// "Duplicate functions" if #include <png.h> is added.
#define PNG_FORMAT_FLAG_ALPHA 0x01U
#define PNG_FORMAT_FLAG_COLOR 0x02U
#define PNG_FORMAT_FLAG_LINEAR 0x04U
#define PNG_FORMAT_FLAG_COLORMAP 0x08U
#ifdef PNG_FORMAT_BGR_SUPPORTED
#define PNG_FORMAT_FLAG_BGR 0x10U
#endif
#ifdef PNG_FORMAT_AFIRST_SUPPORTED
#define PNG_FORMAT_FLAG_AFIRST 0x20U
#endif
#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U
#define PNG_FORMAT_GRAY 0
#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA
#define PNG_FORMAT_AG (PNG_FORMAT_GA | PNG_FORMAT_FLAG_AFIRST)
#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR
#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_BGR)
#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB | PNG_FORMAT_FLAG_ALPHA)
#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA | PNG_FORMAT_FLAG_AFIRST)
#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR | PNG_FORMAT_FLAG_ALPHA)
#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA | PNG_FORMAT_FLAG_AFIRST)
#define PNG_IMAGE_VERSION 1
#define PNG_IMAGE_PIXEL_(test, fmt) \
(((fmt)&PNG_FORMAT_FLAG_COLORMAP) ? 1 : test(fmt))
#define PNG_IMAGE_SAMPLE_CHANNELS(fmt) \
(((fmt) & (PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA)) + 1)
#define PNG_IMAGE_PIXEL_CHANNELS(fmt) \
PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS, fmt)
#define PNG_IMAGE_ROW_STRIDE(image) \
(PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width)
#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt) \
((((fmt)&PNG_FORMAT_FLAG_LINEAR) >> 2) + 1)
#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt) \
PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE, fmt)
#define PNG_IMAGE_BUFFER_SIZE(image, row_stride) \
(PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format) * (image).height * \
(row_stride))
#define PNG_IMAGE_SIZE(image) \
PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image))
typedef uint8_t *png_bytep;
#if UINT_MAX == 65535
typedef unsigned int png_uint_16;
#elif USHRT_MAX == 65535
typedef unsigned short png_uint_16; // NOLINT(runtime/int)
#else
#error "libpng requires an unsigned 16-bit type"
#endif
#define PNG_LIBPNG_VER_STRING "1.6.38.git"
#define PNG_COLOR_MASK_COLOR 2
#define PNG_COLOR_MASK_ALPHA 4
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA
#define PNG_FILTER_TYPE_BASE 0
#define PNG_COMPRESSION_TYPE_BASE 0
#define PNG_INTERLACE_NONE 0
#endif // LIBPNG_TESTS_LIBPNG_H_

View File

@ -0,0 +1,28 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_library(wrapper OBJECT
func.h
func.cc
)
set_target_properties(wrapper
PROPERTIES LINKER_LANGUAGE C
)
target_link_libraries(wrapper
PNG::PNG
sandbox2::temp_file
sapi::sapi
)

View File

@ -0,0 +1,64 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
void png_setjmp(png_structrp ptr) { setjmp(png_jmpbuf(ptr)); }
void* png_fdopen(int fd, const char* mode) {
FILE* f = fdopen(fd, mode);
return static_cast<void*>(f);
}
void png_rewind(void* f) { rewind(static_cast<FILE*>(f)); }
void png_fread(void* buffer, size_t size, size_t count, void* stream) {
fread(buffer, size, count, static_cast<FILE*>(stream));
}
void png_fclose(void* f) { fclose(static_cast<FILE*>(f)); }
void png_init_io_wrapper(png_structrp png_ptr, void* f) {
png_init_io(png_ptr, static_cast<FILE*>(f));
}
png_structp png_create_read_struct_wrapper(png_const_charp user_png_ver,
png_voidp error_ptr) {
return png_create_read_struct(user_png_ver, error_ptr, NULL, NULL);
}
png_structp png_create_write_struct_wrapper(png_const_charp user_png_ver,
png_voidp error_ptr) {
return png_create_write_struct(user_png_ver, error_ptr, NULL, NULL);
}
void png_read_image_wrapper(png_structrp png_ptr, png_bytep image,
size_t height, size_t rowbytes) {
png_bytep* ptrs = (png_bytep*)malloc(height * sizeof(png_bytep));
for (size_t i = 0; i != height; ++i) {
ptrs[i] = image + (i * rowbytes);
}
png_read_image(png_ptr, ptrs);
free(ptrs);
}
void png_write_image_wrapper(png_structrp png_ptr, png_bytep image,
size_t height, size_t rowbytes) {
png_bytep* ptrs = (png_bytep*)malloc(height * sizeof(png_bytep));
for (size_t i = 0; i != height; ++i) {
ptrs[i] = image + (i * rowbytes);
}
png_write_image(png_ptr, ptrs);
free(ptrs);
}

View File

@ -0,0 +1,39 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBPNG_WRAPPER_FUNC_H_
#define LIBPNG_WRAPPER_FUNC_H_
#include "png.h" // NOLINT(build/include)
extern "C" {
void* png_fdopen(int fd, const char* mode);
void png_rewind(void* f);
void png_fread(void* buffer, size_t size, size_t count, void* stream);
void png_fclose(void* f);
void png_setjmp(png_structrp ptr);
png_structp png_create_read_struct_wrapper(png_const_charp user_png_ver,
png_voidp error_ptr);
png_structp png_create_write_struct_wrapper(png_const_charp user_png_ver,
png_voidp error_ptr);
void png_init_io_wrapper(png_structrp png_ptr, void* f);
void png_read_image_wrapper(png_structrp png_ptr, png_bytep image,
size_t height, size_t rowbytes);
void png_write_image_wrapper(png_structrp png_ptr, png_bytep image,
size_t height, size_t rowbytes);
} // extern "C"
#endif // LIBPNG_WRAPPER_FUNC_H_