Merge pull request #55 from FedericoStazi:libuv

PiperOrigin-RevId: 343278766
Change-Id: I708fdc1cd98d5fbb2abcf3261d1cecd65cec46fe
This commit is contained in:
Copybara-Service 2020-11-19 06:32:44 -08:00
commit e6bb05a15d
19 changed files with 1877 additions and 0 deletions

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "oss-internship-2020/curl/curl_wrapper/curl"]
path = oss-internship-2020/curl/curl_wrapper/curl
url = git@github.com:curl/curl.git
[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.12)
project(libuv_sandbox)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Add SAPI directories
add_subdirectory(
"${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL
)
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 "${SAPI_PYTHON3_EXECUTABLE}"
"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(SAPI_UV_ENABLE_EXAMPLES "" ON)
option(SAPI_UV_ENABLE_TESTS "" ON)
# Add callbacks used by examples and tests
if (SAPI_UV_ENABLE_EXAMPLES OR SAPI_UV_ENABLE_TESTS)
list(APPEND SAPI_UV_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 SAPI_UV_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"
"${SAPI_UV_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 ${SAPI_UV_ENABLE_EXAMPLES} CACHE BOOL "" FORCE)
set(SAPI_ENABLE_TESTS ${SAPI_UV_ENABLE_TESTS} CACHE BOOL "" FORCE)
# 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
# TODO(cblichmann): 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 uv
)
# Include generated SAPI header
target_include_directories(uv_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
# Add examples
if (SAPI_UV_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
# Add tests
if (SAPI_UV_ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@ -0,0 +1,93 @@
# 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
`SAPI_UV_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
`SAPI_UV_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
`SAPI_UV_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.

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 "callbacks.h" // NOLINT(build/include)
#include <iostream>
size_t g_iterations = 0;
size_t constexpr kMaxIterations = 1'000'000;
static char g_buffer[1024];
static uv_buf_t g_iov;
// Stop the handle if the methods was called kMaxIterations times
void IdleCallback(uv_idle_t* handle) {
++g_iterations;
if (g_iterations > kMaxIterations) {
std::cout << "IdleCallback was called " << kMaxIterations << " times"
<< std::endl;
uv_idle_stop(handle);
}
}
// 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;
return;
}
// Start reading more after writing these bytes
uv_fs_read(uv_default_loop(), &read_req, open_req.result, &g_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;
return;
}
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
g_iov.len = req->result;
uv_fs_write(uv_default_loop(), &write_req, 1, &g_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;
return;
}
if (req->result < 0) {
std::cerr << "Open error: " << uv_strerror(static_cast<int>(req->result))
<< std::endl;
return;
}
// Initialize uv_buf_t g_buffer
g_iov = uv_buf_init(g_buffer, sizeof(g_buffer));
// Start reading after opening
uv_fs_read(uv_default_loop(), &read_req, req->result, &g_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,38 @@
// 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);
} // extern "C"
#endif // CALLBACKS_H_

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.
add_executable(helloworld
helloworld.cc
)
target_link_libraries(helloworld
uv_a
uv_sapi
sapi::flags
sapi::sapi
)
add_executable(idle-basic
idle-basic.cc
)
target_link_libraries(idle-basic
uv_a
uv_sapi
sapi::sapi
)
add_executable(uvcat
uvcat.cc
)
target_link_libraries(uvcat
uv_a
uv_sapi
sapi::sapi
)

View File

@ -0,0 +1,23 @@
# 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,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 <syscall.h>
#include <uv.h>
#include <iostream>
#include "sandboxed_api/util/flag.h"
#include "uv_sapi.sapi.h" // NOLINT(build/include)
namespace {
class UVSapiHelloworldSandbox : public uv::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();
}
};
absl::Status HelloWorld() {
// Initialize sandbox2 and sapi
UVSapiHelloworldSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
uv::UVApi api(&sandbox);
// Allocate memory for the uv_loop_t object
void* loop_voidptr;
SAPI_RETURN_IF_ERROR(
sandbox.rpc_channel()->Allocate(sizeof(uv_loop_t), &loop_voidptr));
sapi::v::RemotePtr loop(loop_voidptr);
int return_code;
// Initialize loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_init(&loop));
if (return_code != 0) {
return absl::UnavailableError("uv_loop_init returned error " + return_code);
}
std::cout << "The loop is about to quit" << std::endl;
// Run loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT));
if (return_code != 0) {
return absl::UnavailableError("uv_run returned error " + return_code);
}
// Close loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_close(&loop));
if (return_code != 0) {
return absl::UnavailableError("uv_loop_close returned error " +
return_code);
}
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (absl::Status status = HelloWorld(); !status.ok()) {
LOG(ERROR) << "HelloWorld failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,112 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <linux/futex.h>
#include <syscall.h>
#include <uv.h>
#include <iostream>
#include "sandboxed_api/util/flag.h"
#include "uv_sapi.sapi.h" // NOLINT(build/include)
namespace {
class UVSapiIdleBasicSandbox : public uv::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();
}
};
absl::Status IdleBasic() {
// Initialize sandbox2 and sapi
UVSapiIdleBasicSandbox sandbox;
SAPI_RETURN_IF_ERROR(sandbox.Init());
uv::UVApi api(&sandbox);
// Get remote pointer to the IdleCallback method
void* function_ptr;
SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("IdleCallback", &function_ptr));
sapi::v::RemotePtr idle_callback(function_ptr);
// Allocate memory for the uv_idle_t object
void* idle_voidptr;
SAPI_RETURN_IF_ERROR(
sandbox.rpc_channel()->Allocate(sizeof(uv_idle_t), &idle_voidptr));
sapi::v::RemotePtr idler(idle_voidptr);
int return_code;
// Get default loop
SAPI_ASSIGN_OR_RETURN(void* loop_voidptr, api.sapi_uv_default_loop());
sapi::v::RemotePtr loop(loop_voidptr);
// Initialize idler
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_idle_init(&loop, &idler));
if (return_code != 0) {
return absl::UnavailableError("sapi_uv_idle_init returned error " +
return_code);
}
// Start idler
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_idle_start(&idler, &idle_callback));
if (return_code != 0) {
return absl::UnavailableError("sapi_uv_idle_start returned error " +
return_code);
}
// Run loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT));
if (return_code != 0) {
return absl::UnavailableError("uv_run returned error " + return_code);
}
// Close idler
sapi::v::NullPtr null_ptr;
SAPI_RETURN_IF_ERROR(api.sapi_uv_close(&idler, &null_ptr));
// Close loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_close(&loop));
// UV_EBUSY is accepted because it is the return code of uv_loop_close
// in the original example
if (return_code != 0 && return_code != UV_EBUSY) {
return absl::UnavailableError("uv_loop_close returned error " +
return_code);
}
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (absl::Status status = IdleBasic(); !status.ok()) {
LOG(ERROR) << "IdleBasic failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,111 @@
// 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" // NOLINT(build/include)
namespace {
class UVSapiUVCatSandbox : public uv::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;
};
absl::Status UVCat(std::string filearg) {
// Initialize sandbox2 and sapi
UVSapiUVCatSandbox sandbox(filearg);
SAPI_RETURN_IF_ERROR(sandbox.Init());
uv::UVApi api(&sandbox);
// Get remote pointer to the OnOpen method
void* function_ptr;
SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("OnOpen", &function_ptr));
sapi::v::RemotePtr on_open(function_ptr);
// Get remote pointer to the open_req variable
void* open_req_voidptr;
SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("open_req", &open_req_voidptr));
sapi::v::RemotePtr open_req(open_req_voidptr);
// Get default loop
SAPI_ASSIGN_OR_RETURN(void* loop_voidptr, api.sapi_uv_default_loop());
sapi::v::RemotePtr loop(loop_voidptr);
int return_code;
// Open file using the OnOpen callback (which will also read and print it)
sapi::v::ConstCStr filename(filearg.c_str());
SAPI_ASSIGN_OR_RETURN(return_code,
api.sapi_uv_fs_open(&loop, &open_req, filename.PtrBefore(),
O_RDONLY, 0, &on_open));
if (return_code != 0) {
return absl::UnavailableError("uv_fs_open returned error " + return_code);
}
// Run loop
SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT));
if (return_code != 0) {
return absl::UnavailableError("uv_run returned error " + return_code);
}
// Cleanup the request
SAPI_RETURN_IF_ERROR(api.sapi_uv_fs_req_cleanup(&open_req));
return absl::OkStatus();
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
if (argc != 2) {
LOG(ERROR) << "wrong number of arguments (1 expected)";
return EXIT_FAILURE;
}
if (absl::Status status = UVCat(argv[1]); !status.ok()) {
LOG(ERROR) << "UVCat failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,296 @@
# 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.
Note: This scriptis highly specific to LibUV's source code and does not
generalize to any other library
"""
import os
import re
import sys
from typing import List
def get_var_type(string: str) -> str:
"""Gets the type from an argument variable.
Args:
string: Input variable declaration
Returns:
The type of the argument variable as a string, 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: str) -> str:
"""Gets the name from an argument variable.
Args:
string: Input variable declaration
Returns:
The name of the arguments variable as a string, 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: str) -> str:
"""Fixes the method type.
Args:
string: A parameter type declaration
Returns:
A fixed up string replacing pointers to concrete types with pointers to
void, 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: str) -> str:
"""Fixes an argument.
Args:
string: An argument type to fix
Returns:
The fixed up argument as a string, 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: str) -> str:
"""Fixes an argument in a call the orignal method.
Args:
string: A method call argument
Returns:
The fixed call argument, 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: str) -> str:
"""Returns contents of filename as a string.
Args:
filename: The name of the file to read
Returns:
The contents of the file as a string.
"""
file = open(filename, "r")
return str(file.read())
def clean_file(text: str) -> str:
"""Prepares the file for parsing.
In particular, removes comments and macros from text
Additionally, moves pointer asterisks next to its type
Args:
text: The contents of the text file to prepare
Returns:
The cleaned up file contents.
"""
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: str) -> str:
"""Gets the signatures of all the methods in the header.
Note: This method only works on a certain version of LibUV's header.
Args:
text: The contents of the header file
Returns:
The extracted method signatures.
"""
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 append_method(method_type: str, name: str, arguments_list: List[str],
header: List[str], source: List[str]) -> None:
"""Writes the method to the header and the source list of lines.
Args:
method_type: The return type of the method as a string
name: The name of the method
arguments_list: A list of method aruments
header: A list that receives method wrapper declarations
source: A list that receives the declarations of the method wrappers
"""
header.append(
fix_method_type(method_type) + " sapi_" + name + "(" +
", ".join(map(fix_argument, arguments_list)) + ");")
source.append(
fix_method_type(method_type) + " sapi_" + name + "(" +
", ".join(map(fix_argument, arguments_list)) + ") {\n" + " return " +
name + "(" + ", ".join(map(fix_call_argument, arguments_list)) + ");\n" +
"}")
def append_text(text: str, file: List[str]) -> None:
"""Writes text to file list of lines.
Useful for additional methods, includes, extern "C"...
Args:
text: The text to append to the file
file: A list receiving file lines
"""
file.append(text)
def generate_wrapper() -> None:
"""Generates the wrapper."""
header_file = open(sys.argv[2], "w")
source_file = open(sys.argv[3], "w")
text = read_file(sys.argv[1])
text = clean_file(text)
signatures = get_signatures(text)
header = []
source = []
append_text("#include <uv.h>", header)
append_text("#include <cstddef>", header)
append_text("extern \"C\" {", header)
append_text("#include \"" + os.path.abspath(header_file.name) + "\"", 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
append_method(method_type, name, arguments_list, header, source)
# Add sapi_uv_once (uv_once uses a differnet kind of callback)
append_text("void sapi_uv_once(void* guard, void (*callback)(void));", header)
append_text(
"void sapi_uv_once(void* guard, void (*callback)(void)) {\n" +
" return uv_once(reinterpret_cast<uv_once_t*>(guard)," + "callback);\n" +
"}", source)
# Add sapi_uv_loop_configure (uv_loop_configure is variadic)
append_text(
"int sapi_uv_loop_configure(void* loop, uv_loop_option option)" + ";",
header)
append_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" + "}", source)
# Add sapi_uv_loop_configure_int (uv_loop_configure is variadic)
append_text(
"int sapi_uv_loop_configure_int(void* loop, " +
"uv_loop_option option, int ap);", header)
append_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}", source)
append_text("} // extern \"C\"\n", header)
header_file.write("\n\n".join(header))
source_file.write("\n\n".join(source))
generate_wrapper()

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

View File

@ -0,0 +1,34 @@
# 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
gmock
gtest
gtest_main
uv_a
uv_sapi
sapi::sapi
)
gtest_discover_tests(tests)

View File

@ -0,0 +1,71 @@
// 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" // NOLINT(build/include)
namespace {
class UVTestArraySapiSandbox : public uv::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<uv::UVApi>(sandbox_.get());
}
std::unique_ptr<UVTestArraySapiSandbox> sandbox_;
std::unique_ptr<uv::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);
}
} // namespace

View File

@ -0,0 +1,142 @@
// 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" // NOLINT(build/include)
namespace {
class UVTestCallbackSapiSandbox : public uv::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<uv::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<uv::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());
}
} // namespace

View File

@ -0,0 +1,94 @@
// 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" // NOLINT(build/include)
namespace {
class UVTestErrorSapiSandbox : public uv::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<uv::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<uv::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);
}
} // namespace

View File

@ -0,0 +1,106 @@
// 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" // NOLINT(build/include)
namespace {
class UVTestLoopSapiSandbox : public uv::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<uv::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<uv::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());
}
} // namespace

View File

@ -0,0 +1,137 @@
// 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" // NOLINT(build/include)
namespace {
class UVTestOSSapiSandbox : public uv::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<uv::UVApi>(sandbox_.get());
}
std::unique_ptr<UVTestOSSapiSandbox> sandbox_;
std::unique_ptr<uv::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);
}
} // namespace