Initial LibUV commit

pull/55/head
Federico Stazi 2020-09-22 14:41:06 +00:00
parent 35f9268e23
commit 22d21cd1c3
19 changed files with 1771 additions and 0 deletions

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "oss-internship-2020/gdal/gdal"]
path = oss-internship-2020/gdal/gdal
url = https://github.com/OSGeo/gdal/tree/master/gdal
[submodule "oss-internship-2020/libuv/libuv"]
path = oss-internship-2020/libuv/libuv
url = git@github.com:libuv/libuv.git

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

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,394 @@
# 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(libuv_sandbox)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/uv_wrapper")
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h"
"${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMAND python3 "generator/wrapper_generator.py"
"libuv/include/uv.h"
"${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h"
"${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc"
COMMENT "Generate the wrapper header and source files"
)
option(UV_SAPI_ENABLE_EXAMPLES "" ON)
option(UV_SAPI_ENABLE_TESTS "" ON)
# Add callbacks used by examples and tests
if (UV_SAPI_ENABLE_EXAMPLES OR UV_SAPI_ENABLE_TESTS)
list(APPEND UV_SAPI_CALLBACKS
"${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.h"
"${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.cc"
)
endif()
# Wrapper library including wrappers for all libuv methods and callbacks
# The UV_SAPI_CALLBACKS variable should contain the absolute paths of
# all the files implementing the callbacks
add_library(uv_wrapper_and_callbacks OBJECT
"${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h"
"${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc"
"${UV_SAPI_CALLBACKS}"
)
set_target_properties(uv_wrapper_and_callbacks
PROPERTIES LINKER_LANGUAGE C
)
# Link the wrapper to the original uv library
add_subdirectory(libuv)
target_link_libraries(uv_wrapper_and_callbacks uv_a)
# Setup Sandboxed API
set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES ${UV_SAPI_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${UV_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(uv_sapi
# List of all the generated methods
FUNCTIONS sapi_uv_accept
sapi_uv_async_init
sapi_uv_async_send
sapi_uv_backend_fd
sapi_uv_backend_timeout
sapi_uv_barrier_destroy
sapi_uv_barrier_init
sapi_uv_barrier_wait
sapi_uv_buf_init
sapi_uv_cancel
sapi_uv_chdir
sapi_uv_check_init
sapi_uv_check_start
sapi_uv_check_stop
sapi_uv_close
sapi_uv_cond_broadcast
sapi_uv_cond_destroy
sapi_uv_cond_init
sapi_uv_cond_signal
sapi_uv_cond_timedwait
sapi_uv_cond_wait
sapi_uv_cpu_info
sapi_uv_cwd
sapi_uv_default_loop
sapi_uv_disable_stdio_inheritance
sapi_uv_dlclose
sapi_uv_dlerror
sapi_uv_dlopen
sapi_uv_dlsym
sapi_uv_err_name
sapi_uv_err_name_r
sapi_uv_exepath
sapi_uv_fileno
sapi_uv_free_cpu_info
sapi_uv_free_interface_addresses
sapi_uv_freeaddrinfo
sapi_uv_fs_access
sapi_uv_fs_chmod
sapi_uv_fs_chown
sapi_uv_fs_close
sapi_uv_fs_closedir
sapi_uv_fs_copyfile
sapi_uv_fs_event_getpath
sapi_uv_fs_event_init
sapi_uv_fs_event_start
sapi_uv_fs_event_stop
sapi_uv_fs_fchmod
sapi_uv_fs_fchown
sapi_uv_fs_fdatasync
sapi_uv_fs_fstat
sapi_uv_fs_fsync
sapi_uv_fs_ftruncate
sapi_uv_fs_futime
sapi_uv_fs_get_path
sapi_uv_fs_get_ptr
sapi_uv_fs_get_result
sapi_uv_fs_get_statbuf
sapi_uv_fs_get_system_error
sapi_uv_fs_get_type
sapi_uv_fs_lchown
sapi_uv_fs_link
sapi_uv_fs_lstat
sapi_uv_fs_lutime
sapi_uv_fs_mkdir
sapi_uv_fs_mkdtemp
sapi_uv_fs_mkstemp
sapi_uv_fs_open
sapi_uv_fs_opendir
sapi_uv_fs_poll_getpath
sapi_uv_fs_poll_init
sapi_uv_fs_poll_start
sapi_uv_fs_poll_stop
sapi_uv_fs_read
sapi_uv_fs_readdir
sapi_uv_fs_readlink
sapi_uv_fs_realpath
sapi_uv_fs_rename
sapi_uv_fs_req_cleanup
sapi_uv_fs_rmdir
sapi_uv_fs_scandir
sapi_uv_fs_scandir_next
sapi_uv_fs_sendfile
sapi_uv_fs_stat
sapi_uv_fs_statfs
sapi_uv_fs_symlink
sapi_uv_fs_unlink
sapi_uv_fs_utime
sapi_uv_fs_write
sapi_uv_get_constrained_memory
sapi_uv_get_free_memory
sapi_uv_get_osfhandle
sapi_uv_get_process_title
sapi_uv_get_total_memory
sapi_uv_getaddrinfo
sapi_uv_getnameinfo
sapi_uv_getrusage
sapi_uv_gettimeofday
sapi_uv_guess_handle
sapi_uv_handle_get_data
sapi_uv_handle_get_loop
sapi_uv_handle_get_type
sapi_uv_handle_set_data
sapi_uv_handle_size
sapi_uv_handle_type_name
sapi_uv_has_ref
sapi_uv_hrtime
sapi_uv_idle_init
sapi_uv_idle_start
sapi_uv_idle_stop
sapi_uv_if_indextoiid
sapi_uv_if_indextoname
sapi_uv_inet_ntop
sapi_uv_inet_pton
sapi_uv_interface_addresses
sapi_uv_ip4_addr
sapi_uv_ip4_name
sapi_uv_ip6_addr
sapi_uv_ip6_name
sapi_uv_is_active
sapi_uv_is_closing
sapi_uv_is_readable
sapi_uv_is_writable
sapi_uv_key_create
sapi_uv_key_delete
sapi_uv_key_get
sapi_uv_key_set
sapi_uv_kill
sapi_uv_library_shutdown
sapi_uv_listen
sapi_uv_loadavg
sapi_uv_loop_alive
sapi_uv_loop_close
sapi_uv_loop_configure
sapi_uv_loop_configure_int
sapi_uv_loop_delete
sapi_uv_loop_fork
sapi_uv_loop_get_data
sapi_uv_loop_init
sapi_uv_loop_new
sapi_uv_loop_set_data
sapi_uv_loop_size
sapi_uv_metrics_idle_time
sapi_uv_mutex_destroy
sapi_uv_mutex_init
sapi_uv_mutex_init_recursive
sapi_uv_mutex_lock
sapi_uv_mutex_trylock
sapi_uv_mutex_unlock
sapi_uv_now
sapi_uv_once
sapi_uv_open_osfhandle
sapi_uv_os_environ
sapi_uv_os_free_environ
sapi_uv_os_free_passwd
sapi_uv_os_get_passwd
sapi_uv_os_getenv
sapi_uv_os_gethostname
sapi_uv_os_getpid
sapi_uv_os_getppid
sapi_uv_os_getpriority
sapi_uv_os_homedir
sapi_uv_os_setenv
sapi_uv_os_setpriority
sapi_uv_os_tmpdir
sapi_uv_os_uname
sapi_uv_os_unsetenv
sapi_uv_pipe_bind
sapi_uv_pipe_chmod
sapi_uv_pipe_connect
sapi_uv_pipe_getpeername
sapi_uv_pipe_getsockname
sapi_uv_pipe_init
sapi_uv_pipe_open
sapi_uv_pipe_pending_count
sapi_uv_pipe_pending_instances
sapi_uv_pipe_pending_type
sapi_uv_poll_init
sapi_uv_poll_init_socket
sapi_uv_poll_start
sapi_uv_poll_stop
sapi_uv_prepare_init
sapi_uv_prepare_start
sapi_uv_prepare_stop
sapi_uv_print_active_handles
sapi_uv_print_all_handles
sapi_uv_process_get_pid
sapi_uv_process_kill
sapi_uv_queue_work
sapi_uv_random
sapi_uv_read_start
sapi_uv_read_stop
sapi_uv_recv_buffer_size
sapi_uv_ref
sapi_uv_replace_allocator
sapi_uv_req_get_data
sapi_uv_req_get_type
sapi_uv_req_set_data
sapi_uv_req_size
sapi_uv_req_type_name
sapi_uv_resident_set_memory
sapi_uv_run
sapi_uv_rwlock_destroy
sapi_uv_rwlock_init
sapi_uv_rwlock_rdlock
sapi_uv_rwlock_rdunlock
sapi_uv_rwlock_tryrdlock
sapi_uv_rwlock_trywrlock
sapi_uv_rwlock_wrlock
sapi_uv_rwlock_wrunlock
sapi_uv_sem_destroy
sapi_uv_sem_init
sapi_uv_sem_post
sapi_uv_sem_trywait
sapi_uv_sem_wait
sapi_uv_send_buffer_size
sapi_uv_set_process_title
sapi_uv_setup_args
sapi_uv_shutdown
sapi_uv_signal_init
sapi_uv_signal_start
sapi_uv_signal_start_oneshot
sapi_uv_signal_stop
sapi_uv_sleep
sapi_uv_spawn
sapi_uv_stop
sapi_uv_stream_get_write_queue_size
sapi_uv_stream_set_blocking
sapi_uv_strerror
sapi_uv_strerror_r
sapi_uv_tcp_bind
sapi_uv_tcp_close_reset
sapi_uv_tcp_connect
sapi_uv_tcp_getpeername
sapi_uv_tcp_getsockname
sapi_uv_tcp_init
sapi_uv_tcp_init_ex
sapi_uv_tcp_keepalive
sapi_uv_tcp_nodelay
sapi_uv_tcp_open
sapi_uv_tcp_simultaneous_accepts
sapi_uv_thread_create
sapi_uv_thread_create_ex
sapi_uv_thread_equal
sapi_uv_thread_join
sapi_uv_thread_self
sapi_uv_timer_again
sapi_uv_timer_get_due_in
sapi_uv_timer_get_repeat
sapi_uv_timer_init
sapi_uv_timer_set_repeat
sapi_uv_timer_start
sapi_uv_timer_stop
sapi_uv_translate_sys_error
sapi_uv_try_write
sapi_uv_tty_get_vterm_state
sapi_uv_tty_get_winsize
sapi_uv_tty_init
sapi_uv_tty_reset_mode
sapi_uv_tty_set_mode
sapi_uv_tty_set_vterm_state
sapi_uv_udp_bind
sapi_uv_udp_connect
sapi_uv_udp_get_send_queue_count
sapi_uv_udp_get_send_queue_size
sapi_uv_udp_getpeername
sapi_uv_udp_getsockname
sapi_uv_udp_init
sapi_uv_udp_init_ex
sapi_uv_udp_open
sapi_uv_udp_recv_start
sapi_uv_udp_recv_stop
sapi_uv_udp_send
sapi_uv_udp_set_broadcast
sapi_uv_udp_set_membership
sapi_uv_udp_set_multicast_interface
sapi_uv_udp_set_multicast_loop
sapi_uv_udp_set_multicast_ttl
sapi_uv_udp_set_source_membership
sapi_uv_udp_set_ttl
sapi_uv_udp_try_send
sapi_uv_udp_using_recvmmsg
sapi_uv_unref
sapi_uv_update_time
sapi_uv_uptime
sapi_uv_version
sapi_uv_version_string
sapi_uv_walk
sapi_uv_write
sapi_uv_write2
INPUTS "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h"
LIBRARY uv_wrapper_and_callbacks
LIBRARY_NAME UV
NAMESPACE ""
)
# Include generated SAPI header
target_include_directories(uv_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
# Add examples
if (UV_SAPI_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
# Add tests
if (UV_SAPI_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -0,0 +1,74 @@
# LibUV Sandbox
This library is a sandboxed version of [LibUV](https://libuv.org/), 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 `libuv/` directory, build the library:
```
mkdir -p build
cd build
cmake .. -G Ninja -D SAPI_ROOT=[path to sandboxed-api]
cmake --build .
```
## Implementation details
LibUV can't be directly sandboxed by Sandboxed API. Because of the size and complexity of the library, doing so creates some compilation errors and does not create the correct APIs for all methods. The solution for this issue is creating a wrapper library, whose methods (which can be sandboxed properly) internally call the original LibUV's methods.
Using these wrapper methods is extremely simple, the only relevant difference is that they have a `sapi_` prefix before their names (e.g. `uv_loop_init` becomes `sapi_uv_loop_init`). The only exception is the variadic method `uv_loop_configure`. This has two wrappers, `sapi_uv_loop_configure` (with no additional parameters) and `sapi_uv_loop_configure_int` (with an additional `int` parameter). Currently, these are the only valid calls to the method in LibUV.
#### Wrapper generator
The wrapper is generated automatically by a Python script that is called by CMake at build time. This script can be found in the `generator` folder.
#### 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 `UV_SAPI_CALLBACKS`.
The pointers can then be obtained using an `RPCChannel` object, as shown in the example `idle-basic.cc`.
## Examples
The `examples` directory contains the sandboxed versions of example source codes taken from from [LibUV's User Guide](https://docs.libuv.org/en/v1.x/guide.html). 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 `UV_SAPI_ENABLE_EXAMPLES` must be set to `ON`. This enables Sandboxed API examples as well.
## Testing
The `tests` folder contains some test cases created using Google Test.
To build these tests when building the library, the CMake variable `UV_SAPI_ENABLE_TESTS` must be set to `ON`. This enables Sandboxed API tests as well.
## Policies
Each example and test has an ad-hoc policy implemented on its own file. These policies can be used as references for pieces of code that perform similar operations. For example, the `helloworld.cc` example has a policy that only allows the strictly necessary syscalls for running a loop.
## Callbacks
The `callbacks.h` and `callbacks.cc` files in the `callbacks` folder implement all the callbacks used by examples and tests.
## Performance
A LibUV program looks like this:
- Setup the loop
- Run the loop
- Close the loop
And the second step (running the loop) is the only performance critical part, because this is were actual I/0 and events handling happens. But since this only involves a single call (to `sapi_uv_run`), the overhead from Sandboxed API is constant with respect to the number of handles and requests.
In other words, after Sandboxed API has called the `uv_run` method on the sandboxed process, whatever happens there is the same as what would have happened in a regular process running LibUV with no sandbox.
This means that, because of LibUV's nature, Sandboxed API has a totally negligible impact on performance. However, there is and overhead caused by `seccomp-bpf` for each syscall.

View File

@ -0,0 +1,93 @@
// 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"
#include <iostream>
size_t iterations = 0;
size_t constexpr kMaxIterations = 1'000'000;
// Stop the handle if the methods was called kMaxIterations times
void IdleCallback(uv_idle_t* handle) {
iterations++;
if (iterations > kMaxIterations) {
std::cout << "IdleCallback was called " << kMaxIterations << " times"
<< std::endl;
uv_idle_stop(handle);
}
}
static char buffer[1024];
static uv_buf_t iov;
// Called after some chars have been written
// As soon as writing of these bytes is completed, read more
void OnWrite(uv_fs_t* req) {
if (req->result < 0) {
std::cerr << "Write error: " << uv_strerror(static_cast<int>(req->result))
<< std::endl;
} else {
// Start reading more after writing these bytes
uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1,
OnRead);
}
}
// Called after some chars have been read
// As soon as reading of these bytes is completed, write them
void OnRead(uv_fs_t* req) {
if (req->result < 0) {
std::cerr << "Read error: " << uv_strerror(req->result) << std::endl;
} else if (req->result == 0) {
// No more bytes left, close the loop
uv_fs_t close_req;
uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
} else if (req->result > 0) {
// Start writing after reading some bytes
iov.len = req->result;
uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, OnWrite);
}
}
// Called after the file has been opened
// As soon as opening is completed, read the file
void OnOpen(uv_fs_t* req) {
if (req != &open_req) {
std::cerr << "Open error: req != &open_req" << std::endl;
} else if (req->result < 0) {
std::cerr << "Open error: " << uv_strerror(static_cast<int>(req->result))
<< std::endl;
} else {
// Initialize uv_buf_t buffer
iov = uv_buf_init(buffer, sizeof(buffer));
// Start reading after opening
uv_fs_read(uv_default_loop(), &read_req, req->result, &iov, 1, -1, OnRead);
}
}
// Get the integer pointed by handle->data and increment it by one
// Then close the handle
void TimerCallback(uv_timer_t* handle) {
int* data = static_cast<int*>(
uv_handle_get_data(reinterpret_cast<uv_handle_t*>(handle)));
(*data)++;
uv_close(reinterpret_cast<uv_handle_t*>(handle), nullptr);
}

View File

@ -0,0 +1,41 @@
// 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 CALLBACKS_H
#define CALLBACKS_H
#include <uv.h>
extern "C" {
// idle-basic
void IdleCallback(uv_idle_t *handle);
// uvcat
uv_fs_t open_req;
uv_fs_t read_req;
uv_fs_t write_req;
void OnWrite(uv_fs_t *req);
void OnRead(uv_fs_t *req);
void OnOpen(uv_fs_t *req);
// test_callback
void TimerCallback(uv_timer_t *handle);
}
#endif // CALLBACKS_H

View File

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

View File

@ -0,0 +1,9 @@
# LibUV Sandbox Examples
Each example in this folder is the sandboxed version of a code snippet from [LibUV's User Guide](https://docs.libuv.org/en/v1.x/guide.html). These examples perform some basic tasks using LibUV, and can be useful both to understand how to use LibUV Sandbox, but also to get an idea of how regular and sandboxed code compare to each other.
This is the list of examples:
- **helloworld.cc**: sandboxed version of [helloworld/main.c](https://docs.libuv.org/en/v1.x/guide/basics.html#hello-world). It simply starts a loop that exits immediately. It shows how to run a simple loop in LibUV Sandbox.
- **idle-basic.cc**: sandboxed version of [idle-basic/main.c](https://docs.libuv.org/en/v1.x/guide/basics.html#handles-and-requests). Creates an idle watcher that stops the loop after a certain number of iterations. It shows how a simple callback can be used in LibUV Sandbox.
- **uvcat.cc**: sandboxed version of [uvcat/main.c](http://docs.libuv.org/en/v1.x/guide/filesystem.html#reading-writing-files). Takes a single argument, the absolute path of a file, and prints its contents (it is a simplified version of the command line tootl `cat`). It shows how to manage various complex callbacks for opening, reading and writing files.

View File

@ -0,0 +1,92 @@
// 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 <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include <iostream>
#include "sandboxed_api/util/flag.h"
#include "uv_sapi.sapi.h"
class UVSapiHelloworldSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowSyscalls({__NR_epoll_create1, __NR_eventfd2, __NR_pipe2})
.AllowWrite()
.BuildOrDie();
}
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
UVSapiHelloworldSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
UVApi api(&sandbox);
// Allocate memory for the uv_loop_t object
void* loop_voidptr;
status = sandbox.rpc_channel()->Allocate(sizeof(uv_loop_t), &loop_voidptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Allocate failed: " << status;
}
sapi::v::RemotePtr loop(loop_voidptr);
absl::StatusOr<int> return_code;
// Initialize loop
return_code = api.sapi_uv_loop_init(&loop);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_loop_init failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_loop_init returned error " << return_code.value();
}
std::cout << "The loop is about to quit" << std::endl;
// Run loop
return_code = api.sapi_uv_run(&loop, UV_RUN_DEFAULT);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_run failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_run returned error " << return_code.value();
}
// Close loop
return_code = api.sapi_uv_loop_close(&loop);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_loop_close failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_loop_close returned error " << return_code.value();
}
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 <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include <iostream>
#include "sandboxed_api/util/flag.h"
#include "uv_sapi.sapi.h"
class UVSapiIdleBasicSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowSyscalls({__NR_epoll_create1, __NR_epoll_ctl, __NR_epoll_wait,
__NR_eventfd2, __NR_pipe2})
.AllowWrite()
.BuildOrDie();
}
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
absl::Status status;
// Initialize sandbox2 and sapi
UVSapiIdleBasicSandbox sandbox;
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
UVApi api(&sandbox);
// Get remote pointer to the IdleCallback method
void* function_ptr;
status = sandbox.rpc_channel()->Symbol("IdleCallback", &function_ptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Symbol failed: " << status;
}
sapi::v::RemotePtr idle_callback(function_ptr);
// Allocate memory for the uv_idle_t object
void* idle_voidptr;
status = sandbox.rpc_channel()->Allocate(sizeof(uv_idle_t), &idle_voidptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Allocate failed: " << status;
}
sapi::v::RemotePtr idler(idle_voidptr);
absl::StatusOr<int> return_code;
// Get default loop
absl::StatusOr<void*> loop_voidptr = api.sapi_uv_default_loop();
if (!loop_voidptr.ok()) {
LOG(FATAL) << "sapi_uv_default_loop failed: " << loop_voidptr.status();
}
sapi::v::RemotePtr loop(loop_voidptr.value());
// Initialize idler
return_code = api.sapi_uv_idle_init(&loop, &idler);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_idle_init failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "sapi_uv_idle_init returned error " << return_code.value();
}
// Start idler
return_code = api.sapi_uv_idle_start(&idler, &idle_callback);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_idle_start failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "sapi_uv_idle_start returned error " << return_code.value();
}
// Run loop
return_code = api.sapi_uv_run(&loop, UV_RUN_DEFAULT);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_run failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_run returned error " << return_code.value();
}
// Close idler
sapi::v::NullPtr null_ptr;
status = api.sapi_uv_close(&idler, &null_ptr);
if (!status.ok()) {
LOG(FATAL) << "sapi_uv_close failed: " << status;
}
// Close loop
return_code = api.sapi_uv_loop_close(&loop);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_loop_close failed: " << return_code.status();
}
// UV_EBUSY is accepted because it is the return code of uv_loop_close
// in the original example
if (return_code.value() != 0 && return_code.value() != UV_EBUSY) {
LOG(FATAL) << "uv_loop_close returned error " << return_code.value();
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,120 @@
// 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 <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include <iostream>
#include "sandboxed_api/util/flag.h"
#include "uv_sapi.sapi.h"
class UVSapiUVCatSandbox : public UVSandbox {
public:
UVSapiUVCatSandbox(std::string filename) : filename(filename) {}
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AddFile(filename)
.AllowDynamicStartup()
.AllowExit()
.AllowFork()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowFutexOp(FUTEX_WAIT_PRIVATE)
.AllowMmap()
.AllowOpen()
.AllowSyscalls({__NR_epoll_create1, __NR_epoll_ctl, __NR_epoll_wait,
__NR_eventfd2, __NR_pipe2, __NR_prlimit64})
.AllowWrite()
.BuildOrDie();
}
std::string filename;
};
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (argc != 2) {
LOG(FATAL) << "wrong number of arguments (1 expected)";
}
std::string filename_str = argv[1];
absl::Status status;
// Initialize sandbox2 and sapi
UVSapiUVCatSandbox sandbox(filename_str);
status = sandbox.Init();
if (!status.ok()) {
LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status;
}
UVApi api(&sandbox);
// Get remote pointer to the OnOpen method
void* function_ptr;
status = sandbox.rpc_channel()->Symbol("OnOpen", &function_ptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Symbol failed: " << status;
}
sapi::v::RemotePtr on_open(function_ptr);
// Get remote pointer to the open_req variable
void* open_req_voidptr;
status = sandbox.rpc_channel()->Symbol("open_req", &open_req_voidptr);
if (!status.ok()) {
LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Symbol failed: " << status;
}
sapi::v::RemotePtr open_req(open_req_voidptr);
// Get default loop
absl::StatusOr<void*> loop_voidptr = api.sapi_uv_default_loop();
if (!loop_voidptr.ok()) {
LOG(FATAL) << "sapi_uv_default_loop failed: " << loop_voidptr.status();
}
sapi::v::RemotePtr loop(loop_voidptr.value());
absl::StatusOr<int> return_code;
// Open file using the OnOpen callback (which will also read and print it)
sapi::v::ConstCStr filename(filename_str.c_str());
return_code = api.sapi_uv_fs_open(&loop, &open_req, filename.PtrBefore(),
O_RDONLY, 0, &on_open);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_fs_open failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_fs_open returned error " << return_code.value();
}
// Run loop
return_code = api.sapi_uv_run(&loop, UV_RUN_DEFAULT);
if (!return_code.ok()) {
LOG(FATAL) << "sapi_uv_run failed: " << return_code.status();
}
if (return_code.value() != 0) {
LOG(FATAL) << "uv_run returned error " << return_code.value();
}
// Cleanup the request
status = api.sapi_uv_fs_req_cleanup(&open_req);
if (!status.ok()) {
LOG(FATAL) << "sapi_uv_fs_req_cleanup failed: " << status;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,218 @@
# 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.
"""Script generating a wrapper API for LibUV."""
import sys
import re
import os
def get_var_type(string):
"""Gets the type from an argument variable
(e.g. "int x" -> "int")
"""
var = string.strip()
# Unnamed variable
if var in ("void", "...") or var[-1] == "*":
return var
return " ".join(var.split(" ")[:-1]).strip()
def get_var_name(string):
"""Gets the name from an argument variable
(e.g. "int x" -> "x")
"""
var = string.strip()
# Not an actual variable
if var in ("void", "..."):
return ""
# Unnamed variable, use an arbitrary name
if var[-1] == "*":
return var.split("_")[1]
return var.split(" ")[-1].strip()
def fix_method_type(string):
"""Fixes the method type
(e.g. "const int*" -> "const void*")
"""
method_type = string.strip()
# Const pointer
if "*" in method_type and "const" in method_type:
return "const void*"
# Regular pointer
if "*" in method_type:
return "void*"
# Not a pointer
return method_type
def fix_argument(string):
"""Fixes an argument
(e.g. "const int* x" -> "const void* x")
"""
arg_type = get_var_type(string)
arg_name = get_var_name(string)
# Array argument, becomes a pointer
if "[" in arg_name:
arg_type += "*"
arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1]
# Pointer (in LibUV, types endind in "_cb" or "_func" are pointers)
if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type:
if "const" in arg_type:
return "const void* " + arg_name
return "void* " + arg_name
# Not a pointer
return arg_type + " " + arg_name
def fix_call_argument(string):
"""Fixes an argument in a call the orignal method
(e.g. "const int* x" -> "reinterpret_cast<const int*>(x)")
"""
arg_type = get_var_type(string)
arg_name = get_var_name(string)
# Array argument, becomes a pointer
if "[" in arg_name:
arg_type += "*"
arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1]
# Pointer (in LibUV, types endind in "_cb" or "_func" are pointers)
if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type:
return "reinterpret_cast<" + arg_type + ">(" + arg_name + ")"
# Not a pointer
return arg_name
def read_file(filename):
"""Returns contents of filename as a string"""
file = open(filename, "r")
return str(file.read())
def clean_file(text):
"""Prepares the file for parsing
In particular, removes comments and macros from text
Additionally, moves pointer asterisks next to its type
"""
result = text
result = re.sub(r"//.*?\n", "", result, flags=re.S)
result = re.sub(r"/\*.*?\*/", "", result, flags=re.S)
result = re.sub(r"#.*?\n", "", result, flags=re.S)
result = result.replace(" *", "* ")
return result
def get_signatures(text):
"""Gets the signatures of all the methods in the header
.. note:: This method only works on a certain version of LibUV's header
"""
signatures = [x.split(";")[0].strip() for x in text.split("UV_EXTERN")[1:]]
method_types = [" ".join(s.split("(")[0].split(" ")[:-1]).strip()
for s in signatures]
names = [s.split("(")[0].split(" ")[-1].strip() for s in signatures]
arguments = [s.split("(")[1][:-1] for s in signatures]
arguments_lists = [[x.strip() for x in a.split(",")] for a in arguments]
return zip(method_types, names, arguments_lists)
def write_method(method_type, name, arguments_list, header, source):
"""Writes the method to both the header and the source files"""
header.write(fix_method_type(method_type) + " sapi_" + name + "(" +
", ".join(map(fix_argument, arguments_list)) + ");\n\n")
source.write(fix_method_type(method_type) + " sapi_" + name + "(" +
", ".join(map(fix_argument, arguments_list)) + ") {\n")
source.write(" return " + name + "(" +
", ".join(map(fix_call_argument, arguments_list)) + ");\n")
source.write("}\n\n")
def write_text(text, file):
"""Writes text to file
Useful for additional methods, includes, extern "C"...
"""
file.write(text)
def generate_wrapper():
"""Generates the wrapper"""
text = read_file(sys.argv[1])
text = clean_file(text)
signatures = get_signatures(text)
header = open(sys.argv[2], "w")
source = open(sys.argv[3], "w")
write_text("#include <uv.h>\n\n", header)
write_text("extern \"C\" {\n\n", header)
write_text("#include \"" + os.path.abspath(header.name) + "\"\n\n", source)
for (method_type, name, arguments_list) in signatures:
# These wrapper methods are manually added at the end
if name in ("uv_once", "uv_loop_configure"):
continue
write_method(method_type, name, arguments_list, header, source)
# Add sapi_uv_once (uv_once uses a differnet kind of callback)
write_text("void sapi_uv_once(void* guard, void (*callback)(void));\n\n",
header)
write_text("void sapi_uv_once(void* guard, void (*callback)(void)) {\n" +
" return uv_once(reinterpret_cast<uv_once_t*>(guard)," +
"callback);\n" + "}\n\n", source)
# Add sapi_uv_loop_configure (uv_loop_configure is variadic)
write_text("int sapi_uv_loop_configure(void* loop, uv_loop_option option)" +
";\n\n", header)
write_text("int sapi_uv_loop_configure(void* loop, uv_loop_option option)" +
" {\n return uv_loop_configure(" +
"reinterpret_cast<uv_loop_t*>(loop), option);\n" + "}\n\n",
source)
# Add sapi_uv_loop_configure_int (uv_loop_configure is variadic)
write_text("int sapi_uv_loop_configure_int(void* loop, " +
"uv_loop_option option, int ap);\n\n", header)
write_text("int sapi_uv_loop_configure_int(void* loop, " +
"uv_loop_option option, int ap) {\n" +
" return uv_loop_configure(" +
"reinterpret_cast<uv_loop_t*>(loop), option, ap);\n}\n\n",
source)
write_text("} // extern \"C\"\n", header)
generate_wrapper()

