From 22d21cd1c378e7becd4d1ed2aeaa84878a65f1a5 Mon Sep 17 00:00:00 2001 From: Federico Stazi Date: Tue, 22 Sep 2020 14:41:06 +0000 Subject: [PATCH] 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); +}