├── .circleci └── config.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src ├── error.rs ├── ffistr.rs ├── handle_map.rs ├── into_ffi.rs ├── lib.rs ├── macros.rs └── string.rs └── tests └── test.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | workflows: 4 | version: 2 5 | 6 | tests: 7 | jobs: 8 | - Run tests: 9 | matrix: 10 | parameters: 11 | # 1.43.0 is our MSRV at the moment. 12 | image-tag: ["1.43.0", "latest"] 13 | 14 | format: 15 | jobs: 16 | - Check formatting 17 | 18 | lint: 19 | jobs: 20 | - Lint with clippy 21 | 22 | doc-check: 23 | jobs: 24 | - Check rustdoc links 25 | 26 | # coverage: 27 | # jobs: 28 | # - Gen coverage 29 | 30 | cross-test: 31 | jobs: 32 | - Cross Test 33 | 34 | asan-test: 35 | jobs: 36 | - Test under asan 37 | 38 | jobs: 39 | Run tests: 40 | parameters: 41 | image-tag: 42 | type: string 43 | 44 | docker: 45 | - image: circleci/rust:<< parameters.image-tag >> 46 | 47 | steps: 48 | - checkout 49 | - run: rustc -Vv 50 | - run: cargo test --all-features --verbose 51 | - run: cargo test --no-default-features --verbose 52 | 53 | Check formatting: 54 | docker: 55 | - image: circleci/rust:latest 56 | steps: 57 | - checkout 58 | - run: rustup component add rustfmt 59 | - run: cargo fmt -- --version 60 | - run: cargo fmt -- --check 61 | 62 | Lint with clippy: 63 | docker: 64 | - image: circleci/rust:latest 65 | steps: 66 | - checkout 67 | - run: rustup component add clippy 68 | - run: cargo clippy -- --version 69 | - run: cargo clippy --no-default-features -- -D warnings 70 | - run: cargo clippy --all-features -- -D warnings 71 | 72 | Check rustdoc links: 73 | docker: 74 | - image: circleci/rust:latest 75 | steps: 76 | - checkout 77 | - run: rustup toolchain add nightly 78 | - run: rustdoc +nightly --version 79 | # Note: nightly rustdoc (and only nightly) supports telling you if you 80 | # have any links that don't resolve in your doc comments. (Note that this 81 | # only works via `cargo rustdoc` and not `cargo doc`, for some reason) 82 | - run: cargo +nightly rustdoc --no-default-features -- -D warnings 83 | - run: cargo +nightly rustdoc --all-features -- -D warnings 84 | 85 | Gen coverage: 86 | docker: 87 | - image: circleci/rust:latest 88 | steps: 89 | - checkout 90 | - run: 91 | name: Setup rust 92 | command: | 93 | rustup toolchain install nightly --profile minimal 94 | rustup default nightly 95 | - run: 96 | name: Fetch grcov 97 | command: | 98 | GRCOV_URL=https://github.com/mozilla/grcov/releases/latest/download/grcov-linux-x86_64.tar.bz2 99 | mkdir -p "$HOME/.bin" 100 | curl -sfSL --retry 5 --retry-delay 10 "${GRCOV_URL}" | tar jxf - -C "$HOME/.bin" 101 | echo 'export PATH="$HOME/.bin:$PATH"' >> $BASH_ENV 102 | - run: grcov --version 103 | - run: 104 | name: Produce coverage 105 | command: | 106 | export CARGO_INCREMENTAL=0 107 | export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort --cfg coverage" 108 | export RUSTDOCFLAGS="-Cpanic=abort" 109 | cargo clean 110 | cargo test --verbose --all-features --no-fail-fast || true 111 | ./grcov ccov.zip -s . -t lcov --llvm --ignore-not-existing --ignore "/*" -o lcov.info; 112 | bash <(curl -s https://codecov.io/bash) -f lcov.info; 113 | 114 | Cross Test: 115 | machine: true 116 | steps: 117 | - checkout 118 | 119 | - run: 120 | name: Download rustup 121 | command: | 122 | wget https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init 123 | chmod +x rustup-init 124 | 125 | - run: 126 | name: Install Rust 127 | command: | 128 | ./rustup-init -y --no-modify-path 129 | rm rustup-init 130 | echo 'export PATH=$HOME/.cargo/bin:$PATH' >> $BASH_ENV 131 | 132 | - run: rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android 133 | 134 | - run: cargo install cross 135 | 136 | - run: 137 | name: Test android arm32 138 | command: cross test --target=armv7-linux-androideabi 139 | 140 | - run: 141 | name: Test android arm64 142 | command: cross test --target=aarch64-linux-android 143 | 144 | - run: 145 | name: Test android x86_32 146 | command: cross test --target=i686-linux-android 147 | 148 | Test under asan: 149 | docker: 150 | - image: circleci/rust:latest 151 | steps: 152 | - checkout 153 | - run: rustup toolchain add nightly 154 | - run: rustup default nightly 155 | - run: rustup component add rust-src 156 | - run: rustc -Vv 157 | - run: 158 | name: Test under address-sanitizer 159 | command: | 160 | export RUSTFLAGS="-Zsanitizer=address" 161 | export RUSTDOCFLAGS="-Zsanitizer=address" 162 | export ASAN_OPTIONS="detect_stack_use_after_return=1" 163 | export RUST_BACKTRACE=0 164 | cargo -Z build-std test --target=x86_64-unknown-linux-gnu --all-features 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | ## Project Specific Etiquette 11 | 12 | ### Our Responsibilities 13 | 14 | Project maintainers are responsible for clarifying the standards of acceptable 15 | behavior and are expected to take appropriate and fair corrective action in 16 | response to any instances of unacceptable behavior. 17 | 18 | Project maintainers have the right and responsibility to remove, edit, or 19 | reject comments, commits, code, wiki edits, issues, and other contributions 20 | that are not aligned to this Code of Conduct, or to ban temporarily or 21 | permanently any contributor for other behaviors that they deem inappropriate, 22 | threatening, offensive, or harmful. 23 | 24 | Project maintainers who do not follow or enforce Mozilla's Participation Guidelines in good 25 | faith may face temporary or permanent repercussions. 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffi-support" 3 | edition = "2018" 4 | version = "0.4.4" 5 | authors = ["Thom Chiovoloni "] 6 | description = "A crate to help expose Rust functions over the FFI." 7 | repository = "https://github.com/mozilla/ffi-support" 8 | readme = "README.md" 9 | keywords = ["ffi", "bindings"] 10 | categories = ["development-tools::ffi"] 11 | license = "Apache-2.0 / MIT" 12 | 13 | [features] 14 | default = [] 15 | log_panics = [] 16 | log_backtraces = ["log_panics", "backtrace"] 17 | 18 | [dependencies] 19 | log = "0.4" 20 | lazy_static = "1.4" 21 | 22 | [dependencies.backtrace] 23 | optional = true 24 | version = "0.3" 25 | 26 | [dev-dependencies] 27 | rayon = "1.3" 28 | env_logger = { version = "0.7", default-features = false } 29 | log = "0.4" 30 | rand = "0.7" 31 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 Mozilla Foundation 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFI Support 2 | 3 | [![Docs](https://docs.rs/ffi-support/badge.svg)](https://docs.rs/ffi-support) 4 | 5 | This crate implements a low-level support library to simplify implementing certain FFI patterns. 6 | It was originally created for patterns in the [mozilla/application-services](https://github.com/mozilla/application-services) 7 | repository, but that repo is working on replacing all uses of this crate with 8 | the [mozilla/uniffi-rs](https://github.com/mozilla/uniffi-rs) project. 9 | 10 | In other words, we consider this crate soft-deprecated and replaced by [UniFFI](https://github.com/mozilla/uniffi-rs). 11 | 12 | However, if this crate proves useful to others, it can assist with the following areas: 13 | 14 | 1. Avoiding throwing panics over the FFI (which is undefined behavior) 15 | 2. Translating rust errors (and panics) into errors that the caller on the other side of the FFI is able to handle. 16 | 3. Converting strings to/from rust str. 17 | 4. Passing non-string data (in a few ways, including exposing an opaque pointeer, marshalling data to JSON strings with serde, as well as arbitrary custom handling) back and forth between Rust and whatever the caller on the other side of the FFI is. 18 | 19 | Additionally, it's documentation describes a number of the problems we've hit doing this to expose libraries to consumers on mobile platforms. 20 | 21 | ## Usage 22 | 23 | Add the following to your Cargo.toml 24 | 25 | ```toml 26 | ffi-support = "0.4.4" 27 | ``` 28 | 29 | For further examples, the examples in the docs is the best starting point, followed by the usage code in the [mozilla/application-services](https://github.com/mozilla/application-services) repo (for example [here](https://github.com/mozilla/application-services/blob/main/components/places/ffi/src/lib.rs) or [here](https://github.com/mozilla/application-services/blob/main/components/places/src/ffi.rs)). 30 | 31 | ## License 32 | 33 | Dual licensed under the Apache License, Version 2.0 or 34 | or the MIT license or 35 | , at your option. All files in the project 36 | carrying such notice may not be copied, modified, or distributed except 37 | according to those terms. 38 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | use crate::string::{destroy_c_string, rust_string_to_c}; 18 | use std::os::raw::c_char; 19 | use std::{self, ptr}; 20 | 21 | /// Represents an error that occured within rust, storing both an error code, and additional data 22 | /// that may be used by the caller. 23 | /// 24 | /// Misuse of this type can cause numerous issues, so please read the entire documentation before 25 | /// usage. 26 | /// 27 | /// ## Rationale 28 | /// 29 | /// This library encourages a pattern of taking a `&mut ExternError` as the final parameter for 30 | /// functions exposed over the FFI. This is an "out parameter" which we use to write error/success 31 | /// information that occurred during the function's execution. 32 | /// 33 | /// To be clear, this means instances of `ExternError` will be created on the other side of the FFI, 34 | /// and passed (by mutable reference) into Rust. 35 | /// 36 | /// While this pattern is not particularly ergonomic in Rust (although hopefully this library 37 | /// helps!), it offers two main benefits over something more ergonomic (which might be `Result` 38 | /// shaped). 39 | /// 40 | /// 1. It avoids defining a large number of `Result`-shaped types in the FFI consumer, as would 41 | /// be required with something like an `struct ExternResult { ok: *mut T, err:... }` 42 | /// 43 | /// 2. It offers additional type safety over `struct ExternResult { ok: *mut c_void, err:... }`, 44 | /// which helps avoid memory safety errors. It also can offer better performance for returning 45 | /// primitives and repr(C) structs (no boxing required). 46 | /// 47 | /// It also is less tricky to use properly than giving consumers a `get_last_error()` function, or 48 | /// similar. 49 | /// 50 | /// ## Caveats 51 | /// 52 | /// Note that the order of the fields is `code` (an i32) then `message` (a `*mut c_char`), getting 53 | /// this wrong on the other side of the FFI will cause memory corruption and crashes. 54 | /// 55 | /// The fields are public largely for documentation purposes, but you should use 56 | /// [`ExternError::new_error`] or [`ExternError::success`] to create these. 57 | /// 58 | /// ## Layout/fields 59 | /// 60 | /// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so 61 | /// that we can verify rust users are constructing them appropriately), the fields, their types, and 62 | /// their order are *very much* a part of the public API of this type. Consumers on the other side 63 | /// of the FFI will need to know its layout. 64 | /// 65 | /// If this were a C struct, it would look like 66 | /// 67 | /// ```c,no_run 68 | /// struct ExternError { 69 | /// int32_t code; 70 | /// char *message; // note: nullable 71 | /// }; 72 | /// ``` 73 | /// 74 | /// In rust, there are two fields, in this order: `code: ErrorCode`, and `message: *mut c_char`. 75 | /// Note that ErrorCode is a `#[repr(transparent)]` wrapper around an `i32`, so the first property 76 | /// is equivalent to an `i32`. 77 | /// 78 | /// #### The `code` field. 79 | /// 80 | /// This is the error code, 0 represents success, all other values represent failure. If the `code` 81 | /// field is nonzero, there should always be a message, and if it's zero, the message will always be 82 | /// null. 83 | /// 84 | /// #### The `message` field. 85 | /// 86 | /// This is a null-terminated C string containing some amount of additional information about the 87 | /// error. If the `code` property is nonzero, there should always be an error message. Otherwise, 88 | /// this should will be null. 89 | /// 90 | /// This string (when not null) is allocated on the rust heap (using this crate's 91 | /// [`rust_string_to_c`]), and must be freed on it as well. Critically, if there are multiple rust 92 | /// packages using being used in the same application, it *must be freed on the same heap that 93 | /// allocated it*, or you will corrupt both heaps. 94 | /// 95 | /// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which 96 | /// means you must expose a function to release the resources of `message` which can be done easily 97 | /// using the [`define_string_destructor!`] macro provided by this crate. 98 | /// 99 | /// If, for some reason, you need to release the resources directly, you may call 100 | /// `ExternError::release()`. Note that you probably do not need to do this, and it's 101 | /// intentional that this is not called automatically by implementing `drop`. 102 | /// 103 | /// ## Example 104 | /// 105 | /// ```rust,no_run 106 | /// use ffi_support::{ExternError, ErrorCode}; 107 | /// 108 | /// #[derive(Debug)] 109 | /// pub enum MyError { 110 | /// IllegalFoo(String), 111 | /// InvalidBar(i64), 112 | /// // ... 113 | /// } 114 | /// 115 | /// // Putting these in a module is obviously optional, but it allows documentation, and helps 116 | /// // avoid accidental reuse. 117 | /// pub mod error_codes { 118 | /// // note: -1 and 0 are reserved by ffi_support 119 | /// pub const ILLEGAL_FOO: i32 = 1; 120 | /// pub const INVALID_BAR: i32 = 2; 121 | /// // ... 122 | /// } 123 | /// 124 | /// fn get_code(e: &MyError) -> ErrorCode { 125 | /// match e { 126 | /// MyError::IllegalFoo(_) => ErrorCode::new(error_codes::ILLEGAL_FOO), 127 | /// MyError::InvalidBar(_) => ErrorCode::new(error_codes::INVALID_BAR), 128 | /// // ... 129 | /// } 130 | /// } 131 | /// 132 | /// impl From for ExternError { 133 | /// fn from(e: MyError) -> ExternError { 134 | /// ExternError::new_error(get_code(&e), format!("{:?}", e)) 135 | /// } 136 | /// } 137 | /// ``` 138 | #[repr(C)] 139 | // Note: We're intentionally not implementing Clone -- it's too risky. 140 | #[derive(Debug, PartialEq)] 141 | pub struct ExternError { 142 | // Don't reorder or add anything here! 143 | code: ErrorCode, 144 | message: *mut c_char, 145 | } 146 | 147 | impl std::panic::UnwindSafe for ExternError {} 148 | impl std::panic::RefUnwindSafe for ExternError {} 149 | 150 | /// This is sound so long as our fields are private. 151 | unsafe impl Send for ExternError {} 152 | 153 | impl ExternError { 154 | /// Construct an ExternError representing failure from a code and a message. 155 | #[inline] 156 | pub fn new_error(code: ErrorCode, message: impl Into) -> Self { 157 | assert!( 158 | !code.is_success(), 159 | "Attempted to construct a success ExternError with a message" 160 | ); 161 | Self { 162 | code, 163 | message: rust_string_to_c(message), 164 | } 165 | } 166 | 167 | /// Returns a ExternError representing a success. Also returned by ExternError::default() 168 | #[inline] 169 | pub fn success() -> Self { 170 | Self { 171 | code: ErrorCode::SUCCESS, 172 | message: ptr::null_mut(), 173 | } 174 | } 175 | 176 | /// Helper for the case where we aren't exposing this back over the FFI and 177 | /// we just want to warn if an error occurred and then release the allocated 178 | /// memory. 179 | /// 180 | /// Typically, this is done if the error will still be detected and reported 181 | /// by other channels. 182 | /// 183 | /// We assume we're not inside a catch_unwind, and so we wrap inside one 184 | /// ourselves. 185 | pub fn consume_and_log_if_error(self) { 186 | if !self.code.is_success() { 187 | // in practice this should never panic, but you never know... 188 | crate::abort_on_panic::call_with_output(|| { 189 | log::error!("Unhandled ExternError({:?}) {:?}", self.code, unsafe { 190 | crate::FfiStr::from_raw(self.message) 191 | }); 192 | unsafe { 193 | self.manually_release(); 194 | } 195 | }) 196 | } 197 | } 198 | 199 | /// Get the `code` property. 200 | #[inline] 201 | pub fn get_code(&self) -> ErrorCode { 202 | self.code 203 | } 204 | 205 | /// Get the `message` property as a pointer to c_char. 206 | #[inline] 207 | pub fn get_raw_message(&self) -> *const c_char { 208 | self.message as *const _ 209 | } 210 | 211 | /// Get the `message` property as an [`FfiStr`][crate::FfiStr] 212 | #[inline] 213 | pub fn get_message(&self) -> crate::FfiStr<'_> { 214 | // Safe because the lifetime is the same as our lifetime. 215 | unsafe { crate::FfiStr::from_raw(self.get_raw_message()) } 216 | } 217 | 218 | /// Get the `message` property as a String, or None if this is not an error result. 219 | /// 220 | /// ## Safety 221 | /// 222 | /// You should only call this if you are certain that the other side of the FFI doesn't have a 223 | /// reference to this result (more specifically, to the `message` property) anywhere! 224 | #[inline] 225 | pub unsafe fn get_and_consume_message(self) -> Option { 226 | if self.code.is_success() { 227 | None 228 | } else { 229 | let res = self.get_message().into_string(); 230 | self.manually_release(); 231 | Some(res) 232 | } 233 | } 234 | 235 | /// Manually release the memory behind this string. You probably don't want to call this. 236 | /// 237 | /// ## Safety 238 | /// 239 | /// You should only call this if you are certain that the other side of the FFI doesn't have a 240 | /// reference to this result (more specifically, to the `message` property) anywhere! 241 | pub unsafe fn manually_release(self) { 242 | if !self.message.is_null() { 243 | destroy_c_string(self.message) 244 | } 245 | } 246 | } 247 | 248 | impl Default for ExternError { 249 | #[inline] 250 | fn default() -> Self { 251 | ExternError::success() 252 | } 253 | } 254 | 255 | // This is the `Err` of std::thread::Result, which is what 256 | // `panic::catch_unwind` returns. 257 | impl From> for ExternError { 258 | fn from(e: Box) -> Self { 259 | // The documentation suggests that it will *usually* be a str or String. 260 | let message = if let Some(s) = e.downcast_ref::<&'static str>() { 261 | (*s).to_string() 262 | } else if let Some(s) = e.downcast_ref::() { 263 | s.clone() 264 | } else { 265 | "Unknown panic!".to_string() 266 | }; 267 | log::error!("Caught a panic calling rust code: {:?}", message); 268 | ExternError::new_error(ErrorCode::PANIC, message) 269 | } 270 | } 271 | 272 | /// A wrapper around error codes, which is represented identically to an i32 on the other side of 273 | /// the FFI. Essentially exists to check that we don't accidentally reuse success/panic codes for 274 | /// other things. 275 | #[repr(transparent)] 276 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] 277 | pub struct ErrorCode(i32); 278 | 279 | impl ErrorCode { 280 | /// The ErrorCode used for success. 281 | pub const SUCCESS: ErrorCode = ErrorCode(0); 282 | 283 | /// The ErrorCode used for panics. It's unlikely you need to ever use this. 284 | // TODO: Consider moving to the reserved region... 285 | pub const PANIC: ErrorCode = ErrorCode(-1); 286 | 287 | /// The ErrorCode used for handle map errors. 288 | pub const INVALID_HANDLE: ErrorCode = ErrorCode(-1000); 289 | 290 | /// Construct an error code from an integer code. 291 | /// 292 | /// ## Panics 293 | /// 294 | /// Panics if you call it with 0 (reserved for success, but you can use `ErrorCode::SUCCESS` if 295 | /// that's what you want), or -1 (reserved for panics, but you can use `ErrorCode::PANIC` if 296 | /// that's what you want). 297 | pub fn new(code: i32) -> Self { 298 | assert!(code > ErrorCode::INVALID_HANDLE.0 && code != ErrorCode::PANIC.0 && code != ErrorCode::SUCCESS.0, 299 | "Error: The ErrorCodes `{success}`, `{panic}`, and all error codes less than or equal \ 300 | to `{reserved}` are reserved (got {code}). You may use the associated constants on this \ 301 | type (`ErrorCode::PANIC`, etc) if you'd like instances of those error codes.", 302 | panic = ErrorCode::PANIC.0, 303 | success = ErrorCode::SUCCESS.0, 304 | reserved = ErrorCode::INVALID_HANDLE.0, 305 | code = code, 306 | ); 307 | 308 | ErrorCode(code) 309 | } 310 | 311 | /// Get the raw numeric value of this ErrorCode. 312 | #[inline] 313 | pub fn code(self) -> i32 { 314 | self.0 315 | } 316 | 317 | /// Returns whether or not this is a success code. 318 | #[inline] 319 | pub fn is_success(self) -> bool { 320 | self.code() == 0 321 | } 322 | } 323 | 324 | #[cfg(test)] 325 | mod test { 326 | use super::*; 327 | 328 | #[test] 329 | #[should_panic] 330 | fn test_code_new_reserved_success() { 331 | ErrorCode::new(0); 332 | } 333 | 334 | #[test] 335 | #[should_panic] 336 | fn test_code_new_reserved_panic() { 337 | ErrorCode::new(-1); 338 | } 339 | 340 | #[test] 341 | #[should_panic] 342 | fn test_code_new_reserved_handle_error() { 343 | ErrorCode::new(-1000); 344 | } 345 | #[test] 346 | #[should_panic] 347 | fn test_code_new_reserved_unknown() { 348 | // Everything below -1000 should be reserved. 349 | ErrorCode::new(-1043); 350 | } 351 | 352 | #[test] 353 | fn test_code_new_allowed() { 354 | // Should not panic 355 | ErrorCode::new(-2); 356 | } 357 | 358 | #[test] 359 | fn test_code() { 360 | assert!(!ErrorCode::PANIC.is_success()); 361 | assert!(!ErrorCode::INVALID_HANDLE.is_success()); 362 | assert!(ErrorCode::SUCCESS.is_success()); 363 | assert_eq!(ErrorCode::default(), ErrorCode::SUCCESS); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /src/ffistr.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | use std::ffi::CStr; 18 | use std::marker::PhantomData; 19 | use std::os::raw::c_char; 20 | 21 | /// `FfiStr<'a>` is a safe (`#[repr(transparent)]`) wrapper around a 22 | /// nul-terminated `*const c_char` (e.g. a C string). Conceptually, it is 23 | /// similar to [`std::ffi::CStr`], except that it may be used in the signatures 24 | /// of extern "C" functions. 25 | /// 26 | /// Functions accepting strings should use this instead of accepting a C string 27 | /// directly. This allows us to write those functions using safe code without 28 | /// allowing safe Rust to cause memory unsafety. 29 | /// 30 | /// A single function for constructing these from Rust ([`FfiStr::from_raw`]) 31 | /// has been provided. Most of the time, this should not be necessary, and users 32 | /// should accept `FfiStr` in the parameter list directly. 33 | /// 34 | /// ## Caveats 35 | /// 36 | /// An effort has been made to make this struct hard to misuse, however it is 37 | /// still possible, if the `'static` lifetime is manually specified in the 38 | /// struct. E.g. 39 | /// 40 | /// ```rust,no_run 41 | /// # use ffi_support::FfiStr; 42 | /// // NEVER DO THIS 43 | /// #[no_mangle] 44 | /// extern "C" fn never_do_this(s: FfiStr<'static>) { 45 | /// // save `s` somewhere, and access it after this 46 | /// // function returns. 47 | /// } 48 | /// ``` 49 | /// 50 | /// Instead, one of the following patterns should be used: 51 | /// 52 | /// ``` 53 | /// # use ffi_support::FfiStr; 54 | /// #[no_mangle] 55 | /// extern "C" fn valid_use_1(s: FfiStr<'_>) { 56 | /// // Use of `s` after this function returns is impossible 57 | /// } 58 | /// // Alternative: 59 | /// #[no_mangle] 60 | /// extern "C" fn valid_use_2(s: FfiStr) { 61 | /// // Use of `s` after this function returns is impossible 62 | /// } 63 | /// ``` 64 | #[repr(transparent)] 65 | pub struct FfiStr<'a> { 66 | cstr: *const c_char, 67 | _boo: PhantomData<&'a ()>, 68 | } 69 | 70 | impl<'a> FfiStr<'a> { 71 | /// Construct an `FfiStr` from a raw pointer. 72 | /// 73 | /// This should not be needed most of the time, and users should instead 74 | /// accept `FfiStr` in function parameter lists. 75 | /// 76 | /// # Safety 77 | /// 78 | /// Dereferences a pointer and is thus unsafe. 79 | #[inline] 80 | pub unsafe fn from_raw(ptr: *const c_char) -> Self { 81 | Self { 82 | cstr: ptr, 83 | _boo: PhantomData, 84 | } 85 | } 86 | 87 | /// Construct a FfiStr from a `std::ffi::CStr`. This is provided for 88 | /// completeness, as a safe method of producing an `FfiStr` in Rust. 89 | #[inline] 90 | pub fn from_cstr(cstr: &'a CStr) -> Self { 91 | Self { 92 | cstr: cstr.as_ptr(), 93 | _boo: PhantomData, 94 | } 95 | } 96 | 97 | /// Get an `&str` out of the `FfiStr`. This will panic in any case that 98 | /// [`FfiStr::as_opt_str`] would return `None` (e.g. null pointer or invalid 99 | /// UTF-8). 100 | /// 101 | /// If the string should be optional, you should use [`FfiStr::as_opt_str`] 102 | /// instead. If an owned string is desired, use [`FfiStr::into_string`] or 103 | /// [`FfiStr::into_opt_string`]. 104 | #[inline] 105 | pub fn as_str(&self) -> &'a str { 106 | self.as_opt_str() 107 | .expect("Unexpected null string pointer passed to rust") 108 | } 109 | 110 | /// Get an `Option<&str>` out of the `FfiStr`. If this stores a null 111 | /// pointer, then None will be returned. If a string containing invalid 112 | /// UTF-8 was passed, then an error will be logged and `None` will be 113 | /// returned. 114 | /// 115 | /// If the string is a required argument, use [`FfiStr::as_str`], or 116 | /// [`FfiStr::into_string`] instead. If `Option` is desired, use 117 | /// [`FfiStr::into_opt_string`] (which will handle invalid UTF-8 by 118 | /// replacing with the replacement character). 119 | pub fn as_opt_str(&self) -> Option<&'a str> { 120 | if self.cstr.is_null() { 121 | return None; 122 | } 123 | unsafe { 124 | match std::ffi::CStr::from_ptr(self.cstr).to_str() { 125 | Ok(s) => Some(s), 126 | Err(e) => { 127 | log::error!("Invalid UTF-8 was passed to rust! {:?}", e); 128 | None 129 | } 130 | } 131 | } 132 | } 133 | 134 | /// Get an `Option` out of the `FfiStr`. Returns `None` if this 135 | /// `FfiStr` holds a null pointer. Note that unlike [`FfiStr::as_opt_str`], 136 | /// invalid UTF-8 is replaced with the replacement character instead of 137 | /// causing us to return None. 138 | /// 139 | /// If the string should be mandatory, you should use 140 | /// [`FfiStr::into_string`] instead. If an owned string is not needed, you 141 | /// may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`] instead, 142 | /// (however, note the differences in how invalid UTF-8 is handled, should 143 | /// this be relevant to your use). 144 | pub fn into_opt_string(self) -> Option { 145 | if !self.cstr.is_null() { 146 | unsafe { Some(CStr::from_ptr(self.cstr).to_string_lossy().to_string()) } 147 | } else { 148 | None 149 | } 150 | } 151 | 152 | /// Get a `String` out of a `FfiStr`. This function is essential a 153 | /// convenience wrapper for `ffi_str.into_opt_string().unwrap()`, with a 154 | /// message that indicates that a null argument was passed to rust when it 155 | /// should be mandatory. As with [`FfiStr::into_opt_string`], invalid UTF-8 156 | /// is replaced with the replacement character if encountered. 157 | /// 158 | /// If the string should *not* be mandatory, you should use 159 | /// [`FfiStr::into_opt_string`] instead. If an owned string is not needed, 160 | /// you may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`] 161 | /// instead, (however, note the differences in how invalid UTF-8 is handled, 162 | /// should this be relevant to your use). 163 | #[inline] 164 | pub fn into_string(self) -> String { 165 | self.into_opt_string() 166 | .expect("Unexpected null string pointer passed to rust") 167 | } 168 | } 169 | 170 | impl<'a> std::fmt::Debug for FfiStr<'a> { 171 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 172 | if let Some(s) = self.as_opt_str() { 173 | write!(f, "FfiStr({:?})", s) 174 | } else { 175 | write!(f, "FfiStr(null)") 176 | } 177 | } 178 | } 179 | 180 | // Conversions... 181 | 182 | impl<'a> From> for String { 183 | #[inline] 184 | fn from(f: FfiStr<'a>) -> Self { 185 | f.into_string() 186 | } 187 | } 188 | 189 | impl<'a> From> for Option { 190 | #[inline] 191 | fn from(f: FfiStr<'a>) -> Self { 192 | f.into_opt_string() 193 | } 194 | } 195 | 196 | impl<'a> From> for Option<&'a str> { 197 | #[inline] 198 | fn from(f: FfiStr<'a>) -> Self { 199 | f.as_opt_str() 200 | } 201 | } 202 | 203 | impl<'a> From> for &'a str { 204 | #[inline] 205 | fn from(f: FfiStr<'a>) -> Self { 206 | f.as_str() 207 | } 208 | } 209 | 210 | // TODO: `AsRef`? 211 | 212 | // Comparisons... 213 | 214 | // Compare FfiStr with eachother 215 | impl<'a> PartialEq for FfiStr<'a> { 216 | #[inline] 217 | fn eq(&self, other: &FfiStr<'a>) -> bool { 218 | self.as_opt_str() == other.as_opt_str() 219 | } 220 | } 221 | 222 | // Compare FfiStr with str 223 | impl<'a> PartialEq for FfiStr<'a> { 224 | #[inline] 225 | fn eq(&self, other: &str) -> bool { 226 | self.as_opt_str() == Some(other) 227 | } 228 | } 229 | 230 | // Compare FfiStr with &str 231 | impl<'a, 'b> PartialEq<&'b str> for FfiStr<'a> { 232 | #[inline] 233 | fn eq(&self, other: &&'b str) -> bool { 234 | self.as_opt_str() == Some(*other) 235 | } 236 | } 237 | 238 | // rhs/lhs swap version of above 239 | impl<'a> PartialEq> for str { 240 | #[inline] 241 | fn eq(&self, other: &FfiStr<'a>) -> bool { 242 | Some(self) == other.as_opt_str() 243 | } 244 | } 245 | 246 | // rhs/lhs swap... 247 | impl<'a, 'b> PartialEq> for &'b str { 248 | #[inline] 249 | fn eq(&self, other: &FfiStr<'a>) -> bool { 250 | Some(*self) == other.as_opt_str() 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/handle_map.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | //! This module provides a [`Handle`] type, which you can think of something 18 | //! like a dynamically checked, type erased reference/pointer type. Depending on 19 | //! the usage pattern a handle can behave as either a borrowed reference, or an 20 | //! owned pointer. 21 | //! 22 | //! They can be losslessly converted [to](Handle::into_u64) and 23 | //! [from](Handle::from_u64) a 64 bit integer, for ease of passing over the FFI 24 | //! (and they implement [`IntoFfi`] using these primitives for this purpose). 25 | //! 26 | //! The benefit is primarially that they can detect common misuse patterns that 27 | //! would otherwise be silent bugs, such as use-after-free, double-free, passing 28 | //! a wrongly-typed pointer to a function, etc. 29 | //! 30 | //! Handles are provided when inserting an item into either a [`HandleMap`] or a 31 | //! [`ConcurrentHandleMap`]. 32 | //! 33 | //! # Comparison to types from other crates 34 | //! 35 | //! [`HandleMap`] is similar to types offered by other crates, such as 36 | //! `slotmap`, or `slab`. However, it has a number of key differences which make 37 | //! it better for our purposes as compared to the types in those crates: 38 | //! 39 | //! 1. Unlike `slab` (but like `slotmap`), we implement versioning, detecting 40 | //! ABA problems, which allows us to detect use after free. 41 | //! 2. Unlike `slotmap`, we don't have the `T: Copy` restriction. 42 | //! 3. Unlike either, we can detect when you use a Key in a map that did not 43 | //! allocate the key. This is true even when the map is from a `.so` file 44 | //! compiled separately. 45 | //! 3. Our implementation of doesn't use any `unsafe` (at the time of this 46 | //! writing). 47 | //! 48 | //! However, it comes with the following drawbacks: 49 | //! 50 | //! 1. `slotmap` holds its version information in a `u32`, and so it takes 51 | //! 231 colliding insertions and deletions before it could 52 | //! potentially fail to detect an ABA issue, wheras we use a `u16`, and are 53 | //! limited to 215. 54 | //! 2. Similarly, we can only hold 216 items at once, unlike 55 | //! `slotmap`'s 232. (Considering these items are typically things 56 | //! like database handles, this is probably plenty). 57 | //! 3. Our implementation is slower, and uses slightly more memory than 58 | //! `slotmap` (which is in part due to the lack of `unsafe` mentioned above) 59 | //! 60 | //! The first two issues seem exceptionally unlikely, even for extremely 61 | //! long-lived `HandleMap`, and we're still memory safe even if they occur (we 62 | //! just might fail to notice a bug). The third issue also seems unimportant for 63 | //! our use case. 64 | 65 | use crate::error::{ErrorCode, ExternError}; 66 | use crate::into_ffi::IntoFfi; 67 | use std::error::Error as StdError; 68 | use std::fmt; 69 | use std::ops; 70 | use std::sync::atomic::{AtomicUsize, Ordering}; 71 | use std::sync::{Mutex, RwLock}; 72 | 73 | /// `HandleMap` is a collection type which can hold any type of value, and 74 | /// offers a stable handle which can be used to retrieve it on insertion. These 75 | /// handles offer methods for converting [to](Handle::into_u64) and 76 | /// [from](Handle::from_u64) 64 bit integers, meaning they're very easy to pass 77 | /// over the FFI (they also implement [`IntoFfi`] for the same purpose). 78 | /// 79 | /// See the [module level docs](index.html) for more information. 80 | /// 81 | /// Note: In FFI code, most usage of `HandleMap` will be done through the 82 | /// [`ConcurrentHandleMap`] type, which is a thin wrapper around a 83 | /// `RwLock>>`. 84 | #[derive(Debug, Clone)] 85 | pub struct HandleMap { 86 | // The value of `map_id` in each `Handle`. 87 | id: u16, 88 | 89 | // Index to the start of the free list. Always points to a free item -- 90 | // we never allow our free list to become empty. 91 | first_free: u16, 92 | 93 | // The number of entries with `data.is_some()`. This is never equal to 94 | // `entries.len()`, we always grow before that point to ensure we always have 95 | // a valid `first_free` index to add entries onto. This is our `len`. 96 | num_entries: usize, 97 | 98 | // The actual data. Note: entries.len() is our 'capacity'. 99 | entries: Vec>, 100 | } 101 | 102 | #[derive(Debug, Clone)] 103 | struct Entry { 104 | // initially 1, incremented on insertion and removal. Thus, 105 | // if version is even, state should always be EntryState::Active. 106 | version: u16, 107 | state: EntryState, 108 | } 109 | 110 | #[derive(Debug, Clone)] 111 | enum EntryState { 112 | // Not part of the free list 113 | Active(T), 114 | // The u16 is the next index in the free list. 115 | InFreeList(u16), 116 | // Part of the free list, but the sentinel. 117 | EndOfFreeList, 118 | } 119 | 120 | impl EntryState { 121 | #[cfg(any(debug_assertions, test))] 122 | fn is_end_of_list(&self) -> bool { 123 | match self { 124 | EntryState::EndOfFreeList => true, 125 | _ => false, 126 | } 127 | } 128 | 129 | #[inline] 130 | fn is_occupied(&self) -> bool { 131 | self.get_item().is_some() 132 | } 133 | 134 | #[inline] 135 | fn get_item(&self) -> Option<&T> { 136 | match self { 137 | EntryState::Active(v) => Some(v), 138 | _ => None, 139 | } 140 | } 141 | 142 | #[inline] 143 | fn get_item_mut(&mut self) -> Option<&mut T> { 144 | match self { 145 | EntryState::Active(v) => Some(v), 146 | _ => None, 147 | } 148 | } 149 | } 150 | 151 | // Small helper to check our casts. 152 | #[inline] 153 | fn to_u16(v: usize) -> u16 { 154 | use std::u16::MAX as U16_MAX; 155 | // Shouldn't ever happen. 156 | assert!(v <= (U16_MAX as usize), "Bug: Doesn't fit in u16: {}", v); 157 | v as u16 158 | } 159 | 160 | /// The maximum capacity of a [`HandleMap`]. Attempting to instantiate one with 161 | /// a larger capacity will cause a panic. 162 | /// 163 | /// Note: This could go as high as `(1 << 16) - 2`, but doing is seems more 164 | /// error prone. For the sake of paranoia, we limit it to this size, which is 165 | /// already quite a bit larger than it seems like we're likely to ever need. 166 | pub const MAX_CAPACITY: usize = (1 << 15) - 1; 167 | 168 | // Never having to worry about capacity == 0 simplifies the code at the cost of 169 | // worse memory usage. It doesn't seem like there's any reason to make this 170 | // public. 171 | const MIN_CAPACITY: usize = 4; 172 | 173 | /// An error representing the ways a `Handle` may be invalid. 174 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 175 | pub enum HandleError { 176 | /// Identical to invalid handle, but has a slightly more helpful 177 | /// message for the most common case 0. 178 | NullHandle, 179 | 180 | /// Returned from [`Handle::from_u64`] if [`Handle::is_valid`] fails. 181 | InvalidHandle, 182 | 183 | /// Returned from get/get_mut/delete if the handle is stale (this indicates 184 | /// something equivalent to a use-after-free / double-free, etc). 185 | StaleVersion, 186 | 187 | /// Returned if the handle index references an index past the end of the 188 | /// HandleMap. 189 | IndexPastEnd, 190 | 191 | /// The handle has a map_id for a different map than the one it was 192 | /// attempted to be used with. 193 | WrongMap, 194 | } 195 | 196 | impl StdError for HandleError {} 197 | 198 | impl fmt::Display for HandleError { 199 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 200 | use HandleError::*; 201 | match self { 202 | NullHandle => { 203 | f.write_str("Tried to use a null handle (this object has probably been closed)") 204 | } 205 | InvalidHandle => f.write_str("u64 could not encode a valid Handle"), 206 | StaleVersion => f.write_str("Handle has stale version number"), 207 | IndexPastEnd => f.write_str("Handle references a index past the end of this HandleMap"), 208 | WrongMap => f.write_str("Handle is from a different map"), 209 | } 210 | } 211 | } 212 | 213 | impl From for ExternError { 214 | fn from(e: HandleError) -> Self { 215 | ExternError::new_error(ErrorCode::INVALID_HANDLE, e.to_string()) 216 | } 217 | } 218 | 219 | impl HandleMap { 220 | /// Create a new `HandleMap` with the default capacity. 221 | pub fn new() -> Self { 222 | Self::new_with_capacity(MIN_CAPACITY) 223 | } 224 | 225 | /// Allocate a new `HandleMap`. Note that the actual capacity may be larger 226 | /// than the requested value. 227 | /// 228 | /// Panics if `request` is greater than [`handle_map::MAX_CAPACITY`](MAX_CAPACITY) 229 | pub fn new_with_capacity(request: usize) -> Self { 230 | assert!( 231 | request <= MAX_CAPACITY, 232 | "HandleMap capacity is limited to {} (request was {})", 233 | MAX_CAPACITY, 234 | request 235 | ); 236 | 237 | let capacity = request.max(MIN_CAPACITY); 238 | let id = next_handle_map_id(); 239 | let mut entries = Vec::with_capacity(capacity); 240 | 241 | // Initialize each entry with version 1, and as a member of the free list 242 | for i in 0..(capacity - 1) { 243 | entries.push(Entry { 244 | version: 1, 245 | state: EntryState::InFreeList(to_u16(i + 1)), 246 | }); 247 | } 248 | 249 | // And the final entry is at the end of the free list 250 | // (but still has version 1). 251 | entries.push(Entry { 252 | version: 1, 253 | state: EntryState::EndOfFreeList, 254 | }); 255 | Self { 256 | id, 257 | first_free: 0, 258 | num_entries: 0, 259 | entries, 260 | } 261 | } 262 | 263 | /// Get the number of entries in the `HandleMap`. 264 | #[inline] 265 | pub fn len(&self) -> usize { 266 | self.num_entries 267 | } 268 | 269 | /// Returns true if the HandleMap is empty. 270 | #[inline] 271 | pub fn is_empty(&self) -> bool { 272 | self.len() == 0 273 | } 274 | 275 | /// Returns the number of slots allocated in the handle map. 276 | #[inline] 277 | pub fn capacity(&self) -> usize { 278 | // It's not a bug that this isn't entries.capacity() -- We're returning 279 | // how many slots exist, not something about the backing memory allocation 280 | self.entries.len() 281 | } 282 | 283 | fn ensure_capacity(&mut self, cap_at_least: usize) { 284 | assert_ne!(self.len(), self.capacity(), "Bug: should have grown by now"); 285 | assert!(cap_at_least <= MAX_CAPACITY, "HandleMap overfilled"); 286 | if self.capacity() > cap_at_least { 287 | return; 288 | } 289 | 290 | let mut next_cap = self.capacity(); 291 | while next_cap <= cap_at_least { 292 | next_cap *= 2; 293 | } 294 | next_cap = next_cap.min(MAX_CAPACITY); 295 | 296 | let need_extra = next_cap.saturating_sub(self.entries.capacity()); 297 | self.entries.reserve(need_extra); 298 | 299 | assert!( 300 | !self.entries[self.first_free as usize].state.is_occupied(), 301 | "Bug: HandleMap.first_free points at occupied index" 302 | ); 303 | 304 | // Insert new entries at the front of our list. 305 | while self.entries.len() < next_cap - 1 { 306 | // This is a little wasteful but whatever. Add each new entry to the 307 | // front of the free list one at a time. 308 | self.entries.push(Entry { 309 | version: 1, 310 | state: EntryState::InFreeList(self.first_free), 311 | }); 312 | self.first_free = to_u16(self.entries.len() - 1); 313 | } 314 | 315 | self.debug_check_valid(); 316 | } 317 | 318 | #[inline] 319 | fn debug_check_valid(&self) { 320 | // Run the expensive validity check in tests and in debug builds. 321 | #[cfg(any(debug_assertions, test))] 322 | { 323 | self.assert_valid(); 324 | } 325 | } 326 | 327 | #[cfg(any(debug_assertions, test))] 328 | fn assert_valid(&self) { 329 | assert_ne!(self.len(), self.capacity()); 330 | assert!(self.capacity() <= MAX_CAPACITY, "Entries too large"); 331 | // Validate that our free list is correct. 332 | 333 | let number_of_ends = self 334 | .entries 335 | .iter() 336 | .filter(|e| e.state.is_end_of_list()) 337 | .count(); 338 | assert_eq!( 339 | number_of_ends, 1, 340 | "More than one entry think's it's the end of the list, or no entries do" 341 | ); 342 | 343 | // Check that the free list hits every unoccupied item. 344 | // The tuple is: `(should_be_in_free_list, is_in_free_list)`. 345 | let mut free_indices = vec![(false, false); self.capacity()]; 346 | for (i, e) in self.entries.iter().enumerate() { 347 | if !e.state.is_occupied() { 348 | free_indices[i].0 = true; 349 | } 350 | } 351 | 352 | let mut next = self.first_free; 353 | loop { 354 | let ni = next as usize; 355 | 356 | assert!( 357 | ni <= free_indices.len(), 358 | "Free list contains out of bounds index!" 359 | ); 360 | 361 | assert!( 362 | free_indices[ni].0, 363 | "Free list has an index that shouldn't be free! {}", 364 | ni 365 | ); 366 | 367 | assert!( 368 | !free_indices[ni].1, 369 | "Free list hit an index ({}) more than once! Cycle detected!", 370 | ni 371 | ); 372 | 373 | free_indices[ni].1 = true; 374 | 375 | match &self.entries[ni].state { 376 | EntryState::InFreeList(next_index) => next = *next_index, 377 | EntryState::EndOfFreeList => break, 378 | // Hitting `Active` here is probably not possible because of the checks above, but who knows. 379 | EntryState::Active(..) => unreachable!("Bug: Active item in free list at {}", next), 380 | } 381 | } 382 | let mut occupied_count = 0; 383 | for (i, &(should_be_free, is_free)) in free_indices.iter().enumerate() { 384 | assert_eq!( 385 | should_be_free, is_free, 386 | "Free list missed item, or contains an item it shouldn't: {}", 387 | i 388 | ); 389 | if !should_be_free { 390 | occupied_count += 1; 391 | } 392 | } 393 | assert_eq!( 394 | self.num_entries, occupied_count, 395 | "num_entries doesn't reflect the actual number of entries" 396 | ); 397 | } 398 | 399 | /// Insert an item into the map, and return a handle to it. 400 | pub fn insert(&mut self, v: T) -> Handle { 401 | let need_cap = self.len() + 1; 402 | self.ensure_capacity(need_cap); 403 | let index = self.first_free; 404 | let result = { 405 | // Scoped mutable borrow of entry. 406 | let entry = &mut self.entries[index as usize]; 407 | let new_first_free = match entry.state { 408 | EntryState::InFreeList(i) => i, 409 | _ => panic!("Bug: next_index pointed at non-free list entry (or end of list)"), 410 | }; 411 | entry.version += 1; 412 | if entry.version == 0 { 413 | entry.version += 2; 414 | } 415 | entry.state = EntryState::Active(v); 416 | self.first_free = new_first_free; 417 | self.num_entries += 1; 418 | 419 | Handle { 420 | map_id: self.id, 421 | version: entry.version, 422 | index, 423 | } 424 | }; 425 | self.debug_check_valid(); 426 | result 427 | } 428 | 429 | // Helper to contain the handle validation boilerplate. Returns `h.index as usize`. 430 | fn check_handle(&self, h: Handle) -> Result { 431 | if h.map_id != self.id { 432 | log::info!( 433 | "HandleMap access with handle having wrong map id: {:?} (our map id is {})", 434 | h, 435 | self.id 436 | ); 437 | return Err(HandleError::WrongMap); 438 | } 439 | let index = h.index as usize; 440 | if index >= self.entries.len() { 441 | log::info!("HandleMap accessed with handle past end of map: {:?}", h); 442 | return Err(HandleError::IndexPastEnd); 443 | } 444 | if self.entries[index].version != h.version { 445 | log::info!( 446 | "HandleMap accessed with handle with wrong version {:?} (entry version is {})", 447 | h, 448 | self.entries[index].version 449 | ); 450 | return Err(HandleError::StaleVersion); 451 | } 452 | // At this point, we know the handle version matches the entry version, 453 | // but if someone created a specially invalid handle, they could have 454 | // its version match the version they expect an unoccupied index to 455 | // have. 456 | // 457 | // We don't use any unsafe, so the worse thing that can happen here is 458 | // that we get confused and panic, but still that's not great, so we 459 | // check for this explicitly. 460 | // 461 | // Note that `active` versions are always even, as they start at 1, and 462 | // are incremented on both insertion and deletion. 463 | // 464 | // Anyway, this is just for sanity checking, we already check this in 465 | // practice when we convert `u64`s into `Handle`s, which is the only 466 | // way we ever use these in the real world. 467 | if (h.version % 2) != 0 { 468 | log::info!( 469 | "HandleMap given handle with matching but illegal version: {:?}", 470 | h, 471 | ); 472 | return Err(HandleError::StaleVersion); 473 | } 474 | Ok(index) 475 | } 476 | 477 | /// Delete an item from the HandleMap. 478 | pub fn delete(&mut self, h: Handle) -> Result<(), HandleError> { 479 | self.remove(h).map(drop) 480 | } 481 | 482 | /// Remove an item from the HandleMap, returning the old value. 483 | pub fn remove(&mut self, h: Handle) -> Result { 484 | let index = self.check_handle(h)?; 485 | let prev = { 486 | // Scoped mutable borrow of entry. 487 | let entry = &mut self.entries[index]; 488 | entry.version += 1; 489 | let index = h.index; 490 | let last_state = 491 | std::mem::replace(&mut entry.state, EntryState::InFreeList(self.first_free)); 492 | self.num_entries -= 1; 493 | self.first_free = index; 494 | 495 | if let EntryState::Active(value) = last_state { 496 | value 497 | } else { 498 | // This indicates either a bug in HandleMap or memory 499 | // corruption. Abandon all hope. 500 | unreachable!( 501 | "Handle {:?} passed validation but references unoccupied entry", 502 | h 503 | ); 504 | } 505 | }; 506 | self.debug_check_valid(); 507 | Ok(prev) 508 | } 509 | 510 | /// Get a reference to the item referenced by the handle, or return a 511 | /// [`HandleError`] describing the problem. 512 | pub fn get(&self, h: Handle) -> Result<&T, HandleError> { 513 | let idx = self.check_handle(h)?; 514 | let entry = &self.entries[idx]; 515 | // This should be caught by check_handle above, but we avoid panicking 516 | // because we'd rather not poison any locks we don't have to poison 517 | let item = entry 518 | .state 519 | .get_item() 520 | .ok_or_else(|| HandleError::InvalidHandle)?; 521 | Ok(item) 522 | } 523 | 524 | /// Get a mut reference to the item referenced by the handle, or return a 525 | /// [`HandleError`] describing the problem. 526 | pub fn get_mut(&mut self, h: Handle) -> Result<&mut T, HandleError> { 527 | let idx = self.check_handle(h)?; 528 | let entry = &mut self.entries[idx]; 529 | // This should be caught by check_handle above, but we avoid panicking 530 | // because we'd rather not poison any locks we don't have to poison 531 | let item = entry 532 | .state 533 | .get_item_mut() 534 | .ok_or_else(|| HandleError::InvalidHandle)?; 535 | Ok(item) 536 | } 537 | } 538 | 539 | impl Default for HandleMap { 540 | #[inline] 541 | fn default() -> Self { 542 | HandleMap::new() 543 | } 544 | } 545 | 546 | impl ops::Index for HandleMap { 547 | type Output = T; 548 | #[inline] 549 | fn index(&self, h: Handle) -> &T { 550 | self.get(h) 551 | .expect("Indexed into HandleMap with invalid handle!") 552 | } 553 | } 554 | 555 | // We don't implement IndexMut intentionally (implementing ops::Index is 556 | // dubious enough) 557 | 558 | /// A Handle we allow to be returned over the FFI by implementing [`IntoFfi`]. 559 | /// This type is intentionally not `#[repr(C)]`, and getting the data out of the 560 | /// FFI is done using `Handle::from_u64`, or it's implemetation of `From`. 561 | /// 562 | /// It consists of, at a minimum: 563 | /// 564 | /// - A "map id" (used to ensure you're using it with the correct map) 565 | /// - a "version" (incremented when the value in the index changes, used to 566 | /// detect multiple frees, use after free, and ABA and ABA) 567 | /// - and a field indicating which index it goes into. 568 | /// 569 | /// In practice, it may also contain extra information to help detect other 570 | /// errors (currently it stores a "magic value" used to detect invalid 571 | /// [`Handle`]s). 572 | /// 573 | /// These fields may change but the following guarantees are made about the 574 | /// internal representation: 575 | /// 576 | /// - This will always be representable in 64 bits. 577 | /// - The bits, when interpreted as a signed 64 bit integer, will be positive 578 | /// (that is to say, it will *actually* be representable in 63 bits, since 579 | /// this makes the most significant bit unavailable for the purposes of 580 | /// encoding). This guarantee makes things slightly less dubious when passing 581 | /// things to Java, gives us some extra validation ability, etc. 582 | #[derive(Copy, Clone, Debug, PartialEq)] 583 | pub struct Handle { 584 | map_id: u16, 585 | version: u16, 586 | index: u16, 587 | } 588 | 589 | // We stuff this into the top 16 bits of the handle when u16 encoded to detect 590 | // various sorts of weirdness. It's the letters 'A' and 'S' as ASCII, but the 591 | // only important thing about it is that the most significant bit be unset. 592 | const HANDLE_MAGIC: u16 = 0x4153_u16; 593 | 594 | impl Handle { 595 | /// Convert a `Handle` to a `u64`. You can also use `Into::into` directly. 596 | /// Most uses of this will be automatic due to our [`IntoFfi`] implementation. 597 | #[inline] 598 | pub fn into_u64(self) -> u64 { 599 | let map_id = u64::from(self.map_id); 600 | let version = u64::from(self.version); 601 | let index = u64::from(self.index); 602 | // SOMEDAY: we could also use this as a sort of CRC if we were really paranoid. 603 | // e.g. `magic = combine_to_u16(map_id, version, index)`. 604 | let magic = u64::from(HANDLE_MAGIC); 605 | (magic << 48) | (map_id << 32) | (index << 16) | version 606 | } 607 | 608 | /// Convert a `u64` to a `Handle`. Inverse of `into_u64`. We also implement 609 | /// `From::from` (which will panic instead of returning Err). 610 | /// 611 | /// Returns [`HandleError::InvalidHandle`](HandleError) if the bits cannot 612 | /// possibly represent a valid handle. 613 | pub fn from_u64(v: u64) -> Result { 614 | if !Handle::is_valid(v) { 615 | log::warn!("Illegal handle! {:x}", v); 616 | if v == 0 { 617 | Err(HandleError::NullHandle) 618 | } else { 619 | Err(HandleError::InvalidHandle) 620 | } 621 | } else { 622 | let map_id = (v >> 32) as u16; 623 | let index = (v >> 16) as u16; 624 | let version = v as u16; 625 | Ok(Self { 626 | map_id, 627 | version, 628 | index, 629 | }) 630 | } 631 | } 632 | 633 | /// Returns whether or not `v` makes a bit pattern that could represent an 634 | /// encoded [`Handle`]. 635 | #[inline] 636 | pub fn is_valid(v: u64) -> bool { 637 | (v >> 48) == u64::from(HANDLE_MAGIC) && 638 | // The "bottom" field is the version. We increment it both when 639 | // inserting and removing, and they're all initially 1. So, all valid 640 | // handles that we returned should have an even version. 641 | ((v & 1) == 0) 642 | } 643 | } 644 | 645 | impl From for Handle { 646 | fn from(u: u64) -> Self { 647 | Handle::from_u64(u).expect("Illegal handle!") 648 | } 649 | } 650 | 651 | impl From for u64 { 652 | #[inline] 653 | fn from(h: Handle) -> u64 { 654 | h.into_u64() 655 | } 656 | } 657 | 658 | unsafe impl IntoFfi for Handle { 659 | type Value = u64; 660 | // Note: intentionally does not encode a valid handle for any map. 661 | #[inline] 662 | fn ffi_default() -> u64 { 663 | 0u64 664 | } 665 | #[inline] 666 | fn into_ffi_value(self) -> u64 { 667 | self.into_u64() 668 | } 669 | } 670 | 671 | /// `ConcurrentHandleMap` is a relatively thin wrapper around 672 | /// `RwLock>>`. Due to the nested locking, it's not possible 673 | /// to implement the same API as [`HandleMap`], however it does implement an API 674 | /// that offers equivalent functionality, as well as several functions that 675 | /// greatly simplify FFI usage (see example below). 676 | /// 677 | /// See the [module level documentation](index.html) for more info. 678 | /// 679 | /// # Example 680 | /// 681 | /// ```rust,no_run 682 | /// # #[macro_use] extern crate lazy_static; 683 | /// # extern crate ffi_support; 684 | /// # use ffi_support::*; 685 | /// # use std::sync::*; 686 | /// 687 | /// // Somewhere... 688 | /// struct Thing { value: f64 } 689 | /// 690 | /// lazy_static! { 691 | /// static ref ITEMS: ConcurrentHandleMap = ConcurrentHandleMap::new(); 692 | /// } 693 | /// 694 | /// #[no_mangle] 695 | /// pub extern "C" fn mylib_new_thing(value: f64, err: &mut ExternError) -> u64 { 696 | /// // Most uses will be `ITEMS.insert_with_result`. Note that this already 697 | /// // calls `call_with_output` (or `call_with_result` if this were 698 | /// // `insert_with_result`) for you. 699 | /// ITEMS.insert_with_output(err, || Thing { value }) 700 | /// } 701 | /// 702 | /// #[no_mangle] 703 | /// pub extern "C" fn mylib_thing_value(h: u64, err: &mut ExternError) -> f64 { 704 | /// // Or `ITEMS.call_with_result` for the fallible functions. 705 | /// ITEMS.call_with_output(err, h, |thing| thing.value) 706 | /// } 707 | /// 708 | /// #[no_mangle] 709 | /// pub extern "C" fn mylib_thing_set_value(h: u64, new_value: f64, err: &mut ExternError) { 710 | /// ITEMS.call_with_output_mut(err, h, |thing| { 711 | /// thing.value = new_value; 712 | /// }) 713 | /// } 714 | /// 715 | /// // Note: defines the following function: 716 | /// // pub extern "C" fn mylib_destroy_thing(h: u64, err: &mut ExternError) 717 | /// define_handle_map_deleter!(ITEMS, mylib_destroy_thing); 718 | /// ``` 719 | pub struct ConcurrentHandleMap { 720 | /// The underlying map. Public so that more advanced use-cases 721 | /// may use it as they please. 722 | pub map: RwLock>>, 723 | } 724 | 725 | impl ConcurrentHandleMap { 726 | /// Construct a new `ConcurrentHandleMap`. 727 | pub fn new() -> Self { 728 | Self { 729 | map: RwLock::new(HandleMap::new()), 730 | } 731 | } 732 | 733 | /// Get the number of entries in the `ConcurrentHandleMap`. 734 | /// 735 | /// This takes the map's `read` lock. 736 | #[inline] 737 | pub fn len(&self) -> usize { 738 | let map = self.map.read().unwrap(); 739 | map.len() 740 | } 741 | 742 | /// Returns true if the `ConcurrentHandleMap` is empty. 743 | /// 744 | /// This takes the map's `read` lock. 745 | #[inline] 746 | pub fn is_empty(&self) -> bool { 747 | self.len() == 0 748 | } 749 | 750 | /// Insert an item into the map, returning the newly allocated handle to the 751 | /// item. 752 | /// 753 | /// # Locking 754 | /// 755 | /// Note that this requires taking the map's write lock, and so it will 756 | /// block until all other threads have finished any read/write operations. 757 | pub fn insert(&self, v: T) -> Handle { 758 | // Fails if the lock is poisoned. Not clear what we should do here... We 759 | // could always insert anyway (by matching on LockResult), but that 760 | // seems... really quite dubious. 761 | let mut map = self.map.write().unwrap(); 762 | map.insert(Mutex::new(v)) 763 | } 764 | 765 | /// Remove an item from the map. 766 | /// 767 | /// # Locking 768 | /// 769 | /// Note that this requires taking the map's write lock, and so it will 770 | /// block until all other threads have finished any read/write operations. 771 | pub fn delete(&self, h: Handle) -> Result<(), HandleError> { 772 | // We use `remove` and not delete (and use the inner block) to ensure 773 | // that if `v`'s destructor panics, we aren't holding the write lock 774 | // when it happens, so that the map itself doesn't get poisoned. 775 | let v = { 776 | let mut map = self.map.write().unwrap(); 777 | map.remove(h) 778 | }; 779 | v.map(drop) 780 | } 781 | 782 | /// Convenient wrapper for `delete` which takes a `u64` that it will 783 | /// convert to a handle. 784 | /// 785 | /// The main benefit (besides convenience) of this over the version 786 | /// that takes a [`Handle`] is that it allows handling handle-related errors 787 | /// in one place. 788 | pub fn delete_u64(&self, h: u64) -> Result<(), HandleError> { 789 | self.delete(Handle::from_u64(h)?) 790 | } 791 | 792 | /// Remove an item from the map, returning either the item, 793 | /// or None if its guard mutex got poisoned at some point. 794 | /// 795 | /// # Locking 796 | /// 797 | /// Note that this requires taking the map's write lock, and so it will 798 | /// block until all other threads have finished any read/write operations. 799 | pub fn remove(&self, h: Handle) -> Result, HandleError> { 800 | let mut map = self.map.write().unwrap(); 801 | let mutex = map.remove(h)?; 802 | Ok(mutex.into_inner().ok()) 803 | } 804 | 805 | /// Convenient wrapper for `remove` which takes a `u64` that it will 806 | /// convert to a handle. 807 | /// 808 | /// The main benefit (besides convenience) of this over the version 809 | /// that takes a [`Handle`] is that it allows handling handle-related errors 810 | /// in one place. 811 | pub fn remove_u64(&self, h: u64) -> Result, HandleError> { 812 | self.remove(Handle::from_u64(h)?) 813 | } 814 | 815 | /// Call `callback` with a non-mutable reference to the item from the map, 816 | /// after acquiring the necessary locks. 817 | /// 818 | /// # Locking 819 | /// 820 | /// Note that this requires taking both: 821 | /// 822 | /// - The map's read lock, and so it will block until all other threads have 823 | /// finished any write operations. 824 | /// - The mutex on the slot the handle is mapped to. 825 | /// 826 | /// And so it will block if there are ongoing write operations, or if 827 | /// another thread is reading from the same handle. 828 | /// 829 | /// # Panics 830 | /// 831 | /// This will panic if a previous `get()` or `get_mut()` call has panicked 832 | /// inside it's callback. The solution to this 833 | /// 834 | /// (It may also panic if the handle map detects internal state corruption, 835 | /// however this should not happen except for bugs in the handle map code). 836 | pub fn get(&self, h: Handle, callback: F) -> Result 837 | where 838 | F: FnOnce(&T) -> Result, 839 | E: From, 840 | { 841 | self.get_mut(h, |v| callback(v)) 842 | } 843 | 844 | /// Call `callback` with a mutable reference to the item from the map, after 845 | /// acquiring the necessary locks. 846 | /// 847 | /// # Locking 848 | /// 849 | /// Note that this requires taking both: 850 | /// 851 | /// - The map's read lock, and so it will block until all other threads have 852 | /// finished any write operations. 853 | /// - The mutex on the slot the handle is mapped to. 854 | /// 855 | /// And so it will block if there are ongoing write operations, or if 856 | /// another thread is reading from the same handle. 857 | /// 858 | /// # Panics 859 | /// 860 | /// This will panic if a previous `get()` or `get_mut()` call has panicked 861 | /// inside it's callback. The only solution to this is to remove and reinsert 862 | /// said item. 863 | /// 864 | /// (It may also panic if the handle map detects internal state corruption, 865 | /// however this should not happen except for bugs in the handle map code). 866 | pub fn get_mut(&self, h: Handle, callback: F) -> Result 867 | where 868 | F: FnOnce(&mut T) -> Result, 869 | E: From, 870 | { 871 | // XXX figure out how to handle poison... 872 | let map = self.map.read().unwrap(); 873 | let mtx = map.get(h)?; 874 | let mut hm = mtx.lock().unwrap(); 875 | callback(&mut *hm) 876 | } 877 | 878 | /// Convenient wrapper for `get` which takes a `u64` that it will convert to 879 | /// a handle. 880 | /// 881 | /// The other benefit (besides convenience) of this over the version 882 | /// that takes a [`Handle`] is that it allows handling handle-related errors 883 | /// in one place. 884 | /// 885 | /// # Locking 886 | /// 887 | /// Note that this requires taking both: 888 | /// 889 | /// - The map's read lock, and so it will block until all other threads have 890 | /// finished any write operations. 891 | /// - The mutex on the slot the handle is mapped to. 892 | /// 893 | /// And so it will block if there are ongoing write operations, or if 894 | /// another thread is reading from the same handle. 895 | pub fn get_u64(&self, u: u64, callback: F) -> Result 896 | where 897 | F: FnOnce(&T) -> Result, 898 | E: From, 899 | { 900 | self.get(Handle::from_u64(u)?, callback) 901 | } 902 | 903 | /// Convenient wrapper for [`Self::get_mut`] which takes a `u64` that it will 904 | /// convert to a handle. 905 | /// 906 | /// The main benefit (besides convenience) of this over the version 907 | /// that takes a [`Handle`] is that it allows handling handle-related errors 908 | /// in one place. 909 | /// 910 | /// # Locking 911 | /// 912 | /// Note that this requires taking both: 913 | /// 914 | /// - The map's read lock, and so it will block until all other threads have 915 | /// finished any write operations. 916 | /// - The mutex on the slot the handle is mapped to. 917 | /// 918 | /// And so it will block if there are ongoing write operations, or if 919 | /// another thread is reading from the same handle. 920 | pub fn get_mut_u64(&self, u: u64, callback: F) -> Result 921 | where 922 | F: FnOnce(&mut T) -> Result, 923 | E: From, 924 | { 925 | self.get_mut(Handle::from_u64(u)?, callback) 926 | } 927 | 928 | /// Helper that performs both a 929 | /// [`call_with_result`][crate::call_with_result] and 930 | /// [`get`](ConcurrentHandleMap::get_mut). 931 | pub fn call_with_result_mut( 932 | &self, 933 | out_error: &mut ExternError, 934 | h: u64, 935 | callback: F, 936 | ) -> R::Value 937 | where 938 | F: std::panic::UnwindSafe + FnOnce(&mut T) -> Result, 939 | ExternError: From, 940 | R: IntoFfi, 941 | { 942 | use crate::call_with_result; 943 | call_with_result(out_error, || -> Result<_, ExternError> { 944 | // We can't reuse get_mut here because it would require E: 945 | // From, which is inconvenient... 946 | let h = Handle::from_u64(h)?; 947 | let map = self.map.read().unwrap(); 948 | let mtx = map.get(h)?; 949 | let mut hm = mtx.lock().unwrap(); 950 | Ok(callback(&mut *hm)?) 951 | }) 952 | } 953 | 954 | /// Helper that performs both a 955 | /// [`call_with_result`][crate::call_with_result] and 956 | /// [`get`](ConcurrentHandleMap::get). 957 | pub fn call_with_result( 958 | &self, 959 | out_error: &mut ExternError, 960 | h: u64, 961 | callback: F, 962 | ) -> R::Value 963 | where 964 | F: std::panic::UnwindSafe + FnOnce(&T) -> Result, 965 | ExternError: From, 966 | R: IntoFfi, 967 | { 968 | self.call_with_result_mut(out_error, h, |r| callback(r)) 969 | } 970 | 971 | /// Helper that performs both a 972 | /// [`call_with_output`][crate::call_with_output] and 973 | /// [`get`](ConcurrentHandleMap::get). 974 | pub fn call_with_output( 975 | &self, 976 | out_error: &mut ExternError, 977 | h: u64, 978 | callback: F, 979 | ) -> R::Value 980 | where 981 | F: std::panic::UnwindSafe + FnOnce(&T) -> R, 982 | R: IntoFfi, 983 | { 984 | self.call_with_result(out_error, h, |r| -> Result<_, HandleError> { 985 | Ok(callback(r)) 986 | }) 987 | } 988 | 989 | /// Helper that performs both a 990 | /// [`call_with_output`][crate::call_with_output] and 991 | /// [`get_mut`](ConcurrentHandleMap::get). 992 | pub fn call_with_output_mut( 993 | &self, 994 | out_error: &mut ExternError, 995 | h: u64, 996 | callback: F, 997 | ) -> R::Value 998 | where 999 | F: std::panic::UnwindSafe + FnOnce(&mut T) -> R, 1000 | R: IntoFfi, 1001 | { 1002 | self.call_with_result_mut(out_error, h, |r| -> Result<_, HandleError> { 1003 | Ok(callback(r)) 1004 | }) 1005 | } 1006 | 1007 | /// Use `constructor` to create and insert a `T`, while inside a 1008 | /// [`call_with_result`][crate::call_with_result] call (to handle panics and 1009 | /// map errors onto an [`ExternError`][crate::ExternError]). 1010 | pub fn insert_with_result(&self, out_error: &mut ExternError, constructor: F) -> u64 1011 | where 1012 | F: std::panic::UnwindSafe + FnOnce() -> Result, 1013 | ExternError: From, 1014 | { 1015 | use crate::call_with_result; 1016 | call_with_result(out_error, || -> Result<_, ExternError> { 1017 | // Note: it's important that we don't call the constructor while 1018 | // we're holding the write lock, because we don't want to poison 1019 | // the entire map if it panics! 1020 | let to_insert = constructor()?; 1021 | Ok(self.insert(to_insert)) 1022 | }) 1023 | } 1024 | 1025 | /// Equivalent to 1026 | /// [`insert_with_result`](ConcurrentHandleMap::insert_with_result) for the 1027 | /// case where the constructor cannot produce an error. 1028 | /// 1029 | /// The name is somewhat dubious, since there's no `output`, but it's 1030 | /// intended to make it clear that it contains a 1031 | /// [`call_with_output`][crate::call_with_output] internally. 1032 | pub fn insert_with_output(&self, out_error: &mut ExternError, constructor: F) -> u64 1033 | where 1034 | F: std::panic::UnwindSafe + FnOnce() -> T, 1035 | { 1036 | // The Err type isn't important here beyond being convertable to ExternError 1037 | self.insert_with_result(out_error, || -> Result<_, HandleError> { 1038 | Ok(constructor()) 1039 | }) 1040 | } 1041 | } 1042 | 1043 | impl Default for ConcurrentHandleMap { 1044 | #[inline] 1045 | fn default() -> Self { 1046 | Self::new() 1047 | } 1048 | } 1049 | 1050 | // Returns the next map_id. 1051 | fn next_handle_map_id() -> u16 { 1052 | let id = HANDLE_MAP_ID_COUNTER 1053 | .fetch_add(1, Ordering::SeqCst) 1054 | .wrapping_add(1); 1055 | id as u16 1056 | } 1057 | 1058 | // Note: These IDs are only used to detect using a key against the wrong HandleMap. 1059 | // We ensure they're randomly initialized, to prevent using them across separately 1060 | // compiled .so files. 1061 | lazy_static::lazy_static! { 1062 | // This should be `AtomicU16`, but those aren't stablilized yet. 1063 | // Instead, we just cast to u16 on read. 1064 | static ref HANDLE_MAP_ID_COUNTER: AtomicUsize = { 1065 | // Abuse HashMap's RandomState to get a strong RNG without bringing in 1066 | // the `rand` crate (OTOH maybe we should just bring in the rand crate?) 1067 | use std::collections::hash_map::RandomState; 1068 | use std::hash::{BuildHasher, Hasher}; 1069 | let init = RandomState::new().build_hasher().finish() as usize; 1070 | AtomicUsize::new(init) 1071 | }; 1072 | } 1073 | 1074 | #[cfg(test)] 1075 | mod test { 1076 | use super::*; 1077 | 1078 | #[derive(PartialEq, Debug)] 1079 | pub(super) struct Foobar(usize); 1080 | 1081 | #[test] 1082 | fn test_invalid_handle() { 1083 | assert_eq!(Handle::from_u64(0), Err(HandleError::NullHandle)); 1084 | // Valid except `version` is odd 1085 | assert_eq!( 1086 | Handle::from_u64((u64::from(HANDLE_MAGIC) << 48) | 0x1234_0012_0001), 1087 | Err(HandleError::InvalidHandle) 1088 | ); 1089 | 1090 | assert_eq!( 1091 | Handle::from_u64((u64::from(HANDLE_MAGIC) << 48) | 0x1234_0012_0002), 1092 | Ok(Handle { 1093 | version: 0x0002, 1094 | index: 0x0012, 1095 | map_id: 0x1234, 1096 | }) 1097 | ); 1098 | } 1099 | 1100 | #[test] 1101 | fn test_correct_value_single() { 1102 | let mut map = HandleMap::new(); 1103 | let handle = map.insert(Foobar(1234)); 1104 | assert_eq!(map.get(handle).unwrap(), &Foobar(1234)); 1105 | map.delete(handle).unwrap(); 1106 | assert_eq!(map.get(handle), Err(HandleError::StaleVersion)); 1107 | } 1108 | 1109 | #[test] 1110 | fn test_correct_value_multiple() { 1111 | let mut map = HandleMap::new(); 1112 | let handle1 = map.insert(Foobar(1234)); 1113 | let handle2 = map.insert(Foobar(4321)); 1114 | assert_eq!(map.get(handle1).unwrap(), &Foobar(1234)); 1115 | assert_eq!(map.get(handle2).unwrap(), &Foobar(4321)); 1116 | map.delete(handle1).unwrap(); 1117 | assert_eq!(map.get(handle1), Err(HandleError::StaleVersion)); 1118 | assert_eq!(map.get(handle2).unwrap(), &Foobar(4321)); 1119 | } 1120 | 1121 | #[test] 1122 | fn test_wrong_map() { 1123 | let mut map1 = HandleMap::new(); 1124 | let mut map2 = HandleMap::new(); 1125 | 1126 | let handle1 = map1.insert(Foobar(1234)); 1127 | let handle2 = map2.insert(Foobar(1234)); 1128 | 1129 | assert_eq!(map1.get(handle1).unwrap(), &Foobar(1234)); 1130 | assert_eq!(map2.get(handle2).unwrap(), &Foobar(1234)); 1131 | 1132 | assert_eq!(map1.get(handle2), Err(HandleError::WrongMap)); 1133 | assert_eq!(map2.get(handle1), Err(HandleError::WrongMap)); 1134 | } 1135 | 1136 | #[test] 1137 | fn test_bad_index() { 1138 | let map: HandleMap = HandleMap::new(); 1139 | assert_eq!( 1140 | map.get(Handle { 1141 | map_id: map.id, 1142 | version: 2, 1143 | index: 100 1144 | }), 1145 | Err(HandleError::IndexPastEnd) 1146 | ); 1147 | } 1148 | 1149 | #[test] 1150 | fn test_resizing() { 1151 | let mut map = HandleMap::new(); 1152 | let mut handles = vec![]; 1153 | for i in 0..1000 { 1154 | handles.push(map.insert(Foobar(i))) 1155 | } 1156 | for (i, &h) in handles.iter().enumerate() { 1157 | assert_eq!(map.get(h).unwrap(), &Foobar(i)); 1158 | assert_eq!(map.remove(h).unwrap(), Foobar(i)); 1159 | } 1160 | let mut handles2 = vec![]; 1161 | for i in 1000..2000 { 1162 | // Not really related to this test, but it's convenient to check this here. 1163 | let h = map.insert(Foobar(i)); 1164 | let hu = h.into_u64(); 1165 | assert_eq!(Handle::from_u64(hu).unwrap(), h); 1166 | handles2.push(hu); 1167 | } 1168 | 1169 | for (i, (&h0, h1u)) in handles.iter().zip(handles2).enumerate() { 1170 | // It's still a stale version, even though the slot is occupied again. 1171 | assert_eq!(map.get(h0), Err(HandleError::StaleVersion)); 1172 | let h1 = Handle::from_u64(h1u).unwrap(); 1173 | assert_eq!(map.get(h1).unwrap(), &Foobar(i + 1000)); 1174 | } 1175 | } 1176 | 1177 | /// Tests that check our behavior when panicing. 1178 | /// 1179 | /// Naturally these require panic=unwind, which means we can't run them when 1180 | /// generating coverage (well, `-Zprofile`-based coverage can't -- although 1181 | /// ptrace-based coverage like tarpaulin can), and so we turn them off. 1182 | /// 1183 | /// (For clarity, `cfg(coverage)` is not a standard thing. We add it in 1184 | /// `automation/emit_coverage_info.sh`, and you can force it by adding 1185 | /// "--cfg coverage" to your RUSTFLAGS manually if you need to do so). 1186 | #[cfg(not(coverage))] 1187 | mod panic_tests { 1188 | use super::*; 1189 | 1190 | struct PanicOnDrop(()); 1191 | impl Drop for PanicOnDrop { 1192 | fn drop(&mut self) { 1193 | panic!("intentional panic (drop)"); 1194 | } 1195 | } 1196 | 1197 | #[test] 1198 | fn test_panicking_drop() { 1199 | let map = ConcurrentHandleMap::new(); 1200 | let h = map.insert(PanicOnDrop(())).into_u64(); 1201 | let mut e = ExternError::success(); 1202 | crate::call_with_result(&mut e, || map.delete_u64(h)); 1203 | assert_eq!(e.get_code(), crate::ErrorCode::PANIC); 1204 | let _ = unsafe { e.get_and_consume_message() }; 1205 | assert!(!map.map.is_poisoned()); 1206 | let inner = map.map.read().unwrap(); 1207 | inner.assert_valid(); 1208 | assert_eq!(inner.len(), 0); 1209 | } 1210 | 1211 | #[test] 1212 | fn test_panicking_call_with() { 1213 | let map = ConcurrentHandleMap::new(); 1214 | let h = map.insert(Foobar(0)).into_u64(); 1215 | let mut e = ExternError::success(); 1216 | map.call_with_output(&mut e, h, |_thing| { 1217 | panic!("intentional panic (call_with_output)"); 1218 | }); 1219 | 1220 | assert_eq!(e.get_code(), crate::ErrorCode::PANIC); 1221 | let _ = unsafe { e.get_and_consume_message() }; 1222 | 1223 | { 1224 | assert!(!map.map.is_poisoned()); 1225 | let inner = map.map.read().unwrap(); 1226 | inner.assert_valid(); 1227 | assert_eq!(inner.len(), 1); 1228 | let mut seen = false; 1229 | for e in &inner.entries { 1230 | if let EntryState::Active(v) = &e.state { 1231 | assert!(!seen); 1232 | assert!(v.is_poisoned()); 1233 | seen = true; 1234 | } 1235 | } 1236 | } 1237 | assert!(map.delete_u64(h).is_ok()); 1238 | assert!(!map.map.is_poisoned()); 1239 | let inner = map.map.read().unwrap(); 1240 | inner.assert_valid(); 1241 | assert_eq!(inner.len(), 0); 1242 | } 1243 | 1244 | #[test] 1245 | fn test_panicking_insert_with() { 1246 | let map = ConcurrentHandleMap::new(); 1247 | let mut e = ExternError::success(); 1248 | let res = map.insert_with_output(&mut e, || { 1249 | panic!("intentional panic (insert_with_output)"); 1250 | }); 1251 | 1252 | assert_eq!(e.get_code(), crate::ErrorCode::PANIC); 1253 | let _ = unsafe { e.get_and_consume_message() }; 1254 | 1255 | assert_eq!(res, 0); 1256 | 1257 | assert!(!map.map.is_poisoned()); 1258 | let inner = map.map.read().unwrap(); 1259 | inner.assert_valid(); 1260 | assert_eq!(inner.len(), 0); 1261 | } 1262 | } 1263 | } 1264 | -------------------------------------------------------------------------------- /src/into_ffi.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | use crate::string::*; 18 | use std::os::raw::{c_char, c_void}; 19 | use std::ptr; 20 | 21 | /// This trait is used to return types over the FFI. It essentially is a mapping between a type and 22 | /// version of that type we can pass back to C (`IntoFfi::Value`). 23 | /// 24 | /// The main wrinkle is that we need to be able to pass a value back to C in both the success and 25 | /// error cases. In the error cases, we don't want there to need to be any cleanup for the foreign 26 | /// code to do, and we want the API to be relatively easy to use. 27 | /// 28 | /// Additionally, the mapping is not consistent for different types. For some rust types, we want to 29 | /// convert them to JSON. For some, we want to return an opaque `*mut T` handle. For others, 30 | /// we'd like to return by value. 31 | /// 32 | /// This trait supports those cases by adding some type-level indirection, and allowing both cases 33 | /// to be provided (both cases what is done in the error and success cases). 34 | /// 35 | /// We implement this for the following types: 36 | /// 37 | /// - `String`, by conversion to `*mut c_char`. Note that the caller (on the other side of the FFI) 38 | /// is expected to free this, so you will need to provide them with a destructor for strings, 39 | /// which can be done with the [`define_string_destructor!`] macro. 40 | /// 41 | /// - `()`: as a no-op conversion -- this just allows us to expose functions without a return type 42 | /// over the FFI. 43 | /// 44 | /// - `bool`: is implemented by conversion to `u8` (`0u8` is `false`, `1u8` is `true`, and 45 | /// `ffi_default()` is `false`). This is because it doesn't seem to be safe to pass over the FFI 46 | /// directly (or at least, doing so might hit a bug in JNA). 47 | /// 48 | /// - All numeric primitives except `isize`, `usize`, `char`, `i128`, and `u128` are implememented 49 | /// by passing directly through (and using `Default::default()` for `ffi_default()`). 50 | /// - `isize`, `usize` could be added, but they'd be quite easy to accidentally misuse, so we 51 | /// currently omit them. 52 | /// - `char` is less easy to misuse, but it's also less clear why you'd want to be doing this. 53 | /// If we did ever add this, we'd probably want to convert to a `u32` (similar to how we 54 | /// convert `bool` to `u8`) for better ABI stability. 55 | /// - `i128` and `u128` do not have a stable ABI, so they cannot be returned across the FFI. 56 | /// 57 | /// - `Option` where `T` is `IntoFfi`, by returning `IntoFfi::ffi_default()` for `None`. 58 | /// 59 | /// None of these are directly helpful for user types though, so macros are provided for the 60 | /// following cases: 61 | /// 62 | /// 1. For types which are passed around by an opaque pointer, the macro 63 | /// [`implement_into_ffi_by_pointer!`] is provided. 64 | /// 65 | /// 2. For types which should be returned as a JSON string, the macro 66 | /// [`implement_into_ffi_by_json!`] is provided. 67 | /// 68 | /// See the "Examples" section below for some other cases, such as returning by value. 69 | /// 70 | /// ## Safety 71 | /// 72 | /// This is an unsafe trait (implementing it requires `unsafe impl`). This is because we cannot 73 | /// guarantee that your type is safe to pass to C. The helpers we've provided as macros should be 74 | /// safe to use, and in the cases where a common pattern can't be done both safely and generically, 75 | /// we've opted not to provide a macro for it. That said, many of these cases are still safe if you 76 | /// meet some relatively basic requirements, see below for examples. 77 | /// 78 | /// ## Examples 79 | /// 80 | /// ### Returning types by value 81 | /// 82 | /// If you want to return a type by value, we don't provide a macro for this, primarially because 83 | /// doing so cannot be statically guarantee that it is safe. However, it *is* safe for the cases 84 | /// where the type is either `#[repr(C)]` or `#[repr(transparent)]`. If this doesn't hold, you will 85 | /// want to use a different option! 86 | /// 87 | /// Regardless, if this holds, it's fairly simple to implement, for example: 88 | /// 89 | /// ```rust 90 | /// # use ffi_support::IntoFfi; 91 | /// #[derive(Default)] 92 | /// #[repr(C)] 93 | /// pub struct Point { 94 | /// pub x: i32, 95 | /// pub y: i32, 96 | /// } 97 | /// 98 | /// unsafe impl IntoFfi for Point { 99 | /// type Value = Self; 100 | /// #[inline] fn ffi_default() -> Self { Default::default() } 101 | /// #[inline] fn into_ffi_value(self) -> Self { self } 102 | /// } 103 | /// ``` 104 | /// 105 | /// ### Conversion to another type (which is returned over the FFI) 106 | /// 107 | /// In the FxA FFI, we used to have a `SyncKeys` type, which was converted to a different type before 108 | /// returning over the FFI. (The real FxA FFI is a little different, and more complex, but this is 109 | /// relatively close, and more widely recommendable than the one the FxA FFI uses): 110 | /// 111 | /// This is fairly easy to do by performing the conversion inside `IntoFfi`. 112 | /// 113 | /// ```rust 114 | /// # use ffi_support::{self, IntoFfi}; 115 | /// # use std::{ptr, os::raw::c_char}; 116 | /// pub struct SyncKeys(pub String, pub String); 117 | /// 118 | /// #[repr(C)] 119 | /// pub struct SyncKeysC { 120 | /// pub sync_key: *mut c_char, 121 | /// pub xcs: *mut c_char, 122 | /// } 123 | /// 124 | /// unsafe impl IntoFfi for SyncKeys { 125 | /// type Value = SyncKeysC; 126 | /// #[inline] 127 | /// fn ffi_default() -> SyncKeysC { 128 | /// SyncKeysC { 129 | /// sync_key: ptr::null_mut(), 130 | /// xcs: ptr::null_mut(), 131 | /// } 132 | /// } 133 | /// 134 | /// #[inline] 135 | /// fn into_ffi_value(self) -> SyncKeysC { 136 | /// SyncKeysC { 137 | /// sync_key: ffi_support::rust_string_to_c(self.0), 138 | /// xcs: ffi_support::rust_string_to_c(self.1), 139 | /// } 140 | /// } 141 | /// } 142 | /// 143 | /// // Note: this type manages memory, so you still will want to expose a destructor for this, 144 | /// // and possibly implement Drop as well. 145 | /// ``` 146 | pub unsafe trait IntoFfi: Sized { 147 | /// This type must be: 148 | /// 149 | /// 1. Compatible with C, which is to say `#[repr(C)]`, a numeric primitive, 150 | /// another type that has guarantees made about it's layout, or a 151 | /// `#[repr(transparent)]` wrapper around one of those. 152 | /// 153 | /// One could even use `&T`, so long as `T: Sized`, although it's 154 | /// extremely dubious to return a reference to borrowed memory over the 155 | /// FFI, since it's very difficult for the caller to know how long it 156 | /// remains valid. 157 | /// 158 | /// 2. Capable of storing an empty/ignorable/default value. 159 | /// 160 | /// 3. Capable of storing the actual value. 161 | /// 162 | /// Valid examples include: 163 | /// 164 | /// - Primitive numbers (other than i128/u128) 165 | /// 166 | /// - #[repr(C)] structs containing only things on this list. 167 | /// 168 | /// - `Option>`, but only if `T` is `Sized`. (Internally this is 169 | /// guaranteed to be represented equivalently to a pointer) 170 | /// 171 | /// - Raw pointers such as `*const T`, and `*mut T`, but again, only if `T` 172 | /// is `Sized` (`*const [T]`, `*mut dyn SomeTrait` etc are not valid). 173 | /// 174 | /// - Enums with a fixed `repr`, although it's a good idea avoid 175 | /// `#[repr(C)]` enums in favor of, say, `#[repr(i32)]` (for example, any 176 | /// fixed type there should be fine), as it's potentially error prone to 177 | /// access `#[repr(C)]` enums from Android over JNA (it's only safe if C's 178 | /// `sizeof(int) == 4`, which is very common, but not universally true). 179 | /// 180 | /// - `&T`/`&mut T` where `T: Sized` but only if you really know what you're 181 | /// doing, because this is probably a mistake. 182 | /// 183 | /// Invalid examples include things like `&str`, `&[T]`, `String`, `Vec`, 184 | /// `std::ffi::CString`, `&std::ffi::CStr`, etc. 185 | type Value; 186 | 187 | /// Return an 'empty' value. This is what's passed back to C in the case of an error, 188 | /// so it doesn't actually need to be "empty", so much as "ignorable". Note that this 189 | /// is also used when an empty `Option` is returned. 190 | fn ffi_default() -> Self::Value; 191 | 192 | /// Convert ourselves into a value we can pass back to C with confidence. 193 | fn into_ffi_value(self) -> Self::Value; 194 | } 195 | 196 | unsafe impl IntoFfi for String { 197 | type Value = *mut c_char; 198 | 199 | #[inline] 200 | fn ffi_default() -> Self::Value { 201 | ptr::null_mut() 202 | } 203 | 204 | #[inline] 205 | fn into_ffi_value(self) -> Self::Value { 206 | rust_string_to_c(self) 207 | } 208 | } 209 | 210 | // Implement IntoFfi for Option by falling back to ffi_default for None. 211 | unsafe impl IntoFfi for Option { 212 | type Value = ::Value; 213 | 214 | #[inline] 215 | fn ffi_default() -> Self::Value { 216 | T::ffi_default() 217 | } 218 | 219 | #[inline] 220 | fn into_ffi_value(self) -> Self::Value { 221 | if let Some(s) = self { 222 | s.into_ffi_value() 223 | } else { 224 | T::ffi_default() 225 | } 226 | } 227 | } 228 | 229 | // We've had problems in the past returning booleans over the FFI (specifically to JNA), and so 230 | // we convert them to `u8`. 231 | unsafe impl IntoFfi for bool { 232 | type Value = u8; 233 | #[inline] 234 | fn ffi_default() -> Self::Value { 235 | 0u8 236 | } 237 | #[inline] 238 | fn into_ffi_value(self) -> Self::Value { 239 | self as u8 240 | } 241 | } 242 | 243 | unsafe impl IntoFfi for crate::ByteBuffer { 244 | type Value = crate::ByteBuffer; 245 | #[inline] 246 | fn ffi_default() -> Self::Value { 247 | crate::ByteBuffer::default() 248 | } 249 | #[inline] 250 | fn into_ffi_value(self) -> Self::Value { 251 | self 252 | } 253 | } 254 | 255 | // just cuts down on boilerplate. Not public. 256 | macro_rules! impl_into_ffi_for_primitive { 257 | ($($T:ty),+) => {$( 258 | unsafe impl IntoFfi for $T { 259 | type Value = Self; 260 | #[inline] fn ffi_default() -> Self { Default::default() } 261 | #[inline] fn into_ffi_value(self) -> Self { self } 262 | } 263 | )+} 264 | } 265 | 266 | // See IntoFfi docs for why this is not exhaustive 267 | impl_into_ffi_for_primitive![(), i8, u8, i16, u16, i32, u32, i64, u64, f32, f64]; 268 | 269 | // just cuts down on boilerplate. Not public. 270 | macro_rules! impl_into_ffi_for_pointer { 271 | ($($T:ty),+) => {$( 272 | unsafe impl IntoFfi for $T { 273 | type Value = Self; 274 | #[inline] fn ffi_default() -> Self { ptr::null_mut() } 275 | #[inline] fn into_ffi_value(self) -> Self { self } 276 | } 277 | )+} 278 | } 279 | 280 | impl_into_ffi_for_pointer![ 281 | *mut i8, 282 | *const i8, 283 | *mut u8, 284 | *const u8, 285 | *mut c_void, 286 | *const c_void 287 | ]; 288 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | #![deny(missing_docs)] 18 | #![allow(unknown_lints)] 19 | #![warn(rust_2018_idioms)] 20 | 21 | //! # FFI Support 22 | //! 23 | //! This crate implements a support library to simplify implementing the patterns that the 24 | //! `mozilla/application-services` repository uses for it's "Rust Component" FFI libraries. 25 | //! 26 | //! It is *strongly encouraged* that anybody writing FFI code in this repository read this 27 | //! documentation before doing so, as it is a subtle, difficult, and error prone process. 28 | //! 29 | //! ## Terminology 30 | //! 31 | //! For each library, there are currently three parts we're concerned with. There's no clear correct 32 | //! name for these, so this documentation will attempt to use the following terminology: 33 | //! 34 | //! - **Rust Component**: A Rust crate which does not expose an FFI directly, but may be may be 35 | //! wrapped by one that does. These have a `crate-type` in their Cargo.toml (see 36 | //! https://doc.rust-lang.org/reference/linkage.html) of `lib`, and not `staticlib` or `cdylib` 37 | //! (Note that `lib` is the default if `crate-type` is not specified). Examples include the 38 | //! `fxa-client`, and `logins` crates. 39 | //! 40 | //! - **FFI Component**: A wrapper crate that takes a Rust component, and exposes an FFI from it. 41 | //! These typically have `ffi` in the name, and have `crate-type = ["lib", "staticlib", "cdylib"]` 42 | //! in their Cargo.toml. For example, the `fxa-client/ffi` and `logins/ffi` crates (note: 43 | //! paths are subject to change). When built, these produce a native library that is consumed by 44 | //! the "FFI Consumer". 45 | //! 46 | //! - **FFI Consumer**: This is a low level library, typically implemented in Kotlin (for Android) 47 | //! or Swift (for iOS), that exposes a memory-safe wrapper around the memory-unsafe C API produced 48 | //! by the FFI component. It's expected that the maintainers of the FFI Component and FFI Consumer 49 | //! be the same (or at least, the author of the consumer should be completely comfortable with the 50 | //! API exposed by, and code in the FFI component), since the code in these is extremely tightly 51 | //! coupled, and very easy to get wrong. 52 | //! 53 | //! Note that while there are three parts, there may be more than three libraries relevant here, for 54 | //! example there may be more than one FFI consumer (one for Android, one for iOS). 55 | //! 56 | //! ## Usage 57 | //! 58 | //! This library will typically be used in both the Rust component, and the FFI component, however 59 | //! it frequently will be an optional dependency in the Rust component that's only available when a 60 | //! feature flag (which the FFI component will always require) is used. 61 | //! 62 | //! The reason it's required inside the Rust component (and not solely in the FFI component, which 63 | //! would be nice), is so that types provided by that crate may implement the traits provided by 64 | //! this crate (this is because Rust does not allow crate `C` to implement a trait defined in crate 65 | //! `A` for a type defined in crate `B`). 66 | //! 67 | //! In general, examples should be provided for the most important types and functions 68 | //! ([`call_with_result`], [`IntoFfi`], 69 | //! [`ExternError`], etc), but you should also look at the code of 70 | //! consumers of this library. 71 | //! 72 | //! ### Usage in the Rust Component 73 | //! 74 | //! Inside the Rust component, you will implement: 75 | //! 76 | //! 1. [`IntoFfi`] for all types defined in that crate that you want to return 77 | //! over the FFI. For most common cases, the [`implement_into_ffi_by_json!`] and 78 | //! [`implement_into_ffi_by_protobuf!`] macros will do the job here, however you 79 | //! can see that trait's documentation for discussion and examples of 80 | //! implementing it manually. 81 | //! 82 | //! 2. Conversion to [`ExternError`] for the error type(s) exposed by that 83 | //! rust component, that is, `impl From for ExternError`. 84 | //! 85 | //! ### Usage in the FFI Component 86 | //! 87 | //! Inside the FFI component, you will use this library in a few ways: 88 | //! 89 | //! 1. Destructors will be exposed for each types that had [`implement_into_ffi_by_pointer!`] called 90 | //! on it (using [`define_box_destructor!`]), and a destructor for strings should be exposed as 91 | //! well, using [`define_string_destructor`] 92 | //! 93 | //! 2. The body of every / nearly every FFI function will be wrapped in either a 94 | //! [`call_with_result`] or [`call_with_output`]. 95 | //! 96 | //! This is required because if we `panic!` (e.g. from an `assert!`, `unwrap()`, `expect()`, from 97 | //! indexing past the end of an array, etc) across the FFI boundary, the behavior is undefined 98 | //! and in practice very weird things tend to happen (we aren't caught by the caller, since they 99 | //! don't have the same exception behavior as us). 100 | //! 101 | //! If you don't think your program (or possibly just certain calls) can handle panics, you may 102 | //! also use the versions of these functions in the [`abort_on_panic`] module, which 103 | //! do as their name suggest. 104 | //! 105 | //! Additionally, c strings that are passed in as arguments may be represented using [`FfiStr`], 106 | //! which contains several helpful inherent methods for extracting their data. 107 | //! 108 | 109 | use std::{panic, thread}; 110 | 111 | mod error; 112 | mod ffistr; 113 | pub mod handle_map; 114 | mod into_ffi; 115 | #[macro_use] 116 | mod macros; 117 | mod string; 118 | 119 | pub use crate::error::*; 120 | pub use crate::ffistr::FfiStr; 121 | pub use crate::into_ffi::*; 122 | pub use crate::macros::*; 123 | pub use crate::string::*; 124 | 125 | // We export most of the types from this, but some constants 126 | // (MAX_CAPACITY) don't make sense at the top level. 127 | pub use crate::handle_map::{ConcurrentHandleMap, Handle, HandleError, HandleMap}; 128 | 129 | /// Call a callback that returns a `Result` while: 130 | /// 131 | /// - Catching panics, and reporting them to C via [`ExternError`]. 132 | /// - Converting `T` to a C-compatible type using [`IntoFfi`]. 133 | /// - Converting `E` to a C-compatible error via `Into`. 134 | /// 135 | /// This (or [`call_with_output`]) should be in the majority of the FFI functions, see the crate 136 | /// top-level docs for more info. 137 | /// 138 | /// If your function doesn't produce an error, you may use [`call_with_output`] instead, which 139 | /// doesn't require you return a Result. 140 | /// 141 | /// ## Example 142 | /// 143 | /// A few points about the following example: 144 | /// 145 | /// - We need to mark it as `#[no_mangle] pub extern "C"`. 146 | /// 147 | /// - We prefix it with a unique name for the library (e.g. `mylib_`). Foreign functions are not 148 | /// namespaced, and symbol collisions can cause a large number of problems and subtle bugs, 149 | /// including memory safety issues in some cases. 150 | /// 151 | /// ```rust,no_run 152 | /// # use ffi_support::{ExternError, ErrorCode, FfiStr}; 153 | /// # use std::os::raw::c_char; 154 | /// 155 | /// # #[derive(Debug)] 156 | /// # struct BadEmptyString; 157 | /// # impl From for ExternError { 158 | /// # fn from(e: BadEmptyString) -> Self { 159 | /// # ExternError::new_error(ErrorCode::new(1), "Bad empty string") 160 | /// # } 161 | /// # } 162 | /// 163 | /// #[no_mangle] 164 | /// pub extern "C" fn mylib_print_string( 165 | /// // Strings come in as an `FfiStr`, which is a wrapper around a null terminated C string. 166 | /// thing_to_print: FfiStr<'_>, 167 | /// // Note that taking `&mut T` and `&T` is both allowed and encouraged, so long as `T: Sized`, 168 | /// // (e.g. it can't be a trait object, `&[T]`, a `&str`, etc). Also note that `Option<&T>` and 169 | /// // `Option<&mut T>` are also allowed, if you expect the caller to sometimes pass in null, but 170 | /// // that's the only case when it's currently to use `Option` in an argument list like this). 171 | /// error: &mut ExternError 172 | /// ) { 173 | /// // You should try to to do as little as possible outside the call_with_result, 174 | /// // to avoid a case where a panic occurs. 175 | /// ffi_support::call_with_result(error, || { 176 | /// let s = thing_to_print.as_str(); 177 | /// if s.is_empty() { 178 | /// // This is a silly example! 179 | /// return Err(BadEmptyString); 180 | /// } 181 | /// println!("{}", s); 182 | /// Ok(()) 183 | /// }) 184 | /// } 185 | /// ``` 186 | pub fn call_with_result(out_error: &mut ExternError, callback: F) -> R::Value 187 | where 188 | F: panic::UnwindSafe + FnOnce() -> Result, 189 | E: Into, 190 | R: IntoFfi, 191 | { 192 | call_with_result_impl(out_error, callback) 193 | } 194 | 195 | /// Call a callback that returns a `T` while: 196 | /// 197 | /// - Catching panics, and reporting them to C via [`ExternError`] 198 | /// - Converting `T` to a C-compatible type using [`IntoFfi`] 199 | /// 200 | /// Note that you still need to provide an [`ExternError`] to this function, to report panics. 201 | /// 202 | /// See [`call_with_result`] if you'd like to return a `Result` (Note: `E` must 203 | /// be convertible to [`ExternError`]). 204 | /// 205 | /// This (or [`call_with_result`]) should be in the majority of the FFI functions, see 206 | /// the crate top-level docs for more info. 207 | pub fn call_with_output(out_error: &mut ExternError, callback: F) -> R::Value 208 | where 209 | F: panic::UnwindSafe + FnOnce() -> R, 210 | R: IntoFfi, 211 | { 212 | // We need something that's `Into`, even though we never return it, so just use 213 | // `ExternError` itself. 214 | call_with_result(out_error, || -> Result<_, ExternError> { Ok(callback()) }) 215 | } 216 | 217 | fn call_with_result_impl(out_error: &mut ExternError, callback: F) -> R::Value 218 | where 219 | F: panic::UnwindSafe + FnOnce() -> Result, 220 | E: Into, 221 | R: IntoFfi, 222 | { 223 | *out_error = ExternError::success(); 224 | let res: thread::Result<(ExternError, R::Value)> = panic::catch_unwind(|| { 225 | ensure_panic_hook_is_setup(); 226 | match callback() { 227 | Ok(v) => (ExternError::default(), v.into_ffi_value()), 228 | Err(e) => (e.into(), R::ffi_default()), 229 | } 230 | }); 231 | match res { 232 | Ok((err, o)) => { 233 | *out_error = err; 234 | o 235 | } 236 | Err(e) => { 237 | *out_error = e.into(); 238 | R::ffi_default() 239 | } 240 | } 241 | } 242 | 243 | /// This module exists just to expose a variant of [`call_with_result`] and [`call_with_output`] 244 | /// that aborts, instead of unwinding, on panic. 245 | pub mod abort_on_panic { 246 | use super::*; 247 | 248 | // Struct that exists to automatically process::abort if we don't call 249 | // `std::mem::forget()` on it. This can have substantial performance 250 | // benefits over calling `std::panic::catch_unwind` and aborting if a panic 251 | // was caught, in addition to not requiring AssertUnwindSafe (for example). 252 | struct AbortOnDrop; 253 | impl Drop for AbortOnDrop { 254 | fn drop(&mut self) { 255 | std::process::abort(); 256 | } 257 | } 258 | 259 | /// A helper function useful for cases where you'd like to abort on panic, 260 | /// but aren't in a position where you'd like to return an FFI-compatible 261 | /// type. 262 | #[inline] 263 | pub fn with_abort_on_panic(callback: F) -> R 264 | where 265 | F: FnOnce() -> R, 266 | { 267 | let aborter = AbortOnDrop; 268 | let res = callback(); 269 | std::mem::forget(aborter); 270 | res 271 | } 272 | 273 | /// Same as the root `call_with_result`, but aborts on panic instead of unwinding. See the 274 | /// `call_with_result` documentation for more. 275 | pub fn call_with_result(out_error: &mut ExternError, callback: F) -> R::Value 276 | where 277 | F: FnOnce() -> Result, 278 | E: Into, 279 | R: IntoFfi, 280 | { 281 | with_abort_on_panic(|| match callback() { 282 | Ok(v) => { 283 | *out_error = ExternError::default(); 284 | v.into_ffi_value() 285 | } 286 | Err(e) => { 287 | *out_error = e.into(); 288 | R::ffi_default() 289 | } 290 | }) 291 | } 292 | 293 | /// Same as the root `call_with_output`, but aborts on panic instead of unwinding. As a result, 294 | /// it doesn't require a [`ExternError`] out argument. See the `call_with_output` documentation 295 | /// for more info. 296 | pub fn call_with_output(callback: F) -> R::Value 297 | where 298 | F: FnOnce() -> R, 299 | R: IntoFfi, 300 | { 301 | with_abort_on_panic(callback).into_ffi_value() 302 | } 303 | } 304 | 305 | /// Initialize our panic handling hook to optionally log panics 306 | #[cfg(feature = "log_panics")] 307 | pub fn ensure_panic_hook_is_setup() { 308 | use std::sync::Once; 309 | static INIT_BACKTRACES: Once = Once::new(); 310 | INIT_BACKTRACES.call_once(move || { 311 | #[cfg(all(feature = "log_backtraces", not(target_os = "android")))] 312 | { 313 | std::env::set_var("RUST_BACKTRACE", "1"); 314 | } 315 | // Turn on a panic hook which logs both backtraces and the panic 316 | // "Location" (file/line). We do both in case we've been stripped, 317 | // ). 318 | std::panic::set_hook(Box::new(move |panic_info| { 319 | let (file, line) = if let Some(loc) = panic_info.location() { 320 | (loc.file(), loc.line()) 321 | } else { 322 | // Apparently this won't happen but rust has reserved the 323 | // ability to start returning None from location in some cases 324 | // in the future. 325 | ("", 0) 326 | }; 327 | log::error!("### Rust `panic!` hit at file '{}', line {}", file, line); 328 | #[cfg(all(feature = "log_backtraces", not(target_os = "android")))] 329 | { 330 | log::error!(" Complete stack trace:\n{:?}", backtrace::Backtrace::new()); 331 | } 332 | })); 333 | }); 334 | } 335 | 336 | /// Initialize our panic handling hook to optionally log panics 337 | #[cfg(not(feature = "log_panics"))] 338 | pub fn ensure_panic_hook_is_setup() {} 339 | 340 | /// ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries. 341 | /// There are several cases when you might want to use this, but the primary one for us 342 | /// is for returning protobuf-encoded data to Swift and Java. The type is currently rather 343 | /// limited (implementing almost no functionality), however in the future it may be 344 | /// more expanded. 345 | /// 346 | /// ## Caveats 347 | /// 348 | /// Note that the order of the fields is `len` (an i64) then `data` (a `*mut u8`), getting 349 | /// this wrong on the other side of the FFI will cause memory corruption and crashes. 350 | /// `i64` is used for the length instead of `u64` and `usize` because JNA has interop 351 | /// issues with both these types. 352 | /// 353 | /// ### `Drop` is not implemented 354 | /// 355 | /// ByteBuffer does not implement Drop. This is intentional. Memory passed into it will 356 | /// be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`], or 357 | /// [`ByteBuffer::destroy_into_vec`]. This is for two reasons: 358 | /// 359 | /// 1. In the future, we may allow it to be used for data that is not managed by 360 | /// the Rust allocator\*, and `ByteBuffer` assuming it's okay to automatically 361 | /// deallocate this data with the Rust allocator. 362 | /// 363 | /// 2. Automatically running destructors in unsafe code is a 364 | /// [frequent footgun](https://without.boats/blog/two-memory-bugs-from-ringbahn/) 365 | /// (among many similar issues across many crates). 366 | /// 367 | /// Note that calling `destroy` manually is often not needed, as usually you should 368 | /// be passing these to the function defined by [`define_bytebuffer_destructor!`] from 369 | /// the other side of the FFI. 370 | /// 371 | /// Because this type is essentially *only* useful in unsafe or FFI code (and because 372 | /// the most common usage pattern does not require manually managing the memory), it 373 | /// does not implement `Drop`. 374 | /// 375 | /// \* Note: in the case of multiple Rust shared libraries loaded at the same time, 376 | /// there may be multiple instances of "the Rust allocator" (one per shared library), 377 | /// in which case we're referring to whichever instance is active for the code using 378 | /// the `ByteBuffer`. Note that this doesn't occur on all platforms or build 379 | /// configurations, but treating allocators in different shared libraries as fully 380 | /// independent is always safe. 381 | /// 382 | /// ## Layout/fields 383 | /// 384 | /// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so 385 | /// that we can verify rust users are constructing them appropriately), the fields, their types, and 386 | /// their order are *very much* a part of the public API of this type. Consumers on the other side 387 | /// of the FFI will need to know its layout. 388 | /// 389 | /// If this were a C struct, it would look like 390 | /// 391 | /// ```c,no_run 392 | /// struct ByteBuffer { 393 | /// // Note: This should never be negative, but values above 394 | /// // INT64_MAX / i64::MAX are not allowed. 395 | /// int64_t len; 396 | /// // Note: nullable! 397 | /// uint8_t *data; 398 | /// }; 399 | /// ``` 400 | /// 401 | /// In rust, there are two fields, in this order: `len: i64`, and `data: *mut u8`. 402 | /// 403 | /// For clarity, the fact that the data pointer is nullable means that `Option` is not 404 | /// the same size as ByteBuffer, and additionally is not FFI-safe (the latter point is not 405 | /// currently guaranteed anyway as of the time of writing this comment). 406 | /// 407 | /// ### Description of fields 408 | /// 409 | /// `data` is a pointer to an array of `len` bytes. Note that data can be a null pointer and therefore 410 | /// should be checked. 411 | /// 412 | /// The bytes array is allocated on the heap and must be freed on it as well. Critically, if there 413 | /// are multiple rust shared libraries using being used in the same application, it *must be freed 414 | /// on the same heap that allocated it*, or you will corrupt both heaps. 415 | /// 416 | /// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which 417 | /// means you must expose a function to release the resources of `data` which can be done easily 418 | /// using the [`define_bytebuffer_destructor!`] macro provided by this crate. 419 | #[repr(C)] 420 | pub struct ByteBuffer { 421 | len: i64, 422 | data: *mut u8, 423 | } 424 | 425 | impl From> for ByteBuffer { 426 | #[inline] 427 | fn from(bytes: Vec) -> Self { 428 | Self::from_vec(bytes) 429 | } 430 | } 431 | 432 | impl ByteBuffer { 433 | /// Creates a `ByteBuffer` of the requested size, zero-filled. 434 | /// 435 | /// The contents of the vector will not be dropped. Instead, `destroy` must 436 | /// be called later to reclaim this memory or it will be leaked. 437 | /// 438 | /// ## Caveats 439 | /// 440 | /// This will panic if the buffer length (`usize`) cannot fit into a `i64`. 441 | #[inline] 442 | pub fn new_with_size(size: usize) -> Self { 443 | // Note: `Vec` requires this internally on 64 bit platforms (and has a 444 | // stricter requirement on 32 bit ones), so this is just to be explicit. 445 | assert!(size < i64::MAX as usize); 446 | let mut buf = vec![]; 447 | buf.reserve_exact(size); 448 | buf.resize(size, 0); 449 | ByteBuffer::from_vec(buf) 450 | } 451 | 452 | /// Creates a `ByteBuffer` instance from a `Vec` instance. 453 | /// 454 | /// The contents of the vector will not be dropped. Instead, `destroy` must 455 | /// be called later to reclaim this memory or it will be leaked. 456 | /// 457 | /// ## Caveats 458 | /// 459 | /// This will panic if the buffer length (`usize`) cannot fit into a `i64`. 460 | #[inline] 461 | pub fn from_vec(bytes: Vec) -> Self { 462 | if bytes.is_empty() { 463 | // Special case when dealing with Golang. 464 | // 465 | // When copying stacks, Go checks for pointer validity and chokes when `bytes` is empty 466 | // (see https://github.com/golang/go/blob/0d0193409492b96881be6407ad50123e3557fdfb/src/runtime/stack.go#L619) 467 | // 468 | // An empty Vec has no allocation and its pointer points to a dangling value (std::ptr::Unique::dangling()) 469 | // which may not be 0 and therefor invalid for Go. 470 | return Self { 471 | data: std::ptr::null_mut(), 472 | len: 0, 473 | }; 474 | } 475 | 476 | use std::convert::TryFrom; 477 | let mut buf = bytes.into_boxed_slice(); 478 | let data = buf.as_mut_ptr(); 479 | let len = i64::try_from(buf.len()).expect("buffer length cannot fit into a i64."); 480 | std::mem::forget(buf); 481 | Self { data, len } 482 | } 483 | 484 | /// View the data inside this `ByteBuffer` as a `&[u8]`. 485 | // TODO: Is it worth implementing `Deref`? Patches welcome if you need this. 486 | #[inline] 487 | pub fn as_slice(&self) -> &[u8] { 488 | if self.data.is_null() { 489 | &[] 490 | } else { 491 | unsafe { std::slice::from_raw_parts(self.data, self.len()) } 492 | } 493 | } 494 | 495 | #[inline] 496 | fn len(&self) -> usize { 497 | use std::convert::TryInto; 498 | self.len 499 | .try_into() 500 | .expect("ByteBuffer length negative or overflowed") 501 | } 502 | 503 | /// View the data inside this `ByteBuffer` as a `&mut [u8]`. 504 | // TODO: Is it worth implementing `DerefMut`? Patches welcome if you need this. 505 | #[inline] 506 | pub fn as_mut_slice(&mut self) -> &mut [u8] { 507 | if self.data.is_null() { 508 | &mut [] 509 | } else { 510 | unsafe { std::slice::from_raw_parts_mut(self.data, self.len()) } 511 | } 512 | } 513 | 514 | /// Deprecated alias for [`ByteBuffer::destroy_into_vec`]. 515 | #[inline] 516 | #[deprecated = "Name is confusing, please use `destroy_into_vec` instead"] 517 | pub fn into_vec(self) -> Vec { 518 | self.destroy_into_vec() 519 | } 520 | 521 | /// Convert this `ByteBuffer` into a Vec, taking ownership of the 522 | /// underlying memory, which will be freed using the rust allocator once the 523 | /// `Vec`'s lifetime is done. 524 | /// 525 | /// If this is undesirable, you can do `bb.as_slice().to_vec()` to get a 526 | /// `Vec` containing a copy of this `ByteBuffer`'s underlying data. 527 | /// 528 | /// ## Caveats 529 | /// 530 | /// This is safe so long as the buffer is empty, or the data was allocated 531 | /// by Rust code, e.g. this is a ByteBuffer created by 532 | /// `ByteBuffer::from_vec` or `Default::default`. 533 | /// 534 | /// If the ByteBuffer were allocated by something other than the 535 | /// current/local Rust `global_allocator`, then calling `destroy` is 536 | /// fundamentally broken. 537 | /// 538 | /// For example, if it were allocated externally by some other language's 539 | /// runtime, or if it were allocated by the global allocator of some other 540 | /// Rust shared object in the same application, the behavior is undefined 541 | /// (and likely to cause problems). 542 | /// 543 | /// Note that this currently can only happen if the `ByteBuffer` is passed 544 | /// to you via an `extern "C"` function that you expose, as opposed to being 545 | /// created locally. 546 | #[inline] 547 | pub fn destroy_into_vec(self) -> Vec { 548 | if self.data.is_null() { 549 | vec![] 550 | } else { 551 | let len = self.len(); 552 | // Safety: This is correct because we convert to a Box<[u8]> first, 553 | // which is a design constraint of RawVec. 554 | unsafe { Vec::from_raw_parts(self.data, len, len) } 555 | } 556 | } 557 | 558 | /// Reclaim memory stored in this ByteBuffer. 559 | /// 560 | /// You typically should not call this manually, and instead expose a 561 | /// function that does so via [`define_bytebuffer_destructor!`]. 562 | /// 563 | /// ## Caveats 564 | /// 565 | /// This is safe so long as the buffer is empty, or the data was allocated 566 | /// by Rust code, e.g. this is a ByteBuffer created by 567 | /// `ByteBuffer::from_vec` or `Default::default`. 568 | /// 569 | /// If the ByteBuffer were allocated by something other than the 570 | /// current/local Rust `global_allocator`, then calling `destroy` is 571 | /// fundamentally broken. 572 | /// 573 | /// For example, if it were allocated externally by some other language's 574 | /// runtime, or if it were allocated by the global allocator of some other 575 | /// Rust shared object in the same application, the behavior is undefined 576 | /// (and likely to cause problems). 577 | /// 578 | /// Note that this currently can only happen if the `ByteBuffer` is passed 579 | /// to you via an `extern "C"` function that you expose, as opposed to being 580 | /// created locally. 581 | #[inline] 582 | pub fn destroy(self) { 583 | // Note: the drop is just for clarity, of course. 584 | drop(self.destroy_into_vec()) 585 | } 586 | } 587 | 588 | impl Default for ByteBuffer { 589 | #[inline] 590 | fn default() -> Self { 591 | Self { 592 | len: 0 as i64, 593 | data: std::ptr::null_mut(), 594 | } 595 | } 596 | } 597 | 598 | #[cfg(test)] 599 | mod test { 600 | use super::*; 601 | #[test] 602 | fn test_bb_access() { 603 | let mut bb = ByteBuffer::from(vec![1u8, 2, 3]); 604 | assert_eq!(bb.as_slice(), &[1u8, 2, 3]); 605 | assert_eq!(bb.as_mut_slice(), &mut [1u8, 2, 3]); 606 | bb.as_mut_slice()[2] = 4; 607 | 608 | // Use into_vec to cover both into_vec and destroy_into_vec. 609 | #[allow(deprecated)] 610 | { 611 | assert_eq!(bb.into_vec(), &[1u8, 2, 4]); 612 | } 613 | } 614 | 615 | #[test] 616 | fn test_bb_empty() { 617 | let mut bb = ByteBuffer::default(); 618 | assert_eq!(bb.as_slice(), &[]); 619 | assert_eq!(bb.as_mut_slice(), &[]); 620 | assert_eq!(bb.destroy_into_vec(), &[]); 621 | } 622 | 623 | #[test] 624 | fn test_bb_new() { 625 | let bb = ByteBuffer::new_with_size(5); 626 | assert_eq!(bb.as_slice(), &[0u8, 0, 0, 0, 0]); 627 | bb.destroy(); 628 | 629 | let bb = ByteBuffer::new_with_size(0); 630 | assert_eq!(bb.as_slice(), &[]); 631 | assert!(!bb.data.is_null()); 632 | bb.destroy(); 633 | 634 | let bb = ByteBuffer::from_vec(vec![]); 635 | assert_eq!(bb.as_slice(), &[]); 636 | assert!(!bb.data.is_null()); 637 | bb.destroy(); 638 | } 639 | } 640 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | /// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one 18 | /// may be passed in) by allocating `$T` on the heap as an opaque pointer. 19 | /// 20 | /// This is typically going to be used from the "Rust component", and not the 21 | /// "FFI component" (see the top level crate documentation for more 22 | /// information), however you will still need to implement a destructor in the 23 | /// FFI component using [`define_box_destructor!`][crate::define_box_destructor]. 24 | /// 25 | /// In general, is only safe to do for `send` types (even this is dodgy, but 26 | /// it's often necessary to keep the locking on the other side of the FFI, so 27 | /// Sync is too harsh), so we enforce this in this macro. (You're still free to 28 | /// implement this manually, if this restriction is too harsh for your use case 29 | /// and you're certain you know what you're doing). 30 | #[macro_export] 31 | macro_rules! implement_into_ffi_by_pointer { 32 | ($($T:ty),* $(,)*) => {$( 33 | unsafe impl $crate::IntoFfi for $T where $T: Send { 34 | type Value = *mut $T; 35 | 36 | #[inline] 37 | fn ffi_default() -> *mut $T { 38 | std::ptr::null_mut() 39 | } 40 | 41 | #[inline] 42 | fn into_ffi_value(self) -> *mut $T { 43 | Box::into_raw(Box::new(self)) 44 | } 45 | } 46 | )*} 47 | } 48 | 49 | /// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one 50 | /// may be passed in) by converting to the type to a JSON string. 51 | /// 52 | /// Additionally, most of the time we recomment using this crate's protobuf 53 | /// support, instead of JSON. 54 | /// 55 | /// This is typically going to be used from the "Rust component", and not the 56 | /// "FFI component" (see the top level crate documentation for more 57 | /// information). 58 | /// 59 | /// Note: Each type passed in must implement or derive `serde::Serialize`. 60 | /// 61 | /// Note: for this to works, the crate it's called in must depend on `serde` and 62 | /// `serde_json`. 63 | /// 64 | /// ## Panics 65 | /// 66 | /// The [`IntoFfi`][crate::IntoFfi] implementation this macro generates may 67 | /// panic in the following cases: 68 | /// 69 | /// - You've passed a type that contains a Map that has non-string keys (which 70 | /// can't be represented in JSON). 71 | /// 72 | /// - You've passed a type which has a custom serializer, and the custom 73 | /// serializer failed. 74 | /// 75 | /// These cases are both rare enough that this still seems fine for the majority 76 | /// of uses. 77 | #[macro_export] 78 | macro_rules! implement_into_ffi_by_json { 79 | ($($T:ty),* $(,)*) => {$( 80 | unsafe impl $crate::IntoFfi for $T where $T: serde::Serialize { 81 | type Value = *mut std::os::raw::c_char; 82 | #[inline] 83 | fn ffi_default() -> *mut std::os::raw::c_char { 84 | std::ptr::null_mut() 85 | } 86 | #[inline] 87 | fn into_ffi_value(self) -> *mut std::os::raw::c_char { 88 | // This panic is inside our catch_panic, so it should be fine. 89 | // We've also documented the case where the IntoFfi impl that 90 | // calls this panics, and it's rare enough that it shouldn't 91 | // matter that if it happens we return an ExternError 92 | // representing a panic instead of one of some other type 93 | // (especially given that the application isn't likely to be 94 | // able to meaningfully handle JSON serialization failure). 95 | let as_string = serde_json::to_string(&self).unwrap(); 96 | $crate::rust_string_to_c(as_string) 97 | } 98 | } 99 | )*} 100 | } 101 | 102 | /// Implements [`IntoFfi`][crate::IntoFfi] for the provided types (more than one 103 | /// may be passed in) implementing `prost::Message` (protobuf auto-generated 104 | /// type) by converting to the type to a [`ByteBuffer`][crate::ByteBuffer]. This 105 | /// [`ByteBuffer`][crate::ByteBuffer] should later be passed by value. 106 | /// 107 | /// Note: for this to works, the crate it's called in must depend on `prost`. 108 | /// 109 | /// Note: Each type passed in must implement or derive `prost::Message`. 110 | #[macro_export] 111 | macro_rules! implement_into_ffi_by_protobuf { 112 | ($($FFIType:ty),* $(,)*) => {$( 113 | unsafe impl $crate::IntoFfi for $FFIType where $FFIType: prost::Message { 114 | type Value = $crate::ByteBuffer; 115 | #[inline] 116 | fn ffi_default() -> Self::Value { 117 | Default::default() 118 | } 119 | 120 | #[inline] 121 | fn into_ffi_value(self) -> Self::Value { 122 | use prost::Message; 123 | let mut bytes = Vec::with_capacity(self.encoded_len()); 124 | // Unwrap is safe, since we have reserved sufficient capacity in 125 | // the vector. 126 | self.encode(&mut bytes).unwrap(); 127 | bytes.into() 128 | } 129 | } 130 | )*} 131 | } 132 | 133 | /// Implement [`InfoFfi`][crate::IntoFfi] for a type by converting through 134 | /// another type. 135 | /// 136 | /// The argument `$MidTy` argument must implement `From<$SrcTy>` and 137 | /// [`InfoFfi`][crate::IntoFfi]. 138 | /// 139 | /// This is provided (even though it's trivial) because it is always safe (well, 140 | /// so long as `$MidTy`'s [`IntoFfi`][crate::IntoFfi] implementation is 141 | /// correct), but would otherwise require use of `unsafe` to implement. 142 | #[macro_export] 143 | macro_rules! implement_into_ffi_by_delegation { 144 | ($SrcTy:ty, $MidTy:ty) => { 145 | unsafe impl $crate::IntoFfi for $SrcTy 146 | where 147 | $MidTy: From<$SrcTy> + $crate::IntoFfi, 148 | { 149 | // The <$MidTy as SomeTrait>::method is required even when it would 150 | // be ambiguous due to some obscure details of macro syntax. 151 | type Value = <$MidTy as $crate::IntoFfi>::Value; 152 | 153 | #[inline] 154 | fn ffi_default() -> Self::Value { 155 | <$MidTy as $crate::IntoFfi>::ffi_default() 156 | } 157 | 158 | #[inline] 159 | fn into_ffi_value(self) -> Self::Value { 160 | use $crate::IntoFfi; 161 | <$MidTy as From<$SrcTy>>::from(self).into_ffi_value() 162 | } 163 | } 164 | }; 165 | } 166 | 167 | /// For a number of reasons (name collisions are a big one, but, it also wouldn't work on all 168 | /// platforms), we cannot export `extern "C"` functions from this library. However, it's pretty 169 | /// common to want to free strings allocated by rust, so many libraries will need this, so we 170 | /// provide it as a macro. 171 | /// 172 | /// It simply expands to a `#[no_mangle] pub unsafe extern "C" fn` which wraps this crate's 173 | /// [`destroy_c_string`][crate::destroy_c_string] function. 174 | /// 175 | /// ## Caveats 176 | /// 177 | /// If you're using multiple separately compiled rust libraries in your application, it's critical 178 | /// that you are careful to only ever free strings allocated by a Rust library using the same rust 179 | /// library. Passing them to a different Rust library's string destructor will cause you to corrupt 180 | /// multiple heaps. 181 | /// 182 | /// Additionally, be sure that all strings you pass to this were actually allocated by rust. It's a 183 | /// common issue for JNA code to transparently convert Pointers to things to Strings behind the 184 | /// scenes, which is quite risky here. (To avoid this in JNA, only use `String` for passing 185 | /// read-only strings into Rust, e.g. it's for passing `*const c_char`. All other uses should use 186 | /// `Pointer` and `getString()`). 187 | /// 188 | /// Finally, to avoid name collisions, it is strongly recommended that you provide an name for this 189 | /// function unique to your library. 190 | /// 191 | /// ## Example 192 | /// 193 | /// ```rust 194 | /// # use ffi_support::define_string_destructor; 195 | /// define_string_destructor!(mylib_destroy_string); 196 | /// ``` 197 | #[macro_export] 198 | macro_rules! define_string_destructor { 199 | ($mylib_destroy_string:ident) => { 200 | /// Public destructor for strings managed by the other side of the FFI. 201 | /// 202 | /// # Safety 203 | /// 204 | /// This will free the string pointer it gets passed in as an argument, 205 | /// and thus can be wildly unsafe if misused. 206 | /// 207 | /// See the documentation of `ffi_support::destroy_c_string` and 208 | /// `ffi_support::define_string_destructor!` for further info. 209 | #[no_mangle] 210 | pub unsafe extern "C" fn $mylib_destroy_string(s: *mut std::os::raw::c_char) { 211 | // Note: This should never happen, but in the case of a bug aborting 212 | // here is better than the badness that happens if we unwind across 213 | // the FFI boundary. 214 | $crate::abort_on_panic::with_abort_on_panic(|| { 215 | if !s.is_null() { 216 | $crate::destroy_c_string(s) 217 | } 218 | }); 219 | } 220 | }; 221 | } 222 | 223 | /// Define a (public) destructor for a type that was allocated by 224 | /// `Box::into_raw(Box::new(value))` (e.g. a pointer which is probably opaque). 225 | /// 226 | /// ## Caveats 227 | /// 228 | /// When called over the FFI, this can go wrong in a ridiculous number of ways, 229 | /// and we can't really prevent any of them. But essentially, the caller (on the 230 | /// other side of the FFI) needs to be extremely careful to ensure that it stops 231 | /// using the pointer after it's freed. 232 | /// 233 | /// Also, to avoid name collisions, it is strongly recommended that you provide 234 | /// an name for this function unique to your library. (This is true for all 235 | /// functions you expose). 236 | /// 237 | /// However, when called from rust, this is safe, as it becomes a function that 238 | /// just drops a `Option>` with some panic handling. 239 | /// 240 | /// ## Example 241 | /// 242 | /// ```rust 243 | /// # use ffi_support::define_box_destructor; 244 | /// struct CoolType(Vec); 245 | /// 246 | /// define_box_destructor!(CoolType, mylib_destroy_cooltype); 247 | /// ``` 248 | #[macro_export] 249 | macro_rules! define_box_destructor { 250 | ($T:ty, $destructor_name:ident) => { 251 | /// # Safety 252 | /// This is equivalent to calling Box::from_raw with panic handling, and 253 | /// thus inherits [`Box::from_raw`]'s safety properties. That is to say, 254 | /// this function is wildly unsafe. 255 | #[no_mangle] 256 | pub unsafe extern "C" fn $destructor_name(v: *mut $T) { 257 | // We should consider passing an error parameter in here rather than 258 | // aborting, but at the moment the only case where we do this 259 | // (interrupt handles) should never panic in Drop, so it's probably 260 | // fine. 261 | $crate::abort_on_panic::with_abort_on_panic(|| { 262 | if !v.is_null() { 263 | drop(Box::from_raw(v)) 264 | } 265 | }); 266 | } 267 | }; 268 | } 269 | 270 | /// Define a (public) destructor for the [`ByteBuffer`][crate::ByteBuffer] type. 271 | /// 272 | /// ## Caveats 273 | /// 274 | /// If you're using multiple separately compiled rust libraries in your application, it's critical 275 | /// that you are careful to only ever free `ByteBuffer` instances allocated by a Rust library using 276 | /// the same rust library. Passing them to a different Rust library's string destructor will cause 277 | /// you to corrupt multiple heaps. 278 | /// One common ByteBuffer destructor is defined per Rust library. 279 | /// 280 | /// Also, to avoid name collisions, it is strongly recommended that you provide an name for this 281 | /// function unique to your library. (This is true for all functions you expose). 282 | /// 283 | /// ## Example 284 | /// 285 | /// ```rust 286 | /// # use ffi_support::define_bytebuffer_destructor; 287 | /// define_bytebuffer_destructor!(mylib_destroy_bytebuffer); 288 | /// ``` 289 | #[macro_export] 290 | macro_rules! define_bytebuffer_destructor { 291 | ($destructor_name:ident) => { 292 | #[no_mangle] 293 | pub extern "C" fn $destructor_name(v: $crate::ByteBuffer) { 294 | // Note: This should never happen, but in the case of a bug aborting 295 | // here is better than the badness that happens if we unwind across 296 | // the FFI boundary. 297 | $crate::abort_on_panic::with_abort_on_panic(|| v.destroy()) 298 | } 299 | }; 300 | } 301 | 302 | /// Define a (public) destructor for a type that lives inside a lazy_static 303 | /// [`ConcurrentHandleMap`][crate::ConcurrentHandleMap]. 304 | /// 305 | /// Note that this is actually totally safe, unlike the other 306 | /// `define_blah_destructor` macros. 307 | /// 308 | /// A critical difference, however, is that this dtor takes an `err` out 309 | /// parameter to indicate failure. This difference is why the name is different 310 | /// as well (deleter vs destructor). 311 | /// 312 | /// ## Example 313 | /// 314 | /// ```rust 315 | /// # use lazy_static::lazy_static; 316 | /// # use ffi_support::{ConcurrentHandleMap, define_handle_map_deleter}; 317 | /// struct Thing(Vec); 318 | /// // Somewhere... 319 | /// lazy_static! { 320 | /// static ref THING_HANDLES: ConcurrentHandleMap = ConcurrentHandleMap::new(); 321 | /// } 322 | /// define_handle_map_deleter!(THING_HANDLES, mylib_destroy_thing); 323 | /// ``` 324 | #[macro_export] 325 | macro_rules! define_handle_map_deleter { 326 | ($HANDLE_MAP_NAME:ident, $destructor_name:ident) => { 327 | #[no_mangle] 328 | pub extern "C" fn $destructor_name(v: u64, err: &mut $crate::ExternError) { 329 | $crate::call_with_result(err, || { 330 | // Force type errors here. 331 | let map: &$crate::ConcurrentHandleMap<_> = &*$HANDLE_MAP_NAME; 332 | map.delete_u64(v) 333 | }) 334 | } 335 | }; 336 | } 337 | 338 | /// Force a compile error if the condition is not met. Requires a unique name 339 | /// for the assertion for... reasons. This is included mainly because it's a 340 | /// common desire for FFI code, but not for other sorts of code. 341 | /// 342 | /// # Examples 343 | /// 344 | /// Failing example: 345 | /// 346 | /// ```compile_fail 347 | /// ffi_support::static_assert!(THIS_SHOULD_FAIL, false); 348 | /// ``` 349 | /// 350 | /// Passing example: 351 | /// 352 | /// ``` 353 | /// ffi_support::static_assert!(THIS_SHOULD_PASS, true); 354 | /// ``` 355 | #[macro_export] 356 | macro_rules! static_assert { 357 | ($ASSERT_NAME:ident, $test:expr) => { 358 | #[allow(dead_code, nonstandard_style)] 359 | const $ASSERT_NAME: [u8; 0 - (!$test as bool as usize)] = 360 | [0u8; 0 - (!$test as bool as usize)]; 361 | }; 362 | } 363 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. */ 16 | 17 | use crate::FfiStr; 18 | use std::ffi::CString; 19 | use std::os::raw::c_char; 20 | use std::ptr; 21 | 22 | /// Convert a rust string into a NUL-terminated utf-8 string suitable for passing to C, or to things 23 | /// ABI-compatible with C. 24 | /// 25 | /// Important: This string must eventually be freed. You may either do that using the 26 | /// [`destroy_c_string`] method (or, if you must, by dropping the underlying [`std::ffi::CString`] 27 | /// after recovering it via [`std::ffi::CString::from_raw`]). 28 | /// 29 | /// It's common to want to allow the consumer (e.g. on the "C" side of the FFI) to be allowed to 30 | /// free this memory, and the macro [`define_string_destructor!`] may be used to do so. 31 | /// 32 | /// ## Panics 33 | /// 34 | /// This function may panic if the argument has an interior null byte. This is fairly rare, but 35 | /// is possible in theory. 36 | #[inline] 37 | pub fn rust_string_to_c(rust_string: impl Into) -> *mut c_char { 38 | CString::new(rust_string.into()) 39 | .expect("Error: Rust string contained an interior null byte.") 40 | .into_raw() 41 | } 42 | 43 | /// Variant of [`rust_string_to_c`] which takes an Option, and returns null for None. 44 | #[inline] 45 | pub fn opt_rust_string_to_c(opt_rust_string: Option>) -> *mut c_char { 46 | if let Some(s) = opt_rust_string { 47 | rust_string_to_c(s) 48 | } else { 49 | ptr::null_mut() 50 | } 51 | } 52 | 53 | /// Free the memory of a string created by [`rust_string_to_c`] on the rust heap. If `c_string` is 54 | /// null, this is a no-op. 55 | /// 56 | /// See the [`define_string_destructor!`] macro which may be used for exposing this function over 57 | /// the FFI. 58 | /// 59 | /// ## Safety 60 | /// 61 | /// This is inherently unsafe, since we're deallocating memory. Be sure 62 | /// 63 | /// - Nobody can use the memory after it's deallocated. 64 | /// - The memory was actually allocated on this heap (and it's not a string from the other side of 65 | /// the FFI which was allocated on e.g. the C heap). 66 | /// - If multiple separate rust libraries are in use (for example, as DLLs) in a single program, 67 | /// you must also make sure that the rust library that allocated the memory is also the one 68 | /// that frees it. 69 | /// 70 | /// See documentation for [`define_string_destructor!`], which gives a more complete overview of the 71 | /// potential issues. 72 | #[inline] 73 | pub unsafe fn destroy_c_string(cstring: *mut c_char) { 74 | // we're not guaranteed to be in a place where we can complain about this beyond logging, 75 | // and there's an obvious way to handle it. 76 | if !cstring.is_null() { 77 | drop(CString::from_raw(cstring)) 78 | } 79 | } 80 | 81 | /// Convert a null-terminated C string to a rust `str`. This does not take ownership of the string, 82 | /// and you should be careful about the lifetime of the resulting string. Note that strings 83 | /// containing invalid UTF-8 are replaced with the empty string (for many cases, you will want to 84 | /// use [`rust_string_from_c`] instead, which will do a lossy conversion). 85 | /// 86 | /// If you actually need an owned rust `String`, you're encouraged to use [`rust_string_from_c`], 87 | /// which, as mentioned, also behaves better in the face of invalid UTF-8. 88 | /// 89 | /// ## Safety 90 | /// 91 | /// This is unsafe because we read from a raw pointer, which may or may not be valid. 92 | /// 93 | /// We also assume `c_string` is a null terminated string, and have no way of knowing if that's 94 | /// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which 95 | /// can result in a enormous number of problems. 96 | /// 97 | /// ## Panics 98 | /// 99 | /// Panics if it's argument is null, see [`opt_rust_str_from_c`] for a variant that returns None in 100 | /// this case instead. 101 | /// 102 | /// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else 103 | /// that uses [`std::panic::catch_unwind`]), as it is UB to panic across the FFI boundary. 104 | #[inline] 105 | #[deprecated(since = "0.3.0", note = "Please use FfiStr::as_str instead")] 106 | pub unsafe fn rust_str_from_c<'a>(c_string: *const c_char) -> &'a str { 107 | FfiStr::from_raw(c_string).as_str() 108 | } 109 | 110 | /// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting. 111 | /// 112 | /// ## Safety 113 | /// 114 | /// This is unsafe because we read from a raw pointer, which may or may not be valid. 115 | /// 116 | /// We also assume `c_string` is a null terminated string, and have no way of knowing if that's 117 | /// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which 118 | /// can result in a enormous number of problems. 119 | #[inline] 120 | #[deprecated(since = "0.3.0", note = "Please use FfiStr::as_opt_str instead")] 121 | pub unsafe fn opt_rust_str_from_c<'a>(c_string: *const c_char) -> Option<&'a str> { 122 | FfiStr::from_raw(c_string).as_opt_str() 123 | } 124 | 125 | /// Convert a null-terminated C into an owned rust string, replacing invalid UTF-8 with the 126 | /// unicode replacement character. 127 | /// 128 | /// ## Safety 129 | /// 130 | /// This is unsafe because we dereference a raw pointer, which may or may not be valid. 131 | /// 132 | /// We also assume `c_string` is a null terminated string, and have no way of knowing if that's 133 | /// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which 134 | /// can result in a enormous number of problems. 135 | /// 136 | /// ## Panics 137 | /// 138 | /// Panics if it's argument is null. See also [`opt_rust_string_from_c`], which returns None 139 | /// instead. 140 | /// 141 | /// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else 142 | /// that uses `std::panic::catch_unwind`), as it is UB to panic across the FFI boundary. 143 | #[inline] 144 | #[deprecated(since = "0.3.0", note = "Please use FfiStr::into_string instead")] 145 | pub unsafe fn rust_string_from_c(c_string: *const c_char) -> String { 146 | FfiStr::from_raw(c_string).into_string() 147 | } 148 | 149 | /// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting. 150 | /// 151 | /// ## Safety 152 | /// 153 | /// This is unsafe because we dereference a raw pointer, which may or may not be valid. 154 | /// 155 | /// We also assume `c_string` is a null terminated string, and have no way of knowing if that's 156 | /// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which 157 | /// can result in a enormous number of problems. 158 | #[inline] 159 | #[deprecated(since = "0.3.0", note = "Please use FfiStr::into_opt_string instead")] 160 | pub unsafe fn opt_rust_string_from_c(c_string: *const c_char) -> Option { 161 | FfiStr::from_raw(c_string).into_opt_string() 162 | } 163 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-2019 Mozilla Foundation 2 | * 3 | * Licensed under the Apache License (Version 2.0), or the MIT license, 4 | * (the "Licenses") at your option. You may not use this file except in 5 | * compliance with one of the Licenses. You may obtain copies of the 6 | * Licenses at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * http://opensource.org/licenses/MIT 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the Licenses is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the Licenses for the specific language governing permissions and 15 | * limitations under the Licenses. 16 | */ 17 | 18 | //! This test is a stress test meant to trigger some bugs seen prior to the use 19 | //! of handlemaps. See /docs/design/test-faster.md for why it's split -- TLDR: 20 | //! it uses rayon and is hard to rewrite with normal threads. 21 | 22 | use ffi_support::{ConcurrentHandleMap, ExternError}; 23 | use std::sync::atomic::{AtomicUsize, Ordering}; 24 | use std::sync::Arc; 25 | 26 | fn with_error T, T>(callback: F) -> T { 27 | let mut e = ExternError::success(); 28 | let result = callback(&mut e); 29 | if let Some(m) = unsafe { e.get_and_consume_message() } { 30 | panic!("unexpected error: {}", m); 31 | } 32 | result 33 | } 34 | 35 | struct DropChecking { 36 | counter: Arc, 37 | id: usize, 38 | } 39 | impl Drop for DropChecking { 40 | fn drop(&mut self) { 41 | let val = self.counter.fetch_add(1, Ordering::SeqCst); 42 | log::debug!("Dropped {} :: {}", self.id, val); 43 | } 44 | } 45 | #[test] 46 | fn test_concurrent_drop() { 47 | use rand::prelude::*; 48 | use rayon::prelude::*; 49 | let _ = env_logger::try_init(); 50 | let drop_counter = Arc::new(AtomicUsize::new(0)); 51 | let id = Arc::new(AtomicUsize::new(1)); 52 | let map = ConcurrentHandleMap::new(); 53 | let count = 1000; 54 | let mut handles = (0..count) 55 | .into_par_iter() 56 | .map(|_| { 57 | let id = id.fetch_add(1, Ordering::SeqCst); 58 | let handle = with_error(|e| { 59 | map.insert_with_output(e, || { 60 | log::debug!("Created {}", id); 61 | DropChecking { 62 | counter: drop_counter.clone(), 63 | id, 64 | } 65 | }) 66 | }); 67 | (id, handle) 68 | }) 69 | .collect::>(); 70 | 71 | handles.shuffle(&mut thread_rng()); 72 | 73 | assert_eq!(drop_counter.load(Ordering::SeqCst), 0); 74 | handles.par_iter().for_each(|(id, h)| { 75 | with_error(|e| { 76 | map.call_with_output(e, *h, |val| { 77 | assert_eq!(val.id, *id); 78 | }) 79 | }); 80 | }); 81 | 82 | assert_eq!(drop_counter.load(Ordering::SeqCst), 0); 83 | 84 | handles.par_iter().for_each(|(id, h)| { 85 | with_error(|e| { 86 | map.call_with_output(e, *h, |val| { 87 | assert_eq!(val.id, *id); 88 | }) 89 | }); 90 | }); 91 | 92 | handles.par_iter().for_each(|(id, h)| { 93 | let item = map 94 | .remove_u64(*h) 95 | .expect("remove to succeed") 96 | .expect("item to exist"); 97 | assert_eq!(item.id, *id); 98 | let h = map.insert(item).into_u64(); 99 | map.delete_u64(h).expect("delete to succeed"); 100 | }); 101 | assert_eq!(drop_counter.load(Ordering::SeqCst), count); 102 | } 103 | --------------------------------------------------------------------------------