@ -0,0 +1 @@
Subproject commit 2a1b880f5439e074ef4d5c556f626b1044fb6781

View File

@ -0,0 +1,30 @@
# 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_array.cc
test_callback.cc
test_error.cc
test_loop.cc
test_os.cc
)
target_link_libraries(tests
uv_a uv_sapi sapi::sapi
gtest gmock gtest_main
)
gtest_discover_tests(tests)

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.
#include <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
#include "uv_sapi.sapi.h"
class UVTestArraySapiSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowOpen()
.AllowSyscall(__NR_sysinfo)
.AllowWrite()
.BuildOrDie();
}
};
class UVTestArray : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<UVTestArraySapiSandbox>();
ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk());
api_ = std::make_unique<UVApi>(sandbox_.get());
}
std::unique_ptr<UVTestArraySapiSandbox> sandbox_;
std::unique_ptr<UVApi> api_;
};
TEST_F(UVTestArray, LoadAvg) {
double avg_buf[] = {-1, -1, -1};
sapi::v::Array<double> avg(avg_buf, 3);
// Check that loadavg is as initialized before call
ASSERT_EQ(avg_buf[0], -1);
ASSERT_EQ(avg_buf[1], -1);
ASSERT_EQ(avg_buf[2], -1);
// Get loadavg
ASSERT_THAT(api_->sapi_uv_loadavg(avg.PtrBoth()), sapi::IsOk());
// Check that loadavg values are positive
ASSERT_GE(avg_buf[0], 0);
ASSERT_GE(avg_buf[1], 0);
ASSERT_GE(avg_buf[2], 0);
}

