merged .gitmodules with upstream

This commit is contained in:
Andrei Medar 2020-09-24 18:28:25 +00:00
commit 388bcff972
160 changed files with 8543 additions and 1236 deletions

View File

@ -1,4 +1,6 @@
---
Language: Cpp
BasedOnStyle: Google
Language: Cpp
BasedOnStyle: Google
DerivePointerAlignment: false
PointerAlignment: Left
...

12
.gitmodules vendored
View File

@ -1,3 +1,15 @@
[submodule "oss-internship-2020/sapi_libarchive/libarchive"]
path = oss-internship-2020/sapi_libarchive/libarchive
url = https://github.com/libarchive/libarchive
[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/curl/curl_wrapper/curl"]
path = oss-internship-2020/curl/curl_wrapper/curl
url = https://github.com/curl/curl
[submodule "oss-internship-2020/gdal/gdal"]
path = oss-internship-2020/gdal/gdal
url = https://github.com/OSGeo/gdal/tree/master/gdal

View File

@ -14,6 +14,16 @@
cmake_minimum_required(VERSION 3.12)
# Fix Ninja generator output to not rebuild entire sub-trees needlessly.
if(CMAKE_GENERATOR MATCHES "Ninja")
file(WRITE "${CMAKE_BINARY_DIR}/UserMakeRulesOverride.cmake"
"string(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_C \"\${CMAKE_DEPFILE_FLAGS_C}\")\n"
"string(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_CXX \"\${CMAKE_DEPFILE_FLAGS_CXX}\")\n"
)
set(CMAKE_USER_MAKE_RULES_OVERRIDE
"${CMAKE_BINARY_DIR}/UserMakeRulesOverride.cmake" CACHE INTERNAL "")
endif()
project(SandboxedAPI C CXX ASM)
# SAPI-wide setting for the language level
@ -31,15 +41,8 @@ include(SapiDeps)
include(SapiUtil)
include(SapiBuildDefs)
# Fix Ninja generator output to not rebuild entire sub-trees needlessly.
if(CMAKE_GENERATOR MATCHES "Ninja")
file(WRITE "${SAPI_BINARY_DIR}/UserMakeRulesOverride.cmake"
"STRING(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_C \"\${CMAKE_DEPFILE_FLAGS_C}\")\n"
"STRING(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_CXX \"\${CMAKE_DEPFILE_FLAGS_CXX}\")\n"
)
set(CMAKE_USER_MAKE_RULES_OVERRIDE
"${SAPI_BINARY_DIR}/UserMakeRulesOverride.cmake" CACHE INTERNAL "")
endif()
# Allow the header generator to auto-configure include paths
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (SAPI_FORCE_COLOR_OUTPUT)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # GCC

View File

@ -123,7 +123,7 @@ function(add_sapi_library)
endif()
# Interface
list_join(_sapi_FUNCTIONS "," _sapi_funcs)
list(JOIN _sapi_FUNCTIONS "," _sapi_funcs)
foreach(src IN LISTS _sapi_INPUTS)
get_filename_component(src "${src}" ABSOLUTE)
list(APPEND _sapi_full_inputs "${src}")
@ -132,7 +132,6 @@ function(add_sapi_library)
set(_sapi_embed_dir "${CMAKE_CURRENT_BINARY_DIR}")
set(_sapi_embed_name "${_sapi_NAME}")
endif()
# TODO(cblichmann): Implement sapi_isystem
if(SAPI_ENABLE_GENERATOR)
add_custom_command(
OUTPUT "${_sapi_gen_header}"
@ -149,9 +148,14 @@ function(add_sapi_library)
VERBATIM
)
else()
list_join(_sapi_full_inputs "," _sapi_full_inputs)
set(_sapi_isystem "${_sapi_NAME}.isystem")
list(JOIN _sapi_full_inputs "," _sapi_full_inputs)
add_custom_command(
OUTPUT "${_sapi_gen_header}"
OUTPUT "${_sapi_gen_header}" "${_sapi_isystem}"
COMMAND sh -c
"${CMAKE_CXX_COMPILER} -E -x c++ -v /dev/null 2>&1 | \
awk '/> search starts here:/{f=1;next}/^End of search/{f=0}f{print $1}' \
> \"${_sapi_isystem}\""
COMMAND "${SAPI_PYTHON3_EXECUTABLE}" -B
"${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py"
"--sapi_name=${_sapi_LIBRARY_NAME}"
@ -160,8 +164,10 @@ function(add_sapi_library)
"--sapi_embed_name=${_sapi_embed_name}"
"--sapi_functions=${_sapi_funcs}"
"--sapi_ns=${_sapi_NAMESPACE}"
"--sapi_isystem=${_sapi_isystem}"
"--sapi_in=${_sapi_full_inputs}"
COMMENT "Generating interface"
VERBATIM
)
endif()

View File

@ -35,16 +35,6 @@ function(create_directory_symlink SOURCE DESTINATION)
endif()
endfunction()
# Implements list(JOIN ...) for CMake < 3.12.
function(list_join LIST SEP OUTPUT)
foreach(item IN LISTS ${LIST})
set(_concat "${_concat}${SEP}${item}")
endforeach()
string(LENGTH "${SEP}" _len)
string(SUBSTRING "${_concat}" ${_len} -1 _concat)
set(${OUTPUT} "${_concat}" PARENT_SCOPE)
endfunction()
# Helper function that behaves just like Protobuf's protobuf_generate_cpp(),
# except that it strips import paths. This is necessary, because CMake's
# protobuf rules don't work well with imports across different directories.

View File

@ -18,7 +18,7 @@ project(absl-download NONE)
include(ExternalProject)
ExternalProject_Add(absl
GIT_REPOSITORY https://github.com/abseil/abseil-cpp
GIT_TAG 6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3 # 2020-04-28
GIT_TAG 0e9921b75a0fdd639a504ec8443fc1fe801becd7 # 2020-09-02
SOURCE_DIR "${CMAKE_BINARY_DIR}/absl-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/absl-build"
CONFIGURE_COMMAND ""

1
oss-internship-2020/curl/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,144 @@
# 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.16)
project(libcurl_sandbox)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
option(CURL_SAPI_ENABLE_EXAMPLES "" ON)
option(CURL_SAPI_ENABLE_TESTS "" ON)
# Add callbacks used by examples and tests
if (CURL_SAPI_ENABLE_EXAMPLES OR CURL_SAPI_ENABLE_TESTS)
list(APPEND CURL_SAPI_CALLBACKS
"${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.h"
"${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.cc"
)
endif()
# Add folder containing the non-sandboxed custom curl library
add_subdirectory(curl_wrapper)
# Setup Sandboxed API
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES ${CURL_SAPI_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${CURL_SAPI_ENABLE_TESTS} CACHE BOOL "" FORCE)
add_subdirectory(
"${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL
)
# Generate SAPI header
add_sapi_library(curl_sapi
# List of all the methods in https://curl.haxx.se/libcurl/c/allfuncs.html
# Some are added or modified because the original ones are not supported
# by Sandboxed API (details can be found in curl_wrapper.h)
FUNCTIONS curl_easy_cleanup
curl_easy_duphandle
curl_easy_escape
curl_easy_getinfo
curl_easy_getinfo_ptr
curl_easy_init
curl_easy_pause
curl_easy_perform
curl_easy_recv
curl_easy_reset
curl_easy_send
curl_easy_setopt
curl_easy_setopt_ptr
curl_easy_setopt_long
curl_easy_setopt_curl_off_t
curl_easy_strerror
curl_easy_unescape
curl_easy_upkeep
curl_free
curl_getdate_sapi
curl_global_cleanup
curl_global_init
curl_global_init_mem
curl_global_sslset
curl_mime_addpart
curl_mime_data
curl_mime_data_cb
curl_mime_encoder
curl_mime_filedata
curl_mime_filename
curl_mime_free
curl_mime_headers
curl_mime_init
curl_mime_name
curl_mime_subparts
curl_mime_type
curl_multi_add_handle
curl_multi_assign
curl_multi_cleanup
curl_multi_fdset_sapi
curl_multi_info_read
curl_multi_init
curl_multi_perform
curl_multi_remove_handle
curl_multi_setopt
curl_multi_setopt_ptr
curl_multi_setopt_long
curl_multi_setopt_curl_off_t
curl_multi_socket_action
curl_multi_strerror
curl_multi_timeout
curl_multi_poll_sapi
curl_multi_wait_sapi
curl_multi_wakeup
curl_share_init
curl_share_setopt
curl_share_setopt_ptr
curl_share_setopt_long
curl_share_strerror
curl_slist_append
curl_slist_free_all
curl_url
curl_url_cleanup
curl_url_dup
curl_url_get
curl_url_set
curl_version
curl_version_info
INPUTS curl_wrapper/curl/include/curl/curl.h
curl_wrapper/curl_wrapper.h
LIBRARY curl_wrapper_and_callbacks
LIBRARY_NAME Curl
NAMESPACE ""
)
# Include generated SAPI header
target_include_directories(curl_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
# Add examples
if (CURL_SAPI_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
# Add tests
if (CURL_SAPI_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -0,0 +1,112 @@
# LibCurl Sandbox
This library is a sandboxed version of curl's C API,
[libcurl](https://curl.haxx.se/libcurl/c/), implemented using Sandboxed API.
## Setup
The repository can be cloned using: `git clone --recursive [URL to this repo]`
The `--recursive` flag ensures that submodules are also cloned.
Alternatively, if the repository has already been cloned but the submodules have
not, these can be cloned using: `git submodule update --init --recursive`
The full list of Sandboxed API dependencies can be found on
[Sandboxed API Getting Started page](https://developers.google.com/sandboxed-api/docs/getting-started).
The following commands, used from the current `curl/` directory, build the
library: `mkdir -p build cd build cmake .. -G Ninja -D SAPI_ROOT=[path to
sandboxed-api] cmake --build .`
## Implementation details
All of libcurl's methods are supported by the library. However, a few of these
have different signatures defined in the sandboxed header `custom_curl.h`, which
wraps and extends libcurl.
This is necessary because Sandboxed API sandboxes most of libcurl correctly, but
encounters some issues when sandboxing a few methods. The simplest solution is
wrapping these methods into wrapper methods that accomplish the same tasks but
can also be sandboxed.
The next sections describe the issues encountered and contain some information
on the signatures of the wrapper methods solving these issues.
#### Variadic methods
Variadic methods are currently not supported by Sandboxed API. To solve this,
these methods are defined with an additional explicit parameter in
`custom_curl.h`.
The methods are: - `curl_easy_setopt`. Use `curl_easy_setopt_ptr`,
`curl_easy_setopt_long` or `curl_easy_setopt_curl_off_t` instead. -
`curl_easy_getinfo`. Use `curl_easy_getinfo_ptr` instead. - `curl_multi_setopt`.
Use `curl_multi_setopt_ptr`, `curl_multi_setopt_long` or
`curl_multi_setopt_curl_off_t` instead. - `curl_share_setopt`. Use
`curl_share_setopt_ptr` or `curl_share_setopt_long` instead
#### Methods with incomplete array arguments
Incomplete array arguments are currently not supported by Sandboxed API. To
solve this, methods taking an incomplete array argument have a wrapper in
`custom_curl.h`, and take a pointer as the argument.
The methods are: - `curl_multi_poll`. Use `curl_multi_poll_sapi` instead. -
`curl_multi_wait`. Use `curl_multi_wait_sapi` instead.
#### Methods with conflicts on the generated header
Some methods create conflicts on the generated header because of redefined
`#define` directives from files included by the header. To solve this, the
conflicting types and methods are redefined in `custom_curl.h`.
The types are: - `time_t`. Use `time_t_sapi` instead. - `fd_set`. Use
`fd_set_sapi` instead.
The methods are: - `curl_getdate`. Use `curl_getdate_sapi` instead. -
`curl_multi_fdset`. Use `curl_multi_fdset_sapi` instead.
#### Function pointers
The functions whose pointers will be passed to the library's methods
(*callbacks*) can't be implemented in the files making use of the library, but
must be in other files. These files must be compiled together with the library,
and this is done by adding their absolute path to the cmake variable
`CURL_SAPI_CALLBACKS`.
The pointers can then be obtained using an `RPCChannel` object, as shown in
`example2.cc`.
## Examples
The `examples` directory contains the sandboxed versions of example source codes
taken from [this page](https://curl.haxx.se/libcurl/c/example.html) on curl's
website. More information about each example can be found in the examples'
[README](examples/README.md).
To build these examples when building the library, the cmake variable
`CURL_SAPI_ENABLE_EXAMPLES` must be set to `ON`. This enables Sandboxed API
examples as well.
## Policy
The `sandbox.h` file contains a policy allowing all is necessary for libcurl to
perform simple requests. It is used by all the examples, except by example3.
This example needs some additional policies and files in its namespace (since it
uses HTTPS), and the file `example3.cc` shows how to easily extend an existing
policy.
## Testing
The `tests` folder contains some test cases created using Google Test. The class
`CurlTestUtils` is used to facilitate some tasks that all test cases need,
including the setup of a mock local server on which test requests are performed.
To build these tests when building the library, the cmake variable
`CURL_SAPI_ENABLE_TESTS` must be set to `ON`. This enables Sandboxed API tests
as well.
## Callbacks
The `callbacks.h` and `callbacks.cc` files implement all the callbacks used by
examples and tests.

View File

@ -0,0 +1,37 @@
// 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 "callbacks.h" // NOLINT(build/include)
#include <cstdlib>
#include <cstring>
#include "sandboxed_api/vars.h"
size_t WriteToMemory(char* contents, size_t size, size_t num_bytes,
void* userp) {
size_t real_size = size * num_bytes;
auto* mem = static_cast<sapi::LenValStruct*>(userp);
char* ptr = static_cast<char*>(realloc(mem->data, mem->size + real_size + 1));
if (ptr == nullptr) return 0;
mem->data = ptr;
auto data = static_cast<char*>(mem->data);
memcpy(&(data[mem->size]), contents, real_size);
mem->size += real_size;
data[mem->size] = 0;
return real_size;
}

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 TESTS_CALLBACKS_H
#define TESTS_CALLBACKS_H
#include <curl/curl.h>
// Append contents to the string stored by userp, which is a sapi::LenValStruct*
extern "C" size_t WriteToMemory(char* contents, size_t size, size_t num_bytes,
void* userp);
#endif // TESTS_CALLBACKS_H

View File

@ -0,0 +1,37 @@
# 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.
# Wrapper library including curl, wrappers for some methods and callbacks
# The CURL_SAPI_CALLBACKS variable should contain the absolute paths of
# all the files implementing the callbacks
add_library(curl_wrapper_and_callbacks OBJECT
curl_wrapper.h
curl_wrapper.cc
"${CURL_SAPI_CALLBACKS}"
)
set_target_properties(curl_wrapper_and_callbacks
PROPERTIES LINKER_LANGUAGE C
)
# Flags needed to build curl statically
set(CURL_HIDDEN_SYMBOLS OFF)
set(BUILD_SHARED_LIBS OFF)
# Link the wrapper to the original curl library (testing is disabled in curl)
set(BUILD_TESTING OFF)
add_subdirectory(curl)
target_link_libraries(curl_wrapper_and_callbacks
CURL::libcurl
sapi::sapi
)

View File

@ -0,0 +1,86 @@
// 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 "curl_wrapper.h" // NOLINT(build/include)
CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option,
void* parameter) {
return curl_easy_setopt(handle, option, parameter);
}
CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option,
long parameter) {
return curl_easy_setopt(handle, option, parameter);
}
CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option,
curl_off_t parameter) {
return curl_easy_setopt(handle, option, parameter);
}
CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, void* parameter) {
return curl_easy_getinfo(handle, option, parameter);
}
time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now) {
return curl_getdate(datestring, now);
}
CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, fd_set_sapi* read_fd_set,
fd_set_sapi* write_fd_set,
fd_set_sapi* exc_fd_set, int* max_fd) {
return curl_multi_fdset(multi_handle, read_fd_set, write_fd_set, exc_fd_set,
max_fd);
}
CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option,
void* parameter) {
return curl_multi_setopt(handle, option, parameter);
}
CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option,
long parameter) {
return curl_multi_setopt(handle, option, parameter);
}
CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMoption option,
curl_off_t parameter) {
return curl_multi_setopt(handle, option, parameter);
}
CURLMcode curl_multi_poll_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds,
unsigned int extra_nfds, int timeout_ms,
int* numfds) {
return curl_multi_poll(multi_handle, extra_fds, extra_nfds, timeout_ms,
numfds);
}
CURLMcode curl_multi_wait_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds,
unsigned int extra_nfds, int timeout_ms,
int* numfds) {
return curl_multi_wait(multi_handle, extra_fds, extra_nfds, timeout_ms,
numfds);
}
CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option,
void* parameter) {
return curl_share_setopt(handle, option, parameter);
}
CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option,
long parameter) {
return curl_share_setopt(handle, option, parameter);
}

View File

@ -0,0 +1,82 @@
// 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.
// Wrapper for curl library
#ifndef CURL_WRAPPER_H
#define CURL_WRAPPER_H
#include <curl/curl.h>
extern "C" {
// The wrapper method is needed to make the variadic argument explicit
CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, void* parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, long parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option,
curl_off_t parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, void* parameter);
// The typedef and wrapper method are needed because the original method has
// some conflicts in curl_sapi.sapi.h
typedef time_t time_t_sapi;
time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now);
// The typedef and wrapper method are needed because the original method has
// some conflicts in curl_sapi.sapi.h
typedef fd_set fd_set_sapi;
CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, fd_set_sapi* read_fd_set,
fd_set_sapi* write_fd_set,
fd_set_sapi* exc_fd_set, int* max_fd);
// The wrapper method is needed to make the variadic argument explicit
CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option,
void* parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option,
long parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMoption option,
curl_off_t parameter);
// The wrapper method is needed because incomplete array type is not supported
CURLMcode curl_multi_poll_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds,
unsigned int extra_nfds, int timeout_ms,
int* numfds);
// The wrapper method is needed because incomplete array type is not supported
CURLMcode curl_multi_wait_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds,
unsigned int extra_nfds, int timeout_ms,
int* numfds);
// The wrapper method is needed to make the variadic argument explicit
CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option,
void* parameter);
// The wrapper method is needed to make the variadic argument explicit
CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option,
long parameter);
}
#endif // CURL_WRAPPER_H

View File

@ -0,0 +1,76 @@
# 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.
# All the examples are sandboxed versions of curl's examples
# (https://curl.haxx.se/libcurl/c/example.html)
# Example 1: simple.c
add_executable(example1
example1.cc
../sandbox.h
)
target_link_libraries(example1 PRIVATE
curl_sapi
sapi::sapi
)
# Example 2: getinmemory.c
add_executable(example2
example2.cc
../sandbox.h
)
target_link_libraries(example2 PRIVATE
curl_sapi
sapi::sapi
)
# Example 3: simplessl.c
add_executable(example3
example3.cc
../sandbox.h
)
target_link_libraries(example3 PRIVATE
curl_sapi
sapi::sapi
)
# Example 4: multi-poll.c
add_executable(example4
example4.cc
../sandbox.h
)
target_link_libraries(example4 PRIVATE
curl_sapi
sapi::sapi
)
# Example 5: multithread.c
add_executable(example5
example5.cc
../sandbox.h
)
target_link_libraries(example5 PRIVATE
curl_sapi
sapi::sapi
)
# Example 6: simple.c (using transactions)
add_executable(example6
example6.cc
../sandbox.h
)
target_link_libraries(example6 PRIVATE
curl_sapi
sapi::sapi
)

View File

