├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── CMakeLists.txt ├── fail-alloc-page.cc ├── fail-bio.cc ├── fail-ext4-mount.cc ├── fail-mount.cc └── time-chaos.cc ├── progs ├── clock_gettime └── gettimeofday ├── proto └── bpfki.proto └── src ├── CMakeLists.txt ├── bpfki.cc ├── bpfki.h ├── main.cc ├── service.cc └── service.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Project files 35 | .ccls-cache/ 36 | build/ 37 | compile_commands.json 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11.4) 2 | project(bpfki) 3 | 4 | include(ExternalProject) 5 | 6 | # c-ares 7 | set(CARES_VERSION 1_14_0) 8 | set(CARES_URL URL https://github.com/c-ares/c-ares/archive/cares-${CARES_VERSION}.zip) 9 | 10 | ExternalProject_Add(c-ares 11 | PREFIX c-ares 12 | URL ${CARES_URL} 13 | CMAKE_CACHE_ARGS 14 | -DCARES_SHARED:BOOL=OFF 15 | -DCARES_STATIC:BOOL=ON 16 | -DCARES_STATIC_PIC:BOOL=ON 17 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares 18 | ) 19 | 20 | # protobuf 21 | set(PROTOBUF_VERSION v3.9.2) 22 | set(PROTOBUF_URL https://github.com/google/protobuf/archive/${PROTOBUF_VERSION}.zip) 23 | 24 | ExternalProject_Add(protobuf 25 | PREFIX protobuf 26 | URL ${PROTOBUF_URL} 27 | SOURCE_SUBDIR cmake 28 | CMAKE_CACHE_ARGS 29 | -Dprotobuf_BUILD_TESTS:BOOL=OFF 30 | -Dprotobuf_WITH_ZLIB:BOOL=OFF 31 | -Dprotobuf_MSVC_STATIC_RUNTIME:BOOL=OFF 32 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/protobuf 33 | ) 34 | 35 | if (EXISTS "/etc/lsb-release") 36 | set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/lib/cmake/protobuf") 37 | elseif (EXISTS "/etc/redhat-release") 38 | set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/lib64/cmake/protobuf") 39 | endif() 40 | 41 | set(PROTOC "${CMAKE_CURRENT_BINARY_DIR}/protobuf/bin/protoc") 42 | 43 | # zlib 44 | set(ZLIB_VERSION 1.3.1) 45 | set(ZLIB_URL https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz) 46 | 47 | ExternalProject_Add(zlib 48 | PREFIX zlib 49 | URL ${ZLIB_URL} 50 | CMAKE_CACHE_ARGS 51 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/zlib 52 | ) 53 | 54 | # gRPC 55 | set(GRPC_VERSION v1.24.3) 56 | set(GRPC_URL https://github.com/grpc/grpc/archive/${GRPC_VERSION}.zip) 57 | 58 | ExternalProject_Add(grpc 59 | PREFIX grpc 60 | URL ${GRPC_URL} 61 | DEPENDS c-ares protobuf zlib 62 | CMAKE_CACHE_ARGS 63 | -DgRPC_INSTALL:BOOL=ON 64 | -DgRPC_BUILD_TESTS:BOOL=OFF 65 | -DgRPC_PROTOBUF_PROVIDER:STRING=package 66 | -DgRPC_PROTOBUF_PACKAGE_TYPE:STRING=CONFIG 67 | -DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} 68 | -DgRPC_ZLIB_PROVIDER:STRING=package 69 | -DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib 70 | -DgRPC_CARES_PROVIDER:STRING=package 71 | -Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares 72 | -DgRPC_SSL_PROVIDER:STRING=package 73 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc 74 | ) 75 | 76 | # gflags 77 | set(GFLAGS_VERSION v2.2.2) 78 | set(GFLAGS_URL https://github.com/gflags/gflags/archive/${GFLAGS_VERSION}.zip) 79 | 80 | ExternalProject_Add(gflags 81 | PREFIX gflags 82 | URL ${GFLAGS_URL} 83 | CMAKE_CACHE_ARGS 84 | -Dgflags_INSTALL:BOOL=ON 85 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/gflags 86 | ) 87 | 88 | # spdlog 89 | set(SPDLOG_VERSION v1.5.0) 90 | set(SPDLOG_URL https://github.com/gabime/spdlog/archive/${SPDLOG_VERSION}.zip) 91 | 92 | ExternalProject_Add(spdlog 93 | PREFIX spdlog 94 | URL ${SPDLOG_URL} 95 | CMAKE_CACHE_ARGS 96 | -Dspdlog_INSTALL:BOOL=ON 97 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/spdlog 98 | ) 99 | 100 | # abseilcpp 101 | set(ABSL_VERSION 20210324.2) 102 | set(ABSL_URL https://github.com/abseil/abseil-cpp/archive/${ABSL_VERSION}.zip) 103 | ExternalProject_Add(absl 104 | PREFIX absl 105 | URL ${ABSL_URL} 106 | CMAKE_CACHE_ARGS 107 | -Dabsl_INSTALL:BOOL=ON 108 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/absl 109 | ) 110 | 111 | if (EXISTS "/etc/lsb-release") 112 | set(_FINDPACKAGE_ABSL_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/absl/lib/cmake/absl") 113 | elseif (EXISTS "/etc/redhat-release") 114 | set(_FINDPACKAGE_ABSL_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/absl/lib64/cmake/absl") 115 | endif() 116 | 117 | # examples 118 | ExternalProject_Add(examples 119 | PREFIX examples 120 | SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/examples" 121 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/examples" 122 | INSTALL_COMMAND "" 123 | CMAKE_CACHE_ARGS 124 | -DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} 125 | -Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares 126 | -DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib 127 | -DgRPC_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc/lib/cmake/grpc 128 | -Dgflags_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/gflags/lib/cmake/gflags 129 | -Dspdlog_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/spdlog/lib/cmake/spdlog 130 | -Dabsl_DIR:PATH=${_FINDPACKAGE_ABSL_CONFIG_DIR} 131 | DEPENDS protobuf grpc spdlog gflags absl 132 | ) 133 | 134 | # bpfki 135 | ExternalProject_Add(bpfki 136 | PREFIX bpfki 137 | SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src" 138 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/bin" 139 | CMAKE_CACHE_ARGS 140 | -DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} 141 | -Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares 142 | -DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib 143 | -DgRPC_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc/lib/cmake/grpc 144 | -Dgflags_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/gflags/lib/cmake/gflags 145 | -Dspdlog_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/spdlog/lib/cmake/spdlog 146 | -Dabsl_DIR:PATH=${_FINDPACKAGE_ABSL_CONFIG_DIR} 147 | DEPENDS protobuf grpc spdlog gflags absl 148 | ) 149 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bpfki 2 | 3 | A BPF-based kernel fault injection service. 4 | 5 | ## Prerequisites 6 | 7 | Kernel with `bpf_override_return` and `should_fail_page_alloc`, 8 | `should_fail_slab`, `should_fail_bio` for BPF error injection support. 9 | 10 | ## Building 11 | 12 | ```sh 13 | mkdir build && cd build 14 | cmake ../ 15 | make -j 16 | ``` 17 | 18 | ## Using 19 | 20 | `cd bin && nohup ./bpfki &` 21 | 22 | ## Interface 23 | 24 | ```c++ 25 | syntax = "proto3"; 26 | 27 | package bpfki; 28 | 29 | message BumpTimeRequest { 30 | uint32 pid = 1; 31 | uint32 tid = 2; 32 | int32 second = 3; 33 | int32 subsecond = 4; 34 | float probability = 5; 35 | } 36 | 37 | message FailKernRequest { 38 | uint32 pid = 1; 39 | uint32 tid = 2; 40 | enum FAILTYPE { 41 | SLAB = 0; 42 | PAGE = 1; 43 | BIO = 2; 44 | } 45 | FAILTYPE ftype = 3; 46 | repeated string headers = 4; 47 | message frame { 48 | string funcname = 1; 49 | string parameters = 2; 50 | string predicate = 3; 51 | } 52 | repeated frame callchain = 5; 53 | float probability = 6; 54 | uint32 times = 7; 55 | } 56 | 57 | message StatusResponse { 58 | int32 ret = 1; 59 | string msg = 2; 60 | } 61 | 62 | service BPFKIService { 63 | rpc SetTimeVal(BumpTimeRequest) returns (StatusResponse) {} 64 | rpc RecoverTimeVal(BumpTimeRequest) returns (StatusResponse) {} 65 | rpc SetTimeSpec(BumpTimeRequest) returns (StatusResponse) {} 66 | rpc RecoverTimeSpec(BumpTimeRequest) returns (StatusResponse) {} 67 | rpc FailMMOrBIO(FailKernRequest) returns (StatusResponse) {} 68 | rpc RecoverMMOrBIO(FailKernRequest) returns (StatusResponse) {} 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11.4) 2 | project(examples) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 5 | 6 | # Find 3rd parties 7 | set(protobuf_MODULE_COMPATIBLE TRUE) 8 | find_package(Protobuf CONFIG REQUIRED) 9 | message(STATUS "Using protobuf ${protobuf_VERSION}") 10 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 11 | set(_PROTOBUF_PROTOC $) 12 | 13 | find_package(gRPC CONFIG REQUIRED) 14 | message(STATUS "Using gRPC ${gRPC_VERSION}") 15 | set(_GRPC_GRPCPP_UNSECURE gRPC::grpc++_unsecure) 16 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 17 | 18 | find_package(gflags CONFIG REQUIRED) 19 | message(STATUS "Using gflags ${gflags_VERSION}") 20 | set(_GFLAGS_STATIC gflags) 21 | 22 | find_package(spdlog CONFIG REQUIRED) 23 | message(STATUS "Using spdlog ${spdlog_VERSION}") 24 | set(_SPDLOG_STATIC spdlog::spdlog) 25 | 26 | find_package(absl CONFIG REQUIRED) 27 | message(STATUS "Using absl ${absl_VERSION}") 28 | set(_ABSL_STATIC absl::base absl::strings absl::str_format) 29 | 30 | # Proto file 31 | get_filename_component(bpfki_proto "../proto/bpfki.proto" ABSOLUTE) 32 | get_filename_component(bpfki_proto_path "${bpfki_proto}" PATH) 33 | 34 | # Generated sources 35 | set(bpfki_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.pb.cc") 36 | set(bpfki_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.pb.h") 37 | set(bpfki_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.grpc.pb.cc") 38 | set(bpfki_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.grpc.pb.h") 39 | add_custom_command( 40 | OUTPUT "${bpfki_proto_srcs}" "${bpfki_proto_hdrs}" "${bpfki_grpc_srcs}" "${bpfki_grpc_hdrs}" 41 | COMMAND ${_PROTOBUF_PROTOC} 42 | ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" 43 | --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" 44 | -I "${bpfki_proto_path}" 45 | --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" 46 | "${bpfki_proto}" 47 | DEPENDS "${bpfki_proto}") 48 | 49 | # Include generated *.pb.h files 50 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 51 | 52 | # Targets greeter_[async_](client|server) 53 | foreach(_target time-chaos fail-bio fail-mount fail-ext4-mount fail-alloc-page) 54 | add_executable(${_target} "${_target}.cc" 55 | ${bpfki_proto_srcs} 56 | ${bpfki_grpc_srcs}) 57 | 58 | target_link_libraries(${_target} 59 | ${_GRPC_GRPCPP_UNSECURE} 60 | ${_PROTOBUF_LIBPROTOBUF} 61 | ${_SPDLOG_STATIC} 62 | ${_GFLAGS_STATIC} 63 | ${_ABSL_STATIC}) 64 | endforeach() 65 | -------------------------------------------------------------------------------- /examples/fail-alloc-page.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpfki.grpc.pb.h" 11 | 12 | class BPFKIClient final: public bpfki::BPFKIService::Service { 13 | public: 14 | BPFKIClient(std::shared_ptr channel, 15 | const std::shared_ptr& logger) 16 | : stub_(bpfki::BPFKIService::NewStub(channel)), logger_(logger) {} 17 | 18 | void FailMMOrBIO(uint32_t pid, uint32_t tid, float pct) { 19 | bpfki::FailKernRequest req; 20 | grpc::ClientContext ctx; 21 | bpfki::StatusResponse resp; 22 | 23 | req.set_pid(pid); 24 | req.set_tid(tid); 25 | req.set_probability(pct); 26 | req.set_ftype(bpfki::FailKernRequest_FAILTYPE_PAGE); 27 | stub_->FailMMOrBIO(&ctx, req, &resp); 28 | } 29 | 30 | void RecoverMMOrBIO(uint32_t pid, uint32_t tid) { 31 | bpfki::FailKernRequest req; 32 | grpc::ClientContext ctx; 33 | bpfki::StatusResponse resp; 34 | 35 | req.set_pid(pid); 36 | req.set_tid(tid); 37 | stub_->RecoverMMOrBIO(&ctx, req, &resp); 38 | } 39 | 40 | private: 41 | std::unique_ptr stub_; 42 | const std::shared_ptr& logger_; 43 | }; 44 | 45 | DEFINE_uint32(pid, 0, "root ns pid"); 46 | DEFINE_uint32(tid, 0, "root ns tid"); 47 | DEFINE_int32(pct, 0, "probability"); 48 | DEFINE_int32(action, 1, " (1: inject, 0: clear)"); 49 | 50 | int main(int argc, char *argv[]) { 51 | gflags::ParseCommandLineFlags(&argc, &argv, true); 52 | auto logger = spdlog::stdout_color_mt("console"); 53 | BPFKIClient cli(grpc::CreateChannel( 54 | "localhost:50051", grpc::InsecureChannelCredentials()), 55 | logger); 56 | if (FLAGS_action == 1) 57 | cli.FailMMOrBIO(FLAGS_pid, FLAGS_tid, FLAGS_pct); 58 | else 59 | cli.RecoverMMOrBIO(FLAGS_pid, FLAGS_tid); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /examples/fail-bio.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "bpfki.grpc.pb.h" 10 | 11 | class BPFKIClient final: public bpfki::BPFKIService::Service { 12 | public: 13 | BPFKIClient(std::shared_ptr channel, 14 | const std::shared_ptr& logger) 15 | : stub_(bpfki::BPFKIService::NewStub(channel)), logger_(logger) {} 16 | 17 | void FailMMOrBIO(void) { 18 | bpfki::FailKernRequest req; 19 | grpc::ClientContext ctx; 20 | bpfki::StatusResponse resp; 21 | 22 | req.add_headers("linux/blkdev.h"); 23 | req.set_ftype(bpfki::FailKernRequest_FAILTYPE_BIO); 24 | auto frame = req.add_callchain(); 25 | frame->set_predicate(R"(({struct gendisk *d = bio->bi_disk; 26 | struct disk_part_tbl *tbl = d->part_tbl; struct hd_struct **parts = (void *)tbl + 27 | sizeof(struct disk_part_tbl); struct hd_struct **partp = parts + bio->bi_partno; 28 | struct hd_struct *p = *partp; dev_t disk = p->__dev.devt; disk == 29 | MKDEV(254,16);}) && bio->bi_iter.bi_sector == 128)"); 30 | stub_->FailMMOrBIO(&ctx, req, &resp); 31 | } 32 | 33 | private: 34 | std::unique_ptr stub_; 35 | const std::shared_ptr& logger_; 36 | }; 37 | 38 | 39 | int main(int argc, char *argv[]) { 40 | auto logger = spdlog::stdout_color_mt("console"); 41 | BPFKIClient cli(grpc::CreateChannel( 42 | "localhost:50051", grpc::InsecureChannelCredentials()), 43 | logger); 44 | cli.FailMMOrBIO(); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/fail-ext4-mount.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpfki.grpc.pb.h" 11 | 12 | using namespace std::chrono_literals; 13 | 14 | class BPFKIClient final: public bpfki::BPFKIService::Service { 15 | public: 16 | BPFKIClient(std::shared_ptr channel, 17 | const std::shared_ptr& logger) 18 | : stub_(bpfki::BPFKIService::NewStub(channel)), logger_(logger) {} 19 | 20 | void FailMMOrBIO(uint32_t pid, uint32_t tid, float pct) { 21 | bpfki::FailKernRequest req; 22 | grpc::ClientContext ctx; 23 | bpfki::StatusResponse resp; 24 | 25 | req.set_pid(pid); 26 | req.set_tid(tid); 27 | req.set_probability(pct); 28 | req.set_ftype(bpfki::FailKernRequest_FAILTYPE_SLAB); 29 | auto frame0 = req.add_callchain(); 30 | frame0->set_funcname("ext4_mount"); 31 | auto frame1 = req.add_callchain(); 32 | frame1->set_funcname("mount_subtree"); 33 | stub_->FailMMOrBIO(&ctx, req, &resp); 34 | } 35 | 36 | void RecoverMMOrBIO(uint32_t pid, uint32_t tid) { 37 | bpfki::FailKernRequest req; 38 | grpc::ClientContext ctx; 39 | bpfki::StatusResponse resp; 40 | 41 | req.set_pid(pid); 42 | req.set_tid(tid); 43 | stub_->RecoverMMOrBIO(&ctx, req, &resp); 44 | } 45 | 46 | private: 47 | std::unique_ptr stub_; 48 | const std::shared_ptr& logger_; 49 | }; 50 | 51 | DEFINE_uint32(pid, 0, "root ns pid"); 52 | DEFINE_uint32(tid, 0, "root ns tid"); 53 | DEFINE_int32(pct, 0, "probability"); 54 | DEFINE_int32(action, 1, " (1: inject, 0: clear)"); 55 | 56 | int main(int argc, char *argv[]) { 57 | gflags::ParseCommandLineFlags(&argc, &argv, true); 58 | auto logger = spdlog::stdout_color_mt("console"); 59 | BPFKIClient cli(grpc::CreateChannel( 60 | "localhost:50051", grpc::InsecureChannelCredentials()), 61 | logger); 62 | if (FLAGS_action == 1) 63 | cli.FailMMOrBIO(FLAGS_pid, FLAGS_tid, FLAGS_pct); 64 | else 65 | cli.RecoverMMOrBIO(FLAGS_pid, FLAGS_tid); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /examples/fail-mount.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpfki.grpc.pb.h" 11 | 12 | using namespace std::chrono_literals; 13 | 14 | class BPFKIClient final: public bpfki::BPFKIService::Service { 15 | public: 16 | BPFKIClient(std::shared_ptr channel, 17 | const std::shared_ptr& logger) 18 | : stub_(bpfki::BPFKIService::NewStub(channel)), logger_(logger) {} 19 | 20 | void FailMMOrBIO(uint32_t pid, uint32_t tid, float pct) { 21 | bpfki::FailKernRequest req; 22 | grpc::ClientContext ctx; 23 | bpfki::StatusResponse resp; 24 | 25 | req.set_pid(pid); 26 | req.set_tid(tid); 27 | req.set_probability(pct); 28 | req.set_ftype(bpfki::FailKernRequest_FAILTYPE_SLAB); 29 | auto frame = req.add_callchain(); 30 | frame->set_funcname("__x64_sys_mount"); 31 | stub_->FailMMOrBIO(&ctx, req, &resp); 32 | } 33 | 34 | void RecoverMMOrBIO(uint32_t pid, uint32_t tid) { 35 | bpfki::FailKernRequest req; 36 | grpc::ClientContext ctx; 37 | bpfki::StatusResponse resp; 38 | 39 | req.set_pid(pid); 40 | req.set_tid(tid); 41 | stub_->RecoverMMOrBIO(&ctx, req, &resp); 42 | } 43 | 44 | private: 45 | std::unique_ptr stub_; 46 | const std::shared_ptr& logger_; 47 | }; 48 | 49 | DEFINE_uint32(pid, 0, "root ns pid"); 50 | DEFINE_uint32(tid, 0, "root ns tid"); 51 | DEFINE_int32(pct, 0, "probability"); 52 | DEFINE_int32(action, 1, " (1: inject, 0: clear)"); 53 | 54 | int main(int argc, char *argv[]) { 55 | gflags::ParseCommandLineFlags(&argc, &argv, true); 56 | auto logger = spdlog::stdout_color_mt("console"); 57 | BPFKIClient cli(grpc::CreateChannel( 58 | "localhost:50051", grpc::InsecureChannelCredentials()), 59 | logger); 60 | if (FLAGS_action == 1) 61 | cli.FailMMOrBIO(FLAGS_pid, FLAGS_tid, FLAGS_pct); 62 | else 63 | cli.RecoverMMOrBIO(FLAGS_pid, FLAGS_tid); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /examples/time-chaos.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpfki.grpc.pb.h" 11 | 12 | using namespace std::chrono_literals; 13 | 14 | class BPFKIClient final: public bpfki::BPFKIService::Service { 15 | public: 16 | BPFKIClient(std::shared_ptr channel, 17 | const std::shared_ptr& logger) 18 | : stub_(bpfki::BPFKIService::NewStub(channel)), logger_(logger) {} 19 | 20 | void SetTime(int type, uint32_t pid, uint32_t tid, 21 | int32_t sec, int32_t ssec, float pct) { 22 | bpfki::BumpTimeRequest req; 23 | req.set_pid(pid); 24 | req.set_tid(tid); 25 | req.set_second(sec); 26 | req.set_subsecond(ssec); 27 | req.set_probability(pct); 28 | grpc::ClientContext ctx; 29 | bpfki::StatusResponse resp; 30 | grpc::Status status; 31 | if (type == 0) 32 | status = stub_->SetTimeVal(&ctx, req, &resp); 33 | else if (type == 1) 34 | status = stub_->SetTimeSpec(&ctx, req, &resp); 35 | else 36 | logger_->error("Unknown"); 37 | 38 | if (!status.ok()) 39 | logger_->error("SetTime PRC failed"); 40 | } 41 | 42 | void RecoverTime(int type, uint32_t pid, uint32_t tid) { 43 | bpfki::BumpTimeRequest req; 44 | req.set_pid(pid); 45 | req.set_tid(tid); 46 | grpc::ClientContext ctx; 47 | bpfki::StatusResponse resp; 48 | grpc::Status status; 49 | if (type == 0) 50 | status = stub_->RecoverTimeVal(&ctx, req, &resp); 51 | else if (type == 1) 52 | status = stub_->RecoverTimeSpec(&ctx, req, &resp); 53 | else 54 | logger_->error("Unknown"); 55 | if (!status.ok()) 56 | logger_->error("Recover PRC failed"); 57 | } 58 | 59 | private: 60 | std::unique_ptr stub_; 61 | const std::shared_ptr& logger_; 62 | }; 63 | 64 | DEFINE_uint32(pid, 0, "root ns pid"); 65 | DEFINE_uint32(tid, 0, "root ns tid"); 66 | DEFINE_int32(sec, 0, "skew sec"); 67 | DEFINE_int32(ssec, 0, "skew ssec"); 68 | DEFINE_int32(pct, 0, "probability"); 69 | DEFINE_int32(type, 0, "time syscall (0: gettimeofday, 1: clock_gettime)"); 70 | 71 | int main(int argc, char *argv[]) { 72 | gflags::ParseCommandLineFlags(&argc, &argv, true); 73 | auto logger = spdlog::stdout_color_mt("console"); 74 | BPFKIClient cli(grpc::CreateChannel( 75 | "localhost:50051", grpc::InsecureChannelCredentials()), 76 | logger); 77 | cli.SetTime(FLAGS_type, FLAGS_pid, FLAGS_tid, 78 | FLAGS_sec, FLAGS_ssec, FLAGS_pct); 79 | std::this_thread::sleep_for(60s); 80 | cli.RecoverTime(FLAGS_type, FLAGS_pid, FLAGS_tid); 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /progs/clock_gettime: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DEBUG 1 5 | 6 | #define NANOS_PER_SEC 1000000000 7 | 8 | #define MAX_JMP_SEQ 32 9 | 10 | struct clock_filter { 11 | long sec; 12 | long nsec; 13 | long pct; 14 | struct timespec *ts; 15 | struct timespec nts; 16 | }; 17 | 18 | BPF_PROG_ARRAY(balance, 4); 19 | BPF_PROG_ARRAY(write, 2); 20 | BPF_HASH(clock_filters, u32, struct clock_filter); 21 | 22 | int balance_step1(struct pt_regs *ctx) 23 | { 24 | u64 id = bpf_get_current_pid_tgid(); 25 | struct clock_filter *cf = NULL; 26 | u32 pid = id >> 32, tid = id; 27 | struct timespec *nts = NULL; 28 | u16 i = 0; 29 | 30 | cf = clock_filters.lookup(&tid); 31 | if (!cf) { 32 | cf = clock_filters.lookup(&pid); 33 | if (!cf) 34 | return 0; 35 | } 36 | nts = &cf->nts; 37 | #ifdef DEBUG 38 | bpf_trace_printk("balance_timespec_step1\n"); 39 | #endif 40 | for (; i < MAX_JMP_SEQ && nts->tv_nsec <= NANOS_PER_SEC; i++) { 41 | nts->tv_sec -= 1; 42 | nts->tv_nsec += NANOS_PER_SEC; 43 | } 44 | if (i == MAX_JMP_SEQ && nts->tv_nsec <= NANOS_PER_SEC) 45 | balance.call(ctx, 1); 46 | balance.call(ctx, 2); 47 | return 0; 48 | } 49 | 50 | int balance_step2(struct pt_regs *ctx) { 51 | u64 id = bpf_get_current_pid_tgid(); 52 | struct clock_filter *cf = NULL; 53 | u32 pid = id >> 32, tid = id; 54 | struct timespec *nts = NULL; 55 | u16 i = 0; 56 | 57 | cf = clock_filters.lookup(&tid); 58 | if (!cf) { 59 | cf = clock_filters.lookup(&pid); 60 | if (!cf) 61 | return 0; 62 | } 63 | nts = &cf->nts; 64 | #ifdef DEBUG 65 | bpf_trace_printk("balance_timespec_step2\n"); 66 | #endif 67 | for (; i < MAX_JMP_SEQ && NANOS_PER_SEC <= nts->tv_nsec; i++) { 68 | nts->tv_sec += 1; 69 | nts->tv_nsec -= NANOS_PER_SEC; 70 | } 71 | if (i == MAX_JMP_SEQ && NANOS_PER_SEC <= nts->tv_nsec) 72 | balance.call(ctx, 2); 73 | write.call(ctx, 1); 74 | return 0; 75 | } 76 | 77 | int syscall__clock_gettime_entry(struct pt_regs *ctx, 78 | clockid_t cid, struct timespec *ts) 79 | { 80 | u64 id = bpf_get_current_pid_tgid(); 81 | struct clock_filter *cf = NULL; 82 | u32 pid = id >> 32, tid = id; 83 | 84 | cf = clock_filters.lookup(&tid); 85 | if (!cf) { 86 | cf = clock_filters.lookup(&pid); 87 | if (!cf) 88 | return 0; 89 | } 90 | if (!cf) 91 | return 0; 92 | if (cf->pct > 0 && bpf_get_prandom_u32() > cf->pct) 93 | return 0; 94 | #ifdef DEBUG 95 | bpf_trace_printk("clock_gettime_entry\n"); 96 | #endif 97 | cf->ts = ts; 98 | return 0; 99 | } 100 | 101 | int do_ret_sys_clock_gettime_return(struct pt_regs *ctx) 102 | { 103 | u64 id = bpf_get_current_pid_tgid(); 104 | struct clock_filter *cf = NULL; 105 | u32 pid = id >> 32, tid = id; 106 | struct timespec *ts = NULL; 107 | struct timespec nts = {}; 108 | s64 tv_sec = 0; 109 | s64 tv_nsec = 0; 110 | 111 | cf = clock_filters.lookup(&tid); 112 | if (!cf) { 113 | cf = clock_filters.lookup(&pid); 114 | if (!cf) 115 | return 0; 116 | } 117 | if (!cf) 118 | return 0; 119 | 120 | ts = cf->ts; 121 | bpf_probe_read(&tv_nsec, sizeof(tv_nsec), &ts->tv_nsec); 122 | bpf_probe_read(&tv_sec, sizeof(tv_sec), &ts->tv_sec); 123 | nts.tv_nsec = tv_nsec + cf->nsec; 124 | nts.tv_sec = tv_sec + cf->sec; 125 | bpf_probe_read(&cf->nts, sizeof(nts), &nts); 126 | #ifdef DEBUG 127 | bpf_trace_printk("clock_gettime_return\n"); 128 | #endif 129 | balance.call(ctx, 1); 130 | return 0; 131 | } 132 | 133 | int write_step1(struct pt_regs *ctx) 134 | { 135 | u64 id = bpf_get_current_pid_tgid(); 136 | struct clock_filter *cf = NULL; 137 | struct timespec *nts = NULL; 138 | 139 | cf = clock_filters.lookup(&tid); 140 | if (!cf) { 141 | cf = clock_filters.lookup(&pid); 142 | if (!cf) 143 | return 0; 144 | } 145 | if (!cf) 146 | return 0; 147 | 148 | bpf_probe_write_user(cf->ts, &cf->nts, sizeof(cf->nts)); 149 | #ifdef DEBUG 150 | bpf_trace_printk("write_timeval\n"); 151 | #endif 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /progs/gettimeofday: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DEBUG 1 5 | 6 | #define MICROS_PER_SEC 1000000 7 | 8 | #define MAX_JMP_SEQ 32 9 | 10 | struct clock_filter { 11 | long sec; 12 | long usec; 13 | long pct; 14 | struct timeval *tv; 15 | struct timeval ntv; 16 | }; 17 | 18 | BPF_PROG_ARRAY(balance, 4); 19 | BPF_PROG_ARRAY(write, 2); 20 | BPF_HASH(clock_filters, u32, struct clock_filter); 21 | 22 | int balance_step1(struct pt_regs *ctx) 23 | { 24 | u64 id = bpf_get_current_pid_tgid(); 25 | struct clock_filter *cf = NULL; 26 | u32 pid = id >> 32, tid = id; 27 | struct timeval *ntv = NULL; 28 | u16 i = 0; 29 | 30 | cf = clock_filters.lookup(&tid); 31 | if (!cf) { 32 | cf = clock_filters.lookup(&pid); 33 | if (!cf) 34 | return 0; 35 | } 36 | ntv = &cf->ntv; 37 | #ifdef DEBUG 38 | bpf_trace_printk("balance_timeval_step1\n"); 39 | #endif 40 | for (; i < MAX_JMP_SEQ && ntv->tv_sec <= MICROS_PER_SEC; i++) { 41 | ntv->tv_sec -= 1; 42 | ntv->tv_usec += MICROS_PER_SEC; 43 | } 44 | if (i == MAX_JMP_SEQ && ntv->tv_sec <= MICROS_PER_SEC) 45 | balance.call(ctx, 1); 46 | balance.call(ctx, 2); 47 | return 0; 48 | } 49 | 50 | int balance_step2(struct pt_regs *ctx) 51 | { 52 | u64 id = bpf_get_current_pid_tgid(); 53 | struct clock_filter *cf = NULL; 54 | u32 pid = id >> 32, tid = id; 55 | struct timeval *ntv = NULL; 56 | u16 i = 0; 57 | 58 | cf = clock_filters.lookup(&tid); 59 | if (!cf) { 60 | cf = clock_filters.lookup(&pid); 61 | if (!cf) 62 | return 0; 63 | } 64 | ntv = &cf->ntv; 65 | #ifdef DEBUG 66 | bpf_trace_printk("balance_timeval_step2\n"); 67 | #endif 68 | for (; i < MAX_JMP_SEQ && MICROS_PER_SEC <= ntv->tv_usec; i++) { 69 | ntv->tv_sec += 1; 70 | ntv->tv_usec -= MICROS_PER_SEC; 71 | } 72 | if (i == MAX_JMP_SEQ && MICROS_PER_SEC <= ntv->tv_usec) 73 | balance.call(ctx, 1); 74 | write.call(ctx, 1); 75 | return 0; 76 | } 77 | 78 | int syscall__gettimeofday_entry(struct pt_regs *ctx, 79 | struct timeval *tv, struct timezone *tz) 80 | { 81 | u64 id = bpf_get_current_pid_tgid(); 82 | struct clock_filter *cf = NULL; 83 | u32 pid = id >> 32, tid = id; 84 | 85 | cf = clock_filters.lookup(&tid); 86 | if (!cf) { 87 | cf = clock_filters.lookup(&pid); 88 | if (!cf) 89 | return 0; 90 | } 91 | if (!cf) 92 | return 0; 93 | if (cf->pct > 0 && bpf_get_prandom_u32() > cf->pct) 94 | return 0; 95 | #ifdef DEBUG 96 | bpf_trace_printk("gettimeofday_entry\n"); 97 | #endif 98 | cf->tv = tv; 99 | return 0; 100 | } 101 | 102 | int do_ret_sys_gettimeofday_return(struct pt_regs *ctx) 103 | { 104 | u64 id = bpf_get_current_pid_tgid(); 105 | struct clock_filter *cf = NULL; 106 | u32 pid = id >> 32, tid = id; 107 | struct timeval *tv = NULL; 108 | struct timeval ntv = {}; 109 | s64 tv_sec = 0; 110 | s64 tv_usec = 0; 111 | 112 | cf = clock_filters.lookup(&tid); 113 | if (!cf) { 114 | cf = clock_filters.lookup(&pid); 115 | if (!cf) 116 | return 0; 117 | } 118 | if (!cf) 119 | return 0; 120 | 121 | tv = cf->tv; 122 | bpf_probe_read(&tv_usec, sizeof(tv_usec), &tv->tv_usec); 123 | bpf_probe_read(&tv_sec, sizeof(tv_sec), &tv->tv_sec); 124 | ntv.tv_sec = tv_sec + cf->sec; 125 | ntv.tv_usec = tv_usec + cf->usec; 126 | bpf_probe_read(&cf->ntv, sizeof(ntv), &ntv); 127 | #ifdef DEBUG 128 | bpf_trace_printk("gettimeofday_return\n"); 129 | #endif 130 | balance.call(ctx, 1); 131 | return 0; 132 | } 133 | 134 | int write_step1(struct pt_regs *ctx) 135 | { 136 | u64 id = bpf_get_current_pid_tgid(); 137 | struct clock_filter *cf = NULL; 138 | u32 pid = id >> 32, tid = id; 139 | struct timeval *ntv = NULL; 140 | 141 | cf = clock_filters.lookup(&tid); 142 | if (!cf) { 143 | cf = clock_filters.lookup(&pid); 144 | if (!cf) 145 | return 0; 146 | } 147 | if (!cf) 148 | return 0; 149 | 150 | bpf_probe_write_user(cf->tv, &cf->ntv, sizeof(cf->ntv)); 151 | #ifdef DEBUG 152 | bpf_trace_printk("write_timeval\n"); 153 | #endif 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /proto/bpfki.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package bpfki; 4 | 5 | message BumpTimeRequest { 6 | uint32 pid = 1; 7 | uint32 tid = 2; 8 | int32 second = 3; 9 | int32 subsecond = 4; 10 | float probability = 5; 11 | } 12 | 13 | message FailKernRequest { 14 | uint32 pid = 1; 15 | uint32 tid = 2; 16 | enum FAILTYPE { 17 | SLAB = 0; 18 | PAGE = 1; 19 | BIO = 2; 20 | } 21 | FAILTYPE ftype = 3; 22 | repeated string headers = 4; 23 | message frame { 24 | string funcname = 1; 25 | string parameters = 2; 26 | string predicate = 3; 27 | } 28 | repeated frame callchain = 5; 29 | float probability = 6; 30 | uint32 times = 7; 31 | } 32 | 33 | message FailSyscallRequest { 34 | uint32 pid = 1; 35 | uint32 tid = 2; 36 | repeated string methods = 3; 37 | uint32 err = 4; 38 | float probability = 5; 39 | } 40 | 41 | message StatusResponse { 42 | int32 ret = 1; 43 | string msg = 2; 44 | } 45 | 46 | service BPFKIService { 47 | rpc SetTimeVal(BumpTimeRequest) returns (StatusResponse) {} 48 | rpc RecoverTimeVal(BumpTimeRequest) returns (StatusResponse) {} 49 | rpc SetTimeSpec(BumpTimeRequest) returns (StatusResponse) {} 50 | rpc RecoverTimeSpec(BumpTimeRequest) returns (StatusResponse) {} 51 | rpc FailMMOrBIO(FailKernRequest) returns (StatusResponse) {} 52 | rpc RecoverMMOrBIO(FailKernRequest) returns (StatusResponse) {} 53 | rpc FailSyscall(FailSyscallRequest) returns (StatusResponse) {} 54 | rpc RecoverSyscall(FailSyscallRequest) returns (StatusResponse) {} 55 | } 56 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11.4) 2 | project(bpfki) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/include/bcc/compat -std=c++14") 5 | 6 | # Find 3rd parties 7 | set(protobuf_MODULE_COMPATIBLE TRUE) 8 | find_package(Protobuf CONFIG REQUIRED) 9 | message(STATUS "Using protobuf ${protobuf_VERSION}") 10 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 11 | set(_PROTOBUF_PROTOC $) 12 | 13 | find_package(gRPC CONFIG REQUIRED) 14 | message(STATUS "Using gRPC ${gRPC_VERSION}") 15 | set(_GRPC_GRPCPP_UNSECURE gRPC::grpc++_unsecure) 16 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 17 | 18 | find_package(gflags CONFIG REQUIRED) 19 | message(STATUS "Using gflags ${gflags_VERSION}") 20 | set(_GFLAGS_STATIC gflags) 21 | 22 | find_package(spdlog CONFIG REQUIRED) 23 | message(STATUS "Using spdlog ${spdlog_VERSION}") 24 | set(_SPDLOG_STATIC spdlog::spdlog) 25 | 26 | find_package(absl CONFIG REQUIRED) 27 | message(STATUS "Using absl ${absl_VERSION}") 28 | set(_ABSL_STATIC absl::base absl::strings absl::str_format absl::hash) 29 | 30 | # Proto file 31 | get_filename_component(bpfki_proto "../proto/bpfki.proto" ABSOLUTE) 32 | get_filename_component(bpfki_proto_path "${bpfki_proto}" PATH) 33 | 34 | # Generated sources 35 | set(bpfki_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.pb.cc") 36 | set(bpfki_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.pb.h") 37 | set(bpfki_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.grpc.pb.cc") 38 | set(bpfki_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/bpfki.grpc.pb.h") 39 | add_custom_command( 40 | OUTPUT "${bpfki_proto_srcs}" "${bpfki_proto_hdrs}" "${bpfki_grpc_srcs}" "${bpfki_grpc_hdrs}" 41 | COMMAND ${_PROTOBUF_PROTOC} 42 | ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" 43 | --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" 44 | -I "${bpfki_proto_path}" 45 | --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" 46 | "${bpfki_proto}" 47 | DEPENDS "${bpfki_proto}") 48 | 49 | # Include generated *.pb.h files 50 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 51 | 52 | # BPFKI 53 | add_executable(bpfki 54 | main.cc service.cc 55 | bpfki.cc 56 | ${bpfki_proto_srcs} 57 | ${bpfki_grpc_srcs} 58 | ) 59 | 60 | target_link_libraries(bpfki 61 | ${_GRPC_GRPCPP_UNSECURE} 62 | ${_PROTOBUF_LIBPROTOBUF} 63 | ${_SPDLOG_STATIC} 64 | ${_GFLAGS_STATIC} 65 | ${_ABSL_STATIC} 66 | -lbcc 67 | ) 68 | 69 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../progs/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 70 | -------------------------------------------------------------------------------- /src/bpfki.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bpfki.h" 9 | #include "bpfki.pb.h" 10 | 11 | namespace bpfki { 12 | 13 | BPFKI::~BPFKI() { 14 | } 15 | 16 | int BumpTime::init(void) { 17 | std::ifstream ifs(prog_name_); 18 | std::string prog((std::istreambuf_iterator(ifs)), 19 | (std::istreambuf_iterator())); 20 | auto init_res = bpf_->init(prog); 21 | if(init_res.code() != 0) 22 | throw std::invalid_argument(init_res.msg()); 23 | logger_->info("Phase 1: load prog"); 24 | 25 | std::map deps = {{"balance", 2}, {"write", 1}}; 26 | for (const auto& dep: deps) { 27 | auto pt = bpf_->get_prog_table(dep.first); 28 | int idx = 1, pfd = -1; 29 | for (int i{0}; i < dep.second; i++) { 30 | std::string func_name = absl::StrFormat("%s_step%d", dep.first, idx); 31 | auto loadRes = bpf_->load_func(func_name, BPF_PROG_TYPE_KPROBE, pfd); 32 | if (loadRes.code() != 0) 33 | throw std::invalid_argument(loadRes.msg()); 34 | auto updateRes = pt.update_value(idx, pfd); 35 | if (updateRes.code() != 0) 36 | throw std::invalid_argument(updateRes.msg()); 37 | idx++; 38 | } 39 | } 40 | logger_->info("Phase 2: resolve dependencies"); 41 | 42 | std::string fn_name = "syscall__"; 43 | absl::StrAppend(&fn_name, prog_name_, "_entry"); 44 | auto attach_res = bpf_->attach_kprobe(syscall_fnname_, fn_name, 0, 45 | BPF_PROBE_ENTRY); 46 | if (attach_res.code() != 0) 47 | throw std::invalid_argument(attach_res.msg()); 48 | fn_name = "do_ret_sys_"; 49 | absl::StrAppend(&fn_name, prog_name_, "_return"); 50 | attach_res = bpf_->attach_kprobe(syscall_fnname_, fn_name, 0, 51 | BPF_PROBE_RETURN); 52 | if (attach_res.code() != 0) 53 | throw std::invalid_argument(attach_res.msg()); 54 | logger_->info("Phase 3: attach time events {}", syscall_fnname_); 55 | 56 | return 0; 57 | } 58 | 59 | int BumpTime::update_inject_cond(const void *_req, bool clear) { 60 | auto req = reinterpret_cast(_req); 61 | auto filter_map = bpf_->get_hash_table 62 | ("clock_filters"); 63 | BumpTime::clock_filter cf = { 64 | .sec = req->second(), 65 | .ssec = req->subsecond(), 66 | .pct = static_cast((1UL << 32) * req->probability()), 67 | }; 68 | auto fid = req->pid(); 69 | auto tid = req->tid(); 70 | if (tid != 0 && fid != tid) 71 | fid = tid; 72 | if (fid == 0) 73 | throw std::invalid_argument("tid and pid cannot be 0 at the same time"); 74 | if (clear) { 75 | auto remove_res = filter_map.remove_value(fid); 76 | if (remove_res.code() != 0) 77 | throw std::invalid_argument(remove_res.msg()); 78 | fids_.erase(fid); 79 | logger_->info("Recover: task {}'s {}", fid, syscall_fnname_); 80 | } else { 81 | auto update_res = filter_map.update_value(fid, cf); 82 | if (update_res.code() != 0) 83 | throw std::invalid_argument(update_res.msg()); 84 | fids_.insert(fid); 85 | logger_->info("Set: task {}'s {} with sec {} ssec {} pct {}", 86 | fid, syscall_fnname_, cf.sec, cf.ssec, cf.pct); 87 | } 88 | 89 | return fids_.size(); 90 | } 91 | 92 | BumpTime::~BumpTime() { 93 | logger_->info("Detach {}", syscall_fnname_); 94 | } 95 | 96 | int FailKern::init(void) { 97 | auto prog = gen_bpf_prog(); 98 | logger_->info("load FailKern prog:\n{}", prog); 99 | auto init_res = bpf_->init(prog); 100 | if (init_res.code() != 0) 101 | throw std::invalid_argument(init_res.msg()); 102 | attach_probes(); 103 | return 0; 104 | } 105 | 106 | int FailKern::update_inject_cond(const void *_req, bool clear) { 107 | auto req = reinterpret_cast(_req); 108 | auto filter_map = bpf_->get_hash_table 109 | ("ctxs"); 110 | FailKern::fk_filter ff = { 111 | .pct = static_cast((1UL << 32) * req->probability()), 112 | .max_inject_times = req->times(), 113 | .inject_times = 0, 114 | .ignore = 0, 115 | .curr_call = 0, 116 | .conds_met = 0, 117 | }; 118 | auto fid = req->pid(); 119 | auto tid = req->tid(); 120 | if (tid != 0 && fid != tid) 121 | fid = tid; 122 | if (fid == 0) 123 | throw std::invalid_argument("tid and pid cannot be 0 at the same time"); 124 | if (clear) { 125 | auto remove_res = filter_map.remove_value(fid); 126 | if (remove_res.code() != 0) 127 | throw std::invalid_argument(remove_res.msg()); 128 | fids_.erase(fid); 129 | logger_->info("Recover: task {}'s FailKern", fid); 130 | } else { 131 | auto update_res = filter_map.update_value(fid, ff); 132 | if (update_res.code() != 0) 133 | throw std::invalid_argument(update_res.msg()); 134 | fids_.insert(fid); 135 | logger_->info("Set: task {}'s Failkern with pct {} max_inject_times {}", 136 | fid, ff.pct, ff.max_inject_times); 137 | } 138 | return fids_.size(); 139 | } 140 | 141 | std::string FailKern::gen_prelude(int nr_frame) { 142 | std::string include_header = "#include \n"; 143 | for (auto& header: headers_) { 144 | absl::StrAppend(&include_header, 145 | absl::StrFormat("#include <%s>\n", header)); 146 | } 147 | auto maps = absl::StrFormat(R"( 148 | struct fk_ctx { 149 | u32 pct; 150 | u32 max_inject_times; 151 | u32 inject_times; 152 | bool ignore; 153 | u64 curr_call; /* book keeping to handle recursion */ 154 | u64 conds_met; /* stack pointer */ 155 | u64 stack[%d]; 156 | }; 157 | 158 | BPF_HASH(ctxs, u32, struct fk_ctx); 159 | )", nr_frame); 160 | 161 | auto strcmp_ = R"( 162 | static __inline bool STRNCMP(const char *s1, const char *s2, int n) 163 | { 164 | for (int i = 0; i < n; ++i) { 165 | if (s1[i] != s2[i]) { 166 | return false; 167 | } 168 | } 169 | return true; 170 | } 171 | )"; 172 | 173 | std::string prelude; 174 | absl::StrAppend(&prelude, include_header, maps, strcmp_); 175 | return prelude; 176 | } 177 | 178 | void FailKern::gen_func_sig(std::string* func_sig, std::string& event, 179 | std::string& params, bool is_entry) { 180 | auto suffix = "_exit"; 181 | if (is_entry) 182 | suffix = "_entry"; 183 | if (params == "") 184 | absl::StrAppend(func_sig, "int ", event, suffix, "(struct pt_regs *ctx)"); 185 | else 186 | absl::StrAppend(func_sig, "int ", event, suffix, 187 | "(struct pt_regs *ctx, ", params, ")"); 188 | } 189 | 190 | std::string FailKern::gen_should_fail(int nr_frame) { 191 | std::string pred; 192 | if (callchain_.size() == 0) { 193 | pred = "(true)"; 194 | } else { 195 | auto frame = callchain_.back(); 196 | pred = frame.predicate(); 197 | if (frame.predicate() == "") 198 | pred = "(true)"; 199 | if (frame.funcname() == "") 200 | callchain_.pop_back(); 201 | } 202 | auto frame_id = nr_frame - 1; 203 | std::string event, func, err_code, params; 204 | if (type_ == SLAB) { 205 | event = "should_failslab"; 206 | params = "struct kmem_cache *s, gfp_t gfpflags"; 207 | err_code = "-ENOMEM"; 208 | } else if (type_ == PAGE) { 209 | event = "should_fail_alloc_page"; 210 | params = "gfp_t gfp_mask, unsigned int order"; 211 | err_code = "true"; 212 | } else if (type_ == BIO) { 213 | event = "should_fail_bio"; 214 | params = "struct bio *bio"; 215 | err_code = "-EIO"; 216 | } 217 | absl::StrAppend(&func, event, "_entry"); 218 | probes_.push_back(FailKern::Probe(event, func, true)); 219 | std::string func_sig; 220 | gen_func_sig(&func_sig, event, params, true); 221 | 222 | auto part1 = absl::StrFormat(R"( 223 | %s 224 | { 225 | u64 id = bpf_get_current_pid_tgid(); 226 | u32 pid = id >> 32, tid = id; 227 | struct fk_ctx *fc = NULL; 228 | 229 | fc = ctxs.lookup(&tid); 230 | if (!fc) { 231 | fc = ctxs.lookup(&pid); 232 | if (!fc) 233 | return 0; 234 | } 235 | )", func_sig); 236 | 237 | auto part2 = R"( 238 | if (fc->pct != 0 && bpf_get_prandom_u32() > fc->pct) 239 | fc->ignore = true; 240 | else 241 | fc->ignore = false; 242 | )"; 243 | 244 | auto part3 = absl::StrFormat(R"( 245 | if (fc->ignore) 246 | return 0; 247 | 248 | /* 249 | * If this is the only call in the chain and predicate passes 250 | */ 251 | if (%d == 1 && %s && (fc->max_inject_times == 0 || 252 | fc->inject_times < fc->max_inject_times)) { 253 | fc->inject_times++; 254 | bpf_override_return(ctx, %s); 255 | return 0; 256 | } 257 | 258 | /* 259 | * If all conds have been met and predicate passes 260 | */ 261 | if (fc->conds_met == %d && %s && (fc->max_inject_times == 0 || 262 | fc->inject_times < fc->max_inject_times)) { 263 | fc->inject_times++; 264 | bpf_override_return(ctx, %s); 265 | } 266 | return 0; 267 | } 268 | )" , nr_frame, pred, err_code, frame_id, pred, err_code); 269 | 270 | std::string should_fail; 271 | if (frame_id == 0) 272 | absl::StrAppend(&should_fail, part1, part2, part3); 273 | else 274 | absl::StrAppend(&should_fail, part1, part3); 275 | return should_fail; 276 | } 277 | 278 | std::string FailKern::gen_entry(std::string& event, std::string& params, 279 | std::string& pred, int frame_id, 280 | int nr_frame) { 281 | std::string func_sig; 282 | gen_func_sig(&func_sig, event, params, true); 283 | if (pred == "") 284 | pred = "(true)"; 285 | auto part1 = absl::StrFormat(R"( 286 | %s 287 | { 288 | u64 id = bpf_get_current_pid_tgid(); 289 | u32 pid = id >> 32, tid = id; 290 | struct fk_ctx *fc = NULL; 291 | 292 | fc = ctxs.lookup(&tid); 293 | if (!fc) { 294 | fc = ctxs.lookup(&pid); 295 | if (!fc) 296 | return 0; 297 | } 298 | )", func_sig); 299 | 300 | auto part2 = R"( 301 | if (fc->pct != 0 && bpf_get_prandom_u32() > fc->pct) 302 | fc->ignore = true; 303 | else 304 | fc->ignore = false; 305 | )"; 306 | 307 | auto part3 = absl::StrFormat(R"( 308 | if (fc->ignore) 309 | return 0; 310 | if (fc->conds_met >= %d) 311 | return 0; 312 | if (fc->conds_met == %d && %s) { 313 | fc->stack[%d] = fc->curr_call; 314 | fc->conds_met++; 315 | } 316 | 317 | fc->curr_call++; 318 | return 0; 319 | } 320 | )", nr_frame, frame_id, pred, frame_id); 321 | std::string entry; 322 | if (frame_id == 0) 323 | absl::StrAppend(&entry, part1, part2, part3); 324 | else 325 | absl::StrAppend(&entry, part1, part3); 326 | return entry; 327 | } 328 | 329 | std::string FailKern::gen_exit(std::string& event, std::string& params, 330 | int max_frame_id) { 331 | std::string func_sig; 332 | gen_func_sig(&func_sig, event, params, false); 333 | return absl::StrFormat(R"( 334 | %s 335 | { 336 | u64 id = bpf_get_current_pid_tgid(); 337 | u32 pid = id >> 32, tid = id; 338 | struct fk_ctx *fc = NULL; 339 | 340 | fc = ctxs.lookup(&tid); 341 | if (!fc) { 342 | fc = ctxs.lookup(&pid); 343 | if (!fc) 344 | return 0; 345 | } 346 | if (fc->ignore) 347 | return 0; 348 | 349 | fc->curr_call--; 350 | 351 | if (fc->conds_met < 1 || fc->conds_met >= %d) 352 | return 0; 353 | if (fc->stack[fc->conds_met - 1] == fc->curr_call) 354 | fc->conds_met--; 355 | 356 | return 0; 357 | } 358 | )", func_sig, max_frame_id + 1); 359 | } 360 | 361 | std::string FailKern::gen_bpf_prog(void) { 362 | auto size = callchain_.size(); 363 | auto nr_frame = size + 1; 364 | if (size == 1 && callchain_.back().funcname() == "") 365 | nr_frame = 1; 366 | auto prelude = gen_prelude(nr_frame); 367 | auto should_fail = gen_should_fail(nr_frame); 368 | std::string prog; 369 | absl::StrAppend(&prog, prelude, should_fail); 370 | if (nr_frame == 1) 371 | return prog; 372 | auto frame_id = nr_frame - 2; 373 | for (auto it = callchain_.rbegin(); it != callchain_.rend(); it++) { 374 | auto event = it->funcname(); 375 | auto params = it->parameters(); 376 | auto pred = it->predicate(); 377 | auto entry = gen_entry(event, params, pred, frame_id--, nr_frame); 378 | auto exit = gen_exit(event, params, nr_frame); 379 | absl::StrAppend(&prog, entry, exit); 380 | 381 | std::string entry_func, exit_func; 382 | absl::StrAppend(&entry_func, event, "_entry"); 383 | probes_.push_back(FailKern::Probe(event, entry_func, true)); 384 | absl::StrAppend(&exit_func, event, "_exit"); 385 | probes_.push_back(FailKern::Probe(event, exit_func, false)); 386 | } 387 | return prog; 388 | } 389 | 390 | void FailKern::attach_probes(void) { 391 | for (auto& probe: probes_) { 392 | auto type = BPF_PROBE_ENTRY; 393 | if (!probe.is_entry()) 394 | type = BPF_PROBE_RETURN; 395 | auto attach_res = bpf_->attach_kprobe(probe.event(), probe.func(), 0, type); 396 | if (attach_res.code() != 0) 397 | throw std::invalid_argument(attach_res.msg()); 398 | } 399 | } 400 | 401 | }; 402 | -------------------------------------------------------------------------------- /src/bpfki.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "absl/hash/hash.h" 13 | 14 | namespace bpfki { 15 | 16 | class BPFKI { 17 | public: 18 | BPFKI() {} 19 | BPFKI(const BPFKI&) = delete; 20 | BPFKI& operator=(const BPFKI&) = delete; 21 | 22 | virtual int init(void) = 0; 23 | virtual int update_inject_cond(const void *req, bool clear = false) = 0; 24 | virtual ~BPFKI(); 25 | }; 26 | 27 | class BumpTime : public BPFKI { 28 | public: 29 | struct clock_filter { 30 | long sec; 31 | union { 32 | long ssec; 33 | long nsec; 34 | long usec; 35 | }; 36 | long pct; 37 | union { 38 | struct timespec *ts; 39 | struct timeval *tv; 40 | }; 41 | union { 42 | struct timespec nts; 43 | struct timeval ntv; 44 | }; 45 | }; 46 | 47 | public: 48 | BumpTime(const std::shared_ptr& logger, 49 | const std::string& prog_name) 50 | : logger_(logger), 51 | bpf_(std::make_unique()), 52 | prog_name_(prog_name), 53 | syscall_fnname_(bpf_->get_syscall_fnname(prog_name)) {} 54 | int init(void); 55 | int update_inject_cond(const void *req, bool clear = false); 56 | ~BumpTime(); 57 | 58 | private: 59 | const std::shared_ptr& logger_; 60 | std::unique_ptr bpf_; 61 | const std::string& prog_name_; 62 | const std::string syscall_fnname_; 63 | std::set fids_; 64 | }; 65 | 66 | class FailKern : public BPFKI { 67 | public: 68 | enum Type : int { 69 | SLAB, 70 | PAGE, 71 | BIO, 72 | }; 73 | 74 | class Frame { 75 | public: 76 | Frame(const std::string &funcname, 77 | const std::string ¶meters, 78 | const std::string &predicate) 79 | : funcname_(funcname), 80 | parameters_(parameters), 81 | predicate_(predicate) {} 82 | 83 | template 84 | friend H AbslHashValue(H h, const Frame& f) { 85 | return H::combine(std::move(h), f.funcname_, 86 | f.parameters_, f.predicate_); 87 | } 88 | 89 | public: 90 | inline const std::string& funcname() const { 91 | return funcname_; 92 | } 93 | inline const std::string& parameters() const { 94 | return parameters_; 95 | } 96 | inline const std::string& predicate() const { 97 | return predicate_; 98 | } 99 | 100 | private: 101 | const std::string funcname_; 102 | const std::string parameters_; 103 | const std::string predicate_; 104 | }; 105 | 106 | class Probe { 107 | public: 108 | Probe(const std::string& event, const std::string& func, bool is_entry) 109 | : event_(event), func_(func), is_entry_(is_entry) {} 110 | 111 | public: 112 | inline const std::string& event() const { 113 | return event_; 114 | } 115 | inline const std::string& func() const { 116 | return func_; 117 | } 118 | inline bool is_entry() const { 119 | return is_entry_; 120 | } 121 | 122 | private: 123 | const std::string event_; 124 | const std::string func_; 125 | bool is_entry_; 126 | }; 127 | 128 | struct fk_filter { 129 | long pct; 130 | long max_inject_times; 131 | long inject_times; 132 | bool ignore; 133 | uint64_t curr_call; 134 | uint64_t conds_met; 135 | }; 136 | 137 | explicit FailKern(const std::shared_ptr& logger, 138 | const Type& type, std::vector& headers, 139 | std::vector& challchain) 140 | : logger_(logger), 141 | bpf_(std::make_unique()), 142 | type_(type), 143 | headers_(headers), 144 | callchain_(challchain) {} 145 | int init(void); 146 | int update_inject_cond(const void *req, bool clear = false); 147 | ~FailKern() {}; 148 | private: 149 | std::string gen_bpf_prog(void); 150 | void attach_probes(void); 151 | std::string gen_prelude(int max_frame_id); 152 | void gen_func_sig(std::string *func_sig, std::string& event, 153 | std::string& params, bool is_entry); 154 | std::string gen_should_fail(int frame_id); 155 | std::string gen_entry(std::string& event, std::string& params, 156 | std::string& pred, int frame_id, 157 | int max_frame_id); 158 | std::string gen_exit(std::string& event, std::string& params, 159 | int max_frame_id); 160 | 161 | private: 162 | const std::shared_ptr& logger_; 163 | std::unique_ptr bpf_; 164 | const Type &type_; 165 | std::vector &headers_; 166 | std::vector &callchain_; 167 | std::vector probes_; 168 | std::set fids_; 169 | }; 170 | 171 | class FailSyscall : public BPFKI { 172 | public: 173 | FailSyscall(); 174 | ~FailSyscall(); 175 | }; 176 | 177 | template 178 | std::enable_if_t::value, 179 | std::unique_ptr> 180 | construct(Ts&... params) { 181 | return std::make_unique(std::forward(params)...); 182 | } 183 | 184 | template 185 | std::unique_ptr construct(...) { 186 | return nullptr; 187 | } 188 | 189 | template 190 | static std::unique_ptr 191 | createBPFKI(const std::string& name, Ts&... params) { 192 | if (name == "BumpTime") 193 | return construct(std::forward(params)...); 194 | else if (name == "FailKern") 195 | return construct(std::forward(params)...); 196 | else if (name == "FailSyscall") 197 | return construct(std::forward(params)...); 198 | 199 | return nullptr; 200 | } 201 | 202 | }; 203 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "service.h" 9 | 10 | DEFINE_string(host, "0.0.0.0", "Host of BPFKI server"); 11 | DEFINE_string(port, "50051", "Port of BPFKI server"); 12 | DEFINE_string(loglevel, "info", "Set the logging level " 13 | "(\"debug\"|\"info\"|\"warn\"|\"error\"|" 14 | "\"fatal\") (default \"info\")"); 15 | 16 | int main(int argc, char *argv[]) { 17 | gflags::ParseCommandLineFlags(&argc, &argv, true); 18 | std::string addr = FLAGS_host + ":" + FLAGS_port; 19 | auto logger = spdlog::stdout_color_mt("console"); 20 | logger->set_level(spdlog::level::info); 21 | bpfki::BPFKIServiceImpl srv(logger); 22 | grpc::ServerBuilder builder; 23 | builder.AddListeningPort(addr, grpc::InsecureServerCredentials()); 24 | builder.RegisterService(&srv); 25 | std::unique_ptr server(builder.BuildAndStart()); 26 | logger->info("BPFKI server run on {0}", addr); 27 | server->Wait(); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/service.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "service.h" 7 | 8 | namespace bpfki { 9 | 10 | BPFKIServiceImpl::BPFKIServiceImpl(const std::shared_ptr& logger) 11 | : logger_(logger) { 12 | } 13 | 14 | BPFKIServiceImpl::~BPFKIServiceImpl() { 15 | 16 | } 17 | 18 | grpc::Status BPFKIServiceImpl::SetTime(const std::string& type, 19 | const BumpTimeRequest* request, 20 | StatusResponse* response) { 21 | std::unique_ptr bpfki = nullptr; 22 | auto it = bpfki_map_.find(type); 23 | if (it == bpfki_map_.end()) { 24 | bpfki = createBPFKI("BumpTime", logger_, type); 25 | try { 26 | bpfki->init(); 27 | } catch (const std::invalid_argument& ia) { 28 | logger_->error("Internal error invalid argument: {}", ia.what()); 29 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 30 | } 31 | } else { 32 | bpfki = std::move(it->second); 33 | } 34 | try { 35 | auto left_nr_filters = bpfki->update_inject_cond( 36 | reinterpret_cast(request)); 37 | logger_->info("left_nr_filters: {}", left_nr_filters); 38 | } catch (const std::invalid_argument& ia) { 39 | logger_->error("Internal error invalid argument: {}", ia.what()); 40 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 41 | } 42 | bpfki_map_[type] = std::move(bpfki); 43 | return grpc::Status::OK; 44 | } 45 | 46 | grpc::Status BPFKIServiceImpl::RecoverTime(const std::string& type, 47 | const BumpTimeRequest *request, 48 | StatusResponse* response) { 49 | auto it = bpfki_map_.find(type); 50 | if (it == bpfki_map_.end()) 51 | return grpc::Status::OK; 52 | try { 53 | auto left_nr_filters = it->second->update_inject_cond( 54 | reinterpret_cast(request), true); 55 | logger_->info("left_nr_filters: {}", left_nr_filters); 56 | if (left_nr_filters == 0) 57 | bpfki_map_.erase(it); 58 | } catch (const std::invalid_argument& ia) { 59 | logger_->error("Internal error invalid argument: {}", ia.what()); 60 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 61 | } 62 | return grpc::Status::OK; 63 | } 64 | 65 | grpc::Status BPFKIServiceImpl::SetTimeVal(grpc::ServerContext* context, 66 | const BumpTimeRequest* request, 67 | StatusResponse* response) { 68 | std::lock_guard guard(bpfki_map_mtx_); 69 | return SetTime("gettimeofday", request, response); 70 | } 71 | 72 | grpc::Status BPFKIServiceImpl::RecoverTimeVal(grpc::ServerContext* context, 73 | const BumpTimeRequest* request, 74 | StatusResponse* response) { 75 | std::lock_guard guard(bpfki_map_mtx_); 76 | return RecoverTime("gettimeofday", request, response); 77 | } 78 | 79 | grpc::Status BPFKIServiceImpl::SetTimeSpec(grpc::ServerContext* context, 80 | const BumpTimeRequest* request, 81 | StatusResponse* response) { 82 | std::lock_guard guard(bpfki_map_mtx_); 83 | return RecoverTime("clock_gettime", request, response); 84 | } 85 | 86 | grpc::Status BPFKIServiceImpl::RecoverTimeSpec(grpc::ServerContext* context, 87 | const BumpTimeRequest* request, 88 | StatusResponse* response) { 89 | std::lock_guard guard(bpfki_map_mtx_); 90 | return RecoverTime("clock_gettime", request, response); 91 | } 92 | 93 | grpc::Status BPFKIServiceImpl::FailMMOrBIO(grpc::ServerContext *context, 94 | const FailKernRequest *request, 95 | StatusResponse *response) { 96 | std::vector headers; 97 | int header_size = request->headers_size(); 98 | for (int i = 0; i < header_size; i++) { 99 | headers.push_back(request->headers(i)); 100 | } 101 | std::vector callchain; 102 | int callchain_size = request->callchain_size(); 103 | for (int i = 0; i < callchain_size; i++) { 104 | auto frame = request->callchain(i); 105 | if (i != callchain_size - 1 && frame.funcname() == "") 106 | throw std::invalid_argument(""); 107 | callchain.push_back(FailKern::Frame(frame.funcname(), 108 | frame.parameters(), 109 | frame.predicate())); 110 | } 111 | auto k = std::to_string(absl::Hash>{}(callchain)); 112 | std::unique_ptr bpfki = nullptr; 113 | std::lock_guard guard(bpfki_map_mtx_); 114 | auto it = bpfki_map_.find(k); 115 | if (it == bpfki_map_.end()) { 116 | auto type = static_cast(request->ftype()); 117 | bpfki = createBPFKI("FailKern", logger_, type, headers, callchain); 118 | try { 119 | bpfki->init(); 120 | } catch(const std::invalid_argument& ia) { 121 | logger_->error("Internal error invalid argument: {}", ia.what()); 122 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 123 | } 124 | } else { 125 | bpfki = std::move(it->second); 126 | } 127 | try { 128 | auto left_nr_filters = bpfki->update_inject_cond( 129 | reinterpret_cast(request)); 130 | logger_->info("left_nr_filters: {}", left_nr_filters); 131 | } catch (const std::invalid_argument& ia) { 132 | logger_->error("Internal error invalid argument: {}", ia.what()); 133 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 134 | } 135 | bpfki_map_[k] = std::move(bpfki); 136 | return grpc::Status::OK; 137 | } 138 | 139 | grpc::Status BPFKIServiceImpl::RecoverMMOrBIO(grpc::ServerContext* context, 140 | const FailKernRequest* request, 141 | StatusResponse* response) { 142 | std::vector callchain; 143 | int callchain_size = request->callchain_size(); 144 | for (int i = 0; i < callchain_size; i++) { 145 | auto frame = request->callchain(i); 146 | if (i != callchain_size - 1 && frame.funcname() == "") 147 | throw std::invalid_argument(""); 148 | callchain.push_back(FailKern::Frame(frame.funcname(), 149 | frame.parameters(), 150 | frame.predicate())); 151 | } 152 | auto k = std::to_string(absl::Hash>{}(callchain)); 153 | std::unique_ptr bpfki = nullptr; 154 | std::lock_guard guard(bpfki_map_mtx_); 155 | auto it = bpfki_map_.find(k); 156 | if (it == bpfki_map_.end()) 157 | return grpc::Status::OK; 158 | try { 159 | auto left_nr_filters = it->second->update_inject_cond( 160 | reinterpret_cast(request), true); 161 | logger_->info("left_nr_filters: {}", left_nr_filters); 162 | if (left_nr_filters == 0) 163 | bpfki_map_.erase(it); 164 | } catch (const std::invalid_argument& ia) { 165 | logger_->error("Internal error invalid argument: {}", ia.what()); 166 | return grpc::Status(grpc::StatusCode::INTERNAL, ia.what()); 167 | } 168 | return grpc::Status::OK; 169 | } 170 | 171 | grpc::Status BPFKIServiceImpl::FailSyscall(grpc::ServerContext* context, 172 | const FailSyscallRequest* request, 173 | StatusResponse* response) { 174 | return grpc::Status::OK; 175 | } 176 | 177 | grpc::Status BPFKIServiceImpl::RecoverSyscall(grpc::ServerContext* context, 178 | const FailSyscallRequest* request, 179 | StatusResponse* response) { 180 | return grpc::Status::OK; 181 | } 182 | 183 | }; 184 | -------------------------------------------------------------------------------- /src/service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "bpfki.grpc.pb.h" 10 | #include "bpfki.h" 11 | 12 | namespace bpfki { 13 | 14 | class BPFKIServiceImpl final: public BPFKIService::Service { 15 | public: 16 | explicit BPFKIServiceImpl(const std::shared_ptr& logger); 17 | ~BPFKIServiceImpl(); 18 | grpc::Status SetTimeVal(grpc::ServerContext* context, 19 | const BumpTimeRequest* request, 20 | StatusResponse* response) override; 21 | grpc::Status RecoverTimeVal(grpc::ServerContext* context, 22 | const BumpTimeRequest* request, 23 | StatusResponse* response) override; 24 | grpc::Status SetTimeSpec(grpc::ServerContext* context, 25 | const BumpTimeRequest* request, 26 | StatusResponse* response) override; 27 | grpc::Status RecoverTimeSpec(grpc::ServerContext* context, 28 | const BumpTimeRequest* request, 29 | StatusResponse* response) override; 30 | grpc::Status FailMMOrBIO(grpc::ServerContext* context, 31 | const FailKernRequest* request, 32 | StatusResponse* response) override; 33 | grpc::Status RecoverMMOrBIO(grpc::ServerContext* context, 34 | const FailKernRequest* request, 35 | StatusResponse* response) override; 36 | grpc::Status FailSyscall(grpc::ServerContext* context, 37 | const FailSyscallRequest* request, 38 | StatusResponse* response) override; 39 | grpc::Status RecoverSyscall(grpc::ServerContext* context, 40 | const FailSyscallRequest* request, 41 | StatusResponse* response) override; 42 | private: 43 | grpc::Status SetTime(const std::string& type, 44 | const BumpTimeRequest* request, 45 | StatusResponse* response); 46 | grpc::Status RecoverTime(const std::string& type, 47 | const BumpTimeRequest *request, 48 | StatusResponse* response); 49 | private: 50 | const std::shared_ptr& logger_; 51 | std::map> bpfki_map_; 52 | std::mutex bpfki_map_mtx_; 53 | }; 54 | 55 | }; 56 | --------------------------------------------------------------------------------