View File

@ -0,0 +1,139 @@
// 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 <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
#include "uv_sapi.sapi.h"
class UVTestCallbackSapiSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.DangerDefaultAllowAll()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowSyscalls({__NR_epoll_create1, __NR_eventfd2, __NR_pipe2})
.AllowWrite()
.BuildOrDie();
}
};
class UVTestCallback : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<UVTestCallbackSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk());
api_ = std::make_unique<UVApi>(sandbox_.get());
}
// Check sapi_uv_timer_init
void UVTimerInit(sapi::v::Ptr* loop, sapi::v::Ptr* timer) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code,
api_->sapi_uv_timer_init(loop, timer));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_timer_start
// (actual time is ignored because timeout and repeat are 0)
void UVTimerStart(sapi::v::Ptr* timer) {
// Get the TimerCallback callback from the sandbox
void* timer_cb_voidptr;
ASSERT_THAT(
sandbox_->rpc_channel()->Symbol("TimerCallback", &timer_cb_voidptr),
sapi::IsOk());
sapi::v::RemotePtr timer_cb(timer_cb_voidptr);
// Set the timer's callback, timeout and repeat
SAPI_ASSERT_OK_AND_ASSIGN(
int error_code, api_->sapi_uv_timer_start(timer, &timer_cb, 0, 0));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_run
void UVRun(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code,
api_->sapi_uv_run(loop, UV_RUN_DEFAULT));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_loop_close
void UVLoopClose(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code, api_->sapi_uv_loop_close(loop));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_default_loop, set loop to default loop
void UVDefaultLoop(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(void* loop_voidptr, api_->sapi_uv_default_loop());
loop->SetRemote(loop_voidptr);
}
std::unique_ptr<UVTestCallbackSapiSandbox> sandbox_;
std::unique_ptr<UVApi> api_;
static constexpr int kData = 1729;
};
TEST_F(UVTestCallback, TimerCallback) {
// Initialize loop
sapi::v::RemotePtr loop(nullptr);
// Allocate memory for timer
void* timer_voidptr;
ASSERT_THAT(
sandbox_->rpc_channel()->Allocate(sizeof(uv_timer_t), &timer_voidptr),
sapi::IsOk());
sapi::v::RemotePtr timer(timer_voidptr);
// Initialize timer and add it to default loop
UVDefaultLoop(&loop);
UVTimerInit(loop.PtrNone(), timer.PtrBoth());
// Set timer data to kData
sapi::v::Int data(kData);
void* data_voidptr;
ASSERT_THAT(sandbox_->rpc_channel()->Allocate(sizeof(int), &data_voidptr),
sapi::IsOk());
data.SetRemote(data_voidptr);
ASSERT_THAT(api_->sapi_uv_handle_set_data(timer.PtrBoth(), data.PtrBefore()),
sapi::IsOk());
// Start the timer
UVTimerStart(timer.PtrBoth());
// Check that data has not changed (because the loop is not running yet)
// This is done by resetting the local value and then getting the remote one
data.SetValue(0);
ASSERT_THAT(sandbox_->TransferFromSandboxee(&data), sapi::IsOk());
ASSERT_EQ(data.GetValue(), kData);
// Run the loop
UVDefaultLoop(&loop);
UVRun(loop.PtrNone());
// Check that data has changed (and therefore callback was called correctly)
ASSERT_THAT(sandbox_->TransferFromSandboxee(&data), sapi::IsOk());
ASSERT_EQ(data.GetValue(), kData + 1);
// Close the loop
UVDefaultLoop(&loop);
UVLoopClose(loop.PtrNone());
}

