From 22d21cd1c378e7becd4d1ed2aeaa84878a65f1a5 Mon Sep 17 00:00:00 2001 From: Federico Stazi Date: Tue, 22 Sep 2020 14:41:06 +0000 Subject: [PATCH 1/4] Initial LibUV commit --- .gitmodules | 3 + oss-internship-2020/libuv/.gitignore | 1 + oss-internship-2020/libuv/CMakeLists.txt | 394 ++++++++++++++++++ oss-internship-2020/libuv/README.md | 74 ++++ .../libuv/callbacks/callbacks.cc | 93 +++++ .../libuv/callbacks/callbacks.h | 41 ++ .../libuv/examples/CMakeLists.txt | 40 ++ oss-internship-2020/libuv/examples/README.md | 9 + .../libuv/examples/helloworld.cc | 92 ++++ .../libuv/examples/idle-basic.cc | 124 ++++++ oss-internship-2020/libuv/examples/uvcat.cc | 120 ++++++ .../libuv/generator/wrapper_generator.py | 218 ++++++++++ oss-internship-2020/libuv/libuv | 1 + .../libuv/tests/CMakeLists.txt | 30 ++ oss-internship-2020/libuv/tests/test_array.cc | 67 +++ .../libuv/tests/test_callback.cc | 139 ++++++ oss-internship-2020/libuv/tests/test_error.cc | 90 ++++ oss-internship-2020/libuv/tests/test_loop.cc | 102 +++++ oss-internship-2020/libuv/tests/test_os.cc | 133 ++++++ 19 files changed, 1771 insertions(+) create mode 100644 oss-internship-2020/libuv/.gitignore create mode 100644 oss-internship-2020/libuv/CMakeLists.txt create mode 100644 oss-internship-2020/libuv/README.md create mode 100644 oss-internship-2020/libuv/callbacks/callbacks.cc create mode 100644 oss-internship-2020/libuv/callbacks/callbacks.h create mode 100644 oss-internship-2020/libuv/examples/CMakeLists.txt create mode 100644 oss-internship-2020/libuv/examples/README.md create mode 100644 oss-internship-2020/libuv/examples/helloworld.cc create mode 100644 oss-internship-2020/libuv/examples/idle-basic.cc create mode 100644 oss-internship-2020/libuv/examples/uvcat.cc create mode 100644 oss-internship-2020/libuv/generator/wrapper_generator.py create mode 160000 oss-internship-2020/libuv/libuv create mode 100644 oss-internship-2020/libuv/tests/CMakeLists.txt create mode 100644 oss-internship-2020/libuv/tests/test_array.cc create mode 100644 oss-internship-2020/libuv/tests/test_callback.cc create mode 100644 oss-internship-2020/libuv/tests/test_error.cc create mode 100644 oss-internship-2020/libuv/tests/test_loop.cc create mode 100644 oss-internship-2020/libuv/tests/test_os.cc diff --git a/.gitmodules b/.gitmodules index aef07fd..7025d58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/oss-internship-2020/libuv/.gitignore b/oss-internship-2020/libuv/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/oss-internship-2020/libuv/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/oss-internship-2020/libuv/CMakeLists.txt b/oss-internship-2020/libuv/CMakeLists.txt new file mode 100644 index 0000000..06a0107 --- /dev/null +++ b/oss-internship-2020/libuv/CMakeLists.txt @@ -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() diff --git a/oss-internship-2020/libuv/README.md b/oss-internship-2020/libuv/README.md new file mode 100644 index 0000000..66eeb48 --- /dev/null +++ b/oss-internship-2020/libuv/README.md @@ -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. diff --git a/oss-internship-2020/libuv/callbacks/callbacks.cc b/oss-internship-2020/libuv/callbacks/callbacks.cc new file mode 100644 index 0000000..e89861d --- /dev/null +++ b/oss-internship-2020/libuv/callbacks/callbacks.cc @@ -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 + +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(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(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( + uv_handle_get_data(reinterpret_cast(handle))); + (*data)++; + uv_close(reinterpret_cast(handle), nullptr); +} diff --git a/oss-internship-2020/libuv/callbacks/callbacks.h b/oss-internship-2020/libuv/callbacks/callbacks.h new file mode 100644 index 0000000..e9acaad --- /dev/null +++ b/oss-internship-2020/libuv/callbacks/callbacks.h @@ -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 + +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 diff --git a/oss-internship-2020/libuv/examples/CMakeLists.txt b/oss-internship-2020/libuv/examples/CMakeLists.txt new file mode 100644 index 0000000..a2be380 --- /dev/null +++ b/oss-internship-2020/libuv/examples/CMakeLists.txt @@ -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 +) diff --git a/oss-internship-2020/libuv/examples/README.md b/oss-internship-2020/libuv/examples/README.md new file mode 100644 index 0000000..730c783 --- /dev/null +++ b/oss-internship-2020/libuv/examples/README.md @@ -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. \ No newline at end of file diff --git a/oss-internship-2020/libuv/examples/helloworld.cc b/oss-internship-2020/libuv/examples/helloworld.cc new file mode 100644 index 0000000..ef5941b --- /dev/null +++ b/oss-internship-2020/libuv/examples/helloworld.cc @@ -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 +#include +#include + +#include + +#include "sandboxed_api/util/flag.h" +#include "uv_sapi.sapi.h" + +class UVSapiHelloworldSandbox : public UVSandbox { + private: + std::unique_ptr 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 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; +} diff --git a/oss-internship-2020/libuv/examples/idle-basic.cc b/oss-internship-2020/libuv/examples/idle-basic.cc new file mode 100644 index 0000000..889676d --- /dev/null +++ b/oss-internship-2020/libuv/examples/idle-basic.cc @@ -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 +#include +#include + +#include + +#include "sandboxed_api/util/flag.h" +#include "uv_sapi.sapi.h" + +class UVSapiIdleBasicSandbox : public UVSandbox { + private: + std::unique_ptr 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 return_code; + + // Get default loop + absl::StatusOr 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; +} diff --git a/oss-internship-2020/libuv/examples/uvcat.cc b/oss-internship-2020/libuv/examples/uvcat.cc new file mode 100644 index 0000000..7cfa0b6 --- /dev/null +++ b/oss-internship-2020/libuv/examples/uvcat.cc @@ -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 +#include +#include + +#include + +#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 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 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 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; +} diff --git a/oss-internship-2020/libuv/generator/wrapper_generator.py b/oss-internship-2020/libuv/generator/wrapper_generator.py new file mode 100644 index 0000000..5006589 --- /dev/null +++ b/oss-internship-2020/libuv/generator/wrapper_generator.py @@ -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(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 \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(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(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(loop), option, ap);\n}\n\n", + source) + + write_text("} // extern \"C\"\n", header) + + +generate_wrapper() diff --git a/oss-internship-2020/libuv/libuv b/oss-internship-2020/libuv/libuv new file mode 160000 index 0000000..2a1b880 --- /dev/null +++ b/oss-internship-2020/libuv/libuv @@ -0,0 +1 @@ +Subproject commit 2a1b880f5439e074ef4d5c556f626b1044fb6781 diff --git a/oss-internship-2020/libuv/tests/CMakeLists.txt b/oss-internship-2020/libuv/tests/CMakeLists.txt new file mode 100644 index 0000000..d276969 --- /dev/null +++ b/oss-internship-2020/libuv/tests/CMakeLists.txt @@ -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) diff --git a/oss-internship-2020/libuv/tests/test_array.cc b/oss-internship-2020/libuv/tests/test_array.cc new file mode 100644 index 0000000..572d1fc --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_array.cc @@ -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 +#include +#include + +#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 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(); + ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); + } + + std::unique_ptr sandbox_; + std::unique_ptr api_; +}; + +TEST_F(UVTestArray, LoadAvg) { + double avg_buf[] = {-1, -1, -1}; + sapi::v::Array 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); +} diff --git a/oss-internship-2020/libuv/tests/test_callback.cc b/oss-internship-2020/libuv/tests/test_callback.cc new file mode 100644 index 0000000..275daa6 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_callback.cc @@ -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 +#include +#include + +#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 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(); + ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); + api_ = std::make_unique(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 sandbox_; + std::unique_ptr 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()); +} diff --git a/oss-internship-2020/libuv/tests/test_error.cc b/oss-internship-2020/libuv/tests/test_error.cc new file mode 100644 index 0000000..3f681fe --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_error.cc @@ -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 +#include + +#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 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(); + ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); + } + + // Check sapi_uv_strerror on error + void UVStrerror(int error) { + // Call sapi_uv_strerror + absl::StatusOr 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 sandbox_; + std::unique_ptr 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); +} diff --git a/oss-internship-2020/libuv/tests/test_loop.cc b/oss-internship-2020/libuv/tests/test_loop.cc new file mode 100644 index 0000000..478cb62 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_loop.cc @@ -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 +#include +#include + +#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 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(); + ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); + api_ = std::make_unique(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 sandbox_; + std::unique_ptr 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()); +} diff --git a/oss-internship-2020/libuv/tests/test_os.cc b/oss-internship-2020/libuv/tests/test_os.cc new file mode 100644 index 0000000..9d19171 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_os.cc @@ -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 +#include +#include + +#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 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(); + ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); + } + + std::unique_ptr sandbox_; + std::unique_ptr 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 uv_homedir(kBigBufLen); + uv_homedir[0] = '\0'; + sapi::v::IntBase 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 uv_homedir(kSmallBufLen); + uv_homedir[0] = '\0'; + sapi::v::IntBase 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 uv_tmpdir(kBigBufLen); + uv_tmpdir[0] = '\0'; + sapi::v::IntBase 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 uv_tmpdir(kSmallBufLen); + uv_tmpdir[0] = '\0'; + sapi::v::IntBase 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); +} From 192e443b7cd7ff11f6cd89f81bc45449790bac89 Mon Sep 17 00:00:00 2001 From: Federico Stazi <34340238+FedericoStazi@users.noreply.github.com> Date: Tue, 22 Sep 2020 15:44:39 +0100 Subject: [PATCH 2/4] Update README.md --- oss-internship-2020/libuv/README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/oss-internship-2020/libuv/README.md b/oss-internship-2020/libuv/README.md index 66eeb48..bc49e80 100644 --- a/oss-internship-2020/libuv/README.md +++ b/oss-internship-2020/libuv/README.md @@ -60,15 +60,3 @@ Each example and test has an ad-hoc policy implemented on its own file. These po ## 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. From a61421621c194a25779a870bf07a2d155198019f Mon Sep 17 00:00:00 2001 From: Federico Stazi Date: Fri, 25 Sep 2020 15:06:24 +0000 Subject: [PATCH 3/4] Address review comments --- oss-internship-2020/libuv/CMakeLists.txt | 48 ++++---- oss-internship-2020/libuv/README.md | 63 +++++++--- .../libuv/callbacks/callbacks.cc | 51 ++++---- .../libuv/callbacks/callbacks.h | 21 ++-- oss-internship-2020/libuv/examples/README.md | 22 +++- .../libuv/examples/helloworld.cc | 62 +++++----- .../libuv/examples/idle-basic.cc | 94 +++++++-------- oss-internship-2020/libuv/examples/uvcat.cc | 112 ++++++++---------- .../libuv/generator/wrapper_generator.py | 93 ++++++++------- oss-internship-2020/libuv/tests/test_array.cc | 12 +- .../libuv/tests/test_callback.cc | 12 +- oss-internship-2020/libuv/tests/test_error.cc | 12 +- oss-internship-2020/libuv/tests/test_loop.cc | 12 +- oss-internship-2020/libuv/tests/test_os.cc | 12 +- 14 files changed, 333 insertions(+), 293 deletions(-) diff --git a/oss-internship-2020/libuv/CMakeLists.txt b/oss-internship-2020/libuv/CMakeLists.txt index 06a0107..6ffe8b6 100644 --- a/oss-internship-2020/libuv/CMakeLists.txt +++ b/oss-internship-2020/libuv/CMakeLists.txt @@ -1,4 +1,3 @@ - # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,13 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.16) +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( @@ -28,33 +34,33 @@ add_custom_command( 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" + 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(UV_SAPI_ENABLE_EXAMPLES "" ON) -option(UV_SAPI_ENABLE_TESTS "" ON) +option(SAPI_UV_ENABLE_EXAMPLES "" ON) +option(SAPI_UV_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 +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 UV_SAPI_CALLBACKS variable should contain the absolute paths of +# 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" - "${UV_SAPI_CALLBACKS}" + "${SAPI_UV_CALLBACKS}" ) set_target_properties(uv_wrapper_and_callbacks PROPERTIES LINKER_LANGUAGE C @@ -66,13 +72,8 @@ 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 -) +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 @@ -374,8 +375,7 @@ add_sapi_library(uv_sapi LIBRARY_NAME UV - NAMESPACE "" - + NAMESPACE uv ) # Include generated SAPI header @@ -384,11 +384,11 @@ target_include_directories(uv_sapi INTERFACE ) # Add examples -if (UV_SAPI_ENABLE_EXAMPLES) +if (SAPI_UV_ENABLE_EXAMPLES) add_subdirectory(examples) endif() # Add tests -if (UV_SAPI_ENABLE_TESTS) +if (SAPI_UV_ENABLE_TESTS) add_subdirectory(tests) endif() diff --git a/oss-internship-2020/libuv/README.md b/oss-internship-2020/libuv/README.md index bc49e80..0e1cb76 100644 --- a/oss-internship-2020/libuv/README.md +++ b/oss-internship-2020/libuv/README.md @@ -1,6 +1,7 @@ # LibUV Sandbox -This library is a sandboxed version of [LibUV](https://libuv.org/), implemented using Sandboxed API. +This library is a sandboxed version of [LibUV](https://libuv.org/), implemented +using Sandboxed API. ## Setup @@ -8,16 +9,19 @@ The repository can be cloned using: ``` git clone --recursive [URL to this repo] ``` -The `--recursive` flag ensures that submodules are also cloned. +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: +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 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: +The following commands, used from the current `libuv/` directory, build the +library: ``` mkdir -p build cd build @@ -27,36 +31,63 @@ 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. +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. +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. +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 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`. +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). +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. +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 `UV_SAPI_ENABLE_TESTS` must be set to `ON`. This enables Sandboxed API tests as well. +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. +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. +The `callbacks.h` and `callbacks.cc` files in the `callbacks` folder implement +all the callbacks used by examples and tests. diff --git a/oss-internship-2020/libuv/callbacks/callbacks.cc b/oss-internship-2020/libuv/callbacks/callbacks.cc index e89861d..3713f04 100644 --- a/oss-internship-2020/libuv/callbacks/callbacks.cc +++ b/oss-internship-2020/libuv/callbacks/callbacks.cc @@ -12,38 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "callbacks.h" +#include "callbacks.h" // NOLINT(build/include) #include -size_t iterations = 0; +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) { - iterations++; - if (iterations > kMaxIterations) { + ++g_iterations; + if (g_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(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); + 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 @@ -51,16 +49,16 @@ void OnWrite(uv_fs_t* req) { 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) { + 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 - iov.len = req->result; - uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, OnWrite); + g_iov.len = req->result; + uv_fs_write(uv_default_loop(), &write_req, 1, &g_iov, 1, -1, OnWrite); } } @@ -69,18 +67,17 @@ void OnRead(uv_fs_t* req) { void OnOpen(uv_fs_t* req) { if (req != &open_req) { std::cerr << "Open error: req != &open_req" << std::endl; - - } else if (req->result < 0) { + return; + } + if (req->result < 0) { std::cerr << "Open error: " << uv_strerror(static_cast(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); + 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 @@ -88,6 +85,6 @@ void OnOpen(uv_fs_t* req) { void TimerCallback(uv_timer_t* handle) { int* data = static_cast( uv_handle_get_data(reinterpret_cast(handle))); - (*data)++; + ++(*data); uv_close(reinterpret_cast(handle), nullptr); } diff --git a/oss-internship-2020/libuv/callbacks/callbacks.h b/oss-internship-2020/libuv/callbacks/callbacks.h index e9acaad..560d245 100644 --- a/oss-internship-2020/libuv/callbacks/callbacks.h +++ b/oss-internship-2020/libuv/callbacks/callbacks.h @@ -12,30 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CALLBACKS_H -#define CALLBACKS_H +#ifndef CALLBACKS_H_ +#define CALLBACKS_H_ #include extern "C" { // idle-basic - -void IdleCallback(uv_idle_t *handle); +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); +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); -void TimerCallback(uv_timer_t *handle); -} +} // extern "C" -#endif // CALLBACKS_H +#endif // CALLBACKS_H_ diff --git a/oss-internship-2020/libuv/examples/README.md b/oss-internship-2020/libuv/examples/README.md index 730c783..ec274f9 100644 --- a/oss-internship-2020/libuv/examples/README.md +++ b/oss-internship-2020/libuv/examples/README.md @@ -1,9 +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. +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. \ No newline at end of file +- **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. diff --git a/oss-internship-2020/libuv/examples/helloworld.cc b/oss-internship-2020/libuv/examples/helloworld.cc index ef5941b..1974758 100644 --- a/oss-internship-2020/libuv/examples/helloworld.cc +++ b/oss-internship-2020/libuv/examples/helloworld.cc @@ -21,7 +21,7 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" -class UVSapiHelloworldSandbox : public UVSandbox { +class UVSapiHelloworldSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -35,57 +35,51 @@ class UVSapiHelloworldSandbox : public UVSandbox { } }; -int main(int argc, char* argv[]) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - google::InitGoogleLogging(argv[0]); - - absl::Status status; - +absl::Status HelloWorld() { // Initialize sandbox2 and sapi UVSapiHelloworldSandbox sandbox; - status = sandbox.Init(); - if (!status.ok()) { - LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; - } - UVApi api(&sandbox); + SAPI_RETURN_IF_ERROR(sandbox.Init()); + uv::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_RETURN_IF_ERROR( + sandbox.rpc_channel()->Allocate(sizeof(uv_loop_t), &loop_voidptr)); sapi::v::RemotePtr loop(loop_voidptr); - absl::StatusOr return_code; + 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(); + 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 - 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(); + 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 - return_code = api.sapi_uv_loop_close(&loop); - if (!return_code.ok()) { - LOG(FATAL) << "sapi_uv_loop_close failed: " << return_code.status(); + 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); } - if (return_code.value() != 0) { - LOG(FATAL) << "uv_loop_close returned error " << return_code.value(); + + return absl::OkStatus(); +} + +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; diff --git a/oss-internship-2020/libuv/examples/idle-basic.cc b/oss-internship-2020/libuv/examples/idle-basic.cc index 889676d..63e65d7 100644 --- a/oss-internship-2020/libuv/examples/idle-basic.cc +++ b/oss-internship-2020/libuv/examples/idle-basic.cc @@ -21,7 +21,7 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" -class UVSapiIdleBasicSandbox : public UVSandbox { +class UVSapiIdleBasicSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -36,88 +36,74 @@ class UVSapiIdleBasicSandbox : public UVSandbox { } }; -int main(int argc, char* argv[]) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - google::InitGoogleLogging(argv[0]); - - absl::Status status; - +absl::Status IdleBasic() { // Initialize sandbox2 and sapi UVSapiIdleBasicSandbox sandbox; - status = sandbox.Init(); - if (!status.ok()) { - LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; - } - UVApi api(&sandbox); + SAPI_RETURN_IF_ERROR(sandbox.Init()); + uv::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_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; - status = sandbox.rpc_channel()->Allocate(sizeof(uv_idle_t), &idle_voidptr); - if (!status.ok()) { - LOG(FATAL) << "sapi::Sandbox::rpc_channel()->Allocate failed: " << status; - } + SAPI_RETURN_IF_ERROR( + sandbox.rpc_channel()->Allocate(sizeof(uv_idle_t), &idle_voidptr)); sapi::v::RemotePtr idler(idle_voidptr); - absl::StatusOr return_code; + int return_code; // Get default loop - absl::StatusOr 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()); + SAPI_ASSIGN_OR_RETURN(void* loop_voidptr, api.sapi_uv_default_loop()); + sapi::v::RemotePtr loop(loop_voidptr); // 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(); + 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 - 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(); + 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 - 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(); + 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; - status = api.sapi_uv_close(&idler, &null_ptr); - if (!status.ok()) { - LOG(FATAL) << "sapi_uv_close failed: " << status; - } + SAPI_RETURN_IF_ERROR(api.sapi_uv_close(&idler, &null_ptr)); // Close loop - return_code = api.sapi_uv_loop_close(&loop); - if (!return_code.ok()) { - LOG(FATAL) << "sapi_uv_loop_close failed: " << return_code.status(); - } + 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.value() != 0 && return_code.value() != UV_EBUSY) { - LOG(FATAL) << "uv_loop_close returned error " << return_code.value(); + if (return_code != 0 && return_code != UV_EBUSY) { + return absl::UnavailableError("uv_loop_close returned error " + + return_code); + } + + return absl::OkStatus(); +} + +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; diff --git a/oss-internship-2020/libuv/examples/uvcat.cc b/oss-internship-2020/libuv/examples/uvcat.cc index 7cfa0b6..b693c81 100644 --- a/oss-internship-2020/libuv/examples/uvcat.cc +++ b/oss-internship-2020/libuv/examples/uvcat.cc @@ -21,7 +21,7 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" -class UVSapiUVCatSandbox : public UVSandbox { +class UVSapiUVCatSandbox : public uv::UVSandbox { public: UVSapiUVCatSandbox(std::string filename) : filename(filename) {} @@ -46,74 +46,62 @@ class UVSapiUVCatSandbox : public UVSandbox { 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(); +} + 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 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 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(); + LOG(ERROR) << "wrong number of arguments (1 expected)"; + return EXIT_FAILURE; } - // 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; + if (absl::Status status = UVCat(argv[1]); !status.ok()) { + LOG(ERROR) << "UVCat failed: " << status.ToString(); + return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/oss-internship-2020/libuv/generator/wrapper_generator.py b/oss-internship-2020/libuv/generator/wrapper_generator.py index 5006589..43610a3 100644 --- a/oss-internship-2020/libuv/generator/wrapper_generator.py +++ b/oss-internship-2020/libuv/generator/wrapper_generator.py @@ -11,14 +11,20 @@ # 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.""" + +"""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 sys +from typing import List import re import os -def get_var_type(string): +def get_var_type(string : str) -> str: """Gets the type from an argument variable (e.g. "int x" -> "int") """ @@ -32,7 +38,7 @@ def get_var_type(string): return " ".join(var.split(" ")[:-1]).strip() -def get_var_name(string): +def get_var_name(string : str) -> str: """Gets the name from an argument variable (e.g. "int x" -> "x") """ @@ -50,7 +56,7 @@ def get_var_name(string): return var.split(" ")[-1].strip() -def fix_method_type(string): +def fix_method_type(string : str) -> str: """Fixes the method type (e.g. "const int*" -> "const void*") """ @@ -69,7 +75,7 @@ def fix_method_type(string): return method_type -def fix_argument(string): +def fix_argument(string : str) -> str: """Fixes an argument (e.g. "const int* x" -> "const void* x") """ @@ -92,7 +98,7 @@ def fix_argument(string): return arg_type + " " + arg_name -def fix_call_argument(string): +def fix_call_argument(string : str) -> str: """Fixes an argument in a call the orignal method (e.g. "const int* x" -> "reinterpret_cast(x)") """ @@ -113,14 +119,14 @@ def fix_call_argument(string): return arg_name -def read_file(filename): +def read_file(filename : str) -> str: """Returns contents of filename as a string""" file = open(filename, "r") return str(file.read()) -def clean_file(text): +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 @@ -134,7 +140,7 @@ def clean_file(text): return result -def get_signatures(text): +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 """ @@ -148,71 +154,78 @@ def get_signatures(text): 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""" +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""" - 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") + 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 write_text(text, file): - """Writes text to file +def append_text(text : str, file : List[str]) -> None: + """Writes text to file list of lines Useful for additional methods, includes, extern "C"... """ - file.write(text) + file.append(text) -def generate_wrapper(): +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 = open(sys.argv[2], "w") - source = open(sys.argv[3], "w") + header = [] + source = [] - write_text("#include \n\n", header) - write_text("extern \"C\" {\n\n", header) - write_text("#include \"" + os.path.abspath(header.name) + "\"\n\n", source) + append_text("#include ", 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 - write_method(method_type, name, arguments_list, header, source) + append_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", + append_text("void sapi_uv_once(void* guard, void (*callback)(void));", header) - write_text("void sapi_uv_once(void* guard, void (*callback)(void)) {\n" + + append_text("void sapi_uv_once(void* guard, void (*callback)(void)) {\n" + " return uv_once(reinterpret_cast(guard)," + - "callback);\n" + "}\n\n", source) + "callback);\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)" + + 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(loop), option);\n" + "}\n\n", + "reinterpret_cast(loop), option);\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, " + + 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(loop), option, ap);\n}\n\n", + "reinterpret_cast(loop), option, ap);\n}", source) - write_text("} // extern \"C\"\n", header) + append_text("} // extern \"C\"\n", header) + + header_file.write("\n\n".join(header)) + source_file.write("\n\n".join(source)) generate_wrapper() diff --git a/oss-internship-2020/libuv/tests/test_array.cc b/oss-internship-2020/libuv/tests/test_array.cc index 572d1fc..82ceeed 100644 --- a/oss-internship-2020/libuv/tests/test_array.cc +++ b/oss-internship-2020/libuv/tests/test_array.cc @@ -21,7 +21,9 @@ #include "sandboxed_api/util/status_matchers.h" #include "uv_sapi.sapi.h" -class UVTestArraySapiSandbox : public UVSandbox { +namespace { + +class UVTestArraySapiSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -40,12 +42,12 @@ class UVTestArray : public ::testing::Test { protected: void SetUp() override { sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); - api_ = std::make_unique(sandbox_.get()); + ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); } std::unique_ptr sandbox_; - std::unique_ptr api_; + std::unique_ptr api_; }; TEST_F(UVTestArray, LoadAvg) { @@ -65,3 +67,5 @@ TEST_F(UVTestArray, LoadAvg) { ASSERT_GE(avg_buf[1], 0); ASSERT_GE(avg_buf[2], 0); } + +} // namespace diff --git a/oss-internship-2020/libuv/tests/test_callback.cc b/oss-internship-2020/libuv/tests/test_callback.cc index 275daa6..47ee138 100644 --- a/oss-internship-2020/libuv/tests/test_callback.cc +++ b/oss-internship-2020/libuv/tests/test_callback.cc @@ -21,7 +21,9 @@ #include "sandboxed_api/util/status_matchers.h" #include "uv_sapi.sapi.h" -class UVTestCallbackSapiSandbox : public UVSandbox { +namespace { + +class UVTestCallbackSapiSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -40,8 +42,8 @@ class UVTestCallback : public ::testing::Test { protected: void SetUp() override { sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); - api_ = std::make_unique(sandbox_.get()); + ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); } // Check sapi_uv_timer_init @@ -87,7 +89,7 @@ class UVTestCallback : public ::testing::Test { } std::unique_ptr sandbox_; - std::unique_ptr api_; + std::unique_ptr api_; static constexpr int kData = 1729; }; @@ -137,3 +139,5 @@ TEST_F(UVTestCallback, TimerCallback) { UVDefaultLoop(&loop); UVLoopClose(loop.PtrNone()); } + +} // namespace diff --git a/oss-internship-2020/libuv/tests/test_error.cc b/oss-internship-2020/libuv/tests/test_error.cc index 3f681fe..984c1a9 100644 --- a/oss-internship-2020/libuv/tests/test_error.cc +++ b/oss-internship-2020/libuv/tests/test_error.cc @@ -20,7 +20,9 @@ #include "sandboxed_api/util/status_matchers.h" #include "uv_sapi.sapi.h" -class UVTestErrorSapiSandbox : public UVSandbox { +namespace { + +class UVTestErrorSapiSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -37,8 +39,8 @@ class UVTestError : public ::testing::Test { protected: void SetUp() override { sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); - api_ = std::make_unique(sandbox_.get()); + ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); } // Check sapi_uv_strerror on error @@ -67,7 +69,7 @@ class UVTestError : public ::testing::Test { } std::unique_ptr sandbox_; - std::unique_ptr api_; + std::unique_ptr api_; }; TEST_F(UVTestError, ErrorMessage) { @@ -88,3 +90,5 @@ TEST_F(UVTestError, SystemError) { UVTranslateSysError(UV_EACCES); UVTranslateSysError(0); } + +} // namespace diff --git a/oss-internship-2020/libuv/tests/test_loop.cc b/oss-internship-2020/libuv/tests/test_loop.cc index 478cb62..9980f43 100644 --- a/oss-internship-2020/libuv/tests/test_loop.cc +++ b/oss-internship-2020/libuv/tests/test_loop.cc @@ -21,7 +21,9 @@ #include "sandboxed_api/util/status_matchers.h" #include "uv_sapi.sapi.h" -class UVTestLoopSapiSandbox : public UVSandbox { +namespace { + +class UVTestLoopSapiSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -39,8 +41,8 @@ class UVTestLoop : public ::testing::Test { protected: void SetUp() override { sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); - api_ = std::make_unique(sandbox_.get()); + ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); } // Check sapi_uv_loop_init @@ -69,7 +71,7 @@ class UVTestLoop : public ::testing::Test { } std::unique_ptr sandbox_; - std::unique_ptr api_; + std::unique_ptr api_; }; TEST_F(UVTestLoop, InitLoop) { @@ -100,3 +102,5 @@ TEST_F(UVTestLoop, DefaultLoop) { UVDefaultLoop(&loop); UVLoopClose(loop.PtrNone()); } + +} // namespace diff --git a/oss-internship-2020/libuv/tests/test_os.cc b/oss-internship-2020/libuv/tests/test_os.cc index 9d19171..a8195d9 100644 --- a/oss-internship-2020/libuv/tests/test_os.cc +++ b/oss-internship-2020/libuv/tests/test_os.cc @@ -21,7 +21,9 @@ #include "sandboxed_api/util/status_matchers.h" #include "uv_sapi.sapi.h" -class UVTestOSSapiSandbox : public UVSandbox { +namespace { + +class UVTestOSSapiSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder*) override { @@ -43,12 +45,12 @@ class UVTestOS : public ::testing::Test { protected: void SetUp() override { sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), ::sapi::IsOk()); - api_ = std::make_unique(sandbox_.get()); + ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + api_ = std::make_unique(sandbox_.get()); } std::unique_ptr sandbox_; - std::unique_ptr api_; + std::unique_ptr api_; static constexpr size_t kBigBufLen = 4096; static constexpr size_t kSmallBufLen = 1; @@ -131,3 +133,5 @@ TEST_F(UVTestOS, TmpDirSmall) { // Test error code is as expected ASSERT_EQ(error_code, expected_error_code); } + +} // namespace From 437972569605acb8bc30f1708e2e0137177badde Mon Sep 17 00:00:00 2001 From: Federico Stazi Date: Mon, 28 Sep 2020 18:41:37 +0000 Subject: [PATCH 4/4] Add namespaces in examples --- oss-internship-2020/libuv/examples/helloworld.cc | 4 ++++ oss-internship-2020/libuv/examples/idle-basic.cc | 4 ++++ oss-internship-2020/libuv/examples/uvcat.cc | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/oss-internship-2020/libuv/examples/helloworld.cc b/oss-internship-2020/libuv/examples/helloworld.cc index 1974758..e452548 100644 --- a/oss-internship-2020/libuv/examples/helloworld.cc +++ b/oss-internship-2020/libuv/examples/helloworld.cc @@ -21,6 +21,8 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" +namespace { + class UVSapiHelloworldSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( @@ -73,6 +75,8 @@ absl::Status HelloWorld() { return absl::OkStatus(); } +} // namespace + int main(int argc, char* argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); diff --git a/oss-internship-2020/libuv/examples/idle-basic.cc b/oss-internship-2020/libuv/examples/idle-basic.cc index 63e65d7..e89ec50 100644 --- a/oss-internship-2020/libuv/examples/idle-basic.cc +++ b/oss-internship-2020/libuv/examples/idle-basic.cc @@ -21,6 +21,8 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" +namespace { + class UVSapiIdleBasicSandbox : public uv::UVSandbox { private: std::unique_ptr ModifyPolicy( @@ -97,6 +99,8 @@ absl::Status IdleBasic() { return absl::OkStatus(); } +} // namespace + int main(int argc, char* argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); diff --git a/oss-internship-2020/libuv/examples/uvcat.cc b/oss-internship-2020/libuv/examples/uvcat.cc index b693c81..5f9ea3b 100644 --- a/oss-internship-2020/libuv/examples/uvcat.cc +++ b/oss-internship-2020/libuv/examples/uvcat.cc @@ -21,6 +21,8 @@ #include "sandboxed_api/util/flag.h" #include "uv_sapi.sapi.h" +namespace { + class UVSapiUVCatSandbox : public uv::UVSandbox { public: UVSapiUVCatSandbox(std::string filename) : filename(filename) {} @@ -90,6 +92,8 @@ absl::Status UVCat(std::string filearg) { return absl::OkStatus(); } +} // namespace + int main(int argc, char* argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]);