diff --git a/.gitmodules b/.gitmodules index 5287473..e7461eb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "oss-internship-2020/curl/curl_wrapper/curl"] path = oss-internship-2020/curl/curl_wrapper/curl url = git@github.com:curl/curl.git +[submodule "oss-internship-2020/libuv/libuv"] + path = oss-internship-2020/libuv/libuv + url = git@github.com:libuv/libuv.git 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..bcff8d4 --- /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.12) + +project(libuv_sandbox) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Add SAPI directories +add_subdirectory( + "${SAPI_ROOT}" + "${CMAKE_BINARY_DIR}/sandboxed-api-build" + EXCLUDE_FROM_ALL +) + +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/uv_wrapper") +add_custom_command( + + OUTPUT "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h" + "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc" + + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + + COMMAND "${SAPI_PYTHON3_EXECUTABLE}" + "generator/wrapper_generator.py" + "libuv/include/uv.h" + "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h" + "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc" + + COMMENT "Generate the wrapper header and source files" +) + +option(SAPI_UV_ENABLE_EXAMPLES "" ON) +option(SAPI_UV_ENABLE_TESTS "" ON) + +# Add callbacks used by examples and tests +if (SAPI_UV_ENABLE_EXAMPLES OR SAPI_UV_ENABLE_TESTS) + list(APPEND SAPI_UV_CALLBACKS + "${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.h" + "${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.cc" + ) +endif() + +# Wrapper library including wrappers for all libuv methods and callbacks +# The SAPI_UV_CALLBACKS variable should contain the absolute paths of +# all the files implementing the callbacks +add_library(uv_wrapper_and_callbacks OBJECT + "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h" + "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.cc" + "${SAPI_UV_CALLBACKS}" +) +set_target_properties(uv_wrapper_and_callbacks + PROPERTIES LINKER_LANGUAGE C +) + +# Link the wrapper to the original uv library +add_subdirectory(libuv) +target_link_libraries(uv_wrapper_and_callbacks uv_a) + +# Setup Sandboxed API +set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree") +set(SAPI_ENABLE_EXAMPLES ${SAPI_UV_ENABLE_EXAMPLES} CACHE BOOL "" FORCE) +set(SAPI_ENABLE_TESTS ${SAPI_UV_ENABLE_TESTS} CACHE BOOL "" FORCE) + +# Generate SAPI header +add_sapi_library(uv_sapi + + # List of all the generated methods + FUNCTIONS sapi_uv_accept + sapi_uv_async_init + sapi_uv_async_send + sapi_uv_backend_fd + sapi_uv_backend_timeout + sapi_uv_barrier_destroy + sapi_uv_barrier_init + sapi_uv_barrier_wait + # TODO(cblichmann): sapi_uv_buf_init + sapi_uv_cancel + sapi_uv_chdir + sapi_uv_check_init + sapi_uv_check_start + sapi_uv_check_stop + sapi_uv_close + sapi_uv_cond_broadcast + sapi_uv_cond_destroy + sapi_uv_cond_init + sapi_uv_cond_signal + sapi_uv_cond_timedwait + sapi_uv_cond_wait + sapi_uv_cpu_info + sapi_uv_cwd + sapi_uv_default_loop + sapi_uv_disable_stdio_inheritance + sapi_uv_dlclose + sapi_uv_dlerror + sapi_uv_dlopen + sapi_uv_dlsym + sapi_uv_err_name + sapi_uv_err_name_r + sapi_uv_exepath + sapi_uv_fileno + sapi_uv_free_cpu_info + sapi_uv_free_interface_addresses + sapi_uv_freeaddrinfo + sapi_uv_fs_access + sapi_uv_fs_chmod + sapi_uv_fs_chown + sapi_uv_fs_close + sapi_uv_fs_closedir + sapi_uv_fs_copyfile + sapi_uv_fs_event_getpath + sapi_uv_fs_event_init + sapi_uv_fs_event_start + sapi_uv_fs_event_stop + sapi_uv_fs_fchmod + sapi_uv_fs_fchown + sapi_uv_fs_fdatasync + sapi_uv_fs_fstat + sapi_uv_fs_fsync + sapi_uv_fs_ftruncate + sapi_uv_fs_futime + sapi_uv_fs_get_path + sapi_uv_fs_get_ptr + sapi_uv_fs_get_result + sapi_uv_fs_get_statbuf + sapi_uv_fs_get_system_error + sapi_uv_fs_get_type + sapi_uv_fs_lchown + sapi_uv_fs_link + sapi_uv_fs_lstat + sapi_uv_fs_lutime + sapi_uv_fs_mkdir + sapi_uv_fs_mkdtemp + sapi_uv_fs_mkstemp + sapi_uv_fs_open + sapi_uv_fs_opendir + sapi_uv_fs_poll_getpath + sapi_uv_fs_poll_init + sapi_uv_fs_poll_start + sapi_uv_fs_poll_stop + sapi_uv_fs_read + sapi_uv_fs_readdir + sapi_uv_fs_readlink + sapi_uv_fs_realpath + sapi_uv_fs_rename + sapi_uv_fs_req_cleanup + sapi_uv_fs_rmdir + sapi_uv_fs_scandir + sapi_uv_fs_scandir_next + sapi_uv_fs_sendfile + sapi_uv_fs_stat + sapi_uv_fs_statfs + sapi_uv_fs_symlink + sapi_uv_fs_unlink + sapi_uv_fs_utime + sapi_uv_fs_write + sapi_uv_get_constrained_memory + sapi_uv_get_free_memory + sapi_uv_get_osfhandle + sapi_uv_get_process_title + sapi_uv_get_total_memory + sapi_uv_getaddrinfo + sapi_uv_getnameinfo + sapi_uv_getrusage + sapi_uv_gettimeofday + sapi_uv_guess_handle + sapi_uv_handle_get_data + sapi_uv_handle_get_loop + sapi_uv_handle_get_type + sapi_uv_handle_set_data + sapi_uv_handle_size + sapi_uv_handle_type_name + sapi_uv_has_ref + sapi_uv_hrtime + sapi_uv_idle_init + sapi_uv_idle_start + sapi_uv_idle_stop + sapi_uv_if_indextoiid + sapi_uv_if_indextoname + sapi_uv_inet_ntop + sapi_uv_inet_pton + sapi_uv_interface_addresses + sapi_uv_ip4_addr + sapi_uv_ip4_name + sapi_uv_ip6_addr + sapi_uv_ip6_name + sapi_uv_is_active + sapi_uv_is_closing + sapi_uv_is_readable + sapi_uv_is_writable + sapi_uv_key_create + sapi_uv_key_delete + sapi_uv_key_get + sapi_uv_key_set + sapi_uv_kill + sapi_uv_library_shutdown + sapi_uv_listen + sapi_uv_loadavg + sapi_uv_loop_alive + sapi_uv_loop_close + sapi_uv_loop_configure + sapi_uv_loop_configure_int + sapi_uv_loop_delete + sapi_uv_loop_fork + sapi_uv_loop_get_data + sapi_uv_loop_init + sapi_uv_loop_new + sapi_uv_loop_set_data + sapi_uv_loop_size + sapi_uv_metrics_idle_time + sapi_uv_mutex_destroy + sapi_uv_mutex_init + sapi_uv_mutex_init_recursive + sapi_uv_mutex_lock + sapi_uv_mutex_trylock + sapi_uv_mutex_unlock + sapi_uv_now + sapi_uv_once + sapi_uv_open_osfhandle + sapi_uv_os_environ + sapi_uv_os_free_environ + sapi_uv_os_free_passwd + sapi_uv_os_get_passwd + sapi_uv_os_getenv + sapi_uv_os_gethostname + sapi_uv_os_getpid + sapi_uv_os_getppid + sapi_uv_os_getpriority + sapi_uv_os_homedir + sapi_uv_os_setenv + sapi_uv_os_setpriority + sapi_uv_os_tmpdir + sapi_uv_os_uname + sapi_uv_os_unsetenv + sapi_uv_pipe_bind + sapi_uv_pipe_chmod + sapi_uv_pipe_connect + sapi_uv_pipe_getpeername + sapi_uv_pipe_getsockname + sapi_uv_pipe_init + sapi_uv_pipe_open + sapi_uv_pipe_pending_count + sapi_uv_pipe_pending_instances + sapi_uv_pipe_pending_type + sapi_uv_poll_init + sapi_uv_poll_init_socket + sapi_uv_poll_start + sapi_uv_poll_stop + sapi_uv_prepare_init + sapi_uv_prepare_start + sapi_uv_prepare_stop + sapi_uv_print_active_handles + sapi_uv_print_all_handles + sapi_uv_process_get_pid + sapi_uv_process_kill + sapi_uv_queue_work + sapi_uv_random + sapi_uv_read_start + sapi_uv_read_stop + sapi_uv_recv_buffer_size + sapi_uv_ref + sapi_uv_replace_allocator + sapi_uv_req_get_data + sapi_uv_req_get_type + sapi_uv_req_set_data + sapi_uv_req_size + sapi_uv_req_type_name + sapi_uv_resident_set_memory + sapi_uv_run + sapi_uv_rwlock_destroy + sapi_uv_rwlock_init + sapi_uv_rwlock_rdlock + sapi_uv_rwlock_rdunlock + sapi_uv_rwlock_tryrdlock + sapi_uv_rwlock_trywrlock + sapi_uv_rwlock_wrlock + sapi_uv_rwlock_wrunlock + sapi_uv_sem_destroy + sapi_uv_sem_init + sapi_uv_sem_post + sapi_uv_sem_trywait + sapi_uv_sem_wait + sapi_uv_send_buffer_size + sapi_uv_set_process_title + sapi_uv_setup_args + sapi_uv_shutdown + sapi_uv_signal_init + sapi_uv_signal_start + sapi_uv_signal_start_oneshot + sapi_uv_signal_stop + sapi_uv_sleep + sapi_uv_spawn + sapi_uv_stop + sapi_uv_stream_get_write_queue_size + sapi_uv_stream_set_blocking + sapi_uv_strerror + sapi_uv_strerror_r + sapi_uv_tcp_bind + sapi_uv_tcp_close_reset + sapi_uv_tcp_connect + sapi_uv_tcp_getpeername + sapi_uv_tcp_getsockname + sapi_uv_tcp_init + sapi_uv_tcp_init_ex + sapi_uv_tcp_keepalive + sapi_uv_tcp_nodelay + sapi_uv_tcp_open + sapi_uv_tcp_simultaneous_accepts + sapi_uv_thread_create + sapi_uv_thread_create_ex + sapi_uv_thread_equal + sapi_uv_thread_join + sapi_uv_thread_self + sapi_uv_timer_again + sapi_uv_timer_get_due_in + sapi_uv_timer_get_repeat + sapi_uv_timer_init + sapi_uv_timer_set_repeat + sapi_uv_timer_start + sapi_uv_timer_stop + sapi_uv_translate_sys_error + sapi_uv_try_write + sapi_uv_tty_get_vterm_state + sapi_uv_tty_get_winsize + sapi_uv_tty_init + sapi_uv_tty_reset_mode + sapi_uv_tty_set_mode + sapi_uv_tty_set_vterm_state + sapi_uv_udp_bind + sapi_uv_udp_connect + sapi_uv_udp_get_send_queue_count + sapi_uv_udp_get_send_queue_size + sapi_uv_udp_getpeername + sapi_uv_udp_getsockname + sapi_uv_udp_init + sapi_uv_udp_init_ex + sapi_uv_udp_open + sapi_uv_udp_recv_start + sapi_uv_udp_recv_stop + sapi_uv_udp_send + sapi_uv_udp_set_broadcast + sapi_uv_udp_set_membership + sapi_uv_udp_set_multicast_interface + sapi_uv_udp_set_multicast_loop + sapi_uv_udp_set_multicast_ttl + sapi_uv_udp_set_source_membership + sapi_uv_udp_set_ttl + sapi_uv_udp_try_send + sapi_uv_udp_using_recvmmsg + sapi_uv_unref + sapi_uv_update_time + sapi_uv_uptime + sapi_uv_version + sapi_uv_version_string + sapi_uv_walk + sapi_uv_write + sapi_uv_write2 + + INPUTS "${CMAKE_BINARY_DIR}/uv_wrapper/uv_wrapper.h" + + LIBRARY uv_wrapper_and_callbacks + + LIBRARY_NAME UV + + NAMESPACE uv +) + +# Include generated SAPI header +target_include_directories(uv_sapi INTERFACE + "${PROJECT_BINARY_DIR}" +) + +# Add examples +if (SAPI_UV_ENABLE_EXAMPLES) + add_subdirectory(examples) +endif() + +# Add tests +if (SAPI_UV_ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/oss-internship-2020/libuv/README.md b/oss-internship-2020/libuv/README.md new file mode 100644 index 0000000..0e1cb76 --- /dev/null +++ b/oss-internship-2020/libuv/README.md @@ -0,0 +1,93 @@ +# LibUV Sandbox + +This library is a sandboxed version of [LibUV](https://libuv.org/), implemented +using Sandboxed API. + +## Setup + +The repository can be cloned using: +``` +git clone --recursive [URL to this repo] +``` +The `--recursive` flag ensures that submodules are also cloned. + +Alternatively, if the repository has already been cloned but the submodules have +not, these can be cloned using: +``` +git submodule update --init --recursive +``` + +The full list of Sandboxed API dependencies can be found on +[Sandboxed API Getting Started page](https://developers.google.com/sandboxed-api/docs/getting-started). + +The following commands, used from the current `libuv/` directory, build the +library: +``` +mkdir -p build +cd build +cmake .. -G Ninja -D SAPI_ROOT=[path to sandboxed-api] +cmake --build . +``` + +## Implementation details + +LibUV can't be directly sandboxed by Sandboxed API. Because of the size and +complexity of the library, doing so creates some compilation errors and does not +create the correct APIs for all methods. The solution for this issue is creating +a wrapper library, whose methods (which can be sandboxed properly) internally +call the original LibUV's methods. + +Using these wrapper methods is extremely simple, the only relevant difference is +that they have a `sapi_` prefix before their names (e.g. `uv_loop_init` becomes +`sapi_uv_loop_init`). The only exception is the variadic method +`uv_loop_configure`. This has two wrappers, `sapi_uv_loop_configure` (with no +additional parameters) and `sapi_uv_loop_configure_int` (with an additional +`int` parameter). Currently, these are the only valid calls to the method in +LibUV. + +#### Wrapper generator + +The wrapper is generated automatically by a Python script that is called by +CMake at build time. This script can be found in the `generator` folder. + +#### Function pointers + +The functions whose pointers will be passed to the library's methods +(*callbacks*) can't be implemented in the files making use of the library, but +must be in other files. These files must be compiled together with the library, +and this is done by adding their absolute path to the CMake variable +`SAPI_UV_CALLBACKS`. + +The pointers can then be obtained using an `RPCChannel` object, as shown in the +example `idle-basic.cc`. + +## Examples + +The `examples` directory contains the sandboxed versions of example source codes +taken from from [LibUV's User Guide](https://docs.libuv.org/en/v1.x/guide.html). +More information about each example can be found in the examples' +[README](examples/README.md). + +To build these examples when building the library, the CMake variable +`SAPI_UV_ENABLE_EXAMPLES` must be set to `ON`. This enables Sandboxed API +examples as well. + +## Testing + +The `tests` folder contains some test cases created using Google Test. + +To build these tests when building the library, the CMake variable +`SAPI_UV_ENABLE_TESTS` must be set to `ON`. This enables Sandboxed API tests as +well. + +## Policies + +Each example and test has an ad-hoc policy implemented on its own file. These +policies can be used as references for pieces of code that perform similar +operations. For example, the `helloworld.cc` example has a policy that only +allows the strictly necessary syscalls for running a loop. + +## Callbacks + +The `callbacks.h` and `callbacks.cc` files in the `callbacks` folder implement +all the callbacks used by examples and tests. diff --git a/oss-internship-2020/libuv/callbacks/callbacks.cc b/oss-internship-2020/libuv/callbacks/callbacks.cc new file mode 100644 index 0000000..3713f04 --- /dev/null +++ b/oss-internship-2020/libuv/callbacks/callbacks.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 "callbacks.h" // NOLINT(build/include) + +#include + +size_t g_iterations = 0; +size_t constexpr kMaxIterations = 1'000'000; +static char g_buffer[1024]; +static uv_buf_t g_iov; + +// Stop the handle if the methods was called kMaxIterations times +void IdleCallback(uv_idle_t* handle) { + ++g_iterations; + if (g_iterations > kMaxIterations) { + std::cout << "IdleCallback was called " << kMaxIterations << " times" + << std::endl; + uv_idle_stop(handle); + } +} + +// Called after some chars have been written +// As soon as writing of these bytes is completed, read more +void OnWrite(uv_fs_t* req) { + if (req->result < 0) { + std::cerr << "Write error: " << uv_strerror(static_cast(req->result)) + << std::endl; + return; + } + // Start reading more after writing these bytes + uv_fs_read(uv_default_loop(), &read_req, open_req.result, &g_iov, 1, -1, + OnRead); +} + +// Called after some chars have been read +// As soon as reading of these bytes is completed, write them +void OnRead(uv_fs_t* req) { + if (req->result < 0) { + std::cerr << "Read error: " << uv_strerror(req->result) << std::endl; + return; + } + if (req->result == 0) { + // No more bytes left, close the loop + uv_fs_t close_req; + uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL); + } else if (req->result > 0) { + // Start writing after reading some bytes + g_iov.len = req->result; + uv_fs_write(uv_default_loop(), &write_req, 1, &g_iov, 1, -1, OnWrite); + } +} + +// Called after the file has been opened +// As soon as opening is completed, read the file +void OnOpen(uv_fs_t* req) { + if (req != &open_req) { + std::cerr << "Open error: req != &open_req" << std::endl; + return; + } + if (req->result < 0) { + std::cerr << "Open error: " << uv_strerror(static_cast(req->result)) + << std::endl; + return; + } + // Initialize uv_buf_t g_buffer + g_iov = uv_buf_init(g_buffer, sizeof(g_buffer)); + // Start reading after opening + uv_fs_read(uv_default_loop(), &read_req, req->result, &g_iov, 1, -1, OnRead); +} + +// Get the integer pointed by handle->data and increment it by one +// Then close the handle +void TimerCallback(uv_timer_t* handle) { + int* data = static_cast( + 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..560d245 --- /dev/null +++ b/oss-internship-2020/libuv/callbacks/callbacks.h @@ -0,0 +1,38 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CALLBACKS_H_ +#define CALLBACKS_H_ + +#include + +extern "C" { + +// idle-basic +void IdleCallback(uv_idle_t* handle); + +// uvcat +uv_fs_t open_req; +uv_fs_t read_req; +uv_fs_t write_req; +void OnWrite(uv_fs_t* req); +void OnRead(uv_fs_t* req); +void OnOpen(uv_fs_t* req); + +// test_callback +void TimerCallback(uv_timer_t* handle); + +} // extern "C" + +#endif // CALLBACKS_H_ diff --git a/oss-internship-2020/libuv/examples/CMakeLists.txt b/oss-internship-2020/libuv/examples/CMakeLists.txt new file mode 100644 index 0000000..301d5ee --- /dev/null +++ b/oss-internship-2020/libuv/examples/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_executable(helloworld + helloworld.cc +) +target_link_libraries(helloworld + uv_a + uv_sapi + sapi::flags + sapi::sapi +) + +add_executable(idle-basic + idle-basic.cc +) +target_link_libraries(idle-basic + uv_a + uv_sapi + sapi::sapi +) + +add_executable(uvcat + uvcat.cc +) +target_link_libraries(uvcat + uv_a + uv_sapi + sapi::sapi +) diff --git a/oss-internship-2020/libuv/examples/README.md b/oss-internship-2020/libuv/examples/README.md new file mode 100644 index 0000000..ec274f9 --- /dev/null +++ b/oss-internship-2020/libuv/examples/README.md @@ -0,0 +1,23 @@ +# LibUV Sandbox Examples + +Each example in this folder is the sandboxed version of a code snippet from +[LibUV's User Guide](https://docs.libuv.org/en/v1.x/guide.html). These examples +perform some basic tasks using LibUV, and can be useful both to understand how +to use LibUV Sandbox, but also to get an idea of how regular and sandboxed code +compare to each other. + +This is the list of examples: + +- **helloworld.cc**: sandboxed version of +[helloworld/main.c](https://docs.libuv.org/en/v1.x/guide/basics.html#hello-world). +It simply starts a loop that exits immediately. It shows how to run a simple +loop in LibUV Sandbox. +- **idle-basic.cc**: sandboxed version of +[idle-basic/main.c](https://docs.libuv.org/en/v1.x/guide/basics.html#handles-and-requests). +Creates an idle watcher that stops the loop after a certain number of +iterations. It shows how a simple callback can be used in LibUV Sandbox. +- **uvcat.cc**: sandboxed version of +[uvcat/main.c](http://docs.libuv.org/en/v1.x/guide/filesystem.html#reading-writing-files). +Takes a single argument, the absolute path of a file, and prints its contents +(it is a simplified version of the command line tootl `cat`). It shows how to +manage various complex callbacks for opening, reading and writing files. diff --git a/oss-internship-2020/libuv/examples/helloworld.cc b/oss-internship-2020/libuv/examples/helloworld.cc new file mode 100644 index 0000000..7d020c3 --- /dev/null +++ b/oss-internship-2020/libuv/examples/helloworld.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 + +#include + +#include "sandboxed_api/util/flag.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVSapiHelloworldSandbox : public uv::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(); + } +}; + +absl::Status HelloWorld() { + // Initialize sandbox2 and sapi + UVSapiHelloworldSandbox sandbox; + SAPI_RETURN_IF_ERROR(sandbox.Init()); + uv::UVApi api(&sandbox); + + // Allocate memory for the uv_loop_t object + void* loop_voidptr; + SAPI_RETURN_IF_ERROR( + sandbox.rpc_channel()->Allocate(sizeof(uv_loop_t), &loop_voidptr)); + sapi::v::RemotePtr loop(loop_voidptr); + + int return_code; + + // Initialize loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_init(&loop)); + if (return_code != 0) { + return absl::UnavailableError("uv_loop_init returned error " + return_code); + } + + std::cout << "The loop is about to quit" << std::endl; + + // Run loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT)); + if (return_code != 0) { + return absl::UnavailableError("uv_run returned error " + return_code); + } + + // Close loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_close(&loop)); + if (return_code != 0) { + return absl::UnavailableError("uv_loop_close returned error " + + return_code); + } + + return absl::OkStatus(); +} + +} // namespace + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + if (absl::Status status = HelloWorld(); !status.ok()) { + LOG(ERROR) << "HelloWorld failed: " << status.ToString(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} 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..14a1e5b --- /dev/null +++ b/oss-internship-2020/libuv/examples/idle-basic.cc @@ -0,0 +1,112 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include + +#include "sandboxed_api/util/flag.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVSapiIdleBasicSandbox : public uv::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(); + } +}; + +absl::Status IdleBasic() { + // Initialize sandbox2 and sapi + UVSapiIdleBasicSandbox sandbox; + SAPI_RETURN_IF_ERROR(sandbox.Init()); + uv::UVApi api(&sandbox); + + // Get remote pointer to the IdleCallback method + void* function_ptr; + SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("IdleCallback", &function_ptr)); + sapi::v::RemotePtr idle_callback(function_ptr); + + // Allocate memory for the uv_idle_t object + void* idle_voidptr; + SAPI_RETURN_IF_ERROR( + sandbox.rpc_channel()->Allocate(sizeof(uv_idle_t), &idle_voidptr)); + sapi::v::RemotePtr idler(idle_voidptr); + + int return_code; + + // Get default loop + SAPI_ASSIGN_OR_RETURN(void* loop_voidptr, api.sapi_uv_default_loop()); + sapi::v::RemotePtr loop(loop_voidptr); + + // Initialize idler + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_idle_init(&loop, &idler)); + if (return_code != 0) { + return absl::UnavailableError("sapi_uv_idle_init returned error " + + return_code); + } + + // Start idler + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_idle_start(&idler, &idle_callback)); + if (return_code != 0) { + return absl::UnavailableError("sapi_uv_idle_start returned error " + + return_code); + } + + // Run loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT)); + if (return_code != 0) { + return absl::UnavailableError("uv_run returned error " + return_code); + } + + // Close idler + sapi::v::NullPtr null_ptr; + SAPI_RETURN_IF_ERROR(api.sapi_uv_close(&idler, &null_ptr)); + + // Close loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_loop_close(&loop)); + // UV_EBUSY is accepted because it is the return code of uv_loop_close + // in the original example + if (return_code != 0 && return_code != UV_EBUSY) { + return absl::UnavailableError("uv_loop_close returned error " + + return_code); + } + + return absl::OkStatus(); +} + +} // namespace + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + if (absl::Status status = IdleBasic(); !status.ok()) { + LOG(ERROR) << "IdleBasic failed: " << status.ToString(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/libuv/examples/uvcat.cc b/oss-internship-2020/libuv/examples/uvcat.cc new file mode 100644 index 0000000..6a73835 --- /dev/null +++ b/oss-internship-2020/libuv/examples/uvcat.cc @@ -0,0 +1,111 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include + +#include "sandboxed_api/util/flag.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVSapiUVCatSandbox : public uv::UVSandbox { + public: + UVSapiUVCatSandbox(std::string filename) : filename(filename) {} + + private: + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder*) override { + return sandbox2::PolicyBuilder() + .AddFile(filename) + .AllowDynamicStartup() + .AllowExit() + .AllowFork() + .AllowFutexOp(FUTEX_WAKE_PRIVATE) + .AllowFutexOp(FUTEX_WAIT_PRIVATE) + .AllowMmap() + .AllowOpen() + .AllowSyscalls({__NR_epoll_create1, __NR_epoll_ctl, __NR_epoll_wait, + __NR_eventfd2, __NR_pipe2, __NR_prlimit64}) + .AllowWrite() + .BuildOrDie(); + } + + std::string filename; +}; + +absl::Status UVCat(std::string filearg) { + // Initialize sandbox2 and sapi + UVSapiUVCatSandbox sandbox(filearg); + SAPI_RETURN_IF_ERROR(sandbox.Init()); + uv::UVApi api(&sandbox); + + // Get remote pointer to the OnOpen method + void* function_ptr; + SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("OnOpen", &function_ptr)); + sapi::v::RemotePtr on_open(function_ptr); + + // Get remote pointer to the open_req variable + void* open_req_voidptr; + SAPI_RETURN_IF_ERROR(sandbox.rpc_channel()->Symbol("open_req", &open_req_voidptr)); + sapi::v::RemotePtr open_req(open_req_voidptr); + + // Get default loop + SAPI_ASSIGN_OR_RETURN(void* loop_voidptr, api.sapi_uv_default_loop()); + sapi::v::RemotePtr loop(loop_voidptr); + + int return_code; + + // Open file using the OnOpen callback (which will also read and print it) + sapi::v::ConstCStr filename(filearg.c_str()); + SAPI_ASSIGN_OR_RETURN(return_code, + api.sapi_uv_fs_open(&loop, &open_req, filename.PtrBefore(), + O_RDONLY, 0, &on_open)); + if (return_code != 0) { + return absl::UnavailableError("uv_fs_open returned error " + return_code); + } + + // Run loop + SAPI_ASSIGN_OR_RETURN(return_code, api.sapi_uv_run(&loop, UV_RUN_DEFAULT)); + if (return_code != 0) { + return absl::UnavailableError("uv_run returned error " + return_code); + } + + // Cleanup the request + SAPI_RETURN_IF_ERROR(api.sapi_uv_fs_req_cleanup(&open_req)); + + return absl::OkStatus(); +} + +} // namespace + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + if (argc != 2) { + LOG(ERROR) << "wrong number of arguments (1 expected)"; + return EXIT_FAILURE; + } + + if (absl::Status status = UVCat(argv[1]); !status.ok()) { + LOG(ERROR) << "UVCat failed: " << status.ToString(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} 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..c220db5 --- /dev/null +++ b/oss-internship-2020/libuv/generator/wrapper_generator.py @@ -0,0 +1,296 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Script generating a wrapper API for LibUV. + +Note: This scriptis highly specific to LibUV's source code and does not +generalize to any other library +""" + +import os +import re +import sys +from typing import List + + +def get_var_type(string: str) -> str: + """Gets the type from an argument variable. + + Args: + string: Input variable declaration + + Returns: + The type of the argument variable as a string, e.g. "int x" -> "int". + """ + + var = string.strip() + + # Unnamed variable + if var in ("void", "...") or var[-1] == "*": + return var + + return " ".join(var.split(" ")[:-1]).strip() + + +def get_var_name(string: str) -> str: + """Gets the name from an argument variable. + + Args: + string: Input variable declaration + + Returns: + The name of the arguments variable as a string, e.g. "int x" -> "x". + """ + + var = string.strip() + + # Not an actual variable + if var in ("void", "..."): + return "" + + # Unnamed variable, use an arbitrary name + if var[-1] == "*": + return var.split("_")[1] + + return var.split(" ")[-1].strip() + + +def fix_method_type(string: str) -> str: + """Fixes the method type. + + Args: + string: A parameter type declaration + + Returns: + A fixed up string replacing pointers to concrete types with pointers to + void, e.g. "const int*" -> "const void*". + """ + + method_type = string.strip() + + # Const pointer + if "*" in method_type and "const" in method_type: + return "const void*" + + # Regular pointer + if "*" in method_type: + return "void*" + + # Not a pointer + return method_type + + +def fix_argument(string: str) -> str: + """Fixes an argument. + + Args: + string: An argument type to fix + + Returns: + The fixed up argument as a string, e.g. "const int* x" -> "const void* x". + """ + + arg_type = get_var_type(string) + arg_name = get_var_name(string) + + # Array argument, becomes a pointer + if "[" in arg_name: + arg_type += "*" + arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1] + + # Pointer (in LibUV, types endind in "_cb" or "_func" are pointers) + if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type: + if "const" in arg_type: + return "const void* " + arg_name + return "void* " + arg_name + + # Not a pointer + return arg_type + " " + arg_name + + +def fix_call_argument(string: str) -> str: + """Fixes an argument in a call the orignal method. + + Args: + string: A method call argument + + Returns: + The fixed call argument, e.g. "const int* x" -> + "reinterpret_cast(x)". + """ + + arg_type = get_var_type(string) + arg_name = get_var_name(string) + + # Array argument, becomes a pointer + if "[" in arg_name: + arg_type += "*" + arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1] + + # Pointer (in LibUV, types endind in "_cb" or "_func" are pointers) + if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type: + return "reinterpret_cast<" + arg_type + ">(" + arg_name + ")" + + # Not a pointer + return arg_name + + +def read_file(filename: str) -> str: + """Returns contents of filename as a string. + + Args: + filename: The name of the file to read + + Returns: + The contents of the file as a string. + """ + + file = open(filename, "r") + return str(file.read()) + + +def clean_file(text: str) -> str: + """Prepares the file for parsing. + + In particular, removes comments and macros from text + Additionally, moves pointer asterisks next to its type + + Args: + text: The contents of the text file to prepare + + Returns: + The cleaned up file contents. + """ + + result = text + result = re.sub(r"//.*?\n", "", result, flags=re.S) + result = re.sub(r"/\*.*?\*/", "", result, flags=re.S) + result = re.sub(r"#.*?\n", "", result, flags=re.S) + result = result.replace(" *", "* ") + return result + + +def get_signatures(text: str) -> str: + """Gets the signatures of all the methods in the header. + + Note: This method only works on a certain version of LibUV's header. + + Args: + text: The contents of the header file + + Returns: + The extracted method signatures. + """ + + signatures = [x.split(";")[0].strip() for x in text.split("UV_EXTERN")[1:]] + method_types = [ + " ".join(s.split("(")[0].split(" ")[:-1]).strip() for s in signatures + ] + names = [s.split("(")[0].split(" ")[-1].strip() for s in signatures] + arguments = [s.split("(")[1][:-1] for s in signatures] + arguments_lists = [[x.strip() for x in a.split(",")] for a in arguments] + return zip(method_types, names, arguments_lists) + + +def append_method(method_type: str, name: str, arguments_list: List[str], + header: List[str], source: List[str]) -> None: + """Writes the method to the header and the source list of lines. + + Args: + method_type: The return type of the method as a string + name: The name of the method + arguments_list: A list of method aruments + header: A list that receives method wrapper declarations + source: A list that receives the declarations of the method wrappers + """ + + header.append( + fix_method_type(method_type) + " sapi_" + name + "(" + + ", ".join(map(fix_argument, arguments_list)) + ");") + source.append( + fix_method_type(method_type) + " sapi_" + name + "(" + + ", ".join(map(fix_argument, arguments_list)) + ") {\n" + " return " + + name + "(" + ", ".join(map(fix_call_argument, arguments_list)) + ");\n" + + "}") + + +def append_text(text: str, file: List[str]) -> None: + """Writes text to file list of lines. + + Useful for additional methods, includes, extern "C"... + + Args: + text: The text to append to the file + file: A list receiving file lines + """ + + file.append(text) + + +def generate_wrapper() -> None: + """Generates the wrapper.""" + + header_file = open(sys.argv[2], "w") + source_file = open(sys.argv[3], "w") + + text = read_file(sys.argv[1]) + text = clean_file(text) + signatures = get_signatures(text) + + header = [] + source = [] + + append_text("#include ", header) + 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 + append_method(method_type, name, arguments_list, header, source) + + # Add sapi_uv_once (uv_once uses a differnet kind of callback) + append_text("void sapi_uv_once(void* guard, void (*callback)(void));", header) + append_text( + "void sapi_uv_once(void* guard, void (*callback)(void)) {\n" + + " return uv_once(reinterpret_cast(guard)," + "callback);\n" + + "}", source) + + # Add sapi_uv_loop_configure (uv_loop_configure is variadic) + append_text( + "int sapi_uv_loop_configure(void* loop, uv_loop_option option)" + ";", + header) + append_text( + "int sapi_uv_loop_configure(void* loop, uv_loop_option option)" + + " {\n return uv_loop_configure(" + + "reinterpret_cast(loop), option);\n" + "}", source) + + # Add sapi_uv_loop_configure_int (uv_loop_configure is variadic) + append_text( + "int sapi_uv_loop_configure_int(void* loop, " + + "uv_loop_option option, int ap);", header) + append_text( + "int sapi_uv_loop_configure_int(void* loop, " + + "uv_loop_option option, int ap) {\n" + " return uv_loop_configure(" + + "reinterpret_cast(loop), option, ap);\n}", source) + + append_text("} // extern \"C\"\n", header) + + header_file.write("\n\n".join(header)) + source_file.write("\n\n".join(source)) + + +generate_wrapper() 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..77eaf3e --- /dev/null +++ b/oss-internship-2020/libuv/tests/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(GoogleTest) + +add_executable(tests + test_array.cc + test_callback.cc + test_error.cc + test_loop.cc + test_os.cc +) + +target_link_libraries(tests + gmock + gtest + gtest_main + uv_a + uv_sapi + sapi::sapi +) + +gtest_discover_tests(tests) 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..46cf897 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_array.cc @@ -0,0 +1,71 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVTestArraySapiSandbox : public uv::UVSandbox { + private: + std::unique_ptr 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); +} + +} // namespace 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..004e923 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_callback.cc @@ -0,0 +1,142 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVTestCallbackSapiSandbox : public uv::UVSandbox { + private: + std::unique_ptr 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()); +} + +} // namespace 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..a2e228d --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_error.cc @@ -0,0 +1,94 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVTestErrorSapiSandbox : public uv::UVSandbox { + private: + std::unique_ptr 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); +} + +} // namespace 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..6433d78 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_loop.cc @@ -0,0 +1,106 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVTestLoopSapiSandbox : public uv::UVSandbox { + private: + std::unique_ptr 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()); +} + +} // namespace 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..28665e1 --- /dev/null +++ b/oss-internship-2020/libuv/tests/test_os.cc @@ -0,0 +1,137 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" +#include "uv_sapi.sapi.h" // NOLINT(build/include) + +namespace { + +class UVTestOSSapiSandbox : public uv::UVSandbox { + private: + std::unique_ptr 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); +} + +} // namespace