View File

@ -0,0 +1,90 @@
// 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 <linux/futex.h>
#include <uv.h>
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
#include "uv_sapi.sapi.h"
class UVTestErrorSapiSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowWrite()
.BuildOrDie();
}
};
class UVTestError : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<UVTestErrorSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk());
api_ = std::make_unique<UVApi>(sandbox_.get());
}
// Check sapi_uv_strerror on error
void UVStrerror(int error) {
// Call sapi_uv_strerror
absl::StatusOr<void*> error_message_ptr = api_->sapi_uv_strerror(error);
ASSERT_THAT(error_message_ptr, sapi::IsOk());
// Get error message from the sandboxee
SAPI_ASSERT_OK_AND_ASSIGN(
std::string error_message,
sandbox_->GetCString(sapi::v::RemotePtr{error_message_ptr.value()}));
// Check that it is equal to expected error message
ASSERT_EQ(error_message, std::string{uv_strerror(error)});
}
// Check sapi_uv_translate_sys_error on error
void UVTranslateSysError(int error) {
// Call sapi_uv_translate_sys_error and get error code
SAPI_ASSERT_OK_AND_ASSIGN(int error_code,
api_->sapi_uv_translate_sys_error(error));
// Check that it is equal to expected error code
ASSERT_EQ(error_code, uv_translate_sys_error(error));
}
std::unique_ptr<UVTestErrorSapiSandbox> sandbox_;
std::unique_ptr<UVApi> api_;
};
TEST_F(UVTestError, ErrorMessage) {
// Test sapi_uv_strerror method
UVStrerror(0);
UVStrerror(UV_EINVAL);
UVStrerror(1337);
UVStrerror(-1337);
}
TEST_F(UVTestError, SystemError) {
// Test sapi_uv_translate_sys_error method
UVTranslateSysError(EPERM);
UVTranslateSysError(EPIPE);
UVTranslateSysError(EINVAL);
UVTranslateSysError(UV_EINVAL);
UVTranslateSysError(UV_ERANGE);
UVTranslateSysError(UV_EACCES);
UVTranslateSysError(0);
}

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.
#include <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
#include "uv_sapi.sapi.h"
class UVTestLoopSapiSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowSyscalls({__NR_epoll_create1, __NR_eventfd2, __NR_pipe2})
.AllowWrite()
.BuildOrDie();
}
};
class UVTestLoop : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<UVTestLoopSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk());
api_ = std::make_unique<UVApi>(sandbox_.get());
}
// Check sapi_uv_loop_init
void UVLoopInit(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code, api_->sapi_uv_loop_init(loop));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_run
void UVRun(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code,
api_->sapi_uv_run(loop, UV_RUN_DEFAULT));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_loop_close
void UVLoopClose(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(int error_code, api_->sapi_uv_loop_close(loop));
ASSERT_EQ(error_code, 0);
}
// Check sapi_uv_default_loop, set loop to default loop
void UVDefaultLoop(sapi::v::Ptr* loop) {
SAPI_ASSERT_OK_AND_ASSIGN(void* loop_voidptr, api_->sapi_uv_default_loop());
loop->SetRemote(loop_voidptr);
}
std::unique_ptr<UVTestLoopSapiSandbox> sandbox_;
std::unique_ptr<UVApi> api_;
};
TEST_F(UVTestLoop, InitLoop) {
// Allocate memory for loop
void* loop_voidptr;
ASSERT_THAT(
sandbox_->rpc_channel()->Allocate(sizeof(uv_loop_t), &loop_voidptr),
sapi::IsOk());
sapi::v::RemotePtr loop(loop_voidptr);
// Initialize, run and close the manually initialized loop
UVLoopInit(loop.PtrBoth());
UVRun(loop.PtrNone());
UVLoopClose(loop.PtrNone());
// Free loop memory
ASSERT_THAT(sandbox_->rpc_channel()->Free(loop_voidptr), sapi::IsOk());
}
TEST_F(UVTestLoop, DefaultLoop) {
sapi::v::RemotePtr loop(nullptr);
// Run the default loop
UVDefaultLoop(&loop);
UVRun(loop.PtrNone());
// Close the default loop
UVDefaultLoop(&loop);
UVLoopClose(loop.PtrNone());
}