@ -0,0 +1,38 @@
# LibCurl Sandbox Examples
Each example in this folder is the sandboxed version of a code snippet from
[this page](https://curl.haxx.se/libcurl/c/example.html) on curl's website.
These examples perform some basic tasks using libcurl, and can be useful both to
understand how to use LibCurl Sandbox, but also to get an idea of how regular
and sandboxed code compare to each other.
This is the list of the examples:
- **example1**: sandboxed version of
[simple.c](https://curl.haxx.se/libcurl/c/simple.html). Really simple HTTP
request, downloads and prints out the page at
[example.com](http://example.com).
- **example2**: sandboxed version of
[getinmemory.c](https://curl.haxx.se/libcurl/c/getinmemory.html). Same HTTP
request as example1. The difference is that this example uses a callback to
save the page directly in memory. Only the page size is printed out.
- **example3**: sandboxed version of
[simplessl.c](https://curl.haxx.se/libcurl/c/simplessl.html). HTTPS request
of the [example.com](https://example.com) page, using SSL authentication.
This script takes 4 arguments (SSL certificates file, SSL keys file, SSL
keys password and CA certificates files), and prints out the page.
- **example4**: sandboxed version of
[multi-poll.c](https://curl.haxx.se/libcurl/c/multi-poll.html). Same HTTP
request as example1, with the addition of a polling method that can be used
to track the status of the request. The page is printed out after it is
downloaded.
- **example5**: sandboxed version of
[multithread.c](https://curl.haxx.se/libcurl/c/multithread.html). Four HTTP
request of the pages [example.com](http://example.com),
[example.edu](http://example.edu), [example.net](http://example.net) and
[example.org](http://example.org), performed at the same time using
libcurl's multithreading methods. The pages are printed out.
- **example6**: sandboxed version of
[simple.c](https://curl.haxx.se/libcurl/c/simple.html). Performs the same
tasks as example1, but Sandbox API Transactions are used to show how they
can be used to perform a simple request.

View File

@ -0,0 +1,78 @@
// 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.
// Sandboxed version of simple.c
// Simple HTTP GET request
#include <cstdlib>
#include "../sandbox.h" // NOLINT(build/include)
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) LOG(FATAL) << "curl_easy_init failed: curl is NULL";
absl::StatusOr<int> curl_code;
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the library to follow a redirection
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
}
// Disable authentication of peer certificate
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
}
// Cleanup curl
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,107 @@
// 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.
// Sandboxed version of getinmemory.c
// HTTP GET request using callbacks
#include <cstdlib>
#include <iostream>
#include "../sandbox.h" // NOLINT(build/include)
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
// Generate pointer to WriteMemoryCallback function
void* function_ptr;
status = sandbox.rpc_channel()->Symbol("WriteToMemory", &function_ptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel().Symbol failed: " << status;
}
sapi::v::RemotePtr remote_function_ptr(function_ptr);
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
}
absl::StatusOr<int> curl_code;
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set WriteMemoryCallback as the write function
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION,
&remote_function_ptr);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Pass 'chunk' struct to the callback function
sapi::v::LenVal chunk(0);
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set a user agent
sapi::v::ConstCStr user_agent("libcurl-agent/1.0");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT,
user_agent.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
}
// Retrieve memory size
status = sandbox.TransferFromSandboxee(&chunk);
if (!status.ok()) {
LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status;
}
std::cout << "memory size: " << chunk.GetDataSize() << " bytes" << std::endl;
// Cleanup curl
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,166 @@
// 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.
// Sandboxed version of simplessl.c
// HTTPS GET request
#include <cstdlib>
#include "../sandbox.h" // NOLINT(build/include)
class CurlSapiSandboxEx3 : public CurlSapiSandbox {
public:
CurlSapiSandboxEx3(std::string ssl_certificate, std::string ssl_key,
std::string ca_certificates)
: ssl_certificate(ssl_certificate),
ssl_key(ssl_key),
ca_certificates(ca_certificates) {}
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
// Add the syscalls and files missing in CurlSandbox to a new PolicyBuilder
auto policy_builder = std::make_unique<sandbox2::PolicyBuilder>();
(*policy_builder)
.AllowFutexOp(FUTEX_WAIT_PRIVATE)
.AllowGetPIDs()
.AllowGetRandom()
.AllowHandleSignals()
.AllowSyscall(__NR_sysinfo)
.AddFile(ssl_certificate)
.AddFile(ssl_key)
.AddFile(ca_certificates);
// Provide the new PolicyBuilder to ModifyPolicy in CurlSandbox
return CurlSapiSandbox::ModifyPolicy(policy_builder.get());
}
std::string ssl_certificate;
std::string ssl_key;
std::string ca_certificates;
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Get input parameters (should be absolute paths)
if (argc != 5) {
LOG(FATAL) << "wrong number of arguments (4 expected)";
}
std::string ssl_certificate = argv[1];
std::string ssl_key = argv[2];
std::string ssl_key_password = argv[3];
std::string ca_certificates = argv[4];
// Initialize sandbox2 and sapi
CurlSapiSandboxEx3 sandbox(ssl_certificate, ssl_key, ca_certificates);
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
}
// Initialize curl easy handle
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
}
// Specify URL to get (using HTTPS)
sapi::v::ConstCStr url("https://example.com");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the SSL certificate type to "PEM"
sapi::v::ConstCStr ssl_cert_type("PEM");
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE,
ssl_cert_type.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the certificate for client authentication
sapi::v::ConstCStr sapi_ssl_certificate(ssl_certificate.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT,
sapi_ssl_certificate.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the private key for client authentication
sapi::v::ConstCStr sapi_ssl_key(ssl_key.c_str());
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLKEY, sapi_ssl_key.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the password used to protect the private key
sapi::v::ConstCStr sapi_ssl_key_password(ssl_key_password.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD,
sapi_ssl_key_password.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Set the file with the certificates vaildating the server
sapi::v::ConstCStr sapi_ca_certificates(ca_certificates.c_str());
curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO,
sapi_ca_certificates.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Verify the authenticity of the server
curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
}
// Cleanup curl easy handle
status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,128 @@
// 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.
// Sandboxed version of multi-poll.c
// HTTP GET request with polling
#include <cstdlib>
#include "../sandbox.h" // NOLINT(build/include)
#include "curl_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
// Number of running handles
sapi::v::Int still_running(1);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
}
// Initialize http_handle
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr http_handle(curl_handle.value());
if (!http_handle.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: http_handle is NULL";
}
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
curl_code =
api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Initialize multi_handle
absl::StatusOr<CURLM*> curlm_handle = api.curl_multi_init();
if (!curlm_handle.ok()) {
LOG(FATAL) << "curl_multi_init failed: " << curlm_handle.status();
}
sapi::v::RemotePtr multi_handle(curlm_handle.value());
if (!multi_handle.GetValue()) {
LOG(FATAL) << "curl_multi_init failed: multi_handle is NULL";
}
// Add http_handle to the multi stack
curl_code = api.curl_multi_add_handle(&multi_handle, &http_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_add_handle failed: " << curl_code.status();
}
while (still_running.GetValue()) {
sapi::v::Int numfds(0);
// Perform the request
curl_code = api.curl_multi_perform(&multi_handle, still_running.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_mutli_perform failed: " << curl_code.status();
}
if (still_running.GetValue()) {
// Wait for an event or timeout
sapi::v::NullPtr null_ptr;
curl_code = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, 1000,
numfds.PtrBoth());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_poll_sapi failed: " << curl_code.status();
}
}
}
// Remove http_handle from the multi stack
curl_code = api.curl_multi_remove_handle(&multi_handle, &http_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_remove_handle failed: " << curl_code.status();
}
// Cleanup http_handle
status = api.curl_easy_cleanup(&http_handle);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
// Cleanup multi_handle
curl_code = api.curl_multi_cleanup(&multi_handle);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_cleanup failed: " << curl_code.status();
}
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,102 @@
// 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.
// Sandboxed version of multithread.c
// Multithreaded HTTP GET requests
#include <pthread.h>
#include <cstdlib>
#include "../sandbox.h" // NOLINT(build/include)
void pull_one_url(const std::string& url, CurlApi& api) {
// Initialize the curl session
absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status();
}
sapi::v::RemotePtr curl(curl_handle.value());
if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL";
}
absl::StatusOr<int> curl_code;
// Specify URL to get
sapi::v::ConstCStr sapi_url(url.c_str());
curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore());
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
}
// Perform the request
curl_code = api.curl_easy_perform(&curl);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
}
// Cleanup curl
absl::Status status = api.curl_easy_cleanup(&curl);
if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status;
}
}
const std::vector<std::string> urls = {
"http://example.com", "http://example.edu", "http://example.net",
"http://example.org"};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3)
curl_code = api.curl_global_init(3l);
if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
}
// Create the threads
std::vector<std::thread> threads;
for (auto& url : urls) {
threads.emplace_back(pull_one_url, std::ref(url), std::ref(api));
}
// Join the threads
for (auto& thread : threads) {
thread.join();
}
// Cleanup curl
status = api.curl_global_cleanup();
if (!status.ok()) {
LOG(FATAL) << "curl_global_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,72 @@
// 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.
// Sandboxed version of simple.c using transactions
// Simple HTTP GET request
#include <cstdlib>
#include "../sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/transaction.h"
class CurlTransaction : public sapi::Transaction {
public:
explicit CurlTransaction(std::unique_ptr<sapi::Sandbox> sandbox)
: sapi::Transaction(std::move(sandbox)) {
sapi::Transaction::SetTimeLimit(kTimeOutVal);
}
private:
// Default timeout value for each transaction run.
static constexpr time_t kTimeOutVal = 2;
// The main processing function.
absl::Status Main() override;
};
absl::Status CurlTransaction::Main() {
CurlApi api(sandbox());
// Initialize the curl session
SAPI_ASSIGN_OR_RETURN(void* curl_remote, api.curl_easy_init());
sapi::v::RemotePtr curl(curl_remote);
TRANSACTION_FAIL_IF_NOT(curl.GetValue(), "curl_easy_init failed");
// Specify URL to get
sapi::v::ConstCStr url("http://example.com");
SAPI_ASSIGN_OR_RETURN(
int setopt_url_code,
api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()));
TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK,
"curl_easy_setopt_ptr failed");
// Perform the request
SAPI_ASSIGN_OR_RETURN(int perform_code, api.curl_easy_perform(&curl));
TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK,
"curl_easy_perform failed");
// Cleanup curl
TRANSACTION_FAIL_IF_NOT(api.curl_easy_cleanup(&curl).ok(),
"curl_easy_cleanup failed");
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
CurlTransaction curl{std::make_unique<CurlSapiSandbox>()};
absl::Status status = curl.Run();
CHECK(status.ok()) << "CurlTransaction failed";
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,67 @@
// 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 SANDBOX_H_
#define SANDBOX_H_
#include <linux/futex.h>
#include <sys/mman.h> // For mmap arguments
#include <syscall.h>
#include <cstdlib>
#include "curl_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
class CurlSapiSandbox : public CurlSandbox {
protected:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder* policy_builder) override {
// Return a new policy
return (*policy_builder)
.AllowDynamicStartup()
.AllowExit()
.AllowFork()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowMmap()
.AllowOpen()
.AllowRead()
.AllowSafeFcntl()
.AllowWrite()
.AllowSyscalls({
__NR_accept,
__NR_access,
__NR_bind,
__NR_connect,
__NR_getpeername,
__NR_getsockname,
__NR_getsockopt,
__NR_ioctl,
__NR_listen,
__NR_madvise,
__NR_poll,
__NR_recvfrom,
__NR_recvmsg,
__NR_sendmmsg,
__NR_sendto,
__NR_setsockopt,
__NR_socket,
})
.AllowUnrestrictedNetworking()
.AddDirectory("/lib")
.BuildOrDie();
}
};
#endif // SANDBOX_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.
include(GoogleTest)
add_executable(tests
test_utils.h
test_utils.cc
tests.cc
)
target_link_libraries(tests
curl_sapi sapi::sapi
gtest gmock gtest_main
)
gtest_discover_tests(tests)

View File

@ -0,0 +1,301 @@
// 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 "test_utils.h" // NOLINT(build/include)
#include <absl/strings/match.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <thread> // NOLINT(build/c++11)
int CurlTestUtils::port_;
std::thread CurlTestUtils::server_thread_;
absl::Status CurlTestUtils::CurlTestSetUp() {
// Initialize sandbox2 and sapi
sandbox_ = std::make_unique<CurlSapiSandbox>();
absl::Status init = sandbox_->Init();
if (!init.ok()) {
return init;
}
api_ = std::make_unique<CurlApi>(sandbox_.get());
// Initialize curl
absl::StatusOr<CURL*> curl_handle = api_->curl_easy_init();
if (!curl_handle.ok()) {
return curl_handle.status();
}
if (!curl_handle.value()) {
return absl::UnavailableError("curl_easy_init returned NULL ");
}
curl_ = std::make_unique<sapi::v::RemotePtr>(curl_handle.value());
absl::StatusOr<int> curl_code;
// Specify request URL
sapi::v::ConstCStr sapi_url(kUrl.data());
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL,
sapi_url.PtrBefore());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
}
// Set port
curl_code = api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_);
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_long returned with the error code " +
curl_code.value());
}
// Generate pointer to the WriteToMemory callback
void* function_ptr;
absl::Status symbol =
sandbox_->rpc_channel()->Symbol("WriteToMemory", &function_ptr);
if (!symbol.ok()) {
return symbol;
}
sapi::v::RemotePtr remote_function_ptr(function_ptr);
// Set WriteToMemory as the write function
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION,
&remote_function_ptr);
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
}
// Pass memory chunk object to the callback
chunk_ = std::make_unique<sapi::v::LenVal>(0);
curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA,
chunk_->PtrBoth());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
return absl::UnavailableError(
"curl_easy_setopt_ptr returned with the error code " +
curl_code.value());
}
return absl::OkStatus();
}
absl::Status CurlTestUtils::CurlTestTearDown() {
// Cleanup curl
return api_->curl_easy_cleanup(curl_.get());
}
absl::StatusOr<std::string> CurlTestUtils::PerformRequest() {
// Perform the request
absl::StatusOr<int> curl_code = api_->curl_easy_perform(curl_.get());
if (!curl_code.ok()) {
return curl_code.status();
}
if (curl_code.value() != CURLE_OK) {
return absl::UnavailableError(
"curl_easy_perform returned with the error code " + curl_code.value());
}
// Get pointer to the memory chunk
absl::Status status = sandbox_->TransferFromSandboxee(chunk_.get());
if (!status.ok()) {
return status;
}
return std::string{reinterpret_cast<char*>(chunk_->GetData())};
}
namespace {
// Read the socket until str is completely read
std::string ReadUntil(const int socket, const std::string& str,
const size_t max_request_size) {
std::string str_read;
str_read.reserve(max_request_size);
// Read one char at a time until str is suffix of buf
while (!absl::EndsWith(str_read, str)) {
char next_char;
if (str_read.size() >= max_request_size ||
read(socket, &next_char, 1) < 1) {
return "";
}
str_read += next_char;
}
return str_read;
}
// Parse HTTP headers to return the Content-Length
ssize_t GetContentLength(const std::string& headers) {
// Find the Content-Length header
std::string::size_type length_header_start = headers.find("Content-Length: ");
// There is no Content-Length field
if (length_header_start == std::string::npos) {
return 0;
}
// Find Content-Length string
std::string::size_type length_start =
length_header_start + std::string{"Content-Length: "}.size();
std::string::size_type length_bytes =
headers.find("\r\n", length_start) - length_start;
// length_bytes exceeds maximum
if (length_bytes >= 64) {
return -1;
}
// Convert string to int and return
return std::stoi(headers.substr(length_start, length_bytes));
}
// Read exactly content_bytes from the socket
std::string ReadExact(int socket, size_t content_bytes) {
std::string str_read;
str_read.reserve(content_bytes);
// Read one char at a time until all chars are read
while (str_read.size() < content_bytes) {
char next_char;
if (read(socket, &next_char, 1) < 1) {
return "";
}
str_read += next_char;
}
return str_read;
}
// Listen on the socket and answer back to requests
void ServerLoop(int listening_socket, sockaddr_in socket_address) {
socklen_t socket_address_size = sizeof(socket_address);
// Listen on the socket (maximum 1 connection)
if (listen(listening_socket, 1) == -1) {
return;
}
// Keep accepting connections until the thread is terminated
// (i.e. server_thread_ is assigned to a new thread or destroyed)
for (;;) {
// File descriptor to the connection socket
// This blocks the thread until a connection is established
int accepted_socket =
accept(listening_socket, reinterpret_cast<sockaddr*>(&socket_address),
reinterpret_cast<socklen_t*>(&socket_address_size));
if (accepted_socket == -1) {
return;
}
constexpr int kMaxRequestSize = 4096;
// Read until the end of the headers
std::string headers =
ReadUntil(accepted_socket, "\r\n\r\n", kMaxRequestSize);
if (headers == "") {
close(accepted_socket);
return;
}
// Get the length of the request content
ssize_t content_length = GetContentLength(headers);
if (content_length > kMaxRequestSize - headers.size() ||
content_length < 0) {
close(accepted_socket);
return;
}
// Read the request content
std::string content = ReadExact(accepted_socket, content_length);
// Prepare a response for the request
std::string http_response =
"HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: ";
if (headers.substr(0, 3) == "GET") {
http_response += "2\r\n\r\nOK";
} else if (headers.substr(0, 4) == "POST") {
http_response +=
std::to_string(content.size()) + "\r\n\r\n" + std::string{content};
} else {
close(accepted_socket);
return;
}
// Ignore any errors, the connection will be closed anyway
write(accepted_socket, http_response.c_str(), http_response.size());
// Close the socket
close(accepted_socket);
}
}
} // namespace
void CurlTestUtils::StartMockServer() {
// Get the socket file descriptor
int listening_socket = socket(AF_INET, SOCK_STREAM, 0);
// Create the socket address object
// The port is set to 0, meaning that it will be auto assigned
// Only local connections can access this socket
sockaddr_in socket_address{AF_INET, 0, htonl(INADDR_LOOPBACK)};
socklen_t socket_address_size = sizeof(socket_address);
if (listening_socket == -1) {
return;
}
// Bind the file descriptor to the socket address object
if (bind(listening_socket, reinterpret_cast<sockaddr*>(&socket_address),
socket_address_size) == -1) {
return;
}
// Assign an available port to the socket address object
if (getsockname(listening_socket,
reinterpret_cast<sockaddr*>(&socket_address),
&socket_address_size) == -1) {
return;
}
// Get the port number
port_ = ntohs(socket_address.sin_port);
// Set server_thread_ operation to socket listening
server_thread_ = std::thread(ServerLoop, listening_socket, socket_address);
}

View File

@ -0,0 +1,55 @@
// 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 TESTS_H_
#define TESTS_H_
#include "../sandbox.h" // NOLINT(build/include)
#include "curl_sapi.sapi.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
// Helper class that can be used to test Curl Sandboxed
class CurlTestUtils {
protected:
// Initialize and set up the curl handle
absl::Status CurlTestSetUp();
// Clean up the curl handle
absl::Status CurlTestTearDown();
// Perform a request to the mock server, return the response
absl::StatusOr<std::string> PerformRequest();
// Start a mock server (only once) that will manage connections for the tests
// The server listens on a port asynchronously by creating a thread
// The port number is stored in port_
// Responds with "OK" to a GET request
// Responds with the POST request fields to a POST request
static void StartMockServer();
std::unique_ptr<CurlSapiSandbox> sandbox_;
std::unique_ptr<CurlApi> api_;
std::unique_ptr<sapi::v::RemotePtr> curl_;
static std::thread server_thread_;
static constexpr absl::string_view kUrl = "http://127.0.0.1/";
static int port_;
private:
std::unique_ptr<sapi::v::LenVal> chunk_;
};
#endif // TESTS_H_

View File

@ -0,0 +1,138 @@
// 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 "test_utils.h" // NOLINT(build/include)
class CurlTest : public CurlTestUtils, public ::testing::Test {
protected:
void SetUp() override {
// Start mock server, get port number and check for any error
StartMockServer();
ASSERT_TRUE(server_thread_.joinable());
ASSERT_TRUE(CurlTestSetUp().ok());
}
void TearDown() override {
ASSERT_TRUE(CurlTestTearDown().ok());
// Detach the server thread
server_thread_.detach();
}
};
TEST_F(CurlTest, EffectiveUrl) {
sapi::v::RemotePtr effective_url_ptr(nullptr);
ASSERT_TRUE(PerformRequest().ok());
// Get effective URL
SAPI_ASSERT_OK_AND_ASSIGN(
int getinfo_code,
api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_EFFECTIVE_URL,
effective_url_ptr.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
// Store effective URL in a string
SAPI_ASSERT_OK_AND_ASSIGN(std::string effective_url,
sandbox_->GetCString(sapi::v::RemotePtr(
effective_url_ptr.GetPointedVar())));
// Compare effective URL with original URL
ASSERT_EQ(effective_url, kUrl);
}
TEST_F(CurlTest, EffectivePort) {
sapi::v::Int effective_port;
ASSERT_TRUE(PerformRequest().ok());
// Get effective port
SAPI_ASSERT_OK_AND_ASSIGN(int getinfo_code, api_->curl_easy_getinfo_ptr(
curl_.get(), CURLINFO_PRIMARY_PORT,
effective_port.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
// Compare effective port with port set by the mock server
ASSERT_EQ(effective_port.GetValue(), port_);
}
TEST_F(CurlTest, ResponseCode) {
sapi::v::Int response_code;
ASSERT_TRUE(PerformRequest().ok());
// Get response code
SAPI_ASSERT_OK_AND_ASSIGN(
int getinfo_code,
api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_RESPONSE_CODE,
response_code.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
// Check response code
ASSERT_EQ(response_code.GetValue(), 200);
}
TEST_F(CurlTest, ContentType) {
sapi::v::RemotePtr content_type_ptr(nullptr);
ASSERT_TRUE(PerformRequest().ok());
// Get effective URL
SAPI_ASSERT_OK_AND_ASSIGN(int getinfo_code, api_->curl_easy_getinfo_ptr(
curl_.get(), CURLINFO_CONTENT_TYPE,
content_type_ptr.PtrBoth()));
ASSERT_EQ(getinfo_code, CURLE_OK);
// Store content type in a string
SAPI_ASSERT_OK_AND_ASSIGN(std::string content_type,
sandbox_->GetCString(sapi::v::RemotePtr(
content_type_ptr.GetPointedVar())));
// Compare content type with "text/plain"
ASSERT_EQ(content_type, "text/plain");
}
TEST_F(CurlTest, GETResponse) {
SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest());
// Compare response with expected response
ASSERT_EQ(response, "OK");
}
TEST_F(CurlTest, POSTResponse) {
sapi::v::ConstCStr post_fields("postfields");
// Set request method to POST
SAPI_ASSERT_OK_AND_ASSIGN(int setopt_post, api_->curl_easy_setopt_long(
curl_.get(), CURLOPT_POST, 1l));
ASSERT_EQ(setopt_post, CURLE_OK);
// Set the size of the POST fields
SAPI_ASSERT_OK_AND_ASSIGN(
int setopt_post_fields_size,
api_->curl_easy_setopt_long(curl_.get(), CURLOPT_POSTFIELDSIZE,
post_fields.GetSize()));
ASSERT_EQ(setopt_post_fields_size, CURLE_OK);
// Set the POST fields
SAPI_ASSERT_OK_AND_ASSIGN(
int setopt_post_fields,
api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_POSTFIELDS,
post_fields.PtrBefore()));
ASSERT_EQ(setopt_post_fields, CURLE_OK);
SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest());
// Compare response with expected response
ASSERT_EQ(std::string(post_fields.GetData()), response);
}

Binary file not shown.

View File

@ -0,0 +1,86 @@
# 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.10)
project(test CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
find_package(PNG REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(libpcre REQUIRED IMPORTED_TARGET libpcre)
pkg_check_modules(proj REQUIRED IMPORTED_TARGET proj)
set(SAPI_ROOT "${PROJECT_SOURCE_DIR}/../.." CACHE PATH "Path to the Sandboxed API source tree")
# 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_library(libgdal STATIC IMPORTED)
set_property(TARGET libgdal PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libgdal.a")
target_link_libraries(libgdal INTERFACE
crypto
expat
jpeg
PkgConfig::libpcre
PkgConfig::proj
sqlite3
tiff
z
pthread
m
rt
dl
curl
PNG::PNG
)
add_sapi_library(gdal_sapi
FUNCTIONS GDALOpen
GDALAllRegister
GDALGetDatasetDriver
GDALGetDriverShortName
GDALGetDriverLongName
GDALGetGeoTransform
GDALGetRasterBand
GDALGetBlockSize
GDALGetRasterBandXSize
GDALGetRasterBandYSize
GDALRasterIO
INPUTS "/usr/include/gdal/gdal.h"
LIBRARY libgdal
LIBRARY_NAME GDAL
NAMESPACE ""
)
target_include_directories(gdal_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
add_executable(raster
raster.cc
)
target_link_libraries(raster
gdal_sapi
sapi::sapi
)

View File

@ -0,0 +1,88 @@
# GDAL Raster GeoTIFF Workflow
```
Build Tools: CMake/Ninja
OS: Linux
```
### For installing GDAL:
```
sudo apt-get install python3.6-dev
sudo add-apt-repository ppa:ubuntugis/ppa && sudo apt update
sudo apt-get install gdal-bin
sudo apt-get install libgdal-dev
```
### Dependencies:
PNG: `sudo apt-get install libpng-dev`
PCRE: `sudo apt-get install libpcre3 libpcre3-dev`
PROJ: `sudo apt-get install libproj-dev`
OBS! You may need to set `export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib`.
It is required for libproj.so to be found into /usr/local/lib/. You can also fix
this by typing `locate libproj.so` which will give you
<the_absolute_libproj.so_path> and then `cp <the_absolute_libproj.so_path>
/usr/local/lib/`.
### Initializing GDAL submodule:
`git submodule add https://github.com/OSGeo/gdal/tree/master/gdal`
### Building GDAL statically
GNUmakefile from gdal/gdal can handle building the static library.
`cd gdal/gdal && make static-lib`
`cd ../.. && mkdir lib`
`cp gdal/gdal/libgdal.a lib/`
OBS! The file is huge! It may take a while.
### For testing:
`mkdir build && cd build`
`cmake .. -G Ninja`
`ninja`
`./raster <your_absolute_tiff_file_path>`
## About the project
GDAL is a translator library for raster and vector geospatial data format. The
project consist in rastering a GeoTIFF file format using GDAL functionalities
and sandboxed methods.
## Implementation
*Sandboxing...*
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 operations, system calls or namspaces access
may be controlled.
*Raster process...*
Useful functions from the `gdal.h` header are added to the SAPI library built
with CMake.
One .tiff file is manipulated with GDALOpen functionality, which extracts a
pointer to the data set containg a list of raster bands, all pertaining to the
same area. Metadata, a coordinate system, a georeferencing transform, size of
raster and various other information are kept into the data set that corresponds
to the image.
To create an array containing the image information, the dimentions needed are
extracted using some specific GDAL(X/Y)Size functions applied to the block.
GDALRasterBand function takes care of data type conversion, one more step
following: placing the converted data (with RasterIO method) into the created
and well allocated structure.

View File

@ -0,0 +1,195 @@
// 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 <gflags/gflags.h>
#include <syscall.h>
#include <fstream>
#include <iostream>
#include <glog/logging.h>
#include "gdal_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
class GdalSapiSandbox : public GDALSandbox {
public:
GdalSapiSandbox(std::string path)
: GDALSandbox(), file_path_(std::move(path)) {}
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowRead()
.AllowSystemMalloc()
.AllowWrite()
.AllowExit()
.AllowStat()
.AllowOpen()
.AllowSyscalls({
__NR_futex,
__NR_close,
__NR_recvmsg,
__NR_getdents64,
__NR_lseek,
__NR_getpid,
__NR_sysinfo,
__NR_prlimit64,
__NR_ftruncate,
__NR_unlink,
})
.AddFile(file_path_)
.BuildOrDie();
}
private:
std::string file_path_;
};
absl::Status GdalMain(std::string filename) {
// Reading GDALDataset from a (local, specific) file.
GdalSapiSandbox sandbox(filename);
SAPI_RETURN_IF_ERROR(sandbox.Init());
GDALApi api(&sandbox);
sapi::v::CStr s(filename.data());
SAPI_RETURN_IF_ERROR(api.GDALAllRegister());
auto open = api.GDALOpen(s.PtrBoth(), GDALAccess::GA_ReadOnly);
LOG(INFO) << "Dataset pointer adress: " << open.value() << std::endl;
sapi::v::RemotePtr ptr_dataset(open.value());
LOG(INFO) << ptr_dataset.ToString() << std::endl;
if (!open.value()) {
return absl::AbortedError("NULL pointer for Dataset.\n");
}
// Printing some general information about the dataset.
auto driver = api.GDALGetDatasetDriver(&ptr_dataset);
sapi::v::RemotePtr ptr_driver(driver.value());
auto driver_short_name = api.GDALGetDriverShortName(&ptr_driver);
auto driver_long_name = api.GDALGetDriverLongName(&ptr_driver);
sapi::v::RemotePtr ptr_driver_short_name(driver_short_name.value());
sapi::v::RemotePtr ptr_driver_long_name(driver_long_name.value());
LOG(INFO) << "Driver short name: "
<< sandbox.GetCString(ptr_driver_short_name).value().c_str();
LOG(INFO) << "Driver long name: "
<< sandbox.GetCString(ptr_driver_long_name).value().c_str();
// Checking that GetGeoTransform is valid.
std::vector<double> adf_geo_transform(6);
sapi::v::Array<double> adf_geo_transform_array(&adf_geo_transform[0],
adf_geo_transform.size());
// For this function that returns CPLErr, the error-handling must be done
// analyzing the returning object.
// Same for GDALReturnsIO from below.
CPLErr err;
SAPI_ASSIGN_OR_RETURN(err, api.GDALGetGeoTransform(
&ptr_dataset, adf_geo_transform_array.PtrBoth()));
// If GDALGetGeoTransform generates an error.
if (err != CE_None) {
return absl::InternalError("GDAL rasterization failed.");
}
LOG(INFO) << "Origin = (" << adf_geo_transform[0] << ", "
<< adf_geo_transform[3] << ")" << std::endl;
LOG(INFO) << "Pixel Size = (" << adf_geo_transform[0] << ", "
<< adf_geo_transform[3] << ")" << std::endl;
std::vector<int> n_blockX_size(1);
std::vector<int> n_blockY_size(1);
sapi::v::Array<int> nBlockXSizeArray(&n_blockX_size[0], n_blockX_size.size());
sapi::v::Array<int> nBlockYSizeArray(&n_blockY_size[0], n_blockY_size.size());
auto band = api.GDALGetRasterBand(&ptr_dataset, 1);
LOG(INFO) << "Band pointer adress: " << band.value() << std::endl;
if (!band.value()) {
return absl::AbortedError("NULL pointer for Band.\n");
}
sapi::v::RemotePtr ptr_band(band.value());
SAPI_RETURN_IF_ERROR(api.GDALGetBlockSize(&ptr_band, nBlockXSizeArray.PtrBoth(),
nBlockYSizeArray.PtrBoth()));
LOG(INFO) << "Block = " << n_blockX_size[0] << " x " << n_blockY_size[0]
<< std::endl;
std::vector<int> b_got_min(1);
std::vector<int> b_got_max(1);
sapi::v::Array<int> b_got_min_array(&b_got_min[0], b_got_min.size());
sapi::v::Array<int> b_got_max_array(&b_got_max[0], b_got_max.size());
auto nX_size = api.GDALGetRasterBandXSize(&ptr_band);
auto nY_size = api.GDALGetRasterBandYSize(&ptr_band);
std::vector<int8_t> raster_data(nX_size.value() * nY_size.value(), -1);
sapi::v::Array<int8_t> raster_data_array(&raster_data[0], raster_data.size());
// We will use CPLErr type of returning value, as before with
// GDALGetGeoTransorm.
SAPI_ASSIGN_OR_RETURN(
err, api.GDALRasterIO(&ptr_band, GF_Read, 0, 0, nX_size.value(),
nY_size.value(), raster_data_array.PtrBoth(),
nX_size.value(), nY_size.value(), GDT_Byte, 0, 0));
// If GDALRasterIO generates an error.
if (err != CE_None) {
return absl::InternalError("GDAL rasterization failed.");
}
std::cout << "Raster data info: " << raster_data_array.ToString()
<< std::endl;
// To print the data content: `std::cout << raster_data_array.GetData() <<
// std::endl;`
return absl::OkStatus();
}
int main(int argc, char** argv) {
// The file to be converted should be specified in the first argument while
// running the program.
if (argc < 2) {
std::cout << "You need to provide a file name: ./raster "
"your_tiff_file_absolute_path\n"
"Example: ./raster /usr/home/username/file.tiff"
<< std::endl;
return EXIT_FAILURE;
}
std::ifstream aux_file;
aux_file.open(argv[1]);
if (!aux_file.is_open()) {
std::cout << "Your file name is not valid.\nUnable to open the file."
<< std::endl;
return EXIT_FAILURE;
}
std::string filename(argv[1]);
if (absl::Status status = GdalMain(filename); !status.ok()) {
LOG(ERROR) << "Initialization failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,2 @@
# Build in C++17 mode without a custom CROSSTOOL
build --cxxopt=-std=c++17

View File

@ -0,0 +1,83 @@
# 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.
load(
"@com_google_sandboxed_api//sandboxed_api/bazel:sapi.bzl",
"sapi_library",
)
licenses(["notice"])
cc_library(
name = "guetzli_wrapper",
srcs = ["guetzli_entry_points.cc"],
hdrs = ["guetzli_entry_points.h"],
deps = [
"@com_google_sandboxed_api//sandboxed_api:lenval_core",
"@com_google_sandboxed_api//sandboxed_api:vars",
"@guetzli//:guetzli_lib",
"@png_archive//:png",
],
)
sapi_library(
name = "guetzli_sapi",
srcs = ["guetzli_transaction.cc"],
hdrs = [
"guetzli_sandbox.h",
"guetzli_transaction.h",
],
functions = [
"ProcessJpeg",
"ProcessRgb",
"WriteDataToFd",
],
input_files = ["guetzli_entry_points.h"],
lib = ":guetzli_wrapper",
lib_name = "Guetzli",
namespace = "guetzli::sandbox",
visibility = ["//visibility:public"],
)
cc_binary(
name = "guetzli_sandboxed",
srcs = ["guetzli_sandboxed.cc"],
deps = [
":guetzli_sapi",
],
)
cc_test(
name = "transaction_tests",
size = "large",
srcs = ["guetzli_transaction_test.cc"],
data = glob(["testdata/*"]),
visibility = ["//visibility:public"],
deps = [
"//:guetzli_sapi",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "sapi_lib_tests",
size = "large",
srcs = ["guetzli_sapi_test.cc"],
data = glob(["testdata/*"]),
visibility = ["//visibility:public"],
deps = [
"//:guetzli_sapi",
"@com_google_googletest//:gtest_main",
],
)

View File

@ -0,0 +1,32 @@
# Guetzli Sandbox
This is an example implementation of a sandbox for the [Guetzli](https://github.com/google/guetzli) library using [Sandboxed API](https://github.com/google/sandboxed-api).
Please read Guetzli's [documentation](https://github.com/google/guetzli#introduction) to learn more about it.
## Implementation details
Because Guetzli provides a C++ API and SAPI requires functions to be `extern "C"`, a wrapper library has been written for the compatibility. SAPI provides a Transaction class, which is a convenient way to create a wrapper for your sandboxed API that handles internal errors. The original Guetzli has a command-line utility to encode images, so a fully compatible utility that uses sandboxed Guetzli is provided.
The wrapper around Guetzli uses file descriptors to pass data to the sandbox. This approach restricts the sandbox from using the `open()` syscall and also helps to prevent making copies of data, because you need to synchronize it between processes.
## Build Guetzli Sandboxed
Right now Sandboxed API support only Linux systems, so you need one to build it. Guetzli sandboxed uses [Bazel](https://bazel.build/) as a build system so you need to [install it](https://docs.bazel.build/versions/3.4.0/install.html) before building.
To build Guetzli sandboxed encoding utility you can use this command:
`bazel build //:guetzli_sandboxed`
Then you can use it in this way:
```
guetzli_sandboxed [--quality Q] [--verbose] original.png output.jpg
guetzli_sandboxed [--quality Q] [--verbose] original.jpg output.jpg
```
Refer to Guetzli's [documentation](https://github.com/google/guetzli#using) to read more about usage.
## Examples
There are two different sets of unit tests which demonstrate how to use different parts of Guetzli sandboxed:
* `tests/guetzli_sapi_test.cc` - example usage of Guetzli sandboxed API.
* `tests/guetzli_transaction_test.cc` - example usage of Guetzli transaction.
To run tests use the following command:
`bazel test ...`
Also, there is an example of custom security policy for your sandbox in
`guetzli_sandbox.h`

View File

@ -0,0 +1,100 @@
# 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.
workspace(name = "guetzli_sandboxed")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
# Include the Sandboxed API dependency if it does not already exist in this
# project. This ensures that this workspace plays well with other external
# dependencies that might use Sandboxed API.
maybe(
git_repository,
name = "com_google_sandboxed_api",
# This example depends on the latest master. In an embedding project, it
# is advisable to pin Sandboxed API to a specific revision instead.
# commit = "ba47adc21d4c9bc316f3c7c32b0faaef952c111e", # 2020-05-15
branch = "master",
remote = "https://github.com/google/sandboxed-api.git",
)
# From here on, Sandboxed API files are available. The statements below setup
# transitive dependencies such as Abseil. Like above, those will only be
# included if they don't already exist in the project.
load(
"@com_google_sandboxed_api//sandboxed_api/bazel:sapi_deps.bzl",
"sapi_deps",
)
sapi_deps()
# Need to separately setup Protobuf dependencies in order for the build rules
# to work.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
http_archive(
name = "guetzli",
build_file = "guetzli.BUILD",
sha256 = "39632357e49db83d9560bf0de560ad833352f36d23b109b0e995b01a37bddb57",
strip_prefix = "guetzli-master",
url = "https://github.com/google/guetzli/archive/master.zip",
)
http_archive(
name = "butteraugli",
build_file = "butteraugli.BUILD",
sha256 = "39632357e49db83d9560bf0de560ad833352f36d23b109b0e995b01a37bddb57",
strip_prefix = "guetzli-master/third_party/butteraugli",
url = "https://github.com/google/guetzli/archive/master.zip",
)
http_archive(
name = "png_archive",
build_file = "png.BUILD",
sha256 = "a941dc09ca00148fe7aaf4ecdd6a67579c293678ed1e1cf633b5ffc02f4f8cf7",
strip_prefix = "libpng-1.2.57",
url = "http://github.com/glennrp/libpng/archive/v1.2.57.zip",
)
http_archive(
name = "jpeg_archive",
build_file = "jpeg.BUILD",
sha256 = "240fd398da741669bf3c90366f58452ea59041cacc741a489b99f2f6a0bad052",
strip_prefix = "jpeg-9b",
url = "http://www.ijg.org/files/jpegsrc.v9b.tar.gz",
)
http_archive(
name = "net_zlib",
build_file = "zlib.BUILD",
sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", # 2020-04-23
strip_prefix = "zlib-1.2.11",
urls = [
"https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz",
"https://www.zlib.net/zlib-1.2.11.tar.gz",
],
)
# GoogleTest/GoogleMock
maybe(
http_archive,
name = "com_google_googletest",
sha256 = "a6ab7c7d6fd4dd727f6012b5d85d71a73d3aa1274f529ecd4ad84eb9ec4ff767", # 2020-04-16
strip_prefix = "googletest-dcc92d0ab6c4ce022162a23566d44f673251eee4",
urls = ["https://github.com/google/googletest/archive/dcc92d0ab6c4ce022162a23566d44f673251eee4.zip"],
)

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.
licenses(["notice"])
cc_library(
name = "butteraugli",
srcs = [
"butteraugli/butteraugli.cc",
"butteraugli/butteraugli.h",
],
hdrs = [
"butteraugli/butteraugli.h",
],
copts = ["-Wno-sign-compare"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,32 @@
# 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.
licenses(["notice"])
cc_library(
name = "guetzli_lib",
srcs = glob(
[
"guetzli/*.h",
"guetzli/*.cc",
"guetzli/*.inc",
],
exclude = ["guetzli/guetzli.cc"],
),
copts = [ "-Wno-sign-compare" ],
visibility= [ "//visibility:public" ],
deps = [
"@butteraugli//:butteraugli",
],
)

View File

@ -0,0 +1,89 @@
# Description:
# The Independent JPEG Group's JPEG runtime library.
licenses(["notice"]) # custom notice-style license, see LICENSE
cc_library(
name = "jpeg",
srcs = [
"cderror.h",
"cdjpeg.h",
"jaricom.c",
"jcapimin.c",
"jcapistd.c",
"jcarith.c",
"jccoefct.c",
"jccolor.c",
"jcdctmgr.c",
"jchuff.c",
"jcinit.c",
"jcmainct.c",
"jcmarker.c",
"jcmaster.c",
"jcomapi.c",
"jconfig.h",
"jcparam.c",
"jcprepct.c",
"jcsample.c",
"jctrans.c",
"jdapimin.c",
"jdapistd.c",
"jdarith.c",
"jdatadst.c",
"jdatasrc.c",
"jdcoefct.c",
"jdcolor.c",
"jdct.h",
"jddctmgr.c",
"jdhuff.c",
"jdinput.c",
"jdmainct.c",
"jdmarker.c",
"jdmaster.c",
"jdmerge.c",
"jdpostct.c",
"jdsample.c",
"jdtrans.c",
"jerror.c",
"jfdctflt.c",
"jfdctfst.c",
"jfdctint.c",
"jidctflt.c",
"jidctfst.c",
"jidctint.c",
"jinclude.h",
"jmemmgr.c",
"jmemnobs.c",
"jmemsys.h",
"jmorecfg.h",
"jquant1.c",
"jquant2.c",
"jutils.c",
"jversion.h",
"transupp.h",
],
hdrs = [
"jerror.h",
"jpegint.h",
"jpeglib.h",
],
includes = ["."],
visibility = ["//visibility:public"],
)
genrule(
name = "configure",
outs = ["jconfig.h"],
cmd = "cat <<EOF >$@\n" +
"#define HAVE_PROTOTYPES 1\n" +
"#define HAVE_UNSIGNED_CHAR 1\n" +
"#define HAVE_UNSIGNED_SHORT 1\n" +
"#define HAVE_STDDEF_H 1\n" +
"#define HAVE_STDLIB_H 1\n" +
"#ifdef WIN32\n" +
"#define INLINE __inline\n" +
"#else\n" +
"#define INLINE __inline__\n" +
"#endif\n" +
"EOF\n",
)

View File

@ -0,0 +1,33 @@
# Description:
# libpng is the official PNG reference library.
licenses(["notice"]) # BSD/MIT-like license
cc_library(
name = "png",
srcs = [
"png.c",
"pngerror.c",
"pngget.c",
"pngmem.c",
"pngpread.c",
"pngread.c",
"pngrio.c",
"pngrtran.c",
"pngrutil.c",
"pngset.c",
"pngtrans.c",
"pngwio.c",
"pngwrite.c",
"pngwtran.c",
"pngwutil.c",
],
hdrs = [
"png.h",
"pngconf.h",
],
includes = ["."],
linkopts = ["-lm"],
visibility = ["//visibility:public"],
deps = ["@net_zlib//:zlib"],
)

View File

@ -0,0 +1,56 @@
# Copyright 2019 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.
licenses(["notice"])
cc_library(
name = "zlib",
srcs = [
"adler32.c",
"compress.c",
"crc32.c",
"deflate.c",
"gzclose.c",
"gzlib.c",
"gzread.c",
"gzwrite.c",
"infback.c",
"inffast.c",
"inflate.c",
"inflate.h",
"inftrees.c",
"trees.c",
"trees.h",
"uncompr.c",
"zutil.c",
],
hdrs = ["zlib.h"],
# Use -Dverbose=-1 to turn off zlib's trace logging. (#3280)
copts = [
"-w",
"-Dverbose=-1",
],
includes = ["."],
textual_hdrs = [
"crc32.h",
"deflate.h",
"gzguts.h",
"inffast.h",
"inffixed.h",
"inftrees.h",
"zconf.h",
"zutil.h",
],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,293 @@
// 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 "guetzli_entry_points.h" // NOLINT(build/include)
#include <sys/stat.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include "guetzli/jpeg_data_reader.h"
#include "guetzli/quality.h"
#include "png.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/util/statusor.h"
namespace {
constexpr int kBytesPerPixel = 350;
constexpr int kLowestMemusageMB = 100;
struct GuetzliInitData {
std::string in_data;
guetzli::Params params;
guetzli::ProcessStats stats;
};
struct ImageData {
int xsize;
int ysize;
std::vector<uint8_t> rgb;
};
sapi::LenValStruct CreateLenValFromData(const void* data, size_t size) {
void* new_data = malloc(size);
memcpy(new_data, data, size);
return {size, new_data};
}
sapi::StatusOr<std::string> ReadFromFd(int fd) {
struct stat file_data;
int status = fstat(fd, &file_data);
if (status < 0) {
return absl::FailedPreconditionError("Error reading input from fd");
}
std::string result;
result.resize(file_data.st_size);
status = read(fd, result.data(), result.size());
if (status < 0) {
return absl::FailedPreconditionError("Error reading input from fd");
}
return result;
}
sapi::StatusOr<GuetzliInitData> PrepareDataForProcessing(
const ProcessingParams& processing_params) {
sapi::StatusOr<std::string> input = ReadFromFd(processing_params.remote_fd);
if (!input.ok()) {
return input.status();
}
guetzli::Params guetzli_params;
guetzli_params.butteraugli_target = static_cast<float>(
guetzli::ButteraugliScoreForQuality(processing_params.quality));
guetzli::ProcessStats stats;
if (processing_params.verbose) {
stats.debug_output_file = stderr;
}
return GuetzliInitData{std::move(input.value()), guetzli_params, stats};
}
inline uint8_t BlendOnBlack(const uint8_t val, const uint8_t alpha) {
return (static_cast<int>(val) * static_cast<int>(alpha) + 128) / 255;
}
// Modified version of ReadPNG from original guetzli.cc
sapi::StatusOr<ImageData> ReadPNG(const std::string& data) {
std::vector<uint8_t> rgb;
int xsize, ysize;
png_structp png_ptr =
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_ptr) {
return absl::FailedPreconditionError(
"Error reading PNG data from input file");
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
return absl::FailedPreconditionError(
"Error reading PNG data from input file");
}
if (setjmp(png_jmpbuf(png_ptr)) != 0) {
// Ok we are here because of the setjmp.
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return absl::FailedPreconditionError(
"Error reading PNG data from input file");
}
std::istringstream memstream(data, std::ios::in | std::ios::binary);
png_set_read_fn(
png_ptr, static_cast<void*>(&memstream),
[](png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) {
std::istringstream& memstream =
*static_cast<std::istringstream*>(png_get_io_ptr(png_ptr));
memstream.read(reinterpret_cast<char*>(outBytes), byteCountToRead);
if (memstream.eof()) png_error(png_ptr, "unexpected end of data");
if (memstream.fail()) png_error(png_ptr, "read from memory error");
});
// The png_transforms flags are as follows:
// packing == convert 1,2,4 bit images,
// strip == 16 -> 8 bits / channel,
// shift == use sBIT dynamics, and
// expand == palettes -> rgb, grayscale -> 8 bit images, tRNS -> alpha.
const unsigned int png_transforms =
PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16;
png_read_png(png_ptr, info_ptr, png_transforms, nullptr);
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
xsize = png_get_image_width(png_ptr, info_ptr);
ysize = png_get_image_height(png_ptr, info_ptr);
rgb.resize(3 * xsize * ysize);
const int components = png_get_channels(png_ptr, info_ptr);
switch (components) {
case 1: {
// GRAYSCALE
for (int y = 0; y < ysize; ++y) {
const uint8_t* row_in = row_pointers[y];
uint8_t* row_out = &rgb[3 * y * xsize];
for (int x = 0; x < xsize; ++x) {
const uint8_t gray = row_in[x];
row_out[3 * x + 0] = gray;
row_out[3 * x + 1] = gray;
row_out[3 * x + 2] = gray;
}
}
break;
}
case 2: {
// GRAYSCALE + ALPHA
for (int y = 0; y < ysize; ++y) {
const uint8_t* row_in = row_pointers[y];
uint8_t* row_out = &rgb[3 * y * xsize];
for (int x = 0; x < xsize; ++x) {
const uint8_t gray = BlendOnBlack(row_in[2 * x], row_in[2 * x + 1]);
row_out[3 * x + 0] = gray;
row_out[3 * x + 1] = gray;
row_out[3 * x + 2] = gray;
}
}
break;
}
case 3: {
// RGB
for (int y = 0; y < ysize; ++y) {
const uint8_t* row_in = row_pointers[y];
uint8_t* row_out = &rgb[3 * y * xsize];
memcpy(row_out, row_in, 3 * xsize);
}
break;
}
case 4: {
// RGBA
for (int y = 0; y < ysize; ++y) {
const uint8_t* row_in = row_pointers[y];
uint8_t* row_out = &rgb[3 * y * xsize];
for (int x = 0; x < xsize; ++x) {
const uint8_t alpha = row_in[4 * x + 3];
row_out[3 * x + 0] = BlendOnBlack(row_in[4 * x + 0], alpha);
row_out[3 * x + 1] = BlendOnBlack(row_in[4 * x + 1], alpha);
row_out[3 * x + 2] = BlendOnBlack(row_in[4 * x + 2], alpha);
}
}
break;
}
default:
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return absl::FailedPreconditionError(
"Error reading PNG data from input file");
}
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return ImageData{xsize, ysize, std::move(rgb)};
}
bool CheckMemoryLimitExceeded(int memlimit_mb, int xsize, int ysize) {
double pixels = static_cast<double>(xsize) * ysize;
return memlimit_mb != -1 &&
(pixels * kBytesPerPixel / (1 << 20) > memlimit_mb ||
memlimit_mb < kLowestMemusageMB);
}
} // namespace
extern "C" bool ProcessJpeg(const ProcessingParams* processing_params,
sapi::LenValStruct* output) {
auto processing_data = PrepareDataForProcessing(*processing_params);
if (!processing_data.ok()) {
std::cerr << processing_data.status().ToString() << std::endl;
return false;
}
guetzli::JPEGData jpg_header;
if (!guetzli::ReadJpeg(processing_data->in_data, guetzli::JPEG_READ_HEADER,
&jpg_header)) {
std::cerr << "Error reading JPG data from input file" << std::endl;
return false;
}
if (CheckMemoryLimitExceeded(processing_params->memlimit_mb, jpg_header.width,
jpg_header.height)) {
std::cerr << "Memory limit would be exceeded" << std::endl;
return false;
}
std::string out_data;
if (!guetzli::Process(processing_data->params, &processing_data->stats,
processing_data->in_data, &out_data)) {
std::cerr << "Guezli processing failed" << std::endl;
return false;
}
*output = CreateLenValFromData(out_data.data(), out_data.size());
return true;
}
extern "C" bool ProcessRgb(const ProcessingParams* processing_params,
sapi::LenValStruct* output) {
auto processing_data = PrepareDataForProcessing(*processing_params);
if (!processing_data.ok()) {
std::cerr << processing_data.status().ToString() << std::endl;
return false;
}
auto png_data = ReadPNG(processing_data->in_data);
if (!png_data.ok()) {
std::cerr << "Error reading PNG data from input file" << std::endl;
return false;
}
if (CheckMemoryLimitExceeded(processing_params->memlimit_mb, png_data->xsize,
png_data->ysize)) {
std::cerr << "Memory limit would be exceeded" << std::endl;
return false;
}
std::string out_data;
if (!guetzli::Process(processing_data->params, &processing_data->stats,
png_data->rgb, png_data->xsize, png_data->ysize,
&out_data)) {
std::cerr << "Guetzli processing failed" << std::endl;
return false;
}
*output = CreateLenValFromData(out_data.data(), out_data.size());
return true;
}
extern "C" bool WriteDataToFd(int fd, sapi::LenValStruct* data) {
return sandbox2::file_util::fileops::WriteToFD(
fd, static_cast<const char*>(data->data), data->size);
}

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.
#ifndef GUETZLI_SANDBOXED_GUETZLI_ENTRY_POINTS_H_
#define GUETZLI_SANDBOXED_GUETZLI_ENTRY_POINTS_H_
#include "guetzli/processor.h"
#include "sandboxed_api/lenval_core.h"
#include "sandboxed_api/vars.h"
struct ProcessingParams {
int remote_fd = -1;
int verbose = 0;
int quality = 0;
int memlimit_mb = 0;
};
extern "C" bool ProcessJpeg(const ProcessingParams* processing_params,
sapi::LenValStruct* output);
extern "C" bool ProcessRgb(const ProcessingParams* processing_params,
sapi::LenValStruct* output);
extern "C" bool WriteDataToFd(int fd, sapi::LenValStruct* data);
#endif // GUETZLI_SANDBOXED_GUETZLI_ENTRY_POINTS_H_

View File

@ -0,0 +1,45 @@
// 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 GUETZLI_SANDBOXED_GUETZLI_SANDBOX_H_
#define GUETZLI_SANDBOXED_GUETZLI_SANDBOX_H_
#include <syscall.h>
#include "guetzli_sapi.sapi.h" // NOLINT(build/include)
namespace guetzli::sandbox {
class GuetzliSapiSandbox : public GuetzliSandbox {
public:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowRead()
.AllowSystemMalloc()
.AllowWrite()
.AllowExit()
.AllowStat()
.AllowSyscalls({
__NR_futex, __NR_close,
__NR_recvmsg // To work with remote fd
})
.BuildOrDie();
}
};
} // namespace guetzli::sandbox
#endif // GUETZLI_SANDBOXED_GUETZLI_SANDBOX_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.
#include <cstdio>
#include <iostream>
#include "guetzli_transaction.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/util/statusor.h"
namespace {
constexpr int kDefaultJPEGQuality = 95;
constexpr int kDefaultMemlimitMB = 6000;
void Usage() {
fprintf(stderr,
"Guetzli JPEG compressor. Usage: \n"
"guetzli [flags] input_filename output_filename\n"
"\n"
"Flags:\n"
" --verbose - Print a verbose trace of all attempts to standard "
"output.\n"
" --quality Q - Visual quality to aim for, expressed as a JPEG "
"quality value.\n"
" Default value is %d.\n"
" --memlimit M - Memory limit in MB. Guetzli will fail if unable to "
"stay under\n"
" the limit. Default limit is %d MB.\n"
" --nomemlimit - Do not limit memory usage.\n",
kDefaultJPEGQuality, kDefaultMemlimitMB);
exit(1);
}
} // namespace
int main(int argc, const char** argv) {
int verbose = 0;
int quality = kDefaultJPEGQuality;
int memlimit_mb = kDefaultMemlimitMB;
int opt_idx = 1;
for (; opt_idx < argc; opt_idx++) {
if (strnlen(argv[opt_idx], 2) < 2 || argv[opt_idx][0] != '-' ||
argv[opt_idx][1] != '-')
break;
if (!strcmp(argv[opt_idx], "--verbose")) {
verbose = 1;
} else if (!strcmp(argv[opt_idx], "--quality")) {
opt_idx++;
if (opt_idx >= argc) Usage();
quality = atoi(argv[opt_idx]); // NOLINT(runtime/deprecated_fn)
} else if (!strcmp(argv[opt_idx], "--memlimit")) {
opt_idx++;
if (opt_idx >= argc) Usage();
memlimit_mb = atoi(argv[opt_idx]); // NOLINT(runtime/deprecated_fn)
} else if (!strcmp(argv[opt_idx], "--nomemlimit")) {
memlimit_mb = -1;
} else if (!strcmp(argv[opt_idx], "--")) {
opt_idx++;
break;
} else {
fprintf(stderr, "Unknown commandline flag: %s\n", argv[opt_idx]);
Usage();
}
}
if (argc - opt_idx != 2) {
Usage();
}
guetzli::sandbox::TransactionParams params = {
argv[opt_idx], argv[opt_idx + 1], verbose, quality, memlimit_mb};
guetzli::sandbox::GuetzliTransaction transaction(std::move(params));
auto result = transaction.Run();
if (!result.ok()) {
std::cerr << result.ToString() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,124 @@
// 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 <sys/stat.h>
#include <sys/types.h>
#include <syscall.h>
#include <algorithm>
#include <fstream>
#include <memory>
#include <sstream>
#include "guetzli_sandbox.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/vars.h"
namespace guetzli::sandbox::tests {
namespace {
constexpr absl::string_view kInPngFilename = "bees.png";
constexpr absl::string_view kInJpegFilename = "nature.jpg";
constexpr absl::string_view kPngReferenceFilename = "bees_reference.jpg";
constexpr absl::string_view kJpegReferenceFIlename = "nature_reference.jpg";
constexpr int kDefaultQualityTarget = 95;
constexpr int kDefaultMemlimitMb = 6000;
constexpr absl::string_view kRelativePathToTestdata =
"/guetzli_sandboxed/testdata/";
std::string GetPathToInputFile(absl::string_view filename) {
return absl::StrCat(getenv("TEST_SRCDIR"), kRelativePathToTestdata, filename);
}
std::string ReadFromFile(const std::string& filename) {
std::ifstream stream(filename, std::ios::binary);
if (!stream.is_open()) {
return "";
}
std::stringstream result;
result << stream.rdbuf();
return result.str();
}
} // namespace
class GuetzliSapiTest : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<GuetzliSapiSandbox>();
ASSERT_EQ(sandbox_->Init(), absl::OkStatus());
api_ = std::make_unique<GuetzliApi>(sandbox_.get());
}
std::unique_ptr<GuetzliSapiSandbox> sandbox_;
std::unique_ptr<GuetzliApi> api_;
};
// This test can take up to few minutes depending on your hardware
TEST_F(GuetzliSapiTest, ProcessRGB) {
sapi::v::Fd in_fd(open(GetPathToInputFile(kInPngFilename).c_str(), O_RDONLY));
ASSERT_TRUE(in_fd.GetValue() != -1) << "Error opening input file";
ASSERT_EQ(api_->sandbox()->TransferToSandboxee(&in_fd), absl::OkStatus())
<< "Error transfering fd to sandbox";
ASSERT_TRUE(in_fd.GetRemoteFd() != -1) << "Error opening remote fd";
sapi::v::Struct<ProcessingParams> processing_params;
*processing_params.mutable_data() = {
in_fd.GetRemoteFd(), 0, kDefaultQualityTarget, kDefaultMemlimitMb};
sapi::v::LenVal output(0);
sapi::StatusOr<bool> processing_result =
api_->ProcessRgb(processing_params.PtrBefore(), output.PtrBoth());
ASSERT_TRUE(processing_result.value_or(false)) << "Error processing rgb data";
std::string reference_data =
ReadFromFile(GetPathToInputFile(kPngReferenceFilename));
ASSERT_EQ(output.GetDataSize(), reference_data.size())
<< "Incorrect result data size";
ASSERT_EQ(
std::string(output.GetData(), output.GetData() + output.GetDataSize()),
reference_data)
<< "Processed data doesn't match reference output";
}
// This test can take up to few minutes depending on your hardware
TEST_F(GuetzliSapiTest, ProcessJpeg) {
sapi::v::Fd in_fd(
open(GetPathToInputFile(kInJpegFilename).c_str(), O_RDONLY));
ASSERT_TRUE(in_fd.GetValue() != -1) << "Error opening input file";
ASSERT_EQ(api_->sandbox()->TransferToSandboxee(&in_fd), absl::OkStatus())
<< "Error transfering fd to sandbox";
ASSERT_TRUE(in_fd.GetRemoteFd() != -1) << "Error opening remote fd";
sapi::v::Struct<ProcessingParams> processing_params;
*processing_params.mutable_data() = {
in_fd.GetRemoteFd(), 0, kDefaultQualityTarget, kDefaultMemlimitMb};
sapi::v::LenVal output(0);
sapi::StatusOr<bool> processing_result =
api_->ProcessJpeg(processing_params.PtrBefore(), output.PtrBoth());
ASSERT_TRUE(processing_result.value_or(false)) << "Error processing jpg data";
std::string reference_data =
ReadFromFile(GetPathToInputFile(kJpegReferenceFIlename));
ASSERT_EQ(output.GetDataSize(), reference_data.size())
<< "Incorrect result data size";
ASSERT_EQ(
std::string(output.GetData(), output.GetData() + output.GetDataSize()),
reference_data)
<< "Processed data doesn't match reference output";
}
} // namespace guetzli::sandbox::tests

View File

@ -0,0 +1,123 @@
// 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 "guetzli_transaction.h" // NOLINT(build/include)
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <memory>
namespace guetzli::sandbox {
absl::Status GuetzliTransaction::Main() {
sapi::v::Fd in_fd(open(params_.in_file, O_RDONLY));
if (in_fd.GetValue() < 0) {
return absl::FailedPreconditionError("Error opening input file");
}
SAPI_ASSIGN_OR_RETURN(image_type_, GetImageTypeFromFd(in_fd.GetValue()));
SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&in_fd));
if (in_fd.GetRemoteFd() < 0) {
return absl::FailedPreconditionError(
"Error receiving remote FD: remote input fd is set to -1");
}
GuetzliApi api(sandbox());
sapi::v::LenVal output(0);
sapi::v::Struct<ProcessingParams> processing_params;
*processing_params.mutable_data() = {in_fd.GetRemoteFd(), params_.verbose,
params_.quality, params_.memlimit_mb};
auto result =
image_type_ == ImageType::kJpeg
? api.ProcessJpeg(processing_params.PtrBefore(), output.PtrBefore())
: api.ProcessRgb(processing_params.PtrBefore(), output.PtrBefore());
if (!result.value_or(false)) {
return absl::FailedPreconditionError(absl::StrCat(
"Error processing ", (image_type_ == ImageType::kJpeg ? "jpeg" : "rgb"),
" data"));
}
sapi::v::Fd out_fd(open(".", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR));
if (out_fd.GetValue() < 0) {
return absl::FailedPreconditionError("Error creating temp output file");
}
SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&out_fd));
if (out_fd.GetRemoteFd() < 0) {
return absl::FailedPreconditionError(
"Error receiving remote FD: remote output fd is set to -1");
}
auto write_result = api.WriteDataToFd(out_fd.GetRemoteFd(), output.PtrNone());
if (!write_result.value_or(false)) {
return absl::FailedPreconditionError("Error writing file inside sandbox");
}
SAPI_RETURN_IF_ERROR(LinkOutFile(out_fd.GetValue()));
return absl::OkStatus();
}
absl::Status GuetzliTransaction::LinkOutFile(int out_fd) const {
if (access(params_.out_file, F_OK) != -1) {
if (remove(params_.out_file) < 0) {
return absl::FailedPreconditionError(absl::StrCat(
"Error deleting existing output file: ", params_.out_file));
}
}
std::string path = absl::StrCat("/proc/self/fd/", out_fd);
if (linkat(AT_FDCWD, path.c_str(), AT_FDCWD, params_.out_file,
AT_SYMLINK_FOLLOW) < 0) {
return absl::FailedPreconditionError(
absl::StrCat("Error linking: ", params_.out_file));
}
return absl::OkStatus();
}
sapi::StatusOr<ImageType> GuetzliTransaction::GetImageTypeFromFd(int fd) const {
static const unsigned char kPNGMagicBytes[] = {
0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n',
};
char read_buf[sizeof(kPNGMagicBytes)];
if (read(fd, read_buf, sizeof(kPNGMagicBytes)) != sizeof(kPNGMagicBytes)) {
return absl::FailedPreconditionError(
"Error determining type of the input file");
}
if (lseek(fd, 0, SEEK_SET) != 0) {
return absl::FailedPreconditionError(
"Error returnig cursor to the beginning");
}
return memcmp(read_buf, kPNGMagicBytes, sizeof(kPNGMagicBytes)) == 0
? ImageType::kPng
: ImageType::kJpeg;
}
} // namespace guetzli::sandbox

View File

@ -0,0 +1,59 @@
// 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 GUETZLI_SANDBOXED_GUETZLI_TRANSACTION_H_
#define GUETZLI_SANDBOXED_GUETZLI_TRANSACTION_H_
#include <syscall.h>
#include "guetzli_sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/transaction.h"
#include "sandboxed_api/vars.h"
namespace guetzli::sandbox {
enum class ImageType { kJpeg, kPng };
struct TransactionParams {
const char* in_file = nullptr;
const char* out_file = nullptr;
int verbose = 0;
int quality = 0;
int memlimit_mb = 0;
};
// Instance of this transaction shouldn't be reused
// Create a new one for each processing operation
class GuetzliTransaction : public sapi::Transaction {
public:
explicit GuetzliTransaction(TransactionParams params, int retry_count = 0)
: sapi::Transaction(std::make_unique<GuetzliSapiSandbox>()),
params_(std::move(params)) {
set_retry_count(retry_count);
SetTimeLimit(absl::InfiniteDuration());
}
private:
absl::Status Main() final;
absl::Status LinkOutFile(int out_fd) const;
sapi::StatusOr<ImageType> GetImageTypeFromFd(int fd) const;
const TransactionParams params_;
ImageType image_type_ = ImageType::kJpeg;
};
} // namespace guetzli::sandbox
#endif // GUETZLI_SANDBOXED_GUETZLI_TRANSACTION_H_

View File

@ -0,0 +1,145 @@
// 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 "guetzli_transaction.h" // NOLINT(build/include)
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <memory>
#include <sstream>
#include "gtest/gtest.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
namespace guetzli::sandbox::tests {
namespace {
constexpr absl::string_view kInPngFilename = "bees.png";
constexpr absl::string_view kInJpegFilename = "nature.jpg";
constexpr absl::string_view kOutJpegFilename = "out_jpeg.jpg";
constexpr absl::string_view kOutPngFilename = "out_png.png";
constexpr absl::string_view kPngReferenceFilename = "bees_reference.jpg";
constexpr absl::string_view kJpegReferenceFIlename = "nature_reference.jpg";
constexpr int kPngExpectedSize = 38'625;
constexpr int kJpegExpectedSize = 10'816;
constexpr int kDefaultQualityTarget = 95;
constexpr int kDefaultMemlimitMb = 6000;
constexpr absl::string_view kRelativePathToTestdata =
"/guetzli_sandboxed/testdata/";
std::string GetPathToFile(absl::string_view filename) {
return absl::StrCat(getenv("TEST_SRCDIR"), kRelativePathToTestdata, filename);
}
std::string ReadFromFile(const std::string& filename) {
std::ifstream stream(filename, std::ios::binary);
if (!stream.is_open()) {
return "";
}
std::stringstream result;
result << stream.rdbuf();
return result.str();
}
// Helper class to delete file after opening
class FileRemover {
public:
explicit FileRemover(const char* path)
: path_(path), fd_(open(path, O_RDONLY)) {}
~FileRemover() {
close(fd_);
remove(path_);
}
int get() const { return fd_; }
private:
const char* path_;
int fd_;
};
} // namespace
TEST(GuetzliTransactionTest, TestTransactionJpg) {
std::string in_path = GetPathToFile(kInJpegFilename);
std::string out_path = GetPathToFile(kOutJpegFilename);
TransactionParams params = {in_path.c_str(), out_path.c_str(), 0,
kDefaultQualityTarget, kDefaultMemlimitMb};
{
GuetzliTransaction transaction(std::move(params));
absl::Status result = transaction.Run();
ASSERT_TRUE(result.ok()) << result.ToString();
}
std::string reference_data =
ReadFromFile(GetPathToFile(kJpegReferenceFIlename));
FileRemover file_remover(out_path.c_str());
ASSERT_TRUE(file_remover.get() != -1) << "Error opening output file";
off_t output_size = lseek(file_remover.get(), 0, SEEK_END);
ASSERT_EQ(reference_data.size(), output_size)
<< "Different sizes of reference and returned data";
ASSERT_EQ(lseek(file_remover.get(), 0, SEEK_SET), 0)
<< "Error repositioning out file";
std::string output;
output.resize(output_size);
ssize_t status = read(file_remover.get(), output.data(), output_size);
ASSERT_EQ(status, output_size) << "Error reading data from temp output file";
ASSERT_EQ(output, reference_data) << "Returned data doesn't match reference";
}
TEST(GuetzliTransactionTest, TestTransactionPng) {
std::string in_path = GetPathToFile(kInPngFilename);
std::string out_path = GetPathToFile(kOutPngFilename);
TransactionParams params = {in_path.c_str(), out_path.c_str(), 0,
kDefaultQualityTarget, kDefaultMemlimitMb};
{
GuetzliTransaction transaction(std::move(params));
absl::Status result = transaction.Run();
ASSERT_TRUE(result.ok()) << result.ToString();
}
std::string reference_data =
ReadFromFile(GetPathToFile(kPngReferenceFilename));
FileRemover file_remover(out_path.c_str());
ASSERT_TRUE(file_remover.get() != -1) << "Error opening output file";
off_t output_size = lseek(file_remover.get(), 0, SEEK_END);
ASSERT_EQ(reference_data.size(), output_size)
<< "Different sizes of reference and returned data";
ASSERT_EQ(lseek(file_remover.get(), 0, SEEK_SET), 0)
<< "Error repositioning out file";
std::string output;
output.resize(output_size);
ssize_t status = read(file_remover.get(), output.data(), output_size);
ASSERT_EQ(status, output_size) << "Error reading data from temp output file";
ASSERT_EQ(output, reference_data) << "Returned data doesn't match refernce";
}
} // namespace guetzli::sandbox::tests

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,56 @@
# 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.10)
project(openjpeg-sapi C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# To override lib option -- else SAPI won't work
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build OpenJPEG shared library and link executables against it." FORCE)
add_subdirectory(openjpeg)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "")
set(SAPI_ENABLE_TESTS OFF CACHE BOOL "")
set(EXECUTABLE_OUTPUT_PATH "" CACHE PATH "" FORCE)
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
add_sapi_library(openjp2_sapi
FUNCTIONS opj_stream_destroy
opj_stream_create_default_file_stream
opj_create_decompress
opj_image_destroy
opj_setup_decoder
opj_destroy_codec
opj_read_header
opj_decode
opj_set_default_decoder_parameters
opj_end_decompress
INPUTS ${CMAKE_CURRENT_SOURCE_DIR}/openjpeg/src/lib/openjp2/openjpeg.h
LIBRARY openjp2
LIBRARY_NAME Openjp2
NAMESPACE ""
)
target_include_directories(openjp2_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
add_subdirectory(examples)

View File

@ -0,0 +1,29 @@
# OpenJPEG Sandboxed API
This library provides sandboxed version of the [OpenJPEG](https://github.com/uclouvain/openjpeg) library.
## Examples
The examples are sandboxed and simplified version of the main tools provided by the OpenJPEG library, namely (for now) `opj_decompress` from [here](https://github.com/uclouvain/openjpeg/blob/master/src/bin/jp2/opj_decompress.c).
In `decompress_example.cc` the library's sandboxed API is used to convert the _.jp2_ to _.pnm_ image format.
## Build
To build this example, after cloning the whole Sandbox API project, you also need to run
```
git submodule update --init --recursive
```
anywhere in the project tree in order to clone the `openjpeg` submodule.
Then in the `sandboxed-api/oss-internship-2020/openjpeg` run
```
mkdir build && cd build
cmake -G Ninja
ninja
```
To run `decompress_sandboxed`:
```
cd examples
./decompress_sandboxed absolute/path/to/the/file.jp2 absolute/path/to/the/file.pnm
```

View File

@ -0,0 +1,48 @@
# 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.
# we need to use modified versions of some of the library tools
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gen_files")
file(COPY "${PROJECT_SOURCE_DIR}/openjpeg/src/bin/jp2/convert.c" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
file(COPY "${PROJECT_SOURCE_DIR}/openjpeg/src/bin/jp2/convert.h" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
file(COPY "${PROJECT_SOURCE_DIR}/examples/convert.patch" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
file(COPY "${PROJECT_SOURCE_DIR}/examples/convert_h.patch" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
add_custom_command(
OUTPUT ${PROJECT_BINARY_DIR}/gen_files/convert.cc ${PROJECT_BINARY_DIR}/gen_files/convert.h
COMMAND cd ${PROJECT_BINARY_DIR}/gen_files && patch < ${PROJECT_SOURCE_DIR}/examples/convert.patch > /dev/null
COMMAND cd ${PROJECT_BINARY_DIR}/gen_files && patch < ${PROJECT_SOURCE_DIR}/examples/convert_h.patch > /dev/null
COMMAND mv ${PROJECT_BINARY_DIR}/gen_files/convert.c ${PROJECT_BINARY_DIR}/gen_files/convert.cc
)
add_library(convert_helper STATIC
${PROJECT_BINARY_DIR}/gen_files/convert.cc
${PROJECT_BINARY_DIR}/gen_files/convert.h
)
add_executable(decompress_sandboxed
decompress_example.cc
)
target_link_libraries(decompress_sandboxed PRIVATE
convert_helper
openjp2_sapi
sapi::sapi
)
target_link_libraries(convert_helper PRIVATE
openjp2_sapi
sapi::sapi
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
--- convert.h 2020-08-27 15:46:45.028628305 +0000
+++ convert_helper.h 2020-08-27 14:26:02.155455250 +0000
@@ -1,126 +1,8 @@
-/*
- * The copyright in this software is being made available under the 2-clauses
- * BSD License, included below. This software may be subject to other third
- * party and contributor rights, including patent rights, and no such rights
- * are granted under this license.
- *
- * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
- * Copyright (c) 2002-2014, Professor Benoit Macq
- * Copyright (c) 2001-2003, David Janssens
- * Copyright (c) 2002-2003, Yannick Verschueren
- * Copyright (c) 2003-2007, Francois-Olivier Devaux
- * Copyright (c) 2003-2014, Antonin Descampe
- * Copyright (c) 2005, Herve Drolon, FreeImage Team
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifndef __J2K_CONVERT_H
-#define __J2K_CONVERT_H
+// imagetopnm and the two functions it calls internaly are patched
+// versions of the library's tools; from openjpeg/src/bin/jp2/convert.c
-/**@name RAW component encoding parameters */
-/*@{*/
-typedef struct raw_comp_cparameters {
- /** subsampling in X direction */
- int dx;
- /** subsampling in Y direction */
- int dy;
- /*@}*/
-} raw_comp_cparameters_t;
-
-/**@name RAW image encoding parameters */
-/*@{*/
-typedef struct raw_cparameters {
- /** width of the raw image */
- int rawWidth;
- /** height of the raw image */
- int rawHeight;
- /** number of components of the raw image */
- int rawComp;
- /** bit depth of the raw image */
- int rawBitDepth;
- /** signed/unsigned raw image */
- OPJ_BOOL rawSigned;
- /** raw components parameters */
- raw_comp_cparameters_t *rawComps;
- /*@}*/
-} raw_cparameters_t;
-
-/* Component precision clipping */
-void clip_component(opj_image_comp_t* component, OPJ_UINT32 precision);
-/* Component precision scaling */
-void scale_component(opj_image_comp_t* component, OPJ_UINT32 precision);
-
-/* planar / interleaved conversions */
-typedef void (* convert_32s_CXPX)(const OPJ_INT32* pSrc, OPJ_INT32* const* pDst,
- OPJ_SIZE_T length);
-extern const convert_32s_CXPX convert_32s_CXPX_LUT[5];
-typedef void (* convert_32s_PXCX)(OPJ_INT32 const* const* pSrc, OPJ_INT32* pDst,
- OPJ_SIZE_T length, OPJ_INT32 adjust);
-extern const convert_32s_PXCX convert_32s_PXCX_LUT[5];
-/* bit depth conversions */
-typedef void (* convert_XXx32s_C1R)(const OPJ_BYTE* pSrc, OPJ_INT32* pDst,
- OPJ_SIZE_T length);
-extern const convert_XXx32s_C1R convert_XXu32s_C1R_LUT[9]; /* up to 8bpp */
-typedef void (* convert_32sXXx_C1R)(const OPJ_INT32* pSrc, OPJ_BYTE* pDst,
- OPJ_SIZE_T length);
-extern const convert_32sXXx_C1R convert_32sXXu_C1R_LUT[9]; /* up to 8bpp */
-
-
-/* TGA conversion */
-opj_image_t* tgatoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetotga(opj_image_t * image, const char *outfile);
-
-/* BMP conversion */
-opj_image_t* bmptoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetobmp(opj_image_t *image, const char *outfile);
-
-/* TIFF conversion*/
-opj_image_t* tiftoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetotif(opj_image_t *image, const char *outfile);
-/**
-Load a single image component encoded in PGX file format
-@param filename Name of the PGX file to load
-@param parameters *List ?*
-@return Returns a greyscale image if successful, returns NULL otherwise
-*/
-opj_image_t* pgxtoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetopgx(opj_image_t *image, const char *outfile);
-
-opj_image_t* pnmtoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetopnm(opj_image_t *image, const char *outfile, int force_split);
-
-/* RAW conversion */
-int imagetoraw(opj_image_t * image, const char *outfile);
-int imagetorawl(opj_image_t * image, const char *outfile);
-opj_image_t* rawtoimage(const char *filename, opj_cparameters_t *parameters,
- raw_cparameters_t *raw_cp);
-opj_image_t* rawltoimage(const char *filename, opj_cparameters_t *parameters,
- raw_cparameters_t *raw_cp);
-
-/* PNG conversion*/
-extern int imagetopng(opj_image_t *image, const char *write_idf);
-extern opj_image_t* pngtoimage(const char *filename,
- opj_cparameters_t *parameters);
-
-#endif /* __J2K_CONVERT_H */
+#include "openjp2_sapi.sapi.h"
+const char* opj_version(void);
+static int are_comps_similar(opj_image_t* image);
+int imagetopnm(opj_image_t* image, const char* outfile, int force_split);

View File

@ -0,0 +1,157 @@
// 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.
// Perform decompression from *.jp2 to *.pnm format
#include <libgen.h>
#include <syscall.h>
#include <cstdlib>
#include <iostream>
#include <vector>
#include "gen_files/convert.h" // NOLINT(build/include)
#include "openjp2_sapi.sapi.h" // NOLINT(build/include)
class Openjp2SapiSandbox : public Openjp2Sandbox {
public:
explicit Openjp2SapiSandbox(std::string in_file)
: in_file_(std::move(in_file)) {}
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
__NR_lseek,
})
.AddFile(in_file_)
.BuildOrDie();
}
private:
std::string in_file_;
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (argc != 3) {
std::cerr << "Usage: " << basename(argv[0]) << " absolute/path/to/INPUT.jp2"
<< " absolute/path/to/OUTPUT.pnm\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
// Initialize sandbox.
Openjp2SapiSandbox sandbox(in_file);
absl::Status status = sandbox.Init();
CHECK(status.ok()) << "Sandbox initialization failed " << status;
Openjp2Api api(&sandbox);
sapi::v::ConstCStr in_file_v(in_file.c_str());
// Initialize library's main data-holders.
sapi::StatusOr<opj_stream_t*> stream =
api.opj_stream_create_default_file_stream(in_file_v.PtrBefore(), 1);
CHECK(stream.ok()) << "Stream initialization failed: " << stream.status();
sapi::v::RemotePtr stream_pointer(stream.value());
sapi::StatusOr<opj_codec_t*> codec = api.opj_create_decompress(OPJ_CODEC_JP2);
CHECK(codec.ok()) << "Codec initialization failed: " << stream.status();
sapi::v::RemotePtr codec_pointer(codec.value());
sapi::v::Struct<opj_dparameters_t> parameters;
status = api.opj_set_default_decoder_parameters(parameters.PtrBoth());
CHECK(status.ok()) << "Parameters initialization failed " << status;
sapi::StatusOr<OPJ_BOOL> bool_status =
api.opj_setup_decoder(&codec_pointer, parameters.PtrBefore());
CHECK(bool_status.ok() && bool_status.value()) << "Decoder setup failed";
// Start reading image from the input file.
sapi::v::GenericPtr image_pointer;
bool_status = api.opj_read_header(&stream_pointer, &codec_pointer,
image_pointer.PtrAfter());
CHECK(bool_status.ok() && bool_status.value())
<< "Reading image header failed";
sapi::v::Struct<opj_image_t> image;
image.SetRemote(reinterpret_cast<void*>(image_pointer.GetValue()));
CHECK(sandbox.TransferFromSandboxee(&image).ok())
<< "Transfer from sandboxee failed";
bool_status =
api.opj_decode(&codec_pointer, &stream_pointer, image.PtrAfter());
CHECK(bool_status.ok() && bool_status.value()) << "Decoding failed";
bool_status = api.opj_end_decompress(&codec_pointer, &stream_pointer);
CHECK(bool_status.ok() && bool_status.value()) << "Ending decompress failed";
int components = image.data().numcomps;
// Transfer the read data to the main process.
sapi::v::Array<opj_image_comp_t> image_components(components);
image_components.SetRemote(image.data().comps);
CHECK(sandbox.TransferFromSandboxee(&image_components).ok())
<< "Transfer from sandboxee failed";
image.mutable_data()->comps =
static_cast<opj_image_comp_t*>(image_components.GetLocal());
unsigned int width = static_cast<unsigned int>(image.data().comps[0].w);
unsigned int height = static_cast<unsigned int>(image.data().comps[0].h);
std::vector<std::vector<OPJ_INT32>> data(components);
sapi::v::Array<OPJ_INT32> image_components_data(width * height);
for (int i = 0; i < components; ++i) {
image_components_data.SetRemote(image.data().comps[i].data);
CHECK(sandbox.TransferFromSandboxee(&image_components_data).ok())
<< "Transfer from sandboxee failed";
std::vector<OPJ_INT32> component_data(
image_components_data.GetData(),
image_components_data.GetData() + (width * height));
data[i] = std::move(component_data);
image_components[i].data = &data[i][0];
}
// Convert the image to the desired format and save it to the file.
int error =
imagetopnm(static_cast<opj_image_t*>(image.GetLocal()), argv[2], 0);
CHECK(!error) << "Image convert failed";
// Clean up.
status = api.opj_image_destroy(image.PtrNone());
CHECK(status.ok()) << "Image destroy failed " << status;
status = api.opj_stream_destroy(&stream_pointer);
CHECK(status.ok()) << "Stream destroy failed " << status;
status = api.opj_destroy_codec(&codec_pointer);
CHECK(status.ok()) << "Codec destroy failed " << status;
return EXIT_SUCCESS;
}

3
oss-internship-2020/pffft/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,104 @@
# 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.10)
project(pffft CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_library(pffft STATIC
master/pffft.c
master/pffft.h
master/fftpack.c
master/fftpack.h
)
add_executable(pffft_main
master/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")
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
pffft_transform
pffft_transform_ordered
pffft_zreorder
pffft_zconvolve_accumulate
pffft_aligned_malloc
pffft_aligned_free
pffft_simd_size
cffti
cfftf
cfftb
rffti
rfftf
rfftb
cosqi
cosqf
cosqb
costi
cost
sinqi
sinqb
sinqf
sinti
sint
INPUTS master/pffft.h master/fftpack.h
LIBRARY pffft
LIBRARY_NAME Pffft
NAMESPACE ""
)
target_include_directories(pffft_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
add_executable(pffft_sandboxed
main_pffft_sandboxed.cc
)
target_link_libraries(pffft_sandboxed PRIVATE
pffft_sapi
sapi::sapi
)

View File

@ -0,0 +1,87 @@
# Sandboxing PFFFT library
Build System: CMake
OS: Linux
### Check out the PFFFT library & CMake set up
```
git submodule update --init --recursive
mkdir -p build && cd build
cmake .. -G Ninja -DPFFFT_ROOT_DIR=$PWD
ninjas
```
### For testing:
`cd build`, then `./pffft_sandboxed`
### For debug:
display custom info with
`./pffft_sandboxed --logtostderr`
## ***About the project***
*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
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
operations, system calls or namspaces access may be controlled.
From both `pffft.h` and `fftpack.h` headers, useful methods are added to
sapi library builded with CMake. There is also a need to link math library
as the transformations made require mathematical operators.
Regarding the testing of the methods, one main is doing this job by
iterating through a set of values, that represents the accuracy of
transformations and print the speed for each value and type of
transformation. More specifically, the input length is the target for
accuracy (named as `n`) and it stands for the number of data points from
the series that calculate the result of transformation. It is also
important to mention that the `complex` variable stands for a boolean value
that tells the type of transformation (0 for REAL and 1 for COMPLEX) and
it is taken into account while testing.
In the end, the performance of PFFFT library it is outlined by the output.
There are two output formats available, from which you can choose through
`--output_format=` command-line flag.
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.
Variable n, the input length, will take specific values
meaning the number of points to which it is set the calculus
(more details of mathematical purpose of n - https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm).
* output shows speed depending on the input length
* use `--output_format=0` or `--output_format=1` arguments to choose between output formats.
`0` is for a detailed output, while `1` is only displaying each transformation process speed.
### Bugs history
1. [Solved] pffft benchmark bug: "Sandbox not active"
n = 64, status OK, `pffft_transform` generates error
n > 64, status not OK
Problem on initialising `sapi::StatusOr<PFFFT_Setup *> s;` the memory that stays
for s is not the same with the address passed in `pffft_transform` function.
(`sapi::v::GenericPtr` - to be changed)
Temporary solution: change the generated files to accept
`uintptr_t` instead of `PFFFT_Setup`
Solution: using `sapi::v::RemotePtr` instead of `sapi::v::GenericPtr`
to access the memory of object `s`
2. [Unresolved] compiling bug: "No space left on device"
The building process creates some `embed` files that use lots of
memory, trying to write them on `/tmp`.
Temporary solution: clean /tmp directory by `sudo rm -rf /tmp/*`

View File

@ -0,0 +1,207 @@
// 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 <gflags/gflags.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <glog/logging.h>
#include "pffft_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/vars.h"
ABSL_DECLARE_FLAG(string, sandbox2_danger_danger_permit_all);
ABSL_DECLARE_FLAG(string, sandbox2_danger_danger_permit_all_and_log);
class PffftSapiSandbox : public PffftSandbox {
public:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(sandbox2::PolicyBuilder*) {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
__NR_getrusage,
})
.BuildOrDie();
}
};
// output_format flag determines whether the output shows information in detail
// or not. By default, the flag is set as 0, meaning an elaborate display
// (see ShowOutput method).
static bool ValidateFlag(const char* flagname, int32_t value) {
if (value >= 0 && value < 32768) {
return true;
}
LOG(ERROR) << "Invalid value for --" << flagname << ".";
return false;
}
DEFINE_int32(output_format, 0, "Value to specific the output format.");
DEFINE_validator(output_format, &ValidateFlag);
double UclockSec() { return static_cast<double>(clock()) / CLOCKS_PER_SEC; }
void ShowOutput(const char* name, int n, int complex, float flops, float t0,
float t1, int max_iter) {
float mflops = flops / 1e6 / (t1 - t0 + 1e-16);
if (FLAGS_output_format) {
if (flops != -1) {
printf("|%9.0f ", mflops);
} else {
printf("| n/a ");
}
} else if (flops != -1) {
printf("n=%5d, %s %16s : %6.0f MFlops [t=%6.0f ns, %d runs]\n", n,
(complex ? "CPLX" : "REAL"), name, mflops,
(t1 - t0) / 2 / max_iter * 1e9, max_iter);
}
fflush(stdout);
}
absl::Status PffftMain() {
LOG(INFO) << "Initializing sandbox...\n";
PffftSapiSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
PffftApi api(&sandbox);
// kTransformSizes is a vector keeping the values by which iterates n, its
// value representing the input length. More concrete, n is the number of data
// points the caclulus is up to (determinating its accuracy). To show the
// performance of Fast-Fourier Transformations the program is testing for
// various values of n.
constexpr int kTransformSizes[] = {
64, 96, 128, 160, 192, 256, 384, 5 * 96, 512, 5 * 128,
3 * 256, 800, 1024, 2048, 2400, 4096, 8192, 9 * 1024, 16384, 32768};
for (int complex : {0, 1}) {
for (int n : kTransformSizes) {
const int n_float = n * (complex ? 2 : 1);
int n_bytes = n_float * sizeof(float);
std::vector<float> work(2 * n_float + 15, 0.0);
sapi::v::Array<float> work_array(&work[0], work.size());
std::vector<float> x(n_bytes, 0.0);
sapi::v::Array<float> x_array(&x[0], x.size());
std::vector<float> y(n_bytes, 0.0);
sapi::v::Array<float> y_array(&y[0], y.size());
std::vector<float> z(n_bytes, 0.0);
sapi::v::Array<float> z_array(&z[0], z.size());
double t0;
double t1;
double flops;
int max_iter = 5120000 / n * 4;
for (int k = 0; k < n_float; ++k) {
x[k] = 0;
}
// FFTPack benchmark
{
// SIMD_SZ == 4 (returning value of pffft_simd_size())
int simd_size_iter = max_iter / 4;
if (simd_size_iter == 0) simd_size_iter = 1;
if (complex) {
SAPI_RETURN_IF_ERROR(api.cffti(n, work_array.PtrBoth()))
} else {
SAPI_RETURN_IF_ERROR(api.rffti(n, work_array.PtrBoth()));
}
t0 = UclockSec();
for (int iter = 0; iter < simd_size_iter; ++iter) {
if (complex) {
SAPI_RETURN_IF_ERROR(
api.cfftf(n, x_array.PtrBoth(), work_array.PtrBoth()));
SAPI_RETURN_IF_ERROR(
api.cfftb(n, x_array.PtrBoth(), work_array.PtrBoth()));
} else {
SAPI_RETURN_IF_ERROR(
api.rfftf(n, x_array.PtrBoth(), work_array.PtrBoth()));
SAPI_RETURN_IF_ERROR(
api.rfftb(n, x_array.PtrBoth(), work_array.PtrBoth()));
}
}
t1 = UclockSec();
flops = (simd_size_iter * 2) *
((complex ? 5 : 2.5) * static_cast<double>(n) *
log(static_cast<double>(n)) / M_LN2);
ShowOutput("FFTPack", n, complex, flops, t0, t1, simd_size_iter);
}
// PFFFT benchmark
{
SAPI_ASSIGN_OR_RETURN(
PFFFT_Setup * s,
api.pffft_new_setup(n, complex ? PFFFT_COMPLEX : PFFFT_REAL));
sapi::v::RemotePtr s_reg(s);
t0 = UclockSec();
for (int iter = 0; iter < max_iter; ++iter) {
SAPI_RETURN_IF_ERROR(
api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(),
y_array.PtrBoth(), PFFFT_FORWARD));
SAPI_RETURN_IF_ERROR(
api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(),
y_array.PtrBoth(), PFFFT_FORWARD));
}
t1 = UclockSec();
SAPI_RETURN_IF_ERROR(api.pffft_destroy_setup(&s_reg));
flops = (max_iter * 2) * ((complex ? 5 : 2.5) * static_cast<double>(n) *
log(static_cast<double>(n)) / M_LN2);
ShowOutput("PFFFT", n, complex, flops, t0, t1, max_iter);
LOG(INFO) << "n = " << n << " SUCCESSFULLY";
}
}
}
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
// Initialize Google's logging library.
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (absl::Status status = PffftMain(); !status.ok()) {
LOG(ERROR) << "Initialization failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -147,10 +147,10 @@ cc_library(
":var_type",
"//sandboxed_api/sandbox2:comms",
"//sandboxed_api/util:status",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",

View File

@ -43,6 +43,8 @@ add_library(sapi_embed_file STATIC
add_library(sapi::embed_file ALIAS sapi_embed_file)
target_link_libraries(sapi_embed_file PRIVATE
absl::flat_hash_map
absl::status
absl::statusor
absl::strings
absl::synchronization
glog::glog
@ -65,6 +67,8 @@ add_library(sapi::sapi ALIAS sapi_sapi)
target_link_libraries(sapi_sapi
PRIVATE absl::flat_hash_map
absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -76,7 +80,6 @@ target_link_libraries(sapi_sapi
sandbox2::strerror
sandbox2::util
sapi::embed_file
sapi::status
sapi::vars
PUBLIC absl::core_headers
sandbox2::client
@ -137,6 +140,8 @@ add_library(sapi_vars STATIC
add_library(sapi::vars ALIAS sapi_vars)
target_link_libraries(sapi_vars PRIVATE
absl::core_headers
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -147,7 +152,6 @@ target_link_libraries(sapi_vars PRIVATE
sapi::lenval_core
sapi::proto_arg_proto
sapi::status
sapi::statusor
sapi::var_type
)
@ -178,6 +182,7 @@ if(SAPI_ENABLE_TESTS)
)
target_link_libraries(sapi_test PRIVATE
absl::memory
absl::status
benchmark
sapi::sapi
sapi::status

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
licenses(["notice"])
exports_files([
@ -19,3 +21,33 @@ exports_files([
"embed_data.bzl",
"sapi.bzl",
])
bzl_library(
name = "build_defs_bzl",
srcs = ["build_defs.bzl"],
visibility = ["//visibility:private"],
)
bzl_library(
name = "embed_data_bzl",
srcs = ["embed_data.bzl"],
visibility = ["//visibility:private"],
)
bzl_library(
name = "proto_bzl",
srcs = ["proto.bzl"],
visibility = ["//visibility:private"],
)
bzl_library(
name = "repositories_bzl",
srcs = ["repositories.bzl"],
visibility = ["//visibility:private"],
)
bzl_library(
name = "sapi_deps_bzl",
srcs = ["sapi_deps.bzl"],
visibility = ["//visibility:private"],
)

View File

@ -16,8 +16,8 @@
_FILEWRAPPER = "//sandboxed_api/tools/filewrapper"
# TODO(cblichmann): Convert this is to use a "_cc_toolchain" once Bazel #4370
# is fixed.
# TODO(cblichmann): Convert this to use a "_cc_toolchain" once Bazel #4370 is
# fixed.
def _sapi_cc_embed_data_impl(ctx):
cc_file_artifact = None
h_file_artifact = None

View File

@ -56,7 +56,7 @@ def sapi_proto_library(
deps = [],
alwayslink = False,
**kwargs):
"""Generates proto targets in various languages.
"""Generates proto library and C++ targets.
Args:
name: Name for proto_library and base for the cc_proto_library name, name +

View File

@ -254,9 +254,8 @@ def sapi_library(
native.genrule(
name = name + ".isystem",
outs = [name + ".isystem.list"],
cmd = """echo |
$(CC) -E -x c++ - -v 2>&1 |
awk '/> search starts here:/{flag=1;next}/End of search/{flag=0}flag' > $@
cmd = """$(CC) -E -x c++ -v /dev/null 2>&1 |
awk '/> search starts here:/{f=1;next}/^End of search/{f=0}f{print $$1}' > $@
""",
toolchains = ["@bazel_tools//tools/cpp:current_cc_toolchain"],
)

View File

@ -34,9 +34,9 @@ def sapi_deps():
maybe(
http_archive,
name = "com_google_absl",
sha256 = "6668ada01192e2b95b42bb3668cfa5282c047de5176f5e567028e12f8bfb8aef", # 2020-04-28
strip_prefix = "abseil-cpp-6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3",
urls = ["https://github.com/abseil/abseil-cpp/archive/6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3.zip"],
sha256 = "8061df0ebbd3f599bcd3f5e57fb8003564d50a9b6a81a7f968fb0196b952365d", # 2020-09-02
strip_prefix = "abseil-cpp-0e9921b75a0fdd639a504ec8443fc1fe801becd7",
urls = ["https://github.com/abseil/abseil-cpp/archive/0e9921b75a0fdd639a504ec8443fc1fe801becd7.zip"],
)
maybe(
http_archive,

View File

@ -23,8 +23,8 @@ namespace sapi {
namespace comms {
struct ReallocRequest {
uint64_t old_addr;
uint64_t size;
uintptr_t old_addr;
size_t size;
};
// Types of TAGs used with Comms channel.

View File

@ -229,7 +229,7 @@ void HandleCallMsg(const FuncCall& call, FuncRet* ret) {
}
// Handles requests to allocate memory inside the sandboxee.
void HandleAllocMsg(const uintptr_t size, FuncRet* ret) {
void HandleAllocMsg(const size_t size, FuncRet* ret) {
VLOG(1) << "HandleAllocMsg: size=" << size;
ret->ret_type = v::Type::kPointer;
@ -237,16 +237,15 @@ void HandleAllocMsg(const uintptr_t size, FuncRet* ret) {
// Memory is copied to the pointer using an API that the memory sanitizer
// is blind to (process_vm_writev). Initialize the memory here so that
// the sandboxed code can still be tested with the memory sanitizer.
ret->int_val =
reinterpret_cast<uintptr_t>(calloc(1, static_cast<size_t>(size)));
ret->int_val = reinterpret_cast<uintptr_t>(calloc(1, size));
#else
ret->int_val = reinterpret_cast<uintptr_t>(malloc(static_cast<size_t>(size)));
ret->int_val = reinterpret_cast<uintptr_t>(malloc(size));
#endif
ret->success = true;
}
// Like HandleAllocMsg(), but handles requests to reallocate memory.
void HandleReallocMsg(uintptr_t ptr, uintptr_t size, FuncRet* ret) {
void HandleReallocMsg(uintptr_t ptr, size_t size, FuncRet* ret) {
VLOG(1) << "HandleReallocMsg(" << absl::StrCat(absl::Hex(ptr)) << ", " << size
<< ")";
#ifdef MEMORY_SANITIZER
@ -254,8 +253,8 @@ void HandleReallocMsg(uintptr_t ptr, uintptr_t size, FuncRet* ret) {
__sanitizer_get_allocated_size(reinterpret_cast<const void*>(ptr));
#endif
ret->ret_type = v::Type::kPointer;
ret->int_val = reinterpret_cast<uintptr_t>(
realloc(const_cast<void*>(reinterpret_cast<const void*>(ptr)), size));
ret->int_val =
reinterpret_cast<uintptr_t>(realloc(reinterpret_cast<void*>(ptr), size));
ret->success = true;
#ifdef MEMORY_SANITIZER
// Memory is copied to the pointer using an API that the memory sanitizer
@ -273,7 +272,7 @@ void HandleReallocMsg(uintptr_t ptr, uintptr_t size, FuncRet* ret) {
void HandleFreeMsg(uintptr_t ptr, FuncRet* ret) {
VLOG(1) << "HandleFreeMsg: free(0x" << absl::StrCat(absl::Hex(ptr)) << ")";
free(const_cast<void*>(reinterpret_cast<const void*>(ptr)));
free(reinterpret_cast<void*>(ptr));
ret->ret_type = v::Type::kVoid;
ret->success = true;
ret->int_val = 0ULL;
@ -363,14 +362,13 @@ void ServeRequest(sandbox2::Comms* comms) {
break;
case comms::kMsgAllocate:
VLOG(1) << "Client::kMsgAllocate";
HandleAllocMsg(BytesAs<uintptr_t>(bytes), &ret);
HandleAllocMsg(BytesAs<size_t>(bytes), &ret);
break;
case comms::kMsgReallocate:
VLOG(1) << "Client::kMsgReallocate";
{
auto req = BytesAs<comms::ReallocRequest>(bytes);
HandleReallocMsg(static_cast<uintptr_t>(req.old_addr),
static_cast<uintptr_t>(req.size), &ret);
HandleReallocMsg(req.old_addr, req.size, &ret);
}
break;
case comms::kMsgFree:

View File

@ -113,7 +113,7 @@ TEST(StringopTest, RawStringReversal) {
{
// Let's call it again with different data as argument, reusing the
// existing LenVal object.
EXPECT_THAT(param.ResizeData(sandbox.GetRpcChannel(), 16), IsOk());
EXPECT_THAT(param.ResizeData(sandbox.rpc_channel(), 16), IsOk());
memcpy(param.GetData() + 10, "ABCDEF", 6);
absl::string_view data(reinterpret_cast<const char*>(param.GetData()),
param.GetDataSize());
@ -134,8 +134,8 @@ TEST(StringopTest, RawStringLength) {
ASSERT_THAT(sandbox.Init(), IsOk());
StringopApi api(&sandbox);
SAPI_ASSERT_OK_AND_ASSIGN(void* target_mem_ptr, api.get_raw_c_string());
SAPI_ASSERT_OK_AND_ASSIGN(uint64_t len,
sandbox.GetRpcChannel()->Strlen(target_mem_ptr));
SAPI_ASSERT_OK_AND_ASSIGN(size_t len,
sandbox.rpc_channel()->Strlen(target_mem_ptr));
EXPECT_THAT(len, Eq(10));
}
@ -144,8 +144,8 @@ TEST(StringopTest, RawStringReading) {
ASSERT_THAT(sandbox.Init(), IsOk());
StringopApi api(&sandbox);
SAPI_ASSERT_OK_AND_ASSIGN(void* target_mem_ptr, api.get_raw_c_string());
SAPI_ASSERT_OK_AND_ASSIGN(uint64_t len,
sandbox.GetRpcChannel()->Strlen(target_mem_ptr));
SAPI_ASSERT_OK_AND_ASSIGN(size_t len,
sandbox.rpc_channel()->Strlen(target_mem_ptr));
EXPECT_THAT(len, Eq(10));
SAPI_ASSERT_OK_AND_ASSIGN(std::string data,

View File

@ -161,7 +161,7 @@ absl::Status SumTransaction::Main() {
LOG(INFO) << "Read from /proc/self/comm = [" << buffer << "]";
// Close test.
SAPI_RETURN_IF_ERROR(fd2.CloseRemoteFd(sandbox()->GetRpcChannel()));
SAPI_RETURN_IF_ERROR(fd2.CloseRemoteFd(sandbox()->rpc_channel()));
memset(buffer, 0, sizeof(buffer));
SAPI_RETURN_IF_ERROR(sandbox()->Call("read", &ret, &fd2, buf.PtrBoth(), &size));
LOG(INFO) << "Read from closed /proc/self/comm = [" << buffer << "]";

View File

@ -14,11 +14,11 @@
# Description: Sandboxed API reimplementation of zlib's zpipe.c example.
licenses(["notice"])
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
load("//sandboxed_api/bazel:sapi.bzl", "sapi_library")
licenses(["notice"])
sapi_library(
name = "zlib-sapi",
srcs = [],
@ -43,5 +43,6 @@ cc_binary(
"//sandboxed_api:vars",
"//sandboxed_api/util:flags",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/status:statusor",
],
)

View File

@ -32,11 +32,11 @@ add_executable(main_zlib
main_zlib.cc
)
target_link_libraries(main_zlib PRIVATE
absl::status
sapi::base
glog::glog
sapi::flags
sapi::sapi
sapi::status
sapi::statusor
sapi::zlib_sapi
)

View File

@ -20,6 +20,7 @@
#include <glog/logging.h>
#include "absl/base/macros.h"
#include "sandboxed_api/util/flag.h"
#include "absl/status/statusor.h"
#include "sandboxed_api/examples/zlib/zlib-sapi.sapi.h"
#include "sandboxed_api/examples/zlib/zlib-sapi_embed.h"
#include "sandboxed_api/vars.h"
@ -47,7 +48,7 @@ int main(int argc, char** argv) {
<< status.message();
}
sapi::StatusOr<int> ret;
absl::StatusOr<int> ret;
int flush;
unsigned have;
sapi::v::Struct<sapi::zlib::z_stream> strm;

View File

@ -22,13 +22,13 @@
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "sandboxed_api/proto_arg.pb.h"
#include "sandboxed_api/util/statusor.h"
namespace sapi {
template <typename T>
sapi::StatusOr<std::vector<uint8_t>> SerializeProto(const T& proto) {
absl::StatusOr<std::vector<uint8_t>> SerializeProto(const T& proto) {
static_assert(std::is_base_of<google::protobuf::Message, T>::value,
"Template argument must be a proto message");
// Wrap protobuf in a envelope so that we know the name of the protobuf
@ -46,7 +46,7 @@ sapi::StatusOr<std::vector<uint8_t>> SerializeProto(const T& proto) {
}
template <typename T>
sapi::StatusOr<T> DeserializeProto(const char* data, size_t len) {
absl::StatusOr<T> DeserializeProto(const char* data, size_t len) {
static_assert(std::is_base_of<google::protobuf::Message, T>::value,
"Template argument must be a proto message");
ProtoArg envelope;

View File

@ -15,6 +15,7 @@
#include "sandboxed_api/rpcchannel.h"
#include <glog/logging.h>
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "sandboxed_api/call.h"
@ -26,8 +27,7 @@ namespace sapi {
absl::Status RPCChannel::Call(const FuncCall& call, uint32_t tag, FuncRet* ret,
v::Type exp_type) {
absl::MutexLock lock(&mutex_);
if (!comms_->SendTLV(tag, sizeof(call),
reinterpret_cast<const uint8_t*>(&call))) {
if (!comms_->SendTLV(tag, sizeof(call), &call)) {
return absl::UnavailableError("Sending TLV value failed");
}
SAPI_ASSIGN_OR_RETURN(auto fret, Return(exp_type));
@ -35,9 +35,9 @@ absl::Status RPCChannel::Call(const FuncCall& call, uint32_t tag, FuncRet* ret,
return absl::OkStatus();
}
sapi::StatusOr<FuncRet> RPCChannel::Return(v::Type exp_type) {
absl::StatusOr<FuncRet> RPCChannel::Return(v::Type exp_type) {
uint32_t tag;
uint64_t len;
size_t len;
FuncRet ret;
if (!comms_->RecvTLV(&tag, &len, &ret, sizeof(ret))) {
return absl::UnavailableError("Receiving TLV value failed");
@ -66,9 +66,7 @@ sapi::StatusOr<FuncRet> RPCChannel::Return(v::Type exp_type) {
absl::Status RPCChannel::Allocate(size_t size, void** addr) {
absl::MutexLock lock(&mutex_);
uint64_t sz = size;
if (!comms_->SendTLV(comms::kMsgAllocate, sizeof(sz),
reinterpret_cast<uint8_t*>(&sz))) {
if (!comms_->SendTLV(comms::kMsgAllocate, sizeof(size), &size)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -80,12 +78,13 @@ absl::Status RPCChannel::Allocate(size_t size, void** addr) {
absl::Status RPCChannel::Reallocate(void* old_addr, size_t size,
void** new_addr) {
absl::MutexLock lock(&mutex_);
comms::ReallocRequest req;
req.old_addr = reinterpret_cast<uint64_t>(old_addr);
req.size = size;
comms::ReallocRequest req = {
.old_addr = reinterpret_cast<uintptr_t>(old_addr),
.size = size,
};
if (!comms_->SendTLV(comms::kMsgReallocate, sizeof(comms::ReallocRequest),
reinterpret_cast<uint8_t*>(&req))) {
&req)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -102,9 +101,8 @@ absl::Status RPCChannel::Reallocate(void* old_addr, size_t size,
absl::Status RPCChannel::Free(void* addr) {
absl::MutexLock lock(&mutex_);
uint64_t remote = reinterpret_cast<uint64_t>(addr);
if (!comms_->SendTLV(comms::kMsgFree, sizeof(remote),
reinterpret_cast<uint8_t*>(&remote))) {
uintptr_t remote = reinterpret_cast<uintptr_t>(addr);
if (!comms_->SendTLV(comms::kMsgFree, sizeof(remote), &remote)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -117,8 +115,7 @@ absl::Status RPCChannel::Free(void* addr) {
absl::Status RPCChannel::Symbol(const char* symname, void** addr) {
absl::MutexLock lock(&mutex_);
if (!comms_->SendTLV(comms::kMsgSymbol, strlen(symname) + 1,
reinterpret_cast<const uint8_t*>(symname))) {
if (!comms_->SendTLV(comms::kMsgSymbol, strlen(symname) + 1, symname)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -136,9 +133,8 @@ absl::Status RPCChannel::Exit() {
// Try the RPC exit sequence. But, the only thing that matters as a success
// indicator is whether the Comms channel had been closed
bool unused = true;
comms_->SendTLV(comms::kMsgExit, sizeof(unused),
reinterpret_cast<uint8_t*>(&unused));
comms_->SendTLV(comms::kMsgExit, 0, nullptr);
bool unused;
comms_->RecvBool(&unused);
if (!comms_->IsTerminated()) {
@ -153,9 +149,7 @@ absl::Status RPCChannel::Exit() {
absl::Status RPCChannel::SendFD(int local_fd, int* remote_fd) {
absl::MutexLock lock(&mutex_);
bool unused = true;
if (!comms_->SendTLV(comms::kMsgSendFd, sizeof(unused),
reinterpret_cast<uint8_t*>(&unused))) {
if (!comms_->SendTLV(comms::kMsgSendFd, 0, nullptr)) {
return absl::UnavailableError("Sending TLV value failed");
}
if (!comms_->SendFD(local_fd)) {
@ -172,8 +166,7 @@ absl::Status RPCChannel::SendFD(int local_fd, int* remote_fd) {
absl::Status RPCChannel::RecvFD(int remote_fd, int* local_fd) {
absl::MutexLock lock(&mutex_);
if (!comms_->SendTLV(comms::kMsgRecvFd, sizeof(remote_fd),
reinterpret_cast<uint8_t*>(&remote_fd))) {
if (!comms_->SendTLV(comms::kMsgRecvFd, sizeof(remote_fd), &remote_fd)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -190,8 +183,7 @@ absl::Status RPCChannel::RecvFD(int remote_fd, int* local_fd) {
absl::Status RPCChannel::Close(int remote_fd) {
absl::MutexLock lock(&mutex_);
if (!comms_->SendTLV(comms::kMsgClose, sizeof(remote_fd),
reinterpret_cast<uint8_t*>(&remote_fd))) {
if (!comms_->SendTLV(comms::kMsgClose, sizeof(remote_fd), &remote_fd)) {
return absl::UnavailableError("Sending TLV value failed");
}
@ -202,10 +194,9 @@ absl::Status RPCChannel::Close(int remote_fd) {
return absl::OkStatus();
}
sapi::StatusOr<uint64_t> RPCChannel::Strlen(void* str) {
absl::StatusOr<size_t> RPCChannel::Strlen(void* str) {
absl::MutexLock lock(&mutex_);
if (!comms_->SendTLV(comms::kMsgStrlen, sizeof(str),
reinterpret_cast<uint8_t*>(&str))) {
if (!comms_->SendTLV(comms::kMsgStrlen, sizeof(str), &str)) {
return absl::UnavailableError("Sending TLV value failed");
}

View File

@ -18,11 +18,11 @@
#include <cstddef>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/synchronization/mutex.h"
#include "sandboxed_api/call.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/var_type.h"
#include "sandboxed_api/util/statusor.h"
namespace sapi {
@ -61,13 +61,13 @@ class RPCChannel {
absl::Status Close(int remote_fd);
// Returns length of a null-terminated c-style string (invokes strlen).
sapi::StatusOr<uint64_t> Strlen(void* str);
absl::StatusOr<size_t> Strlen(void* str);
sandbox2::Comms* comms() const { return comms_; }
private:
// Receives the result after a call.
sapi::StatusOr<FuncRet> Return(v::Type exp_type);
absl::StatusOr<FuncRet> Return(v::Type exp_type);
sandbox2::Comms* comms_; // Owned by sandbox2;
absl::Mutex mutex_;

View File

@ -84,7 +84,9 @@ void InitDefaultPolicyBuilder(sandbox2::PolicyBuilder* builder) {
__NR_kill,
__NR_tgkill,
__NR_tkill,
#ifdef __NR_readlink
__NR_readlink,
#endif
#ifdef __NR_arch_prctl // x86-64 only
__NR_arch_prctl,
#endif
@ -100,7 +102,7 @@ void InitDefaultPolicyBuilder(sandbox2::PolicyBuilder* builder) {
}
void Sandbox::Terminate(bool attempt_graceful_exit) {
if (!IsActive()) {
if (!is_active()) {
return;
}
@ -129,7 +131,7 @@ static std::string PathToSAPILib(const std::string& lib_path) {
absl::Status Sandbox::Init() {
// It's already initialized
if (IsActive()) {
if (is_active()) {
return absl::OkStatus();
}
@ -156,7 +158,7 @@ absl::Status Sandbox::Init() {
}
}
std::vector<std::string> args{lib_path};
std::vector<std::string> args = {lib_path};
// Additional arguments, if needed.
GetArgs(&args);
std::vector<std::string> envs{};
@ -214,24 +216,24 @@ absl::Status Sandbox::Init() {
return absl::OkStatus();
}
bool Sandbox::IsActive() const { return s2_ && !s2_->IsTerminated(); }
bool Sandbox::is_active() const { return s2_ && !s2_->IsTerminated(); }
absl::Status Sandbox::Allocate(v::Var* var, bool automatic_free) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
return var->Allocate(GetRpcChannel(), automatic_free);
}
absl::Status Sandbox::Free(v::Var* var) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
return var->Free(GetRpcChannel());
}
absl::Status Sandbox::SynchronizePtrBefore(v::Callable* ptr) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
if (ptr->GetType() != v::Type::kPointer) {
@ -259,11 +261,11 @@ absl::Status Sandbox::SynchronizePtrBefore(v::Callable* ptr) {
VLOG(3) << "Synchronization (TO), ptr " << p << ", Type: " << p->GetSyncType()
<< " for var: " << p->GetPointedVar()->ToString();
return p->GetPointedVar()->TransferToSandboxee(GetRpcChannel(), GetPid());
return p->GetPointedVar()->TransferToSandboxee(GetRpcChannel(), pid());
}
absl::Status Sandbox::SynchronizePtrAfter(v::Callable* ptr) const {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
if (ptr->GetType() != v::Type::kPointer) {
@ -287,12 +289,12 @@ absl::Status Sandbox::SynchronizePtrAfter(v::Callable* ptr) const {
p->ToString()));
}
return p->GetPointedVar()->TransferFromSandboxee(GetRpcChannel(), GetPid());
return p->GetPointedVar()->TransferFromSandboxee(GetRpcChannel(), pid());
}
absl::Status Sandbox::Call(const std::string& func, v::Callable* ret,
std::initializer_list<v::Callable*> args) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
// Send data.
@ -371,29 +373,29 @@ absl::Status Sandbox::Call(const std::string& func, v::Callable* ret,
}
absl::Status Sandbox::Symbol(const char* symname, void** addr) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
return rpc_channel_->Symbol(symname, addr);
}
absl::Status Sandbox::TransferToSandboxee(v::Var* var) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
return var->TransferToSandboxee(GetRpcChannel(), GetPid());
return var->TransferToSandboxee(GetRpcChannel(), pid());
}
absl::Status Sandbox::TransferFromSandboxee(v::Var* var) {
if (!IsActive()) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
return var->TransferFromSandboxee(GetRpcChannel(), GetPid());
return var->TransferFromSandboxee(GetRpcChannel(), pid());
}
sapi::StatusOr<std::string> Sandbox::GetCString(const v::RemotePtr& str,
uint64_t max_length) {
if (!IsActive()) {
absl::StatusOr<std::string> Sandbox::GetCString(const v::RemotePtr& str,
size_t max_length) {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
@ -436,22 +438,25 @@ const sandbox2::Result& Sandbox::AwaitResult() {
return result_;
}
absl::Status Sandbox::SetWallTimeLimit(time_t limit) const {
if (!IsActive()) {
absl::Status Sandbox::SetWallTimeLimit(absl::Duration limit) const {
if (!is_active()) {
return absl::UnavailableError("Sandbox not active");
}
s2_->SetWallTimeLimit(limit);
s2_->set_walltime_limit(limit);
return absl::OkStatus();
}
absl::Status Sandbox::SetWallTimeLimit(time_t limit) const {
return SetWallTimeLimit(absl::Seconds(limit));
}
void Sandbox::Exit() const {
if (!IsActive()) {
if (!is_active()) {
return;
}
// Give it 1 second
s2_->SetWallTimeLimit(1);
s2_->set_walltime_limit(absl::Seconds(1));
if (!rpc_channel_->Exit().ok()) {
LOG(WARNING) << "rpc_channel->Exit() failed, killing PID: " << GetPid();
LOG(WARNING) << "rpc_channel->Exit() failed, killing PID: " << pid();
s2_->Kill();
}
}

View File

@ -35,18 +35,21 @@ namespace sapi {
// means to communicate with it (make function calls, transfer memory).
class Sandbox {
public:
explicit Sandbox(const FileToc* embed_lib_toc)
: embed_lib_toc_(embed_lib_toc) {}
Sandbox(const Sandbox&) = delete;
Sandbox& operator=(const Sandbox&) = delete;
explicit Sandbox(const FileToc* embed_lib_toc)
: comms_(nullptr), pid_(0), embed_lib_toc_(embed_lib_toc) {}
virtual ~Sandbox();
// Initializes a new sandboxing session.
absl::Status Init();
// Is the current sandboxing session alive?
bool IsActive() const;
ABSL_DEPRECATED("Use sapi::Sandbox::is_active() instead")
bool IsActive() const { return is_active(); }
// Returns whether the current sandboxing session is active.
bool is_active() const;
// Terminates the current sandboxing session (if it exists).
void Terminate(bool attempt_graceful_exit = true);
@ -60,9 +63,13 @@ class Sandbox {
// Getters for common fields.
sandbox2::Comms* comms() const { return comms_; }
ABSL_DEPRECATED("Use sapi::Sandbox::rpc_channel() instead")
RPCChannel* GetRpcChannel() const { return rpc_channel_.get(); }
RPCChannel* rpc_channel() const { return rpc_channel_.get(); }
ABSL_DEPRECATED("Use sapi::Sandbox::pid() instead")
int GetPid() const { return pid_; }
int pid() const { return pid_; }
// Synchronizes the underlying memory for the pointer before the call.
absl::Status SynchronizePtrBefore(v::Callable* ptr);
@ -87,7 +94,7 @@ class Sandbox {
// Frees memory in the sandboxee.
absl::Status Free(v::Var* var);
// Finds address of a symbol in the sandboxee.
// Finds the address of a symbol in the sandboxee.
absl::Status Symbol(const char* symname, void** addr);
// Transfers memory (both directions). Status is returned (memory transfer
@ -95,14 +102,18 @@ class Sandbox {
absl::Status TransferToSandboxee(v::Var* var);
absl::Status TransferFromSandboxee(v::Var* var);
sapi::StatusOr<std::string> GetCString(const v::RemotePtr& str,
uint64_t max_length = 10 * 1024 *
1024);
absl::StatusOr<std::string> GetCString(const v::RemotePtr& str,
size_t max_length = 10ULL
<< 20 /* 10 MiB*/
);
// Waits until the sandbox terminated and returns the result.
const sandbox2::Result& AwaitResult();
const sandbox2::Result& result() const { return result_; }
absl::Status SetWallTimeLimit(absl::Duration limit) const;
ABSL_DEPRECATED(
"Use sapi::Sandbox::SetWallTimeLimit(absl::Duration) overload instead")
absl::Status SetWallTimeLimit(time_t limit) const;
protected:
@ -144,11 +155,11 @@ class Sandbox {
sandbox2::Result result_;
// Comms with the sandboxee.
sandbox2::Comms* comms_;
sandbox2::Comms* comms_ = nullptr;
// RPCChannel object.
std::unique_ptr<RPCChannel> rpc_channel_;
// The main pid of the sandboxee.
pid_t pid_;
pid_t pid_ = 0;
// FileTOC with the embedded library, takes precedence over GetLibPath if
// present (not nullptr).

View File

@ -26,6 +26,13 @@ licenses(["notice"]) # Apache 2.0
exports_files(["testdata/hostname"])
cc_library(
name = "config",
hdrs = ["config.h"],
copts = sapi_platform_copts(),
deps = ["@com_google_absl//absl/base:config"],
)
cc_library(
name = "bpfdisassembler",
srcs = ["bpfdisassembler.cc"],
@ -40,6 +47,7 @@ cc_library(
hdrs = ["regs.h"],
copts = sapi_platform_copts(),
deps = [
":config",
":syscall",
":violation_cc_proto",
"//sandboxed_api/sandbox2/util:strerror",
@ -60,6 +68,7 @@ cc_library(
copts = sapi_platform_copts(),
visibility = ["//visibility:public"],
deps = [
":config",
":util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
@ -73,6 +82,7 @@ cc_test(
srcs = ["syscall_test.cc"],
copts = sapi_platform_copts(),
deps = [
":config",
":syscall",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
@ -85,12 +95,13 @@ cc_library(
hdrs = ["result.h"],
copts = sapi_platform_copts(),
deps = [
":config",
":regs",
":syscall",
":util",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
],
)
@ -272,6 +283,7 @@ cc_library(
deps = [
":client",
":comms",
":config",
":executor",
":fork_client",
":forkserver_cc_proto",
@ -300,12 +312,12 @@ cc_library(
"//sandboxed_api/util:flags",
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:status",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
@ -374,9 +386,9 @@ cc_library(
"//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@org_kernel_libcap//:libcap",
@ -404,6 +416,7 @@ cc_library(
hdrs = ["mounts.h"],
copts = sapi_platform_copts(),
deps = [
":config",
":mounttree_cc_proto",
"//sandboxed_api/sandbox2/util:file_base",
"//sandboxed_api/sandbox2/util:fileops",
@ -411,10 +424,10 @@ cc_library(
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:status",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
],
@ -468,6 +481,7 @@ cc_test(
],
deps = [
":comms",
":config",
":namespace",
":sandbox2",
":testing",
@ -505,12 +519,13 @@ cc_library(
copts = sapi_platform_copts(),
visibility = ["//visibility:public"],
deps = [
":config",
"//sandboxed_api/sandbox2/util:file_base",
"//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
@ -526,9 +541,9 @@ cc_library(
":util",
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:status",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
],
)
@ -541,6 +556,7 @@ cc_test(
deps = [
":buffer",
":comms",
":config",
":sandbox2",
":testing",
"//sandboxed_api/util:status_matchers",
@ -573,10 +589,10 @@ cc_library(
"//sandboxed_api/util:raw_logging",
"//sandboxed_api/util:status",
"//sandboxed_api/util:status_proto",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
@ -629,6 +645,7 @@ cc_test(
copts = sapi_platform_copts(),
data = ["//sandboxed_api/sandbox2/testcases:limits"],
deps = [
":config",
":limits",
":sandbox2",
":testing",
@ -671,6 +688,7 @@ cc_test(
"//sandboxed_api/sandbox2/testcases:policy",
],
deps = [
":config",
":limits",
":regs",
":sandbox2",
@ -695,6 +713,7 @@ cc_test(
],
tags = ["local"],
deps = [
":config",
":sandbox2",
":testing",
"//sandboxed_api/sandbox2/util:bpf_helper",
@ -805,6 +824,7 @@ cc_test(
"//sandboxed_api/util:status_matchers",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_glog//:glog",
"@com_google_googletest//:gtest_main",

View File

@ -17,6 +17,16 @@ add_subdirectory(unwind)
add_subdirectory(util)
add_subdirectory(network_proxy)
# sandboxed_api/sandbox2:config
add_library(sandbox2_config STATIC
config.h
)
add_library(sandbox2::config ALIAS sandbox2_config)
target_link_libraries(sandbox2_config PRIVATE
absl::config
sapi::base
)
# sandboxed_api/sandbox2:bpfdisassembler
add_library(sandbox2_bpfdisassembler STATIC
bpfdisassembler.cc
@ -37,6 +47,7 @@ add_library(sandbox2::regs ALIAS sandbox2_regs)
target_link_libraries(sandbox2_regs PRIVATE
absl::core_headers
absl::strings
sandbox2::config
sandbox2::strerror
sandbox2::syscall
sandbox2::violation_proto
@ -72,6 +83,7 @@ target_link_libraries(sandbox2_result PRIVATE
absl::base
absl::memory
absl::strings
sandbox2::config
sandbox2::regs
sandbox2::syscall
sandbox2::util
@ -271,6 +283,8 @@ target_link_libraries(sandbox2_sandbox2
absl::flat_hash_set
absl::memory
absl::optional
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -278,6 +292,7 @@ target_link_libraries(sandbox2_sandbox2
sandbox2::bpf_helper
sandbox2::client
sandbox2::comms
sandbox2::config
sandbox2::executor
sandbox2::file_base
sandbox2::fileops
@ -300,7 +315,6 @@ target_link_libraries(sandbox2_sandbox2
sandbox2::util
sandbox2::violation_proto
sapi::base
sapi::statusor
PUBLIC sapi::flags
sapi::status
sandbox2::logsink
@ -351,6 +365,8 @@ add_library(sandbox2_forkserver STATIC
add_library(sandbox2::forkserver ALIAS sandbox2_forkserver)
target_link_libraries(sandbox2_forkserver PRIVATE
absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
libcap::libcap
@ -369,7 +385,6 @@ target_link_libraries(sandbox2_forkserver PRIVATE
sandbox2::util
sapi::base
sapi::raw_logging
sapi::statusor
)
# sandboxed_api/sandbox2:fork_client
@ -397,9 +412,11 @@ target_link_libraries(sandbox2_mounts PRIVATE
absl::core_headers
absl::flat_hash_set
absl::status
absl::statusor
absl::str_format
absl::strings
protobuf::libprotobuf
sandbox2::config
sandbox2::file_base
sandbox2::fileops
sandbox2::minielf
@ -408,7 +425,6 @@ target_link_libraries(sandbox2_mounts PRIVATE
sapi::base
sapi::raw_logging
sapi::status
sapi::statusor
)
# sandboxed_api/sandbox2:namespace
@ -458,13 +474,14 @@ target_link_libraries(sandbox2_util
PRIVATE absl::core_headers
absl::str_format
absl::strings
sandbox2::config
sandbox2::file_base
sandbox2::fileops
sandbox2::strerror
sapi::base
sapi::raw_logging
sapi::statusor
PUBLIC absl::status
absl::statusor
)
target_compile_options(sandbox2_util PRIVATE
# The default is 16384, however we need to do a clone with a
@ -482,12 +499,13 @@ add_library(sandbox2::buffer ALIAS sandbox2_buffer)
target_link_libraries(sandbox2_buffer PRIVATE
absl::core_headers
absl::memory
absl::status
absl::statusor
absl::strings
sandbox2::strerror
sandbox2::util
sapi::base
sapi::status
sapi::statusor
)
# sandboxed_api/sandbox2:forkserver_proto
@ -527,6 +545,8 @@ add_library(sandbox2_comms STATIC
add_library(sandbox2::comms ALIAS sandbox2_comms)
target_link_libraries(sandbox2_comms
PRIVATE absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
sandbox2::strerror
@ -534,7 +554,6 @@ target_link_libraries(sandbox2_comms
sapi::base
sapi::raw_logging
sapi::status_proto
sapi::statusor
PUBLIC absl::core_headers
absl::status
absl::synchronization
@ -566,6 +585,7 @@ if(SAPI_ENABLE_TESTS)
)
target_link_libraries(syscall_test PRIVATE
absl::strings
sandbox2::config
sandbox2::syscall
sapi::test_main
)
@ -604,6 +624,7 @@ if(SAPI_ENABLE_TESTS)
absl::memory
absl::strings
sandbox2::comms
sandbox2::config
sandbox2::fileops
sandbox2::namespace
sandbox2::sandbox2
@ -628,6 +649,7 @@ if(SAPI_ENABLE_TESTS)
absl::memory
sandbox2::buffer
sandbox2::comms
sandbox2::config
sandbox2::sandbox2
sandbox2::testing
sapi::status_matchers
@ -702,6 +724,7 @@ if(SAPI_ENABLE_TESTS)
target_link_libraries(limits_test PRIVATE
absl::memory
sandbox2::bpf_helper
sandbox2::config
sandbox2::limits
sandbox2::sandbox2
sandbox2::testing
@ -751,6 +774,7 @@ if(SAPI_ENABLE_TESTS)
absl::memory
absl::strings
sandbox2::bpf_helper
sandbox2::config
sandbox2::limits
sandbox2::regs
sandbox2::sandbox2
@ -776,6 +800,7 @@ if(SAPI_ENABLE_TESTS)
absl::memory
absl::strings
sandbox2::bpf_helper
sandbox2::config
sandbox2::sandbox2
sandbox2::testing
sapi::status_matchers
@ -832,6 +857,7 @@ if(SAPI_ENABLE_TESTS)
)
target_link_libraries(stack_trace_test PRIVATE
absl::memory
absl::status
absl::strings
sandbox2::bpf_helper
sandbox2::fileops

View File

@ -21,6 +21,7 @@
#include <cerrno>
#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "sandboxed_api/sandbox2/util.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
@ -28,7 +29,7 @@
namespace sandbox2 {
// Creates a new Buffer that is backed by the specified file descriptor.
sapi::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateFromFd(int fd) {
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateFromFd(int fd) {
auto buffer = absl::WrapUnique(new Buffer{});
struct stat stat_buf;
@ -53,7 +54,7 @@ sapi::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateFromFd(int fd) {
// Creates a new Buffer of the specified size, backed by a temporary file that
// will be immediately deleted.
sapi::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(int64_t size) {
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(int64_t size) {
int fd;
if (!util::CreateMemFd(&fd)) {
return absl::InternalError("Could not create buffer temp file");

View File

@ -19,7 +19,7 @@
#include <cstdint>
#include <memory>
#include "sandboxed_api/util/statusor.h"
#include "absl/status/statusor.h"
namespace sandbox2 {
@ -37,11 +37,11 @@ class Buffer final {
// Creates a new Buffer that is backed by the specified file descriptor.
// The Buffer takes ownership of the descriptor and will close it when
// destroyed.
static sapi::StatusOr<std::unique_ptr<Buffer>> CreateFromFd(int fd);
static absl::StatusOr<std::unique_ptr<Buffer>> CreateFromFd(int fd);
// Creates a new Buffer of the specified size, backed by a temporary file that
// will be immediately deleted.
static sapi::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(int64_t size);
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(int64_t size);
// Returns a pointer to the buffer, which is read/write.
uint8_t* data() const { return buf_; }

View File

@ -29,6 +29,7 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/config.h"
#include "sandboxed_api/sandbox2/executor.h"
#include "sandboxed_api/sandbox2/ipc.h"
#include "sandboxed_api/sandbox2/policy.h"
@ -83,19 +84,20 @@ std::unique_ptr<Policy> BufferTestcasePolicy() {
.AllowSyscall(__NR_lseek)
.AllowSyscall(__NR_close)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
#ifdef __NR_open
.BlockSyscallWithErrno(__NR_open, ENOENT)
#endif
.BlockSyscallWithErrno(__NR_openat, ENOENT)
#ifdef __NR_access
// On Debian, even static binaries check existence of
// /etc/ld.so.nohwcap.
.BlockSyscallWithErrno(__NR_access, ENOENT)
#endif
#ifdef __NR_faccessat
.BlockSyscallWithErrno(__NR_faccessat, ENOENT)
#endif
.BuildOrDie();
#if defined(__powerpc64__)
s2p->AllowUnsafeMmapFiles();
s2p->AllowUnsafeMmapShared();
#endif /* defined(__powerpc64__) */
return s2p;
}

View File

@ -36,6 +36,7 @@
#include "google/protobuf/message.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
@ -44,7 +45,6 @@
#include "sandboxed_api/util/raw_logging.h"
#include "sandboxed_api/util/status.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/statusor.h"
#ifdef MEMORY_SANITIZER
#include "base/dynamic_annotations.h"
@ -228,7 +228,7 @@ void Comms::Terminate() {
}
}
bool Comms::SendTLV(uint32_t tag, uint64_t length, const uint8_t* bytes) {
bool Comms::SendTLV(uint32_t tag, size_t length, const void* value) {
if (length > GetMaxMsgSize()) {
SAPI_RAW_LOG(ERROR, "Maximum TLV message size exceeded: (%u > %u)", length,
GetMaxMsgSize());
@ -249,14 +249,14 @@ bool Comms::SendTLV(uint32_t tag, uint64_t length, const uint8_t* bytes) {
length);
{
absl::MutexLock lock(&tlv_send_transmission_mutex_);
if (!Send(reinterpret_cast<uint8_t*>(&tag), sizeof(tag))) {
if (!Send(&tag, sizeof(tag))) {
return false;
}
if (!Send(reinterpret_cast<uint8_t*>(&length), sizeof(length))) {
if (!Send(&length, sizeof(length))) {
return false;
}
if (length > 0) {
if (!Send(bytes, length)) {
if (!Send(value, length)) {
return false;
}
}
@ -265,40 +265,38 @@ bool Comms::SendTLV(uint32_t tag, uint64_t length, const uint8_t* bytes) {
}
bool Comms::RecvString(std::string* v) {
TLV tlv;
if (!RecvTLV(&tlv)) {
uint32_t tag;
if (!RecvTLV(&tag, v)) {
return false;
}
if (tlv.tag != kTagString) {
if (tag != kTagString) {
SAPI_RAW_LOG(ERROR, "Expected (kTagString == 0x%x), got: 0x%x", kTagString,
tlv.tag);
tag);
return false;
}
v->assign(reinterpret_cast<const char*>(tlv.value.data()), tlv.value.size());
return true;
}
bool Comms::SendString(const std::string& v) {
return SendTLV(kTagString, v.length(),
reinterpret_cast<const uint8_t*>(v.c_str()));
return SendTLV(kTagString, v.length(), v.c_str());
}
bool Comms::RecvBytes(std::vector<uint8_t>* buffer) {
TLV tlv;
if (!RecvTLV(&tlv)) {
uint32_t tag;
if (!RecvTLV(&tag, buffer)) {
return false;
}
if (tlv.tag != kTagBytes) {
if (tag != kTagBytes) {
buffer->clear();
SAPI_RAW_LOG(ERROR, "Expected (kTagBytes == 0x%x), got: 0x%u", kTagBytes,
tlv.tag);
tag);
return false;
}
buffer->swap(tlv.value);
return true;
}
bool Comms::SendBytes(const uint8_t* v, uint64_t len) {
bool Comms::SendBytes(const uint8_t* v, size_t len) {
return SendTLV(kTagBytes, len, v);
}
@ -347,7 +345,7 @@ bool Comms::RecvFD(int* fd) {
const auto op = [&msg](int fd) -> ssize_t {
PotentiallyBlockingRegion region;
// Use syscall, otherwise we would need to whitelist socketcall() on PPC.
// Use syscall, otherwise we would need to allow socketcall() on PPC.
return TEMP_FAILURE_RETRY(
util::Syscall(__NR_recvmsg, fd, reinterpret_cast<uintptr_t>(&msg), 0));
};
@ -419,7 +417,7 @@ bool Comms::SendFD(int fd) {
int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
fds[0] = fd;
InternalTLV tlv = {kTagFd, sizeof(tlv.val), 0};
InternalTLV tlv = {kTagFd, 0};
iovec iov;
iov.iov_base = &tlv;
@ -462,8 +460,9 @@ bool Comms::SendFD(int fd) {
}
bool Comms::RecvProtoBuf(google::protobuf::Message* message) {
TLV tlv;
if (!RecvTLV(&tlv)) {
uint32_t tag;
std::vector<uint8_t> bytes;
if (!RecvTLV(&tag, &bytes)) {
if (IsConnected()) {
SAPI_RAW_PLOG(ERROR, "RecvProtoBuf failed for (%s)", socket_name_);
} else {
@ -473,11 +472,11 @@ bool Comms::RecvProtoBuf(google::protobuf::Message* message) {
return false;
}
if (tlv.tag != kTagProto2) {
SAPI_RAW_LOG(ERROR, "Expected tag: 0x%x, got: 0x%u", kTagProto2, tlv.tag);
if (tag != kTagProto2) {
SAPI_RAW_LOG(ERROR, "Expected tag: 0x%x, got: 0x%u", kTagProto2, tag);
return false;
}
return message->ParseFromArray(tlv.value.data(), tlv.value.size());
return message->ParseFromArray(bytes.data(), bytes.size());
}
bool Comms::SendProtoBuf(const google::protobuf::Message& message) {
@ -513,8 +512,9 @@ socklen_t Comms::CreateSockaddrUn(sockaddr_un* sun) {
return slen;
}
bool Comms::Send(const uint8_t* bytes, uint64_t len) {
uint64_t total_sent = 0;
bool Comms::Send(const void* data, size_t len) {
size_t total_sent = 0;
const char* bytes = reinterpret_cast<const char*>(data);
const auto op = [bytes, len, &total_sent](int fd) -> ssize_t {
PotentiallyBlockingRegion region;
return TEMP_FAILURE_RETRY(write(fd, &bytes[total_sent], len - total_sent));
@ -545,8 +545,9 @@ bool Comms::Send(const uint8_t* bytes, uint64_t len) {
return true;
}
bool Comms::Recv(uint8_t* bytes, uint64_t len) {
uint64_t total_recv = 0;
bool Comms::Recv(void* data, size_t len) {
size_t total_recv = 0;
char* bytes = reinterpret_cast<char*>(data);
const auto op = [bytes, len, &total_recv](int fd) -> ssize_t {
PotentiallyBlockingRegion region;
return TEMP_FAILURE_RETRY(read(fd, &bytes[total_recv], len - total_recv));
@ -573,7 +574,7 @@ bool Comms::Recv(uint8_t* bytes, uint64_t len) {
}
// Internal helper method (low level).
bool Comms::RecvTL(uint32_t* tag, uint64_t* length) {
bool Comms::RecvTL(uint32_t* tag, size_t* length) {
if (!Recv(reinterpret_cast<uint8_t*>(tag), sizeof(*tag))) {
return false;
}
@ -599,50 +600,49 @@ bool Comms::RecvTL(uint32_t* tag, uint64_t* length) {
return true;
}
bool Comms::RecvTLV(TLV* tlv) {
absl::MutexLock lock(&tlv_recv_transmission_mutex_);
uint64_t length;
if (!RecvTL(&tlv->tag, &length)) {
return false;
}
tlv->value.resize(length);
return length == 0 || Recv(tlv->value.data(), length);
bool Comms::RecvTLV(uint32_t* tag, std::vector<uint8_t>* value) {
return RecvTLVGeneric(tag, value);
}
bool Comms::RecvTLV(uint32_t* tag, std::vector<uint8_t>* value) {
bool Comms::RecvTLV(uint32_t* tag, std::string* value) {
return RecvTLVGeneric(tag, value);
}
template <typename T>
bool Comms::RecvTLVGeneric(uint32_t* tag, T* value) {
absl::MutexLock lock(&tlv_recv_transmission_mutex_);
uint64_t length;
size_t length;
if (!RecvTL(tag, &length)) {
return false;
}
value->resize(length);
return length == 0 || Recv(value->data(), length);
return length == 0 || Recv(reinterpret_cast<uint8_t*>(value->data()), length);
}
bool Comms::RecvTLV(uint32_t* tag, uint64_t* length, void* buffer,
uint64_t buffer_size) {
bool Comms::RecvTLV(uint32_t* tag, size_t* length, void* buffer,
size_t buffer_size) {
absl::MutexLock lock(&tlv_recv_transmission_mutex_);
if (!RecvTL(tag, length)) {
return false;
}
if (*length == 0) {
return true;
}
if (*length > buffer_size) {
SAPI_RAW_LOG(ERROR, "Buffer size too small (0x%x > 0x%x)", *length,
buffer_size);
return false;
} else if (*length > 0) {
if (!Recv(reinterpret_cast<uint8_t*>(buffer), *length)) {
return false;
}
}
return true;
return Recv(reinterpret_cast<uint8_t*>(buffer), *length);
}
bool Comms::RecvInt(void* buffer, uint64_t len, uint32_t tag) {
bool Comms::RecvInt(void* buffer, size_t len, uint32_t tag) {
uint32_t received_tag;
uint64_t received_length;
size_t received_length;
if (!RecvTLV(&received_tag, &received_length, buffer, len)) {
return false;
}

View File

@ -60,7 +60,7 @@ class Comms {
static constexpr uint32_t kTagFd = 0X80000201;
// Any payload size above this limit will LOG(WARNING).
static constexpr uint64_t kWarnMsgSize = (256ULL << 20);
static constexpr size_t kWarnMsgSize = (256ULL << 20);
// Sandbox2-specific convention where FD=1023 is always passed to the
// sandboxed process as a communication channel (encapsulated in the
@ -103,14 +103,17 @@ class Comms {
// Note: The actual size is "unlimited", although the Buffer API is more
// efficient for large transfers. There is an arbitrary limit to ~2GiB to
// avoid protobuf serialization issues.
uint64_t GetMaxMsgSize() const { return std::numeric_limits<int32_t>::max(); }
size_t GetMaxMsgSize() const { return std::numeric_limits<int32_t>::max(); }
bool SendTLV(uint32_t tag, uint64_t length, const uint8_t* bytes);
bool SendTLV(uint32_t tag, size_t length, const void* value);
// Receive a TLV structure, the memory for the value will be allocated
// by std::vector.
bool RecvTLV(uint32_t* tag, std::vector<uint8_t>* value);
// Receive a TLV structure, the memory for the value will be allocated
// by std::string.
bool RecvTLV(uint32_t* tag, std::string* value);
// Receives a TLV value into a specified buffer without allocating memory.
bool RecvTLV(uint32_t* tag, uint64_t* length, void* buffer, uint64_t buffer_size);
bool RecvTLV(uint32_t* tag, size_t* length, void* buffer, size_t buffer_size);
// Sends/receives various types of data.
bool RecvUint8(uint8_t* v) { return RecvIntGeneric(v, kTagUint8); }
@ -135,7 +138,7 @@ class Comms {
bool SendString(const std::string& v);
bool RecvBytes(std::vector<uint8_t>* buffer);
bool SendBytes(const uint8_t* v, uint64_t len);
bool SendBytes(const uint8_t* v, size_t len);
bool SendBytes(const std::vector<uint8_t>& buffer);
// Receives remote process credentials.
@ -174,38 +177,32 @@ class Comms {
// State of the channel (enum), socket will have to be connected later on.
State state_ = State::kUnconnected;
// TLV structure used to pass messages around.
struct TLV {
uint32_t tag;
std::vector<uint8_t> value;
};
// Special struct for passing credentials or FDs. Different from the one above
// as it inlines the value. This is important as the data is transmitted using
// sendmsg/recvmsg instead of send/recv.
struct ABSL_ATTRIBUTE_PACKED InternalTLV {
uint32_t tag;
uint32_t len;
uint64_t val;
};
// Fills sockaddr_un struct with proper values.
socklen_t CreateSockaddrUn(sockaddr_un* sun);
// Support for EINTR and size completion.
bool Send(const uint8_t* bytes, uint64_t len);
bool Recv(uint8_t* bytes, uint64_t len);
bool Send(const void* data, size_t len);
bool Recv(void* data, size_t len);
// Receives tag and length. Assumes that the `tlv_transmission_mutex_` mutex
// is locked.
bool RecvTL(uint32_t* tag, uint64_t* length)
bool RecvTL(uint32_t* tag, size_t* length)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(tlv_recv_transmission_mutex_);
// Receives whole TLV structure, allocates memory for the data.
bool RecvTLV(TLV* tlv);
// T has to be a ContiguousContainer
template <typename T>
bool RecvTLVGeneric(uint32_t* tag, T* value);
// Receives arbitrary integers.
bool RecvInt(void* buffer, uint64_t len, uint32_t tag);
bool RecvInt(void* buffer, size_t len, uint32_t tag);
template <typename T>
bool RecvIntGeneric(T* output, uint32_t tag) {
@ -214,7 +211,7 @@ class Comms {
template <typename T>
bool SendGeneric(T value, uint32_t tag) {
return SendTLV(tag, sizeof(T), reinterpret_cast<const uint8_t*>(&value));
return SendTLV(tag, sizeof(T), &value);
}
};

View File

@ -0,0 +1,85 @@
// 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 SANDBOXED_API_SANDBOX2_CONFIG_H_
#define SANDBOXED_API_SANDBOX2_CONFIG_H_
#include <cstdint>
#include "absl/base/config.h"
// GCC/Clang define __x86_64__, Visual Studio uses _M_X64
#if defined(__x86_64__) || defined(_M_X64)
#define SAPI_X86_64 1
// Check various spellings for 64-bit POWER. Not checking for Visual Studio, as
// it does not support 64-bit POWER.
#elif (defined(__PPC64__) || defined(__powerpc64__) || defined(__ppc64__)) && \
defined(ABSL_IS_LITTLE_ENDIAN)
#define SAPI_PPC64_LE 1
// Spellings for AArch64
#elif defined(__aarch64__) || defined(_M_ARM64)
#define SAPI_ARM64 1
#endif
namespace sandbox2 {
namespace cpu {
// CPU architectures known to Sandbox2
enum Architecture : uint16_t {
// Linux: Use a magic value, so it can be easily spotted in the seccomp-bpf
// bytecode decompilation stream. Must be < (1<<15), as/ that's the size of
// data which can be returned by BPF.
kUnknown = 0xCAF0,
kX8664,
kX86,
kPPC64LE,
kArm64,
};
} // namespace cpu
namespace host_cpu {
// Returns the current host CPU architecture if supported. If not supported,
// returns cpu::kUnknown.
constexpr cpu::Architecture Architecture() {
#if defined(SAPI_X86_64)
return cpu::kX8664;
#elif defined(SAPI_PPC64_LE)
return cpu::kPPC64LE;
#elif defined(SAPI_ARM64)
return cpu::kArm64;
#else
return cpu::kUnknown;
#endif
}
constexpr bool IsX8664() { return Architecture() == cpu::kX8664; }
constexpr bool IsPPC64LE() { return Architecture() == cpu::kPPC64LE; }
constexpr bool IsArm64() { return Architecture() == cpu::kArm64; }
} // namespace host_cpu
static_assert(host_cpu::Architecture() != cpu::kUnknown,
"Host CPU architecture is not supported: One of x86-64, POWER64 "
"(little endian) or AArch64 is required.");
} // namespace sandbox2
#endif // SANDBOXED_API_SANDBOX2_CONFIG_H_

View File

@ -27,7 +27,6 @@ cc_binary(
deps = [
"//sandboxed_api/sandbox2",
"//sandboxed_api/sandbox2:comms",
"//sandboxed_api/sandbox2/network_proxy:filtering",
"//sandboxed_api/sandbox2/util:bpf_helper",
"//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:runfiles",
@ -48,7 +47,9 @@ cc_binary(
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:flags",
"//sandboxed_api/util:status",
"//sandboxed_api/util:statusor",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)

View File

@ -31,13 +31,14 @@ target_link_libraries(sandbox2_networkproxy_sandbox PRIVATE
sapi::flags
)
# sandboxed_api/sandbox2/examples/networkproxy:networkproxy_bin
add_executable(sandbox2_networkproxy_bin
networkproxy_bin.cc
)
add_executable(sandbox2::networkproxy_bin ALIAS sandbox2_networkproxy_bin)
target_link_libraries(sandbox2_networkproxy_bin PRIVATE
absl::status
absl::statusor
absl::str_format
glog::glog
gflags::gflags
@ -48,6 +49,4 @@ target_link_libraries(sandbox2_networkproxy_bin PRIVATE
sapi::base
sapi::flags
sapi::status
sapi::statusor
)

View File

@ -12,15 +12,16 @@
#include <cstring>
#include "sandboxed_api/util/flag.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "sandboxed_api/sandbox2/client.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/network_proxy/client.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
#include "sandboxed_api/util/status.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/statusor.h"
ABSL_FLAG(bool, connect_with_handler, true, "Connect using automatic mode.");
@ -58,7 +59,7 @@ absl::Status CommunicationTest(int sock) {
return absl::OkStatus();
}
sapi::StatusOr<struct sockaddr_in6> CreateAddres(int port) {
absl::StatusOr<struct sockaddr_in6> CreateAddres(int port) {
static struct sockaddr_in6 saddr {};
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(port);
@ -86,7 +87,7 @@ absl::Status ConnectWithHandler(int s, const struct sockaddr_in6& saddr) {
return absl::OkStatus();
}
sapi::StatusOr<int> ConnectToServer(int port) {
absl::StatusOr<int> ConnectToServer(int port) {
SAPI_ASSIGN_OR_RETURN(struct sockaddr_in6 saddr, CreateAddres(port));
sandbox2::file_util::fileops::FDCloser s(socket(AF_INET6, SOCK_STREAM, 0));
@ -134,7 +135,7 @@ int main(int argc, char** argv) {
return 2;
}
sapi::StatusOr<int> sock_s = ConnectToServer(port);
absl::StatusOr<int> sock_s = ConnectToServer(port);
if (!sock_s.ok()) {
LOG(ERROR) << sock_s.status().message();
return 3;

View File

@ -52,8 +52,10 @@ std::unique_ptr<sandbox2::Policy> GetPolicy() {
// Allow the getpid() syscall.
.AllowSyscall(__NR_getpid)
#ifdef __NR_access
// On Debian, even static binaries check existence of /etc/ld.so.nohwcap.
.BlockSyscallWithErrno(__NR_access, ENOENT)
#endif
// Examples for AddPolicyOnSyscall:
.AddPolicyOnSyscall(__NR_write,

View File

@ -86,7 +86,7 @@ pid_t Executor::StartSubProcess(int32_t clone_flags, const Namespace* ns,
if (!path_.empty()) {
exec_fd_ = open(path_.c_str(), O_PATH);
if (exec_fd_ < 0) {
LOG(ERROR) << "Could not open file " << path_;
PLOG(ERROR) << "Could not open file " << path_;
return -1;
}
}

View File

@ -36,6 +36,7 @@
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
@ -55,7 +56,6 @@
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
#include "sandboxed_api/util/raw_logging.h"
#include "sandboxed_api/util/statusor.h"
namespace {
// "Moves" the old FD to the new FD number.
@ -142,7 +142,7 @@ absl::Status SendPid(int signaling_fd) {
return absl::OkStatus();
}
sapi::StatusOr<pid_t> ReceivePid(int signaling_fd) {
absl::StatusOr<pid_t> ReceivePid(int signaling_fd) {
union {
struct cmsghdr cmh;
char ctrl[CMSG_SPACE(sizeof(struct ucred))];

View File

@ -23,6 +23,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "sandboxed_api/sandbox2/config.h"
#include "sandboxed_api/sandbox2/executor.h"
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"

View File

@ -49,6 +49,7 @@
#include "absl/time/time.h"
#include "sandboxed_api/sandbox2/client.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/config.h"
#include "sandboxed_api/sandbox2/executor.h"
#include "sandboxed_api/sandbox2/limits.h"
#include "sandboxed_api/sandbox2/mounts.h"
@ -761,7 +762,7 @@ void Monitor::LogSyscallViolation(const Syscall& syscall) const {
void Monitor::EventPtraceSeccomp(pid_t pid, int event_msg) {
// If the seccomp-policy is using RET_TRACE, we request that it returns the
// syscall architecture identifier in the SECCOMP_RET_DATA.
const auto syscall_arch = static_cast<Syscall::CpuArch>(event_msg);
const auto syscall_arch = static_cast<cpu::Architecture>(event_msg);
Regs regs(pid);
auto status = regs.Fetch();
if (!status.ok()) {

View File

@ -27,19 +27,20 @@
#include "google/protobuf/util/message_differencer.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "sandboxed_api/sandbox2/config.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/minielf.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
#include "sandboxed_api/util/raw_logging.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/statusor.h"
namespace sandbox2 {
namespace {
@ -97,7 +98,7 @@ absl::string_view GetOutsidePath(const MountTree::Node& node) {
}
}
sapi::StatusOr<std::string> ExistingPathInsideDir(
absl::StatusOr<std::string> ExistingPathInsideDir(
absl::string_view dir_path, absl::string_view relative_path) {
auto path = file::CleanPath(file::JoinPath(dir_path, relative_path));
if (file_util::fileops::StripBasename(path) != dir_path) {
@ -112,6 +113,8 @@ sapi::StatusOr<std::string> ExistingPathInsideDir(
absl::Status ValidateInterpreter(absl::string_view interpreter) {
const absl::flat_hash_set<std::string> allowed_interpreters = {
"/lib64/ld-linux-x86-64.so.2",
"/lib64/ld64.so.2", // PPC64
"/lib/ld-linux-aarch64.so.1", // AArch64
};
if (!allowed_interpreters.contains(interpreter)) {
@ -132,15 +135,21 @@ std::string ResolveLibraryPath(absl::string_view lib_name,
return "";
}
constexpr absl::string_view GetPlatformCPUName() {
switch (host_cpu::Architecture()) {
case cpu::kX8664:
return "x86_64";
case cpu::kPPC64LE:
return "ppc64";
case cpu::kArm64:
return "aarch64";
default:
return "unknown";
}
}
std::string GetPlatform(absl::string_view interpreter) {
#if defined(__x86_64__)
constexpr absl::string_view kCpuPlatform = "x86_64";
#elif defined(__powerpc64__)
constexpr absl::string_view kCpuPlatform = "ppc64";
#else
constexpr absl::string_view kCpuPlatform = "unknown";
#endif
return absl::StrCat(kCpuPlatform, "-linux-gnu");
return absl::StrCat(GetPlatformCPUName(), "-linux-gnu");
}
} // namespace
@ -496,7 +505,7 @@ std::string MountFlagsToString(uint64_t flags) {
SAPI_MAP(MS_POSIXACL),
SAPI_MAP(MS_UNBINDABLE),
SAPI_MAP(MS_PRIVATE),
SAPI_MAP(MS_SLAVE),
SAPI_MAP(MS_SLAVE), // Inclusive language: system constant
SAPI_MAP(MS_SHARED),
SAPI_MAP(MS_RELATIME),
SAPI_MAP(MS_KERNMOUNT),

View File

@ -28,6 +28,7 @@
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/config.h"
#include "sandboxed_api/sandbox2/executor.h"
#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"

Some files were not shown because too many files have changed in this diff Show More