Merge branch 'main' into c-blosc

This commit is contained in:
Christian Blichmann 2022-02-16 10:02:08 +01:00 committed by GitHub
commit c93dae9519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 3593 additions and 219 deletions

View File

@ -13,36 +13,55 @@ jobs:
include:
- container: fedora:35
compiler: gcc # GCC 11
ignore-errors: false
# TODO(cblichmann): Add clang-13 build to matrix (currently fails)
ignore-errors: true # Stack trace test fails on Fedora (issue #118)
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.ignore-errors }}
container:
image: ${{ matrix.container }}
env:
RUN_CMD: docker exec --tty ${{matrix.compiler}}-build-container
steps:
- uses: actions/checkout@v2
- name: Prepare container
# Note: For the sandbox tests to work, we need a privileged, unconfined
# container that retains its capabilities.
run: |
docker run --name ${{matrix.compiler}}-build-container \
--tty \
--privileged \
--cap-add ALL \
--security-opt apparmor:unconfined \
-v $GITHUB_WORKSPACE:$GITHUB_WORKSPACE \
-e TERM=dumb \
-e BUILD_TYPE \
-e GITHUB_WORKSPACE \
-d ${{matrix.container}} \
sleep infinity
- name: Install build tools
run: |
dnf update -y
dnf install -y git make automake patch glibc-static libstdc++-static \
cmake ninja-build python3 python3-pip clang-devel libcap-devel
- name: Install/configure Clang compiler toolchain
if: matrix.compiler == 'clang'
run: |
echo "CXX=clang++" >> $GITHUB_ENV
echo "CC=clang" >> $GITHUB_ENV
$RUN_CMD dnf update -y --quiet
$RUN_CMD dnf install -y --quiet git make automake patch glibc-static \
libstdc++-static cmake ninja-build python3 python3-pip clang-devel \
libcap-devel
- name: Create Build Environment
run: |
pip3 install absl-py clang
cmake -E make_directory $GITHUB_WORKSPACE/build
$RUN_CMD pip3 install --progress-bar=off absl-py clang
$RUN_CMD cmake -E make_directory $GITHUB_WORKSPACE/build
- name: Configure CMake
run: cmake $GITHUB_WORKSPACE -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
run: |
$RUN_CMD cmake -S $GITHUB_WORKSPACE -B $GITHUB_WORKSPACE/build \
-G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
run: cmake --build $GITHUB_WORKSPACE --config $BUILD_TYPE
run: |
$RUN_CMD cmake --build $GITHUB_WORKSPACE/build --config $BUILD_TYPE
- name: Test
run: |
$RUN_CMD ctest --test-dir $GITHUB_WORKSPACE/build -C $BUILD_TYPE \
--output-on-failure \
-R SapiTest

View File

@ -62,17 +62,17 @@ jobs:
- name: Create Build Environment
run: |
pip3 install absl-py clang
cmake -E make_directory ${{runner.workspace}}/build
cmake -E make_directory $GITHUB_WORKSPACE/build
- name: Configure CMake
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
run: |
cmake $GITHUB_WORKSPACE -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config $BUILD_TYPE
run: |
cmake --build $GITHUB_WORKSPACE --config $BUILD_TYPE
# TODO(cblichmann): Before enabling this, make sure all OSS tests pass
#- name: Test
# working-directory: ${{runner.workspace}}/build
# run: ctest -C $BUILD_TYPE
- name: Test
run: |
ctest $GITHUB_WORKSPACE -C $BUILD_TYPE --output-on-failure \
-R SapiTest

3
.gitmodules vendored
View File

@ -4,9 +4,6 @@
[submodule "oss-internship-2020/openjpeg/openjpeg"]
path = oss-internship-2020/openjpeg/openjpeg
url = https://github.com/uclouvain/openjpeg.git
[submodule "oss-internship-2020/pffft/master"]
path = oss-internship-2020/pffft/master
url = https://bitbucket.org/jpommier/pffft/src/master/
[submodule "oss-internship-2020/gdal/gdal"]
path = oss-internship-2020/gdal/gdal
url = https://github.com/OSGeo/gdal/

View File

@ -100,8 +100,7 @@ function(add_sapi_library)
list(APPEND _sapi_exported_funcs "LINKER:--export-dynamic-symbol,${func}")
endforeach()
if(NOT _sapi_exported_funcs)
set(_sapi_exported_funcs LINKER:--whole-archive
LINKER:--allow-multiple-definition)
set(_sapi_exported_funcs LINKER:--allow-multiple-definition)
endif()
# The sandboxed binary
@ -112,7 +111,7 @@ function(add_sapi_library)
add_executable("${_sapi_bin}" "${_sapi_force_cxx_linkage}")
target_link_libraries("${_sapi_bin}" PRIVATE
-fuse-ld=gold
"${_sapi_LIBRARY}"
-Wl,--whole-archive "${_sapi_LIBRARY}" -Wl,--no-whole-archive
sapi::client
${CMAKE_DL_LIBS}
)

View File

@ -14,7 +14,12 @@
# Append to this list whenever a new sandboxed library is added to `contrib/`.
set(SAPI_CONTRIB_SANDBOXES
hunspell
jsonnet
libidn2
pffft
turbojpeg
zopfli
zstd
)

View File

@ -5,12 +5,16 @@ libraries.
## Projects Sandboxed
Directory | Project | Home Page | Integration
----------- | --------------------------------------------------------------- | -------------------------------------------------------------------- | -----------
`c-blosc/` | c-blosc A blocking, shuffling and loss-less compression library | [github.com/Blosc/c-blosc](https://github.com/Blosc/c-blosc) | CMake
`jsonnet/` | Jsonnet - The Data Templating Language | [github.com/google/jsonnet](https://github.com/google/jsonnet) | CMake
`hunspell/` | Hunspell - The most popular spellchecking library | [github.com/hunspell/hunspell](https://github.com/hunspell/hunspell) | CMake
`zstd/` | Zstandard - Fast real-time compression algorithm | [github.com/facebook/zstd](https://github.com/facebook/zstd) | CMake
Directory | Project | Home Page | Integration
------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -----------
`c-blosc/` | c-blosc - A blocking, shuffling and loss-less compression library | [github.com/Blosc/c-blosc](https://github.com/Blosc/c-blosc) | CMake
`hunspell/` | Hunspell - The most popular spellchecking library | [github.com/hunspell/hunspell](https://github.com/hunspell/hunspell) | CMake
`jsonnet/` | Jsonnet - The Data Templating Language | [github.com/google/jsonnet](https://github.com/google/jsonnet) | CMake
`pffft/` | PFFFT - a pretty fast Fourier Transform | [bitbucket.org/jpommier/pffft.git](https://bitbucket.org/jpommier/pffft.git) | CMake
`zopfli` | Zopfli - Compression Algorithm | [github.com/google/zopfli](https://github.com/google/zopfli) | CMake
`zstd/` | Zstandard - Fast real-time compression algorithm | [github.com/facebook/zstd](https://github.com/facebook/zstd) | CMake
`libidn2/` | libidn2 - GNU IDN library | [www.gnu.org/software/libidn/#libidn2](https://www.gnu.org/software/libidn/#libidn2) | CMake
`turbojpeg/` | High-level JPEG library | [libjpeg-turbo.org/About/TurboJPEG](https://libjpeg-turbo.org/About/TurboJPEG) | CMake
## Projects Shipping with Sandboxed API Sandboxes

View File

@ -21,15 +21,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory(
"${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
libhunspell
if(NOT TARGET sapi::sapi)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
endif()
FetchContent_Declare(libhunspell
GIT_REPOSITORY https://github.com/hunspell/hunspell.git
GIT_TAG 31e6d6323026a3bef12c5912ce032d88bfef2091
)
@ -44,13 +43,23 @@ if(NOT libhunspell_POPULATED)
if(NOT _sapi_CONFIG_STATUS STREQUAL "${libhunspell_CONFIG_STATUS}")
message("-- Configuring libhunspell...")
execute_process(
COMMAND autoreconf -vfi
COMMAND ./configure --disable-dependency-tracking
COMMAND autoreconf -i
WORKING_DIRECTORY "${libhunspell_SOURCE_DIR}"
RESULT_VARIABLE libhunspell_config_result
RESULT_VARIABLE _sapi_libhunspell_autoreconf_result
)
if(NOT libhunspell_config_result EQUAL "0")
message(FATAL_ERROR "Configuration for libhunspell failed")
if(NOT _sapi_libhunspell_autoreconf_result EQUAL "0")
message(FATAL_ERROR "Configuration for libhunspell failed: "
"${_sapi_libhunspell_autoreconf_result}")
endif()
execute_process(
COMMAND ./configure --disable-dependency-tracking
--quiet
WORKING_DIRECTORY "${libhunspell_SOURCE_DIR}"
RESULT_VARIABLE _sapi_libhunspell_config_result
)
if(NOT _sapi_libhunspell_config_result EQUAL "0")
message(FATAL_ERROR "Configuration for libhunspell failed: "
"${_sapi_libhunspell_config_result}")
endif()
file(SHA256 "${libhunspell_SOURCE_DIR}/config.status" _sapi_CONFIG_STATUS)
set(libhunspell_CONFIG_STATUS "${_sapi_CONFIG_STATUS}" CACHE INTERNAL "")
@ -58,37 +67,37 @@ if(NOT libhunspell_POPULATED)
endif()
add_library(hunspell STATIC
${libhunspell_SOURCE_DIR}/src/hunspell/affentry.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/affentry.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/affixmgr.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/affixmgr.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/atypes.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/baseaffix.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/csutil.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/csutil.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/filemgr.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/filemgr.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/hashmgr.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/hashmgr.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/htypes.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.h
${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/hunzip.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/hunzip.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/langnum.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/phonet.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/phonet.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/replist.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/replist.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/suggestmgr.cxx
${libhunspell_SOURCE_DIR}/src/hunspell/suggestmgr.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/utf_info.hxx
${libhunspell_SOURCE_DIR}/src/hunspell/w_char.hxx
"${libhunspell_SOURCE_DIR}/src/hunspell/affentry.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/affentry.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/affixmgr.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/affixmgr.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/atypes.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/baseaffix.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/csutil.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/csutil.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/filemgr.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/filemgr.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hashmgr.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hashmgr.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/htypes.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.h"
"${libhunspell_SOURCE_DIR}/src/hunspell/hunspell.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hunzip.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/hunzip.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/langnum.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/phonet.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/phonet.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/replist.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/replist.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/suggestmgr.cxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/suggestmgr.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/utf_info.hxx"
"${libhunspell_SOURCE_DIR}/src/hunspell/w_char.hxx"
)
target_include_directories(hunspell PUBLIC
${libhunspell_SOURCE_DIR}/src/hunspell
"${libhunspell_SOURCE_DIR}/src/hunspell"
)
set(libhunspell_INCLUDE_DIR "${libhunspell_SOURCE_DIR}/src/hunspell")
@ -112,7 +121,7 @@ add_sapi_library(
Hunspell_free_list
INPUTS
${libhunspell_INCLUDE_DIR}/hunspell.h
"${libhunspell_INCLUDE_DIR}/hunspell.h"
LIBRARY hunspell
LIBRARY_NAME Hunspell

View File

@ -35,6 +35,7 @@ class HunspellSapiSandbox : public HunspellSandbox {
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowGetPIDs()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({

View File

@ -1,6 +1,9 @@
# Jsonnet Sandboxed API
This library provides a sandboxed version of the
This library was sandboxed as part of Google's summer 2020 internship program
([blog post](https://security.googleblog.com/2020/12/improving-open-source-security-during.html)).
This directory contains a sandbox for the
[Jsonnet](https://github.com/google/jsonnet) library.
## How to use from an existing Project

View File

@ -12,50 +12,48 @@
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.13..3.22)
project(pffft CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if(NOT TARGET sapi::sapi)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
endif()
include(CheckLibraryExists)
FetchContent_Declare(pffft
GIT_REPOSITORY https://bitbucket.org/jpommier/pffft.git
GIT_TAG 988259a41d1522047a9420e6265a6ba8289c1654 # 2021-12-02
)
FetchContent_MakeAvailable(pffft)
add_library(pffft STATIC
master/pffft.c
master/pffft.h
master/fftpack.c
master/fftpack.h
"${pffft_SOURCE_DIR}/pffft.c"
"${pffft_SOURCE_DIR}/pffft.h"
"${pffft_SOURCE_DIR}/fftpack.c"
"${pffft_SOURCE_DIR}/fftpack.h"
)
add_executable(pffft_main
master/test_pffft.c
"${pffft_SOURCE_DIR}/test_pffft.c"
)
target_link_libraries(pffft_main PRIVATE
pffft
)
set(MATH_LIBS "")
include(CheckLibraryExists)
check_library_exists(m sin "" LIBM)
if(LIBM)
list(APPEND MATH_LIBS "m")
check_library_exists(m sin "" _sapi_HAVE_LIBM)
if(_sapi_HAVE_LIBM)
target_link_libraries(pffft PUBLIC
m
)
endif()
target_link_libraries(pffft PUBLIC ${MATH_LIBS})
# Adding dependencies
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
# Then configure:
# mkdir -p build && cd build
# cmake .. -G Ninja -DSAPI_ROOT=$HOME/sapi_root
set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "")
set(SAPI_ENABLE_TESTS OFF CACHE BOOL "")
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(pffft_sapi
FUNCTIONS pffft_new_setup
pffft_destroy_setup
@ -83,22 +81,23 @@ add_sapi_library(pffft_sapi
sinti
sint
INPUTS master/pffft.h master/fftpack.h
INPUTS "${pffft_SOURCE_DIR}/pffft.h"
"${pffft_SOURCE_DIR}/fftpack.h"
LIBRARY pffft
LIBRARY_NAME Pffft
NAMESPACE ""
)
add_library(sapi_contrib::pffft ALIAS pffft_sapi)
target_include_directories(pffft_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
"${SAPI_SOURCE_DIR}"
)
add_executable(pffft_sandboxed
main_pffft_sandboxed.cc
)
target_link_libraries(pffft_sandboxed PRIVATE
pffft_sapi
sapi_contrib::pffft
sapi::sapi
)

View File

@ -1,16 +1,35 @@
# Sandboxing PFFFT library
This library was sandboxed as part of Google's summer 2020 internship program
([blog post](https://security.googleblog.com/2020/12/improving-open-source-security-during.html)).
Build System: CMake
OS: Linux
### Check out the PFFFT library & CMake set up
```
git submodule update --init --recursive
### How to use from an existing Project
mkdir -p build && cd build
cmake .. -G Ninja -DPFFFT_ROOT_DIR=$PWD
ninjas
If your project does not include Sandboxed API as a dependency yet, add the
following lines to the main `CMakeLists.txt`:
```cmake
include(FetchContent)
FetchContent_Declare(sandboxed-api
GIT_REPOSITORY https://github.com/google/sandboxed-api
GIT_TAG main # Or pin a specific commit/tag
)
FetchContent_MakeAvailable(sandboxed-api) # CMake 3.14 or higher
add_sapi_subdirectory(contrib/pffft)
```
The `add_sapi_subdirectory()` macro sets up the source and binary directories
for the sandboxed jsonnet targets.
Afterwards your project's code can link to `sapi_contrib::pffft` and use the
generated header `pffft_sapi.sapi.h`. An example sandbox policy can be found
in `main_pffft_sandboxed.cc`.
### For testing:
`cd build`, then `./pffft_sandboxed`
@ -19,14 +38,15 @@ display custom info with
`./pffft_sandboxed --logtostderr`
## ***About the project***
*PFFFT library is concerned with 1D Fast-Fourier Transformations finding a
PFFFT library is concerned with 1D Fast-Fourier Transformations finding a
compromise between accuracy and speed. It deals with real and complex
vectors, both cases being illustrated in the testing part (`test_pffft.c`
for initially and original version, `main_pffft_sandboxed.cc` for our
currently implemented sandboxed version).
The original files can be found at: https://bitbucket.org/jpommier/pffft/src.*
*The purpose of sandboxing is to limit the permissions and capabilities of
The purpose of sandboxing is to limit the permissions and capabilities of
librarys methods, in order to secure the usage of them.
After obtaining the sandbox, the functions will be called through an
Sandbox API (being called `api` in the current test) and so, the
@ -50,10 +70,12 @@ Without using this type of argument when running, the output format is set
by default.*
#### CMake observations resume:
* linking pffft and fftpack (which contains necessary functions for pffft)
* set math library
#### Sandboxed main observations resume:
* containing two testing parts (fft / pffft benchmarks)
* showing the performance of the transformations implies
testing them through various FFT dimenstions.

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gflags/gflags.h>
#include <syscall.h>
#include <cmath>
#include <cstdio>
@ -21,6 +21,7 @@
#include <ctime>
#include <glog/logging.h>
#include "gflags/gflags.h"
#include "pffft_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/vars.h"

View File

@ -0,0 +1,44 @@
# Copyright 2022 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
#
# https://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.13..3.22)
project(turbojpeg-sapi CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if(NOT TARGET sapi::sapi)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(TURBOJPEG REQUIRED IMPORTED_TARGET libturbojpeg)
add_sapi_library(turbojpeg_sapi
INPUTS "${TURBOJPEG_INCLUDEDIR}/turbojpeg.h"
LIBRARY turbojpeg
LIBRARY_NAME TurboJPEG
NAMESPACE "turbojpeg_sapi"
)
add_library(sapi_contrib::turbojpeg ALIAS turbojpeg_sapi)
target_include_directories(turbojpeg_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
if(SAPI_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -0,0 +1,26 @@
# Copyright 2022 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
#
# https://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)
add_executable(turbojpeg_sapi_test turbojpeg_sapi_test.cc)
target_link_libraries(turbojpeg_sapi_test PRIVATE
turbojpeg_sapi
sapi::base
gtest
gmock
)
gtest_discover_tests(turbojpeg_sapi_test PROPERTIES ENVIRONMENT "TEST_FILES_DIR=${PROJECT_SOURCE_DIR}/tests")

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

View File

@ -0,0 +1,181 @@
// Copyright 2022 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
//
// https://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.
#define _GNU_SOURCE 1
#include "../turbojpeg_sapi.h" // NOLINT(build/include)
#include <turbojpeg.h>
#include <cerrno>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "sandboxed_api/testing.h"
#include "sandboxed_api/util/fileops.h"
#include "sandboxed_api/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
#include "turbojpeg_sapi.sapi.h" // NOLINT(build/include)
namespace {
using ::sapi::IsOk;
using ::testing::Eq;
using ::testing::Gt;
using ::testing::Not;
using ::testing::NotNull;
using ::testing::StrEq;
class TurboJpegSapiSandboxTest : public testing::Test {
protected:
static void SetUpTestSuite() {
ASSERT_THAT(getenv("TEST_FILES_DIR"), NotNull());
sandbox_ = new TurboJpegSapiSandbox();
ASSERT_THAT(sandbox_->Init(), IsOk());
api_ = new turbojpeg_sapi::TurboJPEGApi(sandbox_);
}
static void TearDownTestSuite() {
delete api_;
delete sandbox_;
}
static std::string GetTurboJpegErrorStr(sapi::v::Ptr* handle) {
auto errmsg_ptr = api_->tjGetErrorStr2(handle);
if (!errmsg_ptr.ok()) return "Error getting error message";
auto errmsg =
sandbox_->GetCString(sapi::v::RemotePtr(errmsg_ptr.value()), 256);
if (!errmsg.ok()) return "Error getting error message";
return errmsg.value();
}
static turbojpeg_sapi::TurboJPEGApi* api_;
static TurboJpegSapiSandbox* sandbox_;
};
turbojpeg_sapi::TurboJPEGApi* TurboJpegSapiSandboxTest::api_;
TurboJpegSapiSandbox* TurboJpegSapiSandboxTest::sandbox_;
std::string GetTestFilePath(const std::string& filename) {
return sapi::file::JoinPath(getenv("TEST_FILES_DIR"), filename);
}
std::streamsize GetStreamSize(std::ifstream& stream) {
stream.seekg(0, std::ios_base::end);
std::streamsize ssize = stream.tellg();
stream.seekg(0, std::ios_base::beg);
return ssize;
}
absl::StatusOr<std::vector<uint8_t>> ReadFile(const std::string& in_file,
size_t expected_size = SIZE_MAX) {
std::ifstream f(GetTestFilePath(in_file));
if (!f.is_open()) {
return absl::UnavailableError("File could not be opened");
}
std::streamsize ssize = GetStreamSize(f);
if (expected_size != SIZE_MAX && ssize != expected_size) {
return absl::UnavailableError("Incorrect size of file");
}
std::vector<uint8_t> inbuf(ssize);
f.read(reinterpret_cast<char*>(inbuf.data()), ssize);
if (ssize != f.gcount()) {
return absl::UnavailableError("Premature end of file");
}
if (f.fail() || f.eof()) {
return absl::UnavailableError("Error reading file");
}
return inbuf;
}
TEST_F(TurboJpegSapiSandboxTest, Compressor) {
absl::StatusOr<void*> compression_handle_raw = api_->tjInitCompress();
ASSERT_THAT(compression_handle_raw, IsOk());
ASSERT_THAT(compression_handle_raw.value(), NotNull());
sapi::v::RemotePtr compression_handle{compression_handle_raw.value()};
auto result = ReadFile("sample.rgb", 12 * 67 * 3);
ASSERT_THAT(result, IsOk());
sapi::v::Array array(result->data(), result->size());
sapi::v::GenericPtr buffer;
{
sapi::v::ULong length{0};
auto result = api_->tjCompress2(&compression_handle, array.PtrBefore(), 12,
36, 67, TJPF_RGB, buffer.PtrAfter(),
length.PtrBoth(), TJSAMP_444, 10, 0);
ASSERT_THAT(result, IsOk());
ASSERT_THAT(result.value(), Eq(0))
<< "Error from sandboxee: "
<< GetTurboJpegErrorStr(&compression_handle);
ASSERT_TRUE(buffer.GetValue());
ASSERT_TRUE(buffer.GetRemote());
ASSERT_THAT(length.GetValue(), Gt(0));
}
auto value = buffer.GetValue();
auto destroy_result = api_->tjDestroy(&compression_handle);
ASSERT_THAT(destroy_result, IsOk());
ASSERT_THAT(destroy_result.value(), Eq(0));
}
TEST_F(TurboJpegSapiSandboxTest, Decompressor) {
absl::StatusOr<void*> decompression_handle_raw = api_->tjInitDecompress();
ASSERT_THAT(decompression_handle_raw, IsOk());
ASSERT_THAT(decompression_handle_raw.value(), NotNull());
sapi::v::RemotePtr decompression_handle{decompression_handle_raw.value()};
auto result = ReadFile("sample.jpeg");
ASSERT_THAT(result, IsOk());
sapi::v::Array array(result->data(), result->size());
sapi::v::Int width{0};
sapi::v::Int height{0};
sapi::v::Int subsamp{0};
sapi::v::Int colorspace{0};
auto decompress_result = api_->tjDecompressHeader3(
&decompression_handle, array.PtrBefore(), result->size(),
width.PtrAfter(), height.PtrAfter(), subsamp.PtrAfter(),
colorspace.PtrAfter());
ASSERT_THAT(decompress_result, IsOk());
ASSERT_THAT(decompress_result.value(), Eq(0))
<< "Error from sandboxee: "
<< GetTurboJpegErrorStr(&decompression_handle);
ASSERT_THAT(width.GetValue(), Eq(67));
ASSERT_THAT(height.GetValue(), Eq(12));
ASSERT_THAT(subsamp.GetValue(), Eq(TJSAMP_GRAY));
ASSERT_THAT(colorspace.GetValue(), Eq(TJCS_GRAY));
auto arr = sapi::v::Array<unsigned char>(12 * 67 * 3);
decompress_result = api_->tjDecompress2(
&decompression_handle, array.PtrBefore(), result->size(), arr.PtrAfter(),
12, 36, 67, TJCS_RGB, 0);
ASSERT_THAT(decompress_result, IsOk());
EXPECT_THAT(decompress_result.value(), Eq(0))
<< "Error from sandboxee: "
<< GetTurboJpegErrorStr(&decompression_handle);
decompress_result = api_->tjDestroy(&decompression_handle);
ASSERT_THAT(decompress_result, IsOk());
ASSERT_THAT(decompress_result.value(), Eq(0));
}
} // namespace
int main(int argc, char* argv[]) {
::google::InitGoogleLogging(program_invocation_short_name);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,44 @@
// Copyright 2022 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
//
// https://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 CONTRIB_TURBOJPEG_TURBOJPEG_SAPI_H_
#define CONTRIB_TURBOJPEG_TURBOJPEG_SAPI_H_
#include <syscall.h>
#include "sandboxed_api/util/fileops.h"
#include "turbojpeg_sapi.sapi.h" // NOLINT(build/include)
class TurboJpegSapiSandbox : public turbojpeg_sapi::TurboJPEGSandbox {
public:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowSystemMalloc()
.AllowRead()
.AllowStat()
.AllowWrite()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
__NR_lseek,
__NR_getpid,
__NR_clock_gettime,
})
.AllowLlvmSanitizers()
.BuildOrDie();
}
};
#endif // CONTRIB_TURBOJPEG_TURBOJPEG_SAPI_H_

View File

@ -0,0 +1,68 @@
# Copyright 2022 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
#
# https://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.13..3.22)
project(sapi_zopfli CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if(NOT TARGET sapi::sapi)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
endif()
FetchContent_Declare(zopfli
GIT_REPOSITORY https://github.com/google/zopfli.git
GIT_TAG 831773bc28e318b91a3255fa12c9fcde1606058b
)
FetchContent_MakeAvailable(zopfli)
add_sapi_library(
sapi_zopfli
FUNCTIONS
ZopfliInitOptions
ZopfliCompress
ZopfliDeflate
ZopfliZlibCompress
ZopfliGzipCompress
INPUTS
${zopfli_SOURCE_DIR}/src/zopfli/deflate.h
${zopfli_SOURCE_DIR}/src/zopfli/gzip_container.h
${zopfli_SOURCE_DIR}/src/zopfli/zlib_container.h
LIBRARY Zopfli::libzopfli
LIBRARY_NAME Zopfli
NAMESPACE ""
)
add_library(sapi_contrib::zopfli ALIAS sapi_zopfli)
target_include_directories(sapi_zopfli INTERFACE
"${PROJECT_BINARY_DIR}"
"${SAPI_SOURCE_DIR}"
)
if(SAPI_ENABLE_EXAMPLES)
add_subdirectory(example)
endif()
if(SAPI_ENABLE_TESTS)
add_subdirectory(test)
endif()

View File

@ -0,0 +1,23 @@
# Copyright 2022 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
#
# https://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(sapi_minizopfli
main.cc
../utils/utils_zopfli.cc
)
target_link_libraries(sapi_minizopfli PRIVATE
sapi_contrib::zopfli
sapi::sapi
absl::flags_parse
)

View File

@ -0,0 +1,73 @@
// Copyright 2022 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
//
// https://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 <unistd.h>
#include <fstream>
#include <iostream>
#include <string>
#include "sandboxed_api/util/flag.h"
#include "absl/flags/parse.h"
#include "contrib/zopfli/sandboxed.h"
#include "contrib/zopfli/utils/utils_zopfli.h"
ABSL_FLAG(bool, zlib, false, "zlib compression");
ABSL_FLAG(bool, gzip, false, "gzip compression");
int main(int argc, char* argv[]) {
std::string prog_name(argv[0]);
google::InitGoogleLogging(argv[0]);
std::vector<char*> args = absl::ParseCommandLine(argc, argv);
if (args.size() != 3) {
std::cerr << "Usage:\n " << prog_name << " INPUT OUTPUT\n";
return EXIT_FAILURE;
}
std::ifstream infile(args[1], std::ios::binary);
if (!infile.is_open()) {
std::cerr << "Unable to open " << args[1] << std::endl;
return EXIT_FAILURE;
}
std::ofstream outfile(args[2], std::ios::binary);
if (!outfile.is_open()) {
std::cerr << "Unable to open " << args[2] << std::endl;
return EXIT_FAILURE;
}
ZopfliSapiSandbox sandbox;
if (!sandbox.Init().ok()) {
std::cerr << "Unable to start sandbox\n";
return EXIT_FAILURE;
}
ZopfliApi api(&sandbox);
ZopfliFormat format = ZOPFLI_FORMAT_DEFLATE;
if (absl::GetFlag(FLAGS_zlib)) {
format = ZOPFLI_FORMAT_ZLIB;
} else if (absl::GetFlag(FLAGS_gzip)) {
format = ZOPFLI_FORMAT_GZIP;
}
absl::Status status = Compress(api, infile, outfile, format);
if (!status.ok()) {
std::cerr << "Unable to compress file.\n";
std::cerr << status << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

BIN
contrib/zopfli/files/binary Normal file

Binary file not shown.

2000
contrib/zopfli/files/text Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
// Copyright 2022 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
//
// https://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 CONTRIB_ZOPFLI_SANDBOXED_
#define CONTRIB_ZOPFLI_SANDBOXED_
#include <libgen.h>
#include <syscall.h>
#include <cerrno>
#include <memory>
#include "sapi_zopfli.sapi.h" // NOLINT(build/include)
class ZopfliSapiSandbox : public ZopfliSandbox {
public:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder *) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowWrite()
.AllowExit()
.AllowMmap()
.AllowSystemMalloc()
.AllowSyscalls({
__NR_sysinfo,
})
#ifdef __NR_open
.BlockSyscallWithErrno(__NR_open, ENOENT)
#endif
#ifdef __NR_openat
.BlockSyscallWithErrno(__NR_openat, ENOENT)
#endif
.BuildOrDie();
}
};
#endif // CONTRIB_ZOPFLI_SANDBOXED_

View File

@ -0,0 +1,28 @@
# Copyright 2022 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
#
# https://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)
add_executable(sapi_zopfli_test
zopfli_test.cc
../utils/utils_zopfli.cc
)
target_link_libraries(sapi_zopfli_test PRIVATE
sapi_contrib::zopfli
sapi::temp_file
sapi::test_main
)
gtest_discover_tests(sapi_zopfli_test PROPERTIES
ENVIRONMENT "TEST_FILES_DIR=${PROJECT_SOURCE_DIR}/files"
)

View File

@ -0,0 +1,98 @@
// Copyright 2022 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
//
// https://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 <fstream>
#include "contrib/zopfli/sandboxed.h"
#include "contrib/zopfli/utils/utils_zopfli.h"
#include "sandboxed_api/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
#include "sandboxed_api/util/temp_file.h"
namespace {
using ::sapi::IsOk;
using ::testing::IsEmpty;
using ::testing::Not;
std::string GetTestFilePath(const std::string& filename) {
return sapi::file::JoinPath(getenv("TEST_FILES_DIR"), filename);
}
std::string GetTemporaryFile(const std::string& filename) {
absl::StatusOr<std::string> tmp_file =
sapi::CreateNamedTempFileAndClose(filename);
if (!tmp_file.ok()) {
return "";
}
return sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *tmp_file);
}
class TestText : public testing::TestWithParam<ZopfliFormat> {};
class TestBinary : public testing::TestWithParam<ZopfliFormat> {};
TEST_P(TestText, Compress) {
ZopfliSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZopfliApi api(&sandbox);
std::string infile_s = GetTestFilePath("text");
std::string outfile_s = GetTemporaryFile("text.out");
ASSERT_THAT(outfile_s, Not(IsEmpty()));
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
std::ofstream outfile(outfile_s, std::ios::binary);
ASSERT_TRUE(outfile.is_open());
absl::Status status = Compress(api, infile, outfile, GetParam());
ASSERT_THAT(status, IsOk()) << "Unable to compress file";
ASSERT_LT(outfile.tellp(), infile.tellg());
}
INSTANTIATE_TEST_SUITE_P(SandboxTest, TestText,
testing::Values(ZOPFLI_FORMAT_DEFLATE,
ZOPFLI_FORMAT_GZIP,
ZOPFLI_FORMAT_ZLIB));
TEST_P(TestBinary, Compress) {
ZopfliSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZopfliApi api(&sandbox);
std::string infile_s = GetTestFilePath("binary");
std::string outfile_s = GetTemporaryFile("binary.out");
ASSERT_THAT(outfile_s, Not(IsEmpty()));
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
std::ofstream outfile(outfile_s, std::ios::binary);
ASSERT_TRUE(outfile.is_open());
absl::Status status = Compress(api, infile, outfile, GetParam());
ASSERT_THAT(status, IsOk()) << "Unable to compress file";
ASSERT_LT(outfile.tellp(), infile.tellg());
}
INSTANTIATE_TEST_SUITE_P(SandboxTest, TestBinary,
testing::Values(ZOPFLI_FORMAT_DEFLATE,
ZOPFLI_FORMAT_GZIP,
ZOPFLI_FORMAT_ZLIB));
} // namespace

View File

@ -0,0 +1,56 @@
// Copyright 2022 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
//
// https://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 "contrib/zopfli/utils/utils_zopfli.h"
#include <unistd.h>
#include <fstream>
absl::Status Compress(ZopfliApi& api, std::ifstream& instream,
std::ofstream& outstream, ZopfliFormat format) {
// Get size of Stream
instream.seekg(0, std::ios_base::end);
std::streamsize ssize = instream.tellg();
instream.seekg(0, std::ios_base::beg);
// Read data
sapi::v::Array<uint8_t> inbuf(ssize);
instream.read(reinterpret_cast<char*>(inbuf.GetData()), ssize);
if (instream.gcount() != ssize) {
return absl::UnavailableError("Unable to read file");
}
// Compress
sapi::v::Struct<ZopfliOptions> options;
SAPI_RETURN_IF_ERROR(api.ZopfliInitOptions(options.PtrAfter()));
sapi::v::GenericPtr outptr;
sapi::v::IntBase<size_t> outsize(0);
SAPI_RETURN_IF_ERROR(
api.ZopfliCompress(options.PtrBefore(), format, inbuf.PtrBefore(), ssize,
outptr.PtrAfter(), outsize.PtrBoth()));
// Get and save data
sapi::v::Array<int8_t> outbuf(outsize.GetValue());
outbuf.SetRemote(reinterpret_cast<void*>(outptr.GetValue()));
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferFromSandboxee(&outbuf));
outstream.write(reinterpret_cast<char*>(outbuf.GetData()), outbuf.GetSize());
if (!outstream.good()) {
return absl::UnavailableError("Unable to write file");
}
return absl::OkStatus();
}

View File

@ -0,0 +1,26 @@
// Copyright 2022 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
//
// https://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 CONTRIB_ZOPFLI_UTILS_UTILS_ZOPFLI_H_
#define CONTRIB_ZOPFLI_UTILS_UTILS_ZOPFLI_H_
#include <fstream>
#include "absl/status/status.h"
#include "contrib/zopfli/sandboxed.h"
absl::Status Compress(ZopfliApi& api, std::ifstream& instream,
std::ofstream& outstream, ZopfliFormat format);
#endif // CONTRIB_ZOPFLI_UTILS_UTILS_ZOPFLI_H_

View File

@ -34,6 +34,8 @@ FetchContent_Declare(libzstd
FetchContent_MakeAvailable(libzstd)
set(libzstd_INCLUDE_DIR "${libzstd_SOURCE_DIR}/lib")
add_subdirectory(wrapper)
add_sapi_library(
sapi_zstd
@ -70,10 +72,16 @@ add_sapi_library(
ZSTD_getFrameContentSize
ZSTD_compress_fd
ZSTD_compressStream_fd
ZSTD_decompress_fd
ZSTD_decompressStream_fd
INPUTS
${libzstd_INCLUDE_DIR}/zstd.h
wrapper/wrapper_zstd.h
LIBRARY libzstd_static
LIBRARY wrapper_zstd
LIBRARY_NAME Zstd
NAMESPACE ""
)

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>
@ -25,10 +26,56 @@
#include "contrib/zstd/sandboxed.h"
#include "contrib/zstd/utils/utils_zstd.h"
ABSL_FLAG(bool, stream, false, "stream data to sandbox");
ABSL_FLAG(bool, decompress, false, "decompress");
ABSL_FLAG(bool, memory_mode, false, "in memory operations");
ABSL_FLAG(uint32_t, level, 0, "compression level");
absl::Status Stream(ZstdApi& api, std::string infile_s, std::string outfile_s) {
std::ifstream infile(infile_s, std::ios::binary);
if (!infile.is_open()) {
return absl::UnavailableError(absl::StrCat("Unable to open ", infile_s));
}
std::ofstream outfile(outfile_s, std::ios::binary);
if (!outfile.is_open()) {
return absl::UnavailableError(absl::StrCat("Unable to open ", outfile_s));
}
if (absl::GetFlag(FLAGS_memory_mode)) {
if (absl::GetFlag(FLAGS_decompress)) {
return DecompressInMemory(api, infile, outfile);
}
return CompressInMemory(api, infile, outfile, absl::GetFlag(FLAGS_level));
}
if (absl::GetFlag(FLAGS_decompress)) {
return DecompressStream(api, infile, outfile);
}
return CompressStream(api, infile, outfile, absl::GetFlag(FLAGS_level));
}
absl::Status FileDescriptor(ZstdApi& api, std::string infile_s,
std::string outfile_s) {
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
if (infd.GetValue() < 0) {
return absl::UnavailableError(absl::StrCat("Unable to open ", infile_s));
}
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY | O_CREAT));
if (outfd.GetValue() < 0) {
return absl::UnavailableError(absl::StrCat("Unable to open ", outfile_s));
}
if (absl::GetFlag(FLAGS_memory_mode)) {
if (absl::GetFlag(FLAGS_decompress)) {
return DecompressInMemoryFD(api, infd, outfd);
}
return CompressInMemoryFD(api, infd, outfd, absl::GetFlag(FLAGS_level));
}
if (absl::GetFlag(FLAGS_decompress)) {
return DecompressStreamFD(api, infd, outfd);
}
return CompressStreamFD(api, infd, outfd, absl::GetFlag(FLAGS_level));
}
int main(int argc, char* argv[]) {
std::string prog_name(argv[0]);
google::InitGoogleLogging(argv[0]);
@ -39,17 +86,6 @@ int main(int argc, char* argv[]) {
return EXIT_FAILURE;
}
std::ifstream infile(args[1], std::ios::binary);
if (!infile.is_open()) {
std::cerr << "Unable to open " << args[1] << std::endl;
return EXIT_FAILURE;
}
std::ofstream outfile(args[2], std::ios::binary);
if (!outfile.is_open()) {
std::cerr << "Unable to open " << args[2] << std::endl;
return EXIT_FAILURE;
}
ZstdSapiSandbox sandbox;
if (!sandbox.Init().ok()) {
std::cerr << "Unable to start sandbox\n";
@ -59,16 +95,10 @@ int main(int argc, char* argv[]) {
ZstdApi api(&sandbox);
absl::Status status;
if (absl::GetFlag(FLAGS_memory_mode) && absl::GetFlag(FLAGS_decompress)) {
status = DecompressInMemory(api, infile, outfile);
} else if (absl::GetFlag(FLAGS_memory_mode) &&
!absl::GetFlag(FLAGS_decompress)) {
status = CompressInMemory(api, infile, outfile, absl::GetFlag(FLAGS_level));
} else if (!absl::GetFlag(FLAGS_memory_mode) &&
absl::GetFlag(FLAGS_decompress)) {
status = DecompressStream(api, infile, outfile);
if (absl::GetFlag(FLAGS_stream)) {
status = Stream(api, argv[1], argv[2]);
} else {
status = CompressStream(api, infile, outfile, absl::GetFlag(FLAGS_level));
status = FileDescriptor(api, argv[1], argv[2]);
}
if (!status.ok()) {

View File

@ -27,10 +27,12 @@ class ZstdSapiSandbox : public ZstdSandbox {
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowRead()
.AllowWrite()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({__NR_recvmsg})
.BuildOrDie();
}
};

View File

@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <unistd.h>
#include <fstream>
#include <string>
@ -101,11 +104,10 @@ TEST(SandboxTest, CheckCompressInMemory) {
std::string infile_s = GetTestFilePath("text");
absl::StatusOr<std::string> path =
sapi::CreateNamedTempFileAndClose("out.zstd");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out.zstd"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -126,10 +128,10 @@ TEST(SandboxTest, CheckDecompressInMemory) {
std::string infile_s = GetTestFilePath("text.blob.zstd");
absl::StatusOr<std::string> path = sapi::CreateNamedTempFileAndClose("out");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -153,16 +155,15 @@ TEST(SandboxTest, CheckCompressAndDecompressInMemory) {
std::string infile_s = GetTestFilePath("text");
absl::StatusOr<std::string> path_middle =
sapi::CreateNamedTempFileAndClose("middle.zstd");
ASSERT_THAT(path_middle, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
sapi::CreateNamedTempFileAndClose("middle.zstd"));
std::string middle_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path_middle);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
absl::StatusOr<std::string> path = sapi::CreateNamedTempFileAndClose("out");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -193,12 +194,10 @@ TEST(SandboxTest, CheckCompressStream) {
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text");
absl::StatusOr<std::string> path =
sapi::CreateNamedTempFileAndClose("out.zstd");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out.zstd"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -220,11 +219,10 @@ TEST(SandboxTest, CheckDecompressStream) {
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text.stream.zstd");
absl::StatusOr<std::string> path = sapi::CreateNamedTempFileAndClose("out");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -248,16 +246,15 @@ TEST(SandboxTest, CheckCompressAndDecompressStream) {
std::string infile_s = GetTestFilePath("text");
absl::StatusOr<std::string> path_middle =
sapi::CreateNamedTempFileAndClose("middle.zstd");
ASSERT_THAT(path_middle, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
sapi::CreateNamedTempFileAndClose("middle.zstd"));
std::string middle_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path_middle);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
absl::StatusOr<std::string> path = sapi::CreateNamedTempFileAndClose("out");
ASSERT_THAT(path, IsOk()) << "Could not create temp output file";
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), *path);
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
std::ifstream infile(infile_s, std::ios::binary);
ASSERT_TRUE(infile.is_open());
@ -278,8 +275,235 @@ TEST(SandboxTest, CheckCompressAndDecompressStream) {
ASSERT_TRUE(outfile.is_open());
status = DecompressStream(api, inmiddle, outfile);
ASSERT_THAT(status, IsOk()) << "Unable to decompress";
ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
}
TEST(SandboxTest, CheckCompressInMemoryFD) {
ZstdSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text");
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out.zstd"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
absl::Status status = CompressInMemoryFD(api, infd, outfd, 0);
ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
EXPECT_GE(outpos, 0);
EXPECT_LT(outpos, inpos);
}
TEST(SandboxTest, CheckDecompressInMemoryFD) {
ZstdSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text.blob.zstd");
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
absl::Status status = DecompressInMemoryFD(api, infd, outfd);
ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
EXPECT_GE(outpos, 0);
EXPECT_GT(outpos, inpos);
ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
}
TEST(SandboxTest, CheckCompressAndDecompressInMemoryFD) {
ZstdSapiSandbox sandbox;
absl::Status status;
int ret;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text");
SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
sapi::CreateNamedTempFileAndClose("middle.zstd"));
std::string middle_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
sapi::v::Fd outmiddlefd(open(middle_s.c_str(), O_WRONLY));
ASSERT_GE(outmiddlefd.GetValue(), 0);
status = CompressInMemoryFD(api, infd, outmiddlefd, 0);
ASSERT_THAT(status, IsOk()) << "Unable to compress file in memory";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outpos = lseek(outmiddlefd.GetValue(), 0, SEEK_END);
EXPECT_GE(outpos, 0);
EXPECT_LT(outpos, inpos);
infd.CloseLocalFd();
outmiddlefd.CloseLocalFd();
sapi::v::Fd inmiddlefd(open(middle_s.c_str(), O_RDONLY));
ASSERT_GE(inmiddlefd.GetValue(), 0);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
status = DecompressInMemoryFD(api, inmiddlefd, outfd);
ASSERT_THAT(status, IsOk()) << "Unable to decompress file in memory";
outfd.CloseLocalFd();
inmiddlefd.CloseLocalFd();
ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
}
TEST(SandboxTest, CheckCompressStreamFD) {
absl::Status status;
ZstdSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text");
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out.zstd"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
status = CompressStreamFD(api, infd, outfd, 0);
ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
EXPECT_GE(outpos, 0);
EXPECT_LT(outpos, inpos);
}
TEST(SandboxTest, CheckDecompressStreamFD) {
absl::Status status;
ZstdSapiSandbox sandbox;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text.stream.zstd");
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
status = DecompressStreamFD(api, infd, outfd);
ASSERT_THAT(status, IsOk()) << "Unable to decompress stream";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outpos = lseek(outfd.GetValue(), 0, SEEK_END);
EXPECT_GE(outpos, 0);
EXPECT_GT(outpos, inpos);
ASSERT_TRUE(CompareFiles(GetTestFilePath("text"), outfile_s));
}
TEST(SandboxTest, CheckCompressAndDecompressStreamFD) {
ZstdSapiSandbox sandbox;
absl::Status status;
int ret;
ASSERT_THAT(sandbox.Init(), IsOk()) << "Couldn't initialize Sandboxed API";
ZstdApi api = ZstdApi(&sandbox);
std::string infile_s = GetTestFilePath("text");
SAPI_ASSERT_OK_AND_ASSIGN(std::string path_middle,
sapi::CreateNamedTempFileAndClose("middle.zstd"));
std::string middle_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path_middle);
SAPI_ASSERT_OK_AND_ASSIGN(std::string path,
sapi::CreateNamedTempFileAndClose("out"));
std::string outfile_s =
sapi::file::JoinPath(sapi::file_util::fileops::GetCWD(), path);
sapi::v::Fd infd(open(infile_s.c_str(), O_RDONLY));
ASSERT_GE(infd.GetValue(), 0);
sapi::v::Fd outmiddlefd(open(middle_s.c_str(), O_WRONLY));
ASSERT_GE(outmiddlefd.GetValue(), 0);
status = CompressStreamFD(api, infd, outmiddlefd, 0);
ASSERT_THAT(status, IsOk()) << "Unable to compress stream";
off_t inpos = lseek(infd.GetValue(), 0, SEEK_END);
EXPECT_GE(inpos, 0);
off_t outmiddlepos = lseek(outmiddlefd.GetValue(), 0, SEEK_END);
EXPECT_GE(outmiddlepos, 0);
EXPECT_LT(outmiddlepos, inpos);
infd.CloseLocalFd();
outmiddlefd.CloseLocalFd();
sapi::v::Fd inmiddlefd(open(middle_s.c_str(), O_RDONLY));
ASSERT_GE(inmiddlefd.GetValue(), 0);
sapi::v::Fd outfd(open(outfile_s.c_str(), O_WRONLY));
ASSERT_GE(outfd.GetValue(), 0);
status = DecompressStreamFD(api, inmiddlefd, outfd);
ASSERT_THAT(status, IsOk()) << "Unable to decompress stream";
ASSERT_TRUE(CompareFiles(infile_s, outfile_s));
}

View File

@ -241,3 +241,95 @@ absl::Status DecompressStream(ZstdApi& api, std::ifstream& in_stream,
return absl::OkStatus();
}
absl::Status CompressInMemoryFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd, int level) {
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&infd));
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&outfd));
SAPI_ASSIGN_OR_RETURN(
int iserr,
api.ZSTD_compress_fd(infd.GetRemoteFd(), outfd.GetRemoteFd(), 0));
SAPI_ASSIGN_OR_RETURN(iserr, api.ZSTD_isError(iserr))
if (iserr) {
return absl::UnavailableError("Unable to compress file");
}
infd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
outfd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
return absl::OkStatus();
}
absl::Status DecompressInMemoryFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd) {
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&infd));
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&outfd));
SAPI_ASSIGN_OR_RETURN(int iserr, api.ZSTD_decompress_fd(infd.GetRemoteFd(),
outfd.GetRemoteFd()));
SAPI_ASSIGN_OR_RETURN(iserr, api.ZSTD_isError(iserr))
if (iserr) {
return absl::UnavailableError("Unable to compress file");
}
infd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
outfd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
return absl::OkStatus();
}
absl::Status CompressStreamFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd, int level) {
SAPI_ASSIGN_OR_RETURN(ZSTD_CCtx * cctx, api.ZSTD_createCCtx());
sapi::v::RemotePtr rcctx(cctx);
int iserr;
SAPI_ASSIGN_OR_RETURN(iserr, api.ZSTD_CCtx_setParameter(
&rcctx, ZSTD_c_compressionLevel, level));
SAPI_ASSIGN_OR_RETURN(iserr, api.ZSTD_isError(iserr));
if (iserr) {
return absl::UnavailableError("Unable to set parameter l");
}
SAPI_ASSIGN_OR_RETURN(
iserr, api.ZSTD_CCtx_setParameter(&rcctx, ZSTD_c_checksumFlag, 1));
SAPI_ASSIGN_OR_RETURN(iserr, api.ZSTD_isError(iserr));
if (iserr) {
return absl::UnavailableError("Unable to set parameter c");
}
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&infd));
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&outfd));
SAPI_ASSIGN_OR_RETURN(iserr,
api.ZSTD_compressStream_fd(&rcctx, infd.GetRemoteFd(),
outfd.GetRemoteFd()));
if (iserr) {
return absl::UnavailableError("Unable to compress");
}
infd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
outfd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
return absl::OkStatus();
}
absl::Status DecompressStreamFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd) {
SAPI_ASSIGN_OR_RETURN(ZSTD_DCtx * dctx, api.ZSTD_createDCtx());
sapi::v::RemotePtr rdctx(dctx);
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&infd));
SAPI_RETURN_IF_ERROR(api.GetSandbox()->TransferToSandboxee(&outfd));
SAPI_ASSIGN_OR_RETURN(int iserr,
api.ZSTD_decompressStream_fd(&rdctx, infd.GetRemoteFd(),
outfd.GetRemoteFd()));
if (iserr) {
return absl::UnavailableError("Unable to decompress");
}
infd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
outfd.CloseRemoteFd(api.GetSandbox()->rpc_channel()).IgnoreError();
return absl::OkStatus();
}

View File

@ -24,10 +24,18 @@ absl::Status CompressInMemory(ZstdApi& api, std::ifstream& in_stream,
std::ofstream& out_stream, int level);
absl::Status DecompressInMemory(ZstdApi& api, std::ifstream& in_stream,
std::ofstream& out_stream);
absl::Status CompressInMemoryFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd, int level);
absl::Status DecompressInMemoryFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd);
absl::Status CompressStream(ZstdApi& api, std::ifstream& in_stream,
std::ofstream& out_stream, int level);
absl::Status DecompressStream(ZstdApi& api, std::ifstream& in_stream,
std::ofstream& out_stream);
absl::Status CompressStreamFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd, int level);
absl::Status DecompressStreamFD(ZstdApi& api, sapi::v::Fd& infd,
sapi::v::Fd& outfd);
#endif // CONTRIB_ZSTD_UTILS_UTILS_ZSTD_H_

View File

@ -0,0 +1,24 @@
# Copyright 2022 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
#
# https://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_zstd STATIC
wrapper_zstd.cc
)
target_link_libraries(wrapper_zstd PUBLIC
libzstd_static
)
target_include_directories(wrapper_zstd PUBLIC
"${SAPI_SOURCE_DIR}"
"${libzstd_INCLUDE_DIR}"
)

View File

@ -0,0 +1,182 @@
// Copyright 2022 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
//
// https://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 "contrib/zstd/wrapper/wrapper_zstd.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <memory>
constexpr size_t kFileMaxSize = 1024 * 1024 * 1024; // 1GB
off_t FDGetSize(int fd) {
off_t size = lseek(fd, 0, SEEK_END);
if (size < 0) {
return -1;
}
if (lseek(fd, 0, SEEK_SET) < 0) {
return -1;
}
return size;
}
int ZSTD_compress_fd(int fdin, int fdout, int level) {
off_t sizein = FDGetSize(fdin);
if (sizein <= 0) {
return -1;
}
size_t sizeout = ZSTD_compressBound(sizein);
auto bufin = std::make_unique<int8_t[]>(sizein);
auto bufout = std::make_unique<int8_t[]>(sizeout);
if (read(fdin, bufin.get(), sizein) != sizein) {
return -1;
}
int retsize =
ZSTD_compress(bufout.get(), sizeout, bufin.get(), sizein, level);
if (ZSTD_isError(retsize)) {
return -1;
}
if (write(fdout, bufout.get(), retsize) != retsize) {
return -1;
}
return 0;
}
int ZSTD_compressStream_fd(ZSTD_CCtx* cctx, int fdin, int fdout) {
size_t sizein = ZSTD_CStreamInSize();
size_t sizeout = ZSTD_CStreamOutSize();
auto bufin = std::make_unique<int8_t[]>(sizein);
auto bufout = std::make_unique<int8_t[]>(sizeout);
ssize_t size;
while ((size = read(fdin, bufin.get(), sizein)) > 0) {
ZSTD_inBuffer_s struct_in;
struct_in.src = bufin.get();
struct_in.pos = 0;
struct_in.size = size;
ZSTD_EndDirective mode = ZSTD_e_continue;
if (size < sizein) {
mode = ZSTD_e_end;
}
bool isdone = false;
while (!isdone) {
ZSTD_outBuffer_s struct_out;
struct_out.dst = bufout.get();
struct_out.pos = 0;
struct_out.size = sizeout;
size_t remaining =
ZSTD_compressStream2(cctx, &struct_out, &struct_in, mode);
if (ZSTD_isError(remaining)) {
return -1;
}
if (write(fdout, bufout.get(), struct_out.pos) != struct_out.pos) {
return -1;
}
if (mode == ZSTD_e_continue) {
isdone = (struct_in.pos == size);
} else {
isdone = (remaining == 0);
}
}
}
if (size != 0) {
return -1;
}
return 0;
}
int ZSTD_decompress_fd(int fdin, int fdout) {
off_t sizein = FDGetSize(fdin);
if (sizein <= 0) {
return -1;
}
auto bufin = std::make_unique<int8_t[]>(sizein);
if (read(fdin, bufin.get(), sizein) != sizein) {
return -1;
}
size_t sizeout = ZSTD_getFrameContentSize(bufin.get(), sizein);
if (ZSTD_isError(sizeout) || sizeout > kFileMaxSize) {
return -1;
}
auto bufout = std::make_unique<int8_t[]>(sizeout);
size_t desize = ZSTD_decompress(bufout.get(), sizeout, bufin.get(), sizein);
if (ZSTD_isError(desize) || desize != sizeout) {
return -1;
}
if (write(fdout, bufout.get(), sizeout) != sizeout) {
return -1;
}
return 0;
}
int ZSTD_decompressStream_fd(ZSTD_DCtx* dctx, int fdin, int fdout) {
size_t sizein = ZSTD_CStreamInSize();
size_t sizeout = ZSTD_CStreamOutSize();
auto bufin = std::make_unique<int8_t[]>(sizein);
auto bufout = std::make_unique<int8_t[]>(sizeout);
ssize_t size;
while ((size = read(fdin, bufin.get(), sizein)) > 0) {
ZSTD_inBuffer_s struct_in;
struct_in.src = bufin.get();
struct_in.pos = 0;
struct_in.size = size;
while (struct_in.pos < size) {
ZSTD_outBuffer_s struct_out;
struct_out.dst = bufout.get();
struct_out.pos = 0;
struct_out.size = sizeout;
size_t ret = ZSTD_decompressStream(dctx, &struct_out, &struct_in);
if (ZSTD_isError(ret)) {
return -1;
}
if (write(fdout, bufout.get(), struct_out.pos) != struct_out.pos) {
return -1;
}
}
}
if (size != 0) {
return -1;
}
return 0;
}

View File

@ -0,0 +1,28 @@
// Copyright 2022 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
//
// https://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 CONTRIB_ZSTD_WRAPPER_WRAPPER_ZSTD_H_
#define CONTRIB_ZSTD_WRAPPER_WRAPPER_ZSTD_H_
#include <zstd.h>
extern "C" {
int ZSTD_compress_fd(int fdin, int fdout, int level);
int ZSTD_compressStream_fd(ZSTD_CCtx* cctx, int fdin, int fdout);
int ZSTD_decompress_fd(int fdin, int fdout);
int ZSTD_decompressStream_fd(ZSTD_DCtx* dctx, int fdin, int fdout);
};
#endif // CONTRIB_ZSTD_WRAPPER_WRAPPER_ZSTD_H_

View File

@ -1,3 +0,0 @@
*.o
*.a
pffft_main

View File

@ -36,7 +36,7 @@ sapi_library(
cc_binary(
name = "main_zlib",
srcs = ["main_zlib.cc"],
copts = sapi_platform_copts(["-Wframe-larger-than=65536"]),
copts = sapi_platform_copts(),
deps = [
":zlib-sapi",
":zlib-sapi_embed",

View File

@ -17,7 +17,7 @@ add_sapi_library(zlib-sapi
FUNCTIONS deflateInit_
deflate
deflateEnd
INPUTS ${ZLIB_INCLUDE_DIRS}/zlib.h
INPUTS "${ZLIB_INCLUDE_DIRS}/zlib.h"
LIBRARY ZLIB::ZLIB
LIBRARY_NAME Zlib
NAMESPACE "sapi::zlib"

View File

@ -555,9 +555,11 @@ bool Comms::Recv(void* data, size_t len) {
// Internal helper method (low level).
bool Comms::RecvTL(uint32_t* tag, size_t* length) {
if (!Recv(reinterpret_cast<uint8_t*>(tag), sizeof(*tag))) {
SAPI_RAW_VLOG(2, "RecvTL: Can't read tag");
return false;
}
if (!Recv(reinterpret_cast<uint8_t*>(length), sizeof(*length))) {
SAPI_RAW_VLOG(2, "RecvTL: Can't read length for tag %u", *tag);
return false;
}
if (*length > GetMaxMsgSize()) {

View File

@ -57,4 +57,5 @@ sh_test(
name = "static_sandbox_test",
srcs = ["static_sandbox_test.sh"],
data = [":static_sandbox"],
tags = ["no_qemu_user_mode"],
)

View File

@ -37,9 +37,6 @@ cc_binary(
name = "zpipe",
srcs = ["zpipe.c"],
copts = sapi_platform_copts(),
features = [
"fully_static_link", # link libc statically
],
linkstatic = 1,
features = ["fully_static_link"],
deps = ["@net_zlib//:zlib"],
)

View File

@ -24,7 +24,6 @@ target_link_libraries(sandbox2_zpipe_sandbox PRIVATE
absl::memory
sandbox2::bpf_helper
sandbox2::comms
# sandbox2::ipc
sapi::runfiles
sandbox2::sandbox2
sapi::base
@ -38,5 +37,6 @@ add_executable(sandbox2_zpipe
set_target_properties(sandbox2_zpipe PROPERTIES OUTPUT_NAME zpipe)
add_executable(sandbox2::zpipe ALIAS sandbox2_zpipe)
target_link_libraries(sandbox2_zpipe PRIVATE
-static
ZLIB::ZLIB
)

View File

@ -54,7 +54,7 @@ std::unique_ptr<sandbox2::Policy> GetPolicy() {
// Allow write on STDOUT / STDERR.
.AddPolicyOnSyscall(__NR_write,
{ARG_32(0), JEQ32(1, ALLOW), JEQ32(2, ALLOW)})
.AllowSyscall(__NR_fstat)
.AllowStat()
.AllowStaticStartup()
.AllowSystemMalloc()
.AllowExit()

View File

@ -653,12 +653,10 @@ PolicyBuilder& PolicyBuilder::AllowStaticStartup() {
BlockSyscallWithErrno(__NR_readlink, ENOENT);
#endif
if constexpr (sapi::host_cpu::IsArm()) {
AddPolicyOnSyscall(__NR_mprotect, {
ARG_32(2),
JEQ32(PROT_READ, ALLOW),
});
}
AddPolicyOnSyscall(__NR_mprotect, {
ARG_32(2),
JEQ32(PROT_READ, ALLOW),
});
return *this;
}
@ -884,7 +882,7 @@ PolicyBuilder& PolicyBuilder::AddFile(absl::string_view path, bool is_ro) {
PolicyBuilder& PolicyBuilder::AddFileAt(absl::string_view outside,
absl::string_view inside, bool is_ro) {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
auto valid_outside = ValidateAbsolutePath(outside);
if (!valid_outside.ok()) {
@ -912,7 +910,7 @@ PolicyBuilder& PolicyBuilder::AddFileAt(absl::string_view outside,
PolicyBuilder& PolicyBuilder::AddLibrariesForBinary(
absl::string_view path, absl::string_view ld_library_path) {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
auto valid_path = ValidatePath(path);
if (!valid_path.ok()) {
@ -941,7 +939,7 @@ PolicyBuilder& PolicyBuilder::AddDirectory(absl::string_view path, bool is_ro) {
PolicyBuilder& PolicyBuilder::AddDirectoryAt(absl::string_view outside,
absl::string_view inside,
bool is_ro) {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
auto valid_outside = ValidateAbsolutePath(outside);
if (!valid_outside.ok()) {
@ -969,7 +967,7 @@ PolicyBuilder& PolicyBuilder::AddDirectoryAt(absl::string_view outside,
}
PolicyBuilder& PolicyBuilder::AddTmpfs(absl::string_view inside, size_t size) {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
if (auto status = mounts_.AddTmpfs(inside, size); !status.ok()) {
SetError(absl::InternalError(absl::StrCat("Could not mount tmpfs ", inside,
@ -979,14 +977,14 @@ PolicyBuilder& PolicyBuilder::AddTmpfs(absl::string_view inside, size_t size) {
}
PolicyBuilder& PolicyBuilder::AllowUnrestrictedNetworking() {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
allow_unrestricted_networking_ = true;
return *this;
}
PolicyBuilder& PolicyBuilder::SetHostname(absl::string_view hostname) {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
hostname_ = std::string(hostname);
return *this;
@ -1089,7 +1087,7 @@ PolicyBuilder& PolicyBuilder::AddNetworkProxyHandlerPolicy() {
}
PolicyBuilder& PolicyBuilder::SetRootWritable() {
EnableNamespaces();
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
mounts_.SetRootWritable();
return *this;

View File

@ -286,9 +286,6 @@ absl::StatusOr<UnwindResult> StackTracePeer::LaunchLibunwindSandbox(
absl::StatusOr<std::vector<std::string>> GetStackTrace(const Regs* regs,
const Mounts& mounts) {
if constexpr (sapi::host_cpu::IsArm64()) {
return absl::UnavailableError("Stack traces unavailable on Aarch64");
}
if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) {
return absl::UnavailableError("Stacktraces disabled");
}

View File

@ -62,6 +62,7 @@ cc_library(
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:status",
"//sandboxed_api/util:strerror",
"@com_google_absl//absl/cleanup",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@org_gnu_libunwind//:unwind-ptrace-wrapped",

View File

@ -30,6 +30,7 @@ add_library(sandbox2_unwind STATIC
)
add_library(sandbox2::unwind ALIAS sandbox2_unwind)
target_link_libraries(sandbox2_unwind PRIVATE
absl::cleanup
absl::statusor
absl::strings
sandbox2::comms

View File

@ -19,11 +19,13 @@
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/cleanup/cleanup.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
@ -55,21 +57,20 @@ std::string DemangleSymbol(const std::string& maybe_mangled) {
}
absl::StatusOr<std::vector<uintptr_t>> RunLibUnwind(pid_t pid, int max_frames) {
unw_cursor_t cursor;
static unw_addr_space_t as =
unw_create_addr_space(&_UPT_accessors, 0 /* byte order */);
if (as == nullptr) {
return absl::InternalError("unw_create_addr_space() failed");
}
std::unique_ptr<struct UPT_info, void (*)(void*)> ui(
reinterpret_cast<struct UPT_info*>(_UPT_create(pid)), _UPT_destroy);
if (ui == nullptr) {
void* context = _UPT_create(pid);
if (context == nullptr) {
return absl::InternalError("_UPT_create() failed");
}
absl::Cleanup context_cleanup = [&context] { _UPT_destroy(context); };
int rc = unw_init_remote(&cursor, as, ui.get());
if (rc < 0) {
unw_cursor_t cursor;
if (int rc = unw_init_remote(&cursor, as, context); rc < 0) {
// Could be UNW_EINVAL (8), UNW_EUNSPEC (1) or UNW_EBADREG (3).
return absl::InternalError(
absl::StrCat("unw_init_remote() failed with error ", rc));
@ -78,7 +79,7 @@ absl::StatusOr<std::vector<uintptr_t>> RunLibUnwind(pid_t pid, int max_frames) {
std::vector<uintptr_t> ips;
for (int i = 0; i < max_frames; ++i) {
unw_word_t ip;
rc = unw_get_reg(&cursor, UNW_REG_IP, &ip);
int rc = unw_get_reg(&cursor, UNW_REG_IP, &ip);
if (rc < 0) {
// Could be UNW_EUNSPEC or UNW_EBADREG.
SAPI_RAW_LOG(WARNING, "unw_get_reg() failed with error %d", rc);
@ -176,8 +177,10 @@ absl::StatusOr<SymbolMap> LoadSymbolsMap(pid_t pid) {
for (const ElfFile::Symbol& symbol : elf->symbols()) {
if (elf->position_independent()) {
if (symbol.address < entry.end - entry.start) {
addr_to_symbol[symbol.address + entry.start] = symbol.name;
if (symbol.address >= entry.pgoff &&
symbol.address - entry.pgoff < entry.end - entry.start) {
addr_to_symbol[symbol.address + entry.start - entry.pgoff] =
symbol.name;
}
} else {
if (symbol.address >= entry.start && symbol.address < entry.end) {

View File

@ -26,13 +26,14 @@
#include "sandboxed_api/util/file_helpers.h"
#include "sandboxed_api/util/status_matchers.h"
extern "C" void ExportedFunctionName() {
extern "C" void ExportedFunction() {
// Don't do anything - used to generate a symbol.
}
namespace file = ::sapi::file;
using ::sapi::GetTestSourcePath;
using ::sapi::IsOk;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::IsTrue;
using ::testing::Ne;
@ -65,19 +66,20 @@ TEST(MinielfTest, SymbolResolutionWorks) {
ParseProcMaps(maps_buffer));
// Find maps entry that covers this entry.
uint64_t function_address = reinterpret_cast<uint64_t>(ExportedFunctionName);
auto function_entry =
uint64_t function_address = reinterpret_cast<uint64_t>(&ExportedFunction);
auto entry =
absl::c_find_if(maps, [function_address](const MapsEntry& entry) {
return entry.start <= function_address && entry.end > function_address;
});
ASSERT_THAT(function_entry, Ne(maps.end()));
function_address -= function_entry->start;
ASSERT_THAT(entry, Ne(maps.end()));
auto function_symbol =
absl::c_find_if(elf.symbols(), [](const ElfFile::Symbol& symbol) {
return symbol.name == "ExportedFunctionName";
return symbol.name == "ExportedFunction";
});
ASSERT_THAT(function_symbol, Ne(elf.symbols().end()));
function_address -= entry->start - entry->pgoff;
EXPECT_THAT(function_symbol->address, Eq(function_address));
}
@ -86,8 +88,7 @@ TEST(MinielfTest, ImportedLibraries) {
ElfFile elf, ElfFile::ParseFromFile(
GetTestSourcePath("sandbox2/util/testdata/hello_world"),
ElfFile::kLoadImportedLibraries));
std::vector<std::string> imported_libraries = {"libc.so.6"};
EXPECT_THAT(elf.imported_libraries(), Eq(imported_libraries));
EXPECT_THAT(elf.imported_libraries(), ElementsAre("libc.so.6"));
}
} // namespace

View File

@ -31,8 +31,11 @@ void SaveStatusToProto(const absl::Status& status, StatusProto* out) {
absl::Status MakeStatusFromProto(const StatusProto& proto) {
absl::Status status(static_cast<absl::StatusCode>(proto.code()),
proto.message());
for (const auto& [type_key, payload] : proto.payloads()) {
status.SetPayload(type_key, absl::Cord(payload));
// Note: Using C++17 structured bindings instead of `entry` crashes Clang 6.0
// on Ubuntu 18.04 (bionic).
for (const auto& entry : proto.payloads()) {
status.SetPayload(/*type_url=*/entry.first,
/*payload=*/absl::Cord(entry.second));
}
return status;
}