View File

@ -0,0 +1,133 @@
// 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 <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/util/status_matchers.h"
#include "uv_sapi.sapi.h"
class UVTestOSSapiSandbox : public UVSandbox {
private:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowExit()
.AllowFutexOp(FUTEX_WAKE_PRIVATE)
.AllowGetIDs()
.AllowMmap()
.AllowOpen()
.AllowWrite()
.AllowSyscalls({__NR_connect, __NR_socket})
.DisableNamespaces()
.BuildOrDie();
}
};
class UVTestOS : public ::testing::Test {
protected:
void SetUp() override {
sandbox_ = std::make_unique<UVTestOSSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk());
api_ = std::make_unique<UVApi>(sandbox_.get());
}
std::unique_ptr<UVTestOSSapiSandbox> sandbox_;
std::unique_ptr<UVApi> api_;
static constexpr size_t kBigBufLen = 4096;
static constexpr size_t kSmallBufLen = 1;
};
TEST_F(UVTestOS, HomeDirBig) {
// Get expected home directory
char expected_homedir[kBigBufLen];
size_t len = sizeof(expected_homedir);
ASSERT_GE(uv_os_homedir(expected_homedir, &len), 0);
// Get home directory from the sandbox
sapi::v::Array<char> uv_homedir(kBigBufLen);
uv_homedir[0] = '\0';
sapi::v::IntBase<size_t> uv_homedir_len(kBigBufLen);
SAPI_ASSERT_OK_AND_ASSIGN(
int error_code,
api_->sapi_uv_os_homedir(uv_homedir.PtrBoth(), uv_homedir_len.PtrBoth()));
ASSERT_GE(error_code, 0);
// Test home directory is as expected
ASSERT_EQ(std::string{uv_homedir.GetData()}, std::string{expected_homedir});
}
TEST_F(UVTestOS, HomeDirSmall) {
// Try getting expected home directory, error because array is too small
char expected_homedir[kSmallBufLen];
size_t len = sizeof(expected_homedir);
int expected_error_code = uv_os_homedir(expected_homedir, &len);
ASSERT_NE(expected_error_code, 0);
// Try getting home directory from sandbox, error because array is too small
sapi::v::Array<char> uv_homedir(kSmallBufLen);
uv_homedir[0] = '\0';
sapi::v::IntBase<size_t> uv_homedir_len(kSmallBufLen);
SAPI_ASSERT_OK_AND_ASSIGN(
int error_code,
api_->sapi_uv_os_homedir(uv_homedir.PtrBoth(), uv_homedir_len.PtrBoth()));
ASSERT_NE(error_code, 0);
// Test error code is as expected
ASSERT_EQ(error_code, expected_error_code);
}
TEST_F(UVTestOS, TmpDirBig) {
// Get expected tmp directory
char expected_tmpdir[kBigBufLen];
size_t len = sizeof(expected_tmpdir);
ASSERT_GE(uv_os_tmpdir(expected_tmpdir, &len), 0);
// Get tmp directory from the sandbox
sapi::v::Array<char> uv_tmpdir(kBigBufLen);
uv_tmpdir[0] = '\0';
sapi::v::IntBase<size_t> uv_tmpdir_len(kBigBufLen);
SAPI_ASSERT_OK_AND_ASSIGN(
int error_code,
api_->sapi_uv_os_tmpdir(uv_tmpdir.PtrBoth(), uv_tmpdir_len.PtrBoth()));
ASSERT_GE(error_code, 0);
// Test tmp directory is as expected
ASSERT_EQ(std::string{uv_tmpdir.GetData()}, std::string{expected_tmpdir});
}
TEST_F(UVTestOS, TmpDirSmall) {
// Try getting expected tmp directory, error because array is too small
char expected_tmpdir[kSmallBufLen];
size_t len = sizeof(expected_tmpdir);
int expected_error_code = uv_os_tmpdir(expected_tmpdir, &len);
ASSERT_NE(expected_error_code, 0);
// Try getting tmp directory from sandbox, error because array is too small
sapi::v::Array<char> uv_tmpdir(kSmallBufLen);
uv_tmpdir[0] = '\0';
sapi::v::IntBase<size_t> uv_tmpdir_len(kSmallBufLen);
SAPI_ASSERT_OK_AND_ASSIGN(
int error_code,
api_->sapi_uv_os_tmpdir(uv_tmpdir.PtrBoth(), uv_tmpdir_len.PtrBoth()));
ASSERT_NE(error_code, 0);
// Test error code is as expected
ASSERT_EQ(error_code, expected_error_code);
}