[libos] Implement sub-crates including uring_callback/keyable-arc/object-id/untrusted-alloc etc
This commit is contained in:
		
							parent
							
								
									44eb5ca3fe
								
							
						
					
					
						commit
						fd6e1bae45
					
				| @ -34,9 +34,16 @@ ctor = "0.1" | ||||
| regex = { git = "https://github.com/mesalock-linux/regex-sgx", default-features = false, features = ["std", "unicode", "mesalock_sgx"] } | ||||
| goblin = { version = "0.5.4", default-features = false, features = ["elf64", "elf32", "endian_fd"] } | ||||
| intrusive-collections = "0.9" | ||||
| spin = "0.7" | ||||
| modular-bitfield = "0.11.2" | ||||
| 
 | ||||
| sgx-untrusted-alloc = { path = "./crates/sgx-untrusted-alloc", features = ["sgx"]} | ||||
| io-uring-callback = { path = "./crates/io-uring-callback", features = ["sgx"]} | ||||
| num_enum = { version = "0.5", default-features = false } | ||||
| keyable-arc = { path = "./crates/keyable-arc" } | ||||
| downcast-rs = { version = "1.2.0", default-features = false } | ||||
| spin = "0.7" | ||||
| byteorder = { version = "1.3.2", default-features = false } | ||||
| 
 | ||||
| [patch.'https://github.com/apache/teaclave-sgx-sdk.git'] | ||||
| sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd" } | ||||
| 
 | ||||
| @ -54,7 +61,7 @@ kernel_heap_monitor = []# Kernel heap usage tracking. With overhead. | ||||
| 
 | ||||
| [target.'cfg(not(target_env = "sgx"))'.dependencies] | ||||
| sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" } | ||||
| sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] } | ||||
| sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace", "thread"] } | ||||
| sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" } | ||||
| sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" } | ||||
| sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" } | ||||
|  | ||||
| @ -64,7 +64,8 @@ LIBOS_CORE_A := $(OBJ_DIR)/libos/lib/lib$(LIBOS_CORE_LIB_NAME).a | ||||
| LIBOS_CORE_RS_A := $(OBJ_DIR)/libos/lib/libocclum_libos_core_rs.a | ||||
| 
 | ||||
| # All source code
 | ||||
| RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs src/*/*/*/*.rs src/*/*/*/*/*.rs) | ||||
| RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs src/*/*/*/*.rs src/*/*/*/*/*.rs \
 | ||||
| 				crates/*/src/*.rs crates/*/src/*/*.rs crates/*/src/*/*/*.rs crates/*/src/*/*/*/*.rs) | ||||
| RUST_TARGET_DIR := $(OBJ_DIR)/libos/cargo-target | ||||
| RUST_OUT_DIR := $(OBJ_DIR)/libos/lib | ||||
| EDL_C_SRCS := $(addprefix $(OBJ_DIR)/libos/,$(SRC_OBJ)/Enclave_t.c $(SRC_OBJ)/Enclave_t.h) | ||||
| @ -166,7 +167,8 @@ $(OBJ_DIR)/libos/$(SRC_OBJ)/Enclave_t.c: $(SGX_EDGER8R) ../Enclave.edl | ||||
| 		$(SGX_EDGER8R) $(SGX_EDGER8R_MODE) --trusted $(CUR_DIR)/../Enclave.edl \
 | ||||
| 		--search-path $(SGX_SDK)/include \
 | ||||
| 		--search-path $(RUST_SGX_SDK_DIR)/edl \
 | ||||
| 		--search-path $(CRATES_DIR)/vdso-time/ocalls | ||||
| 		--search-path $(CRATES_DIR)/vdso-time/ocalls \
 | ||||
| 		--search-path $(PROJECT_DIR)/deps/io-uring/ocalls | ||||
| 	@echo "GEN <= $@" | ||||
| 
 | ||||
| $(C_OBJS):$(OBJ_DIR)/libos/$(SRC_OBJ)/%.o: src/%.c | ||||
| @ -188,6 +190,7 @@ format-c: $(C_SRCS) $(CXX_SRCS) | ||||
| 
 | ||||
| format-rust: $(RUST_SRCS) | ||||
| 	@$(call format-rust) | ||||
| 	@cd crates && $(call format-rust) | ||||
| 
 | ||||
| format-check: format-check-c format-check-rust | ||||
| 
 | ||||
| @ -196,6 +199,7 @@ format-check-c: $(C_SRCS) $(CXX_SRCS) | ||||
| 
 | ||||
| format-check-rust: $(RUST_SRCS) | ||||
| 	@$(call format-check-rust) | ||||
| 	@cd crates && $(call format-check-rust) | ||||
| 
 | ||||
| COV_TARGET_DIR := $(RUST_TARGET_DIR)/debug/deps | ||||
| DEPS_DIR := $(shell pwd)/../../deps | ||||
|  | ||||
							
								
								
									
										24
									
								
								src/libos/crates/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										24
									
								
								src/libos/crates/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| [workspace] | ||||
| 
 | ||||
| members = [ | ||||
|     "errno", | ||||
|     "io-uring-callback", | ||||
|     "keyable-arc", | ||||
|     "object-id", | ||||
|     "sgx-untrusted-alloc", | ||||
|     "vdso-time" | ||||
| ] | ||||
| 
 | ||||
| # Default members can run on Linux; non-default members can only run inside SGX. | ||||
| default-members = [ | ||||
|     "errno", | ||||
|     "io-uring-callback", | ||||
|     "keyable-arc", | ||||
|     "object-id", | ||||
|     "sgx-untrusted-alloc", | ||||
|     "vdso-time" | ||||
| ] | ||||
| 
 | ||||
| exclude = [  | ||||
|     "test", | ||||
| ] | ||||
							
								
								
									
										7
									
								
								src/libos/crates/common.mk
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										7
									
								
								src/libos/crates/common.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| MAIN_MAKEFILE := $(firstword $(MAKEFILE_LIST)) | ||||
| INCLUDE_MAKEFILE := $(lastword $(MAKEFILE_LIST)) | ||||
| CURRENT_DIR := $(shell dirname $(realpath $(MAIN_MAKEFILE))) | ||||
| ROOT_DIR := $(realpath $(shell dirname $(realpath $(INCLUDE_MAKEFILE)))/../../../) | ||||
| RUST_SGX_SDK_DIR := $(ROOT_DIR)/deps/rust-sgx-sdk | ||||
| LIBOS_DIR := $(ROOT_DIR)/src/libos | ||||
| LIBOS_CRATES_DIR := $(LIBOS_DIR)/crates | ||||
| @ -116,8 +116,8 @@ mod result; | ||||
| mod to_errno; | ||||
| 
 | ||||
| pub use self::backtrace::ErrorBacktrace; | ||||
| pub use self::errno::*; | ||||
| pub use self::errno::Errno::*; | ||||
| pub use self::errno::*; | ||||
| pub use self::error::{Error, ErrorLocation}; | ||||
| pub use self::result::{Result, ResultExt}; | ||||
| pub use self::to_errno::ToErrno; | ||||
| @ -130,13 +130,18 @@ macro_rules! errno { | ||||
|             let msg: &'static str = $error_msg; | ||||
|             (errno, msg) | ||||
|         }; | ||||
|         let error = | ||||
|             $crate::Error::embedded(inner_error, Some($crate::ErrorLocation::new(file!(), line!()))); | ||||
|         let error = $crate::Error::embedded( | ||||
|             inner_error, | ||||
|             Some($crate::ErrorLocation::new(file!(), line!())), | ||||
|         ); | ||||
|         error | ||||
|     }}; | ||||
|     ($error_expr: expr) => {{ | ||||
|         let inner_error = $error_expr; | ||||
|         let error = $crate::Error::boxed(inner_error, Some($crate::ErrorLocation::new(file!(), line!()))); | ||||
|         let error = $crate::Error::boxed( | ||||
|             inner_error, | ||||
|             Some($crate::ErrorLocation::new(file!(), line!())), | ||||
|         ); | ||||
|         error | ||||
|     }}; | ||||
| } | ||||
|  | ||||
							
								
								
									
										400
									
								
								src/libos/crates/io-uring-callback/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										400
									
								
								src/libos/crates/io-uring-callback/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,400 @@ | ||||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "atomic" | ||||
| version = "0.5.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "autocfg" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "2.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.0.83" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cfg-if" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "errno" | ||||
| version = "0.3.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "2.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" | ||||
| dependencies = [ | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
|  "futures-io", | ||||
|  "futures-sink", | ||||
|  "futures-task", | ||||
|  "futures-util", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-channel" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-sink", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-core" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-io" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-sink" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-task" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-util" | ||||
| version = "0.3.29" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-sink", | ||||
|  "futures-task", | ||||
|  "pin-project-lite", | ||||
|  "pin-utils", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown_tstd" | ||||
| version = "0.12.0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "io-uring" | ||||
| version = "0.5.9" | ||||
| dependencies = [ | ||||
|  "bitflags 1.3.2", | ||||
|  "libc", | ||||
|  "sgx_libc", | ||||
|  "sgx_trts", | ||||
|  "sgx_tstd", | ||||
|  "sgx_types", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "io-uring-callback" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "atomic", | ||||
|  "cfg-if", | ||||
|  "futures", | ||||
|  "io-uring", | ||||
|  "lazy_static", | ||||
|  "libc", | ||||
|  "lock_api", | ||||
|  "log", | ||||
|  "sgx_libc", | ||||
|  "sgx_tstd", | ||||
|  "slab", | ||||
|  "spin 0.7.1", | ||||
|  "tempfile", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "lazy_static" | ||||
| version = "1.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||
| dependencies = [ | ||||
|  "spin 0.5.2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.150" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.4.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "lock_api" | ||||
| version = "0.4.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" | ||||
| dependencies = [ | ||||
|  "scopeguard", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "log" | ||||
| version = "0.4.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pin-project-lite" | ||||
| version = "0.2.13" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pin-utils" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "redox_syscall" | ||||
| version = "0.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" | ||||
| dependencies = [ | ||||
|  "bitflags 1.3.2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "0.38.25" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" | ||||
| dependencies = [ | ||||
|  "bitflags 2.4.1", | ||||
|  "errno", | ||||
|  "libc", | ||||
|  "linux-raw-sys", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "scopeguard" | ||||
| version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_alloc" | ||||
| version = "1.1.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_backtrace_sys" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "sgx_build_helper", | ||||
|  "sgx_libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_build_helper" | ||||
| version = "1.1.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_demangle" | ||||
| version = "1.1.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_libc" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "sgx_types", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_tprotected_fs" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "sgx_trts", | ||||
|  "sgx_types", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_trts" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "sgx_libc", | ||||
|  "sgx_types", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_tstd" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "hashbrown_tstd", | ||||
|  "sgx_alloc", | ||||
|  "sgx_backtrace_sys", | ||||
|  "sgx_demangle", | ||||
|  "sgx_libc", | ||||
|  "sgx_tprotected_fs", | ||||
|  "sgx_trts", | ||||
|  "sgx_types", | ||||
|  "sgx_unwind", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_types" | ||||
| version = "1.1.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sgx_unwind" | ||||
| version = "1.1.5" | ||||
| dependencies = [ | ||||
|  "sgx_build_helper", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "slab" | ||||
| version = "0.4.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "spin" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "spin" | ||||
| version = "0.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.8.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "fastrand", | ||||
|  "redox_syscall", | ||||
|  "rustix", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-sys" | ||||
| version = "0.48.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||||
| dependencies = [ | ||||
|  "windows-targets", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-targets" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | ||||
| dependencies = [ | ||||
|  "windows_aarch64_gnullvm", | ||||
|  "windows_aarch64_msvc", | ||||
|  "windows_i686_gnu", | ||||
|  "windows_i686_msvc", | ||||
|  "windows_x86_64_gnu", | ||||
|  "windows_x86_64_gnullvm", | ||||
|  "windows_x86_64_msvc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_aarch64_gnullvm" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_aarch64_msvc" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_i686_gnu" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_i686_msvc" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_gnu" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_gnullvm" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_msvc" | ||||
| version = "0.48.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | ||||
							
								
								
									
										29
									
								
								src/libos/crates/io-uring-callback/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										29
									
								
								src/libos/crates/io-uring-callback/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| [package] | ||||
| name = "io-uring-callback" | ||||
| version = "0.1.0" | ||||
| authors = ["Tate, Hongliang Tian <tate.thl@antfin.com>"] | ||||
| edition = "2021" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [features] | ||||
| default = ["libc"] | ||||
| sgx = ["sgx_tstd", "sgx_libc", "io-uring/sgx"] | ||||
| 
 | ||||
| [dependencies] | ||||
| atomic = "0.5.0" | ||||
| cfg-if = "1.0.0" | ||||
| lock_api = "=0.4.2" | ||||
| log = "0.4" | ||||
| futures = { version = "0.3", default-features = false, features = ["alloc"]  } | ||||
| lazy_static = { version = "1.4.0", features = ["spin_no_std"] } | ||||
| slab = { version = "0.4.5", default-features = false } | ||||
| libc = { version = "0.2", optional = true } | ||||
| 
 | ||||
| io-uring = { path = "../../../../deps/io-uring", features = ["unstable"]  } | ||||
| sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] } | ||||
| sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true } | ||||
| spin = "0.7" | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| tempfile = "3" | ||||
							
								
								
									
										15
									
								
								src/libos/crates/io-uring-callback/README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										15
									
								
								src/libos/crates/io-uring-callback/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| # io-uring-callback | ||||
| 
 | ||||
| io-uring with callback interface. | ||||
| 
 | ||||
| ## Usage | ||||
| To use io-uring-callback, place the following line under the `[dependencies]` section in your `Cargo.toml`: | ||||
| 
 | ||||
| ``` | ||||
| io-uring-callback = { path = "your_path/io-uring-callback" } | ||||
| ``` | ||||
| 
 | ||||
| if use io-uring-callback in SGX (based on rust-sgx-sdk), place the following line under the `[dependencies]` section in your `Cargo.toml` and prepare incubator-teaclave-sgx-sdk envirenments according to io-uring-callback's `Cargo.toml`: | ||||
| ``` | ||||
| io-uring-callback = { path = "your_path/io-uring-callback", features = ["sgx"]  } | ||||
| ``` | ||||
							
								
								
									
										11
									
								
								src/libos/crates/io-uring-callback/examples/sgx/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										11
									
								
								src/libos/crates/io-uring-callback/examples/sgx/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| Cargo.lock | ||||
| Enclave_u.c | ||||
| Enclave_u.h | ||||
| Enclave_t.c | ||||
| Enclave_t.h | ||||
| app/target | ||||
| enclave/target | ||||
| bin/app | ||||
| *.o | ||||
| *.a | ||||
| *.so | ||||
							
								
								
									
										161
									
								
								src/libos/crates/io-uring-callback/examples/sgx/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										161
									
								
								src/libos/crates/io-uring-callback/examples/sgx/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,161 @@ | ||||
| # Licensed to the Apache Software Foundation (ASF) under one
 | ||||
| # or more contributor license agreements.  See the NOTICE file
 | ||||
| # distributed with this work for additional information
 | ||||
| # regarding copyright ownership.  The ASF licenses this file
 | ||||
| # to you under the Apache License, Version 2.0 (the
 | ||||
| # "License"); you may not use this file except in compliance
 | ||||
| # with the License.  You may obtain a copy of the License at
 | ||||
| #
 | ||||
| #   http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| #
 | ||||
| # Unless required by applicable law or agreed to in writing,
 | ||||
| # software distributed under the License is distributed on an
 | ||||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | ||||
| # KIND, either express or implied.  See the License for the
 | ||||
| # specific language governing permissions and limitations
 | ||||
| # under the License.
 | ||||
| 
 | ||||
| ######## SGX SDK Settings ########
 | ||||
| 
 | ||||
| SGX_SDK ?= /opt/sgxsdk | ||||
| SGX_MODE ?= HW | ||||
| SGX_ARCH ?= x64 | ||||
| 
 | ||||
| include ../../../common.mk | ||||
| 
 | ||||
| include $(RUST_SGX_SDK_DIR)/buildenv.mk | ||||
| 
 | ||||
| OCALLS_DIR := $(ROOT_DIR)/deps/io-uring/ocalls | ||||
| 
 | ||||
| ifeq ($(shell getconf LONG_BIT), 32) | ||||
| 	SGX_ARCH := x86 | ||||
| else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) | ||||
| 	SGX_ARCH := x86 | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(SGX_ARCH), x86) | ||||
| 	SGX_COMMON_CFLAGS := -m32 | ||||
| 	SGX_LIBRARY_PATH := $(SGX_SDK)/lib | ||||
| 	SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign | ||||
| 	SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r | ||||
| else | ||||
| 	SGX_COMMON_CFLAGS := -m64 | ||||
| 	SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 | ||||
| 	SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign | ||||
| 	SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(SGX_DEBUG), 1) | ||||
| ifeq ($(SGX_PRERELEASE), 1) | ||||
| $(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) | ||||
| endif | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(SGX_DEBUG), 1) | ||||
| 	SGX_COMMON_CFLAGS += -O0 -g | ||||
| else | ||||
| 	SGX_COMMON_CFLAGS += -O2 | ||||
| endif | ||||
| 
 | ||||
| SGX_COMMON_CFLAGS += -fstack-protector | ||||
| 
 | ||||
| ######## CUSTOM Settings ########
 | ||||
| 
 | ||||
| CUSTOM_LIBRARY_PATH := ./lib | ||||
| CUSTOM_BIN_PATH := ./bin | ||||
| CUSTOM_EDL_PATH := $(RUST_SGX_SDK_DIR)/edl | ||||
| CUSTOM_COMMON_PATH := $(RUST_SGX_SDK_DIR)/common | ||||
| 
 | ||||
| ######## EDL Settings ########
 | ||||
| 
 | ||||
| Enclave_EDL_Files := enclave/Enclave_t.c enclave/Enclave_t.h app/Enclave_u.c app/Enclave_u.h | ||||
| 
 | ||||
| ######## APP Settings ########
 | ||||
| 
 | ||||
| App_Rust_Flags := --release | ||||
| App_SRC_Files := $(shell find app/ -type f -name '*.rs') $(shell find app/ -type f -name 'Cargo.toml') | ||||
| App_Include_Paths := -I ./app -I./include -I$(SGX_SDK)/include -I$(CUSTOM_EDL_PATH) | ||||
| App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) | ||||
| 
 | ||||
| App_Rust_Path := ./app/target/release | ||||
| App_Enclave_u_Object := lib/libEnclave_u.a | ||||
| App_Name := bin/app | ||||
| 
 | ||||
| ######## Enclave Settings ########
 | ||||
| 
 | ||||
| ifneq ($(SGX_MODE), HW) | ||||
| 	Trts_Library_Name := sgx_trts_sim | ||||
| 	Service_Library_Name := sgx_tservice_sim | ||||
| else | ||||
| 	Trts_Library_Name := sgx_trts | ||||
| 	Service_Library_Name := sgx_tservice | ||||
| endif | ||||
| Crypto_Library_Name := sgx_tcrypto | ||||
| KeyExchange_Library_Name := sgx_tkey_exchange | ||||
| ProtectedFs_Library_Name := sgx_tprotected_fs | ||||
| 
 | ||||
| RustEnclave_C_Files := $(wildcard ./enclave/*.c) | ||||
| RustEnclave_C_Objects := $(RustEnclave_C_Files:.c=.o) | ||||
| RustEnclave_Include_Paths := -I$(CUSTOM_COMMON_PATH)/inc -I$(CUSTOM_EDL_PATH) -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport -I$(SGX_SDK)/include/epid -I ./enclave -I./include | ||||
| 
 | ||||
| RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lenclave | ||||
| RustEnclave_Compile_Flags := $(SGX_COMMON_CFLAGS) $(ENCLAVE_CFLAGS) $(RustEnclave_Include_Paths) | ||||
| RustEnclave_Link_Flags := -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \
 | ||||
| 	-Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \
 | ||||
| 	-Wl,--start-group -lsgx_tstdc -l$(Service_Library_Name) -l$(Crypto_Library_Name) $(RustEnclave_Link_Libs) -Wl,--end-group \
 | ||||
| 	-Wl,--version-script=enclave/Enclave.lds \
 | ||||
| 	$(ENCLAVE_LDFLAGS) | ||||
| 
 | ||||
| RustEnclave_Name := enclave/enclave.so | ||||
| Signed_RustEnclave_Name := bin/enclave.signed.so | ||||
| 
 | ||||
| .PHONY: all | ||||
| all: $(App_Name) $(Signed_RustEnclave_Name) | ||||
| 
 | ||||
| ######## EDL Objects ########
 | ||||
| 
 | ||||
| $(Enclave_EDL_Files): $(SGX_EDGER8R) enclave/Enclave.edl | ||||
| 	$(SGX_EDGER8R) --trusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --trusted-dir enclave | ||||
| 	$(SGX_EDGER8R) --untrusted enclave/Enclave.edl --search-path $(OCALLS_DIR) --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --untrusted-dir app | ||||
| 	@echo "GEN  =>  $(Enclave_EDL_Files)" | ||||
| 
 | ||||
| ######## App Objects ########
 | ||||
| 
 | ||||
| app/Enclave_u.o: $(Enclave_EDL_Files) | ||||
| 	@$(CC) $(App_C_Flags) -c app/Enclave_u.c -o $@ | ||||
| 	@echo "CC   <=  $<" | ||||
| 
 | ||||
| $(App_Enclave_u_Object): app/Enclave_u.o | ||||
| 	$(AR) rcsD $@ $^ | ||||
| 
 | ||||
| $(App_Name): $(App_Enclave_u_Object) $(App_SRC_Files) | ||||
| 	@cd app && SGX_SDK=$(SGX_SDK) cargo build $(App_Rust_Flags) | ||||
| 	@echo "Cargo  =>  $@" | ||||
| 	mkdir -p bin | ||||
| 	cp $(App_Rust_Path)/app ./bin | ||||
| 
 | ||||
| ######## Enclave Objects ########
 | ||||
| 
 | ||||
| enclave/Enclave_t.o: $(Enclave_EDL_Files) | ||||
| 	@$(CC) $(RustEnclave_Compile_Flags) -c enclave/Enclave_t.c -o $@ | ||||
| 	@echo "CC   <=  $<" | ||||
| 
 | ||||
| $(RustEnclave_Name): enclave enclave/Enclave_t.o | ||||
| 	@$(CXX) enclave/Enclave_t.o -o $@ $(RustEnclave_Link_Flags) | ||||
| 	@echo "LINK =>  $@" | ||||
| 
 | ||||
| $(Signed_RustEnclave_Name): $(RustEnclave_Name) | ||||
| 	mkdir -p bin | ||||
| 	@$(SGX_ENCLAVE_SIGNER) sign -key enclave/Enclave_private.pem -enclave $(RustEnclave_Name) -out $@ -config enclave/Enclave.config.xml | ||||
| 	@echo "SIGN =>  $@" | ||||
| 
 | ||||
| .PHONY: enclave | ||||
| enclave: | ||||
| 	$(MAKE) -C ./enclave/ | ||||
| 
 | ||||
| 
 | ||||
| .PHONY: clean | ||||
| clean: | ||||
| 	@rm -f $(App_Name) $(RustEnclave_Name) $(Signed_RustEnclave_Name) enclave/*_t.* app/*_u.* lib/*.a | ||||
| 	@cd enclave && cargo clean && rm -f Cargo.lock | ||||
| 	@cd app && cargo clean && rm -f Cargo.lock | ||||
							
								
								
									
										14
									
								
								src/libos/crates/io-uring-callback/examples/sgx/README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										14
									
								
								src/libos/crates/io-uring-callback/examples/sgx/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| ## tcp_echo example for SGX | ||||
| This is an example of using io-uring-callback in SGX.  | ||||
| This example combines tcp_echo example of io-uring-callback and hello-rust example of incubator-teaclave-sgx-sdk. | ||||
| - ./app : untrusted code | ||||
| - ./bin : executable program | ||||
| - ./enclave : trusted code | ||||
| - ./lib : library | ||||
| 
 | ||||
| ### run tcp_echo example in SGX | ||||
| 1. Prepare environments. | ||||
|     - clone incubator-teaclave-sgx-sdk repo to ```../../../third_parties/```. And checkout incubator-teaclave-sgx-sdk to ```d94996``` commit. | ||||
|     - prepare environments for incubator-teaclave-sgx-sdk. | ||||
| 2. ```make``` | ||||
| 3. ```cd bin && ./app``` | ||||
| @ -0,0 +1,11 @@ | ||||
| [package] | ||||
| name = "app" | ||||
| version = "1.0.0" | ||||
| build = "build.rs" | ||||
| 
 | ||||
| [dependencies] | ||||
| sgx-io-uring-ocalls = { path = "../deps/io-uring/ocalls" } | ||||
| sgx_types = { path = "../deps/rust-sgx-sdk/sgx_types" } | ||||
| sgx_urts = { path = "../deps/rust-sgx-sdk/sgx_urts" } | ||||
| 
 | ||||
| [workspace] | ||||
							
								
								
									
										33
									
								
								src/libos/crates/io-uring-callback/examples/sgx/app/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										33
									
								
								src/libos/crates/io-uring-callback/examples/sgx/app/build.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| // Licensed to the Apache Software Foundation (ASF) under one
 | ||||
| // or more contributor license agreements.  See the NOTICE file
 | ||||
| // distributed with this work for additional information
 | ||||
| // regarding copyright ownership.  The ASF licenses this file
 | ||||
| // to you under the Apache License, Version 2.0 (the
 | ||||
| // "License"); you may not use this file except in compliance
 | ||||
| // with the License.  You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //   http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing,
 | ||||
| // software distributed under the License is distributed on an
 | ||||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | ||||
| // KIND, either express or implied.  See the License for the
 | ||||
| // specific language governing permissions and limitations
 | ||||
| // under the License..
 | ||||
| 
 | ||||
| use std::env; | ||||
| 
 | ||||
| fn main() { | ||||
|     let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/sgxsdk".to_string()); | ||||
|     let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); | ||||
| 
 | ||||
|     println!("cargo:rustc-link-search=native=../lib"); | ||||
|     println!("cargo:rustc-link-lib=static=Enclave_u"); | ||||
| 
 | ||||
|     println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); | ||||
|     match is_sim.as_ref() { | ||||
|         "SW" => println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"), | ||||
|         "HW" => println!("cargo:rustc-link-lib=dylib=sgx_urts"), | ||||
|         _ => println!("cargo:rustc-link-lib=dylib=sgx_urts"), // Treat undefined as HW
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| nightly-2020-10-25 | ||||
| @ -0,0 +1,81 @@ | ||||
| // Licensed to the Apache Software Foundation (ASF) under one
 | ||||
| // or more contributor license agreements.  See the NOTICE file
 | ||||
| // distributed with this work for additional information
 | ||||
| // regarding copyright ownership.  The ASF licenses this file
 | ||||
| // to you under the Apache License, Version 2.0 (the
 | ||||
| // "License"); you may not use this file except in compliance
 | ||||
| // with the License.  You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //   http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing,
 | ||||
| // software distributed under the License is distributed on an
 | ||||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | ||||
| // KIND, either express or implied.  See the License for the
 | ||||
| // specific language governing permissions and limitations
 | ||||
| // under the License..
 | ||||
| 
 | ||||
| extern crate sgx_io_uring_ocalls; | ||||
| extern crate sgx_types; | ||||
| extern crate sgx_urts; | ||||
| use sgx_types::*; | ||||
| use sgx_urts::SgxEnclave; | ||||
| 
 | ||||
| pub use sgx_io_uring_ocalls::*; | ||||
| 
 | ||||
| static ENCLAVE_FILE: &'static str = "enclave.signed.so"; | ||||
| 
 | ||||
| extern "C" { | ||||
|     fn run_sgx_example(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; | ||||
| } | ||||
| 
 | ||||
| fn init_enclave() -> SgxResult<SgxEnclave> { | ||||
|     let mut launch_token: sgx_launch_token_t = [0; 1024]; | ||||
|     let mut launch_token_updated: i32 = 0; | ||||
|     // call sgx_create_enclave to initialize an enclave instance
 | ||||
|     // Debug Support: set 2nd parameter to 1
 | ||||
|     let debug = 1; | ||||
|     let mut misc_attr = sgx_misc_attribute_t { | ||||
|         secs_attr: sgx_attributes_t { flags: 0, xfrm: 0 }, | ||||
|         misc_select: 0, | ||||
|     }; | ||||
|     SgxEnclave::create( | ||||
|         ENCLAVE_FILE, | ||||
|         debug, | ||||
|         &mut launch_token, | ||||
|         &mut launch_token_updated, | ||||
|         &mut misc_attr, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     let enclave = match init_enclave() { | ||||
|         Ok(r) => { | ||||
|             println!("[+] Init Enclave Successful {}!", r.geteid()); | ||||
|             r | ||||
|         } | ||||
|         Err(x) => { | ||||
|             println!("[-] Init Enclave Failed {}!", x.as_str()); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let mut retval = sgx_status_t::SGX_SUCCESS; | ||||
|     let result = unsafe { run_sgx_example(enclave.geteid(), &mut retval) }; | ||||
|     match result { | ||||
|         sgx_status_t::SGX_SUCCESS => {} | ||||
|         _ => { | ||||
|             println!("[-] ECALL Enclave Failed {}!", result.as_str()); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     match retval { | ||||
|         sgx_status_t::SGX_SUCCESS => {} | ||||
|         _ => { | ||||
|             println!("[-] ECALL Returned Error {}!", retval.as_str()); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     println!("[+] run_sgx_example success..."); | ||||
|     enclave.destroy(); | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| bin | ||||
| @ -0,0 +1,23 @@ | ||||
| [package] | ||||
| name = "Helloworldsampleenclave" | ||||
| version = "1.0.0" | ||||
| 
 | ||||
| [lib] | ||||
| name = "helloworldsampleenclave" | ||||
| crate-type = ["staticlib"] | ||||
| 
 | ||||
| [features] | ||||
| default = [] | ||||
| 
 | ||||
| [dependencies] | ||||
| io-uring-callback = { path = "../../../../io-uring-callback", features = ["sgx"] } | ||||
| io-uring = { path = "../../../../../../../deps/io-uring", features = ["sgx"] } | ||||
| slab = { version = "0.4.5", default-features = false } | ||||
| lazy_static = { version = "1.4.0", features = ["spin_no_std"] } | ||||
| 
 | ||||
| [target.'cfg(not(target_env = "sgx"))'.dependencies] | ||||
| sgx_types = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_types" } | ||||
| sgx_tstd = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace", "thread"] } | ||||
| sgx_trts = { path = "../../../../../../../deps/rust-sgx-sdk/sgx_trts" } | ||||
| 
 | ||||
| [workspace] | ||||
| @ -0,0 +1,13 @@ | ||||
| <!-- Please refer to User's Guide for the explanation of each field --> | ||||
| <EnclaveConfiguration> | ||||
|   <ProdID>0</ProdID> | ||||
|   <ISVSVN>0</ISVSVN> | ||||
|   <StackMaxSize>0x40000</StackMaxSize> | ||||
|   <HeapMaxSize>0x400000</HeapMaxSize> | ||||
|   <TCSNum>1</TCSNum> | ||||
|   <TCSMaxNum>1</TCSMaxNum> | ||||
|   <TCSPolicy>0</TCSPolicy> | ||||
|   <DisableDebug>0</DisableDebug> | ||||
|   <MiscSelect>0</MiscSelect> | ||||
|   <MiscMask>0xFFFFFFFF</MiscMask> | ||||
| </EnclaveConfiguration> | ||||
| @ -0,0 +1,36 @@ | ||||
| // Licensed to the Apache Software Foundation (ASF) under one | ||||
| // or more contributor license agreements.  See the NOTICE file | ||||
| // distributed with this work for additional information | ||||
| // regarding copyright ownership.  The ASF licenses this file | ||||
| // to you under the Apache License, Version 2.0 (the | ||||
| // "License"); you may not use this file except in compliance | ||||
| // with the License.  You may obtain a copy of the License at | ||||
| // | ||||
| //   http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, | ||||
| // software distributed under the License is distributed on an | ||||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
| // KIND, either express or implied.  See the License for the | ||||
| // specific language governing permissions and limitations | ||||
| // under the License. | ||||
| 
 | ||||
| enclave { | ||||
|     from "sgx_tstd.edl" import *; | ||||
|     from "sgx_stdio.edl" import *; | ||||
|     from "sgx_backtrace.edl" import *; | ||||
|     from "sgx_tstdc.edl" import *; | ||||
|     from "sgx_net.edl" import *; | ||||
|     from "sgx_thread.edl" import *; | ||||
|      | ||||
|     from "sgx_io_uring_ocalls.edl" import *; | ||||
|      | ||||
|     trusted { | ||||
|         /* define ECALLs here. */ | ||||
|         public sgx_status_t run_sgx_example(); | ||||
|     }; | ||||
| 
 | ||||
|     untrusted { | ||||
|         /* define OCALLs here. */ | ||||
|     }; | ||||
| }; | ||||
| @ -0,0 +1,9 @@ | ||||
| enclave.so | ||||
| { | ||||
|     global: | ||||
|         g_global_data_sim; | ||||
|         g_global_data; | ||||
|         enclave_entry; | ||||
|     local: | ||||
|         *; | ||||
| }; | ||||
| @ -0,0 +1,39 @@ | ||||
| -----BEGIN RSA PRIVATE KEY----- | ||||
| MIIG4gIBAAKCAYEAroOogvsj/fZDZY8XFdkl6dJmky0lRvnWMmpeH41Bla6U1qLZ | ||||
| AmZuyIF+mQC/cgojIsrBMzBxb1kKqzATF4+XwPwgKz7fmiddmHyYz2WDJfAjIveJ | ||||
| ZjdMjM4+EytGlkkJ52T8V8ds0/L2qKexJ+NBLxkeQLfV8n1mIk7zX7jguwbCG1Pr | ||||
| nEMdJ3Sew20vnje+RsngAzdPChoJpVsWi/K7cettX/tbnre1DL02GXc5qJoQYk7b | ||||
| 3zkmhz31TgFrd9VVtmUGyFXAysuSAb3EN+5VnHGr0xKkeg8utErea2FNtNIgua8H | ||||
| ONfm9Eiyaav1SVKzPHlyqLtcdxH3I8Wg7yqMsaprZ1n5A1v/levxnL8+It02KseD | ||||
| 5HqV4rf/cImSlCt3lpRg8U5E1pyFQ2IVEC/XTDMiI3c+AR+w2jSRB3Bwn9zJtFlW | ||||
| KHG3m1xGI4ck+Lci1JvWWLXQagQSPtZTsubxTQNx1gsgZhgv1JHVZMdbVlAbbRMC | ||||
| 1nSuJNl7KPAS/VfzAgEDAoIBgHRXxaynbVP5gkO0ug6Qw/E27wzIw4SmjsxG6Wpe | ||||
| K7kfDeRskKxESdsA/xCrKkwGwhcx1iIgS5+Qscd1Yg+1D9X9asd/P7waPmWoZd+Z | ||||
| AhlKwhdPsO7PiF3e1AzHhGQwsUTt/Y/aSI1MpHBvy2/s1h9mFCslOUxTmWw0oj/Q | ||||
| ldIEgWeNR72CE2+jFIJIyml6ftnb6qzPiga8Bm48ubKh0kvySOqnkmnPzgh+JBD6 | ||||
| JnBmtZbfPT97bwTT+N6rnPqOOApvfHPf15kWI8yDbprG1l4OCUaIUH1AszxLd826 | ||||
| 5IPM+8gINLRDP1MA6azECPjTyHXhtnSIBZCyWSVkc05vYmNXYUNiXWMajcxW9M02 | ||||
| wKzFELO8NCEAkaTPxwo4SCyIjUxiK1LbQ9h8PSy4c1+gGP4LAMR8xqP4QKg6zdu9 | ||||
| osUGG/xRe/uufgTBFkcjqBHtK5L5VI0jeNIUAgW/6iNbYXjBMJ0GfauLs+g1VsOm | ||||
| WfdgXzsb9DYdMa0OXXHypmV4GwKBwQDUwQj8RKJ6c8cT4vcWCoJvJF00+RFL+P3i | ||||
| Gx2DLERxRrDa8AVGfqaCjsR+3vLgG8V/py+z+dxZYSqeB80Qeo6PDITcRKoeAYh9 | ||||
| xlT3LJOS+k1cJcEmlbbO2IjLkTmzSwa80fWexKu8/Xv6vv15gpqYl1ngYoqJM3pd | ||||
| vzmTIOi7MKSZ0WmEQavrZj8zK4endE3v0eAEeQ55j1GImbypSf7Idh7wOXtjZ7WD | ||||
| Dg6yWDrri+AP/L3gClMj8wsAxMV4ZR8CgcEA0fzDHkFa6raVOxWnObmRoDhAtE0a | ||||
| cjUj976NM5yyfdf2MrKy4/RhdTiPZ6b08/lBC/+xRfV3xKVGzacm6QjqjZrUpgHC | ||||
| 0LKiZaMtccCJjLtPwQd0jGQEnKfMFaPsnhOc5y8qVkCzVOSthY5qhz0XNotHHFmJ | ||||
| gffVgB0iqrMTvSL7IA2yqqpOqNRlhaYhNl8TiFP3gIeMtVa9rZy31JPgT2uJ+kfo | ||||
| gV7sdTPEjPWZd7OshGxWpT6QfVDj/T9T7L6tAoHBAI3WBf2DFvxNL2KXT2QHAZ9t | ||||
| k3imC4f7U+wSE6zILaDZyzygA4RUbwG0gv8/TJVn2P/Eynf76DuWHGlaiLWnCbSz | ||||
| Az2DHBQBBaku409zDQym3j1ugMRjzzSQWzJg0SIyBH3hTmnYcn3+Uqcp/lEBvGW6 | ||||
| O+rsXFt3pukqJmIV8HzLGGaLm62BHUeZf3dyWm+i3p/hQAL7Xvu04QW70xuGqdr5 | ||||
| afV7p5eaeQIJXyGQJ0eylV/90+qxjMKiB1XYg6WYvwKBwQCL/ddpgOdHJGN8uRom | ||||
| e7Zq0Csi3hGheMKlKbN3vcxT5U7MdyHtTZZOJbTvxKNNUNYH/8uD+PqDGNneb29G | ||||
| BfGzvI3EASyLIcGZF3OhKwZd0jUrWk2y7Vhob91jwp2+t73vdMbkKyI4mHOuXvGv | ||||
| fg95si9oO7EBT+Oqvhccd2J+F1IVXncccYnF4u5ZGWt5lLewN/pVr7MjjykeaHqN | ||||
| t+rfnQam2psA6fL4zS2zTmZPzR2tnY8Y1GBTi0Ko1OKd1HMCgcAb5cB/7/AQlhP9 | ||||
| yQa04PLH9ygQkKKptZp7dy5WcWRx0K/hAHRoi2aw1wZqfm7VBNu2SLcs90kCCCxp | ||||
| 6C5sfJi6b8NpNbIPC+sc9wsFr7pGo9SFzQ78UlcWYK2Gu2FxlMjonhka5hvo4zvg | ||||
| WxlpXKEkaFt3gLd92m/dMqBrHfafH7VwOJY2zT3WIpjwuk0ZzmRg5p0pG/svVQEH | ||||
| NZmwRwlopysbR69B/n1nefJ84UO50fLh5s5Zr3gBRwbWNZyzhXk= | ||||
| -----END RSA PRIVATE KEY----- | ||||
| @ -0,0 +1,38 @@ | ||||
| # Licensed to the Apache Software Foundation (ASF) under one
 | ||||
| # or more contributor license agreements.  See the NOTICE file
 | ||||
| # distributed with this work for additional information
 | ||||
| # regarding copyright ownership.  The ASF licenses this file
 | ||||
| # to you under the Apache License, Version 2.0 (the
 | ||||
| # "License"); you may not use this file except in compliance
 | ||||
| # with the License.  You may obtain a copy of the License at
 | ||||
| #
 | ||||
| #   http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| #
 | ||||
| # Unless required by applicable law or agreed to in writing,
 | ||||
| # software distributed under the License is distributed on an
 | ||||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | ||||
| # KIND, either express or implied.  See the License for the
 | ||||
| # specific language governing permissions and limitations
 | ||||
| # under the License.
 | ||||
| Rust_Enclave_Name := libenclave.a | ||||
| Rust_Enclave_Files := $(wildcard src/*.rs) | ||||
| Rust_Target_Path := $(CURDIR)/../../../../incubator-teaclave-sgx-sdk/xargo | ||||
| 
 | ||||
| ifeq ($(MITIGATION-CVE-2020-0551), LOAD) | ||||
| export MITIGATION_CVE_2020_0551=LOAD | ||||
| else ifeq ($(MITIGATION-CVE-2020-0551), CF) | ||||
| export MITIGATION_CVE_2020_0551=CF | ||||
| endif | ||||
| 
 | ||||
| .PHONY: all | ||||
| 
 | ||||
| all: $(Rust_Enclave_Name) | ||||
| 
 | ||||
| $(Rust_Enclave_Name): $(Rust_Enclave_Files) | ||||
| ifeq ($(XARGO_SGX), 1) | ||||
| 	RUST_TARGET_PATH=$(Rust_Target_Path) xargo build --target x86_64-unknown-linux-sgx --release | ||||
| 	cp ./target/x86_64-unknown-linux-sgx/release/libhelloworldsampleenclave.a ../lib/libenclave.a | ||||
| else | ||||
| 	cargo build --release | ||||
| 	cp ./target/release/libhelloworldsampleenclave.a ../lib/libenclave.a | ||||
| endif | ||||
| @ -0,0 +1 @@ | ||||
| nightly-2020-10-25 | ||||
| @ -0,0 +1,334 @@ | ||||
| // Licensed to the Apache Software Foundation (ASF) under one
 | ||||
| // or more contributor license agreements.  See the NOTICE file
 | ||||
| // distributed with this work for additional information
 | ||||
| // regarding copyright ownership.  The ASF licenses this file
 | ||||
| // to you under the Apache License, Version 2.0 (the
 | ||||
| // "License"); you may not use this file except in compliance
 | ||||
| // with the License.  You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //   http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing,
 | ||||
| // software distributed under the License is distributed on an
 | ||||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | ||||
| // KIND, either express or implied.  See the License for the
 | ||||
| // specific language governing permissions and limitations
 | ||||
| // under the License..
 | ||||
| 
 | ||||
| #![crate_name = "helloworldsampleenclave"] | ||||
| #![crate_type = "staticlib"] | ||||
| #![cfg_attr(not(target_env = "sgx"), no_std)] | ||||
| #![cfg_attr(target_env = "sgx", feature(rustc_private))] | ||||
| 
 | ||||
| extern crate sgx_trts; | ||||
| extern crate sgx_types; | ||||
| #[cfg(not(target_env = "sgx"))] | ||||
| #[macro_use] | ||||
| extern crate sgx_tstd as std; | ||||
| 
 | ||||
| extern crate io_uring; | ||||
| extern crate io_uring_callback; | ||||
| extern crate lazy_static; | ||||
| extern crate slab; | ||||
| 
 | ||||
| use sgx_trts::libc; | ||||
| use sgx_types::*; | ||||
| use std::collections::VecDeque; | ||||
| use std::os::unix::io::RawFd; | ||||
| use std::prelude::v1::*; | ||||
| use std::ptr; | ||||
| use std::sync::SgxMutex as Mutex; | ||||
| 
 | ||||
| use io_uring::opcode::types; | ||||
| use io_uring_callback::{Builder, Handle, IoUring}; | ||||
| use lazy_static::lazy_static; | ||||
| use slab::Slab; | ||||
| 
 | ||||
| lazy_static! { | ||||
|     static ref TOKEN_QUEUE: Mutex<VecDeque<(Token, i32)>> = Mutex::new(VecDeque::new()); | ||||
|     static ref HANDLE_SLAB: Mutex<slab::Slab<Handle>> = Mutex::new(slab::Slab::new()); | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| enum Token { | ||||
|     Accept, | ||||
|     Poll { | ||||
|         fd: RawFd, | ||||
|     }, | ||||
|     Read { | ||||
|         fd: RawFd, | ||||
|         buf_index: usize, | ||||
|     }, | ||||
|     Write { | ||||
|         fd: RawFd, | ||||
|         buf_index: usize, | ||||
|         offset: usize, | ||||
|         len: usize, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| pub struct AcceptCount { | ||||
|     fd: types::Fd, | ||||
|     count: usize, | ||||
| } | ||||
| 
 | ||||
| impl AcceptCount { | ||||
|     fn new(fd: RawFd, count: usize) -> AcceptCount { | ||||
|         AcceptCount { | ||||
|             fd: types::Fd(fd), | ||||
|             count: count, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn try_push_accept(&mut self, ring: &IoUring) { | ||||
|         while self.count > 0 { | ||||
|             let to_complete_token = Token::Accept; | ||||
|             let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|             let slab_entry = handle_slab.vacant_entry(); | ||||
|             let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|             let complete_fn = move |retval: i32| { | ||||
|                 let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                 queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                 HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|             }; | ||||
| 
 | ||||
|             let handle = | ||||
|                 unsafe { ring.accept(self.fd, ptr::null_mut(), ptr::null_mut(), 0, complete_fn) }; | ||||
| 
 | ||||
|             slab_entry.insert(handle); | ||||
| 
 | ||||
|             self.count -= 1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[no_mangle] | ||||
| pub extern "C" fn run_sgx_example() -> sgx_status_t { | ||||
|     // std::backtrace::enable_backtrace("enclave.signed.so", std::backtrace::PrintFormat::Full);
 | ||||
|     println!("[ECALL] run_sgx_example"); | ||||
| 
 | ||||
|     let ring = Builder::new() | ||||
|         .setup_sqpoll(Some(500/* ms */)) | ||||
|         .build(256) | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     let socket_fd = { | ||||
|         let socket_fd = unsafe { libc::ocall::socket(libc::AF_INET, libc::SOCK_STREAM, 0) }; | ||||
|         if socket_fd < 0 { | ||||
|             println!("[ECALL] create socket failed, ret: {}", socket_fd); | ||||
|             return sgx_status_t::SGX_ERROR_UNEXPECTED; | ||||
|         } | ||||
| 
 | ||||
|         let ret = unsafe { | ||||
|             let servaddr = libc::sockaddr_in { | ||||
|                 sin_family: libc::AF_INET as u16, | ||||
|                 sin_port: 3456_u16.to_be(), | ||||
|                 sin_addr: libc::in_addr { s_addr: 0 }, | ||||
|                 sin_zero: [0; 8], | ||||
|             }; | ||||
|             libc::ocall::bind( | ||||
|                 socket_fd, | ||||
|                 &servaddr as *const _ as *const libc::sockaddr, | ||||
|                 core::mem::size_of::<libc::sockaddr_in>() as u32, | ||||
|             ) | ||||
|         }; | ||||
|         if ret < 0 { | ||||
|             println!("[ECALL] bind failed, ret: {}", ret); | ||||
|             unsafe { | ||||
|                 libc::ocall::close(socket_fd); | ||||
|             } | ||||
|             return sgx_status_t::SGX_ERROR_UNEXPECTED; | ||||
|         } | ||||
| 
 | ||||
|         let ret = unsafe { libc::ocall::listen(socket_fd, 10) }; | ||||
|         if ret < 0 { | ||||
|             println!("[ECALL] listen failed, ret: {}", ret); | ||||
|             unsafe { | ||||
|                 libc::ocall::close(socket_fd); | ||||
|             } | ||||
|             return sgx_status_t::SGX_ERROR_UNEXPECTED; | ||||
|         } | ||||
|         socket_fd | ||||
|     }; | ||||
| 
 | ||||
|     let mut bufpool = Vec::with_capacity(64); | ||||
|     let mut buf_alloc = Slab::with_capacity(64); | ||||
| 
 | ||||
|     println!("[ECALL] listen 127.0.0.1:3456"); | ||||
| 
 | ||||
|     let mut accept = AcceptCount::new(socket_fd, 3); | ||||
| 
 | ||||
|     loop { | ||||
|         accept.try_push_accept(&ring); | ||||
| 
 | ||||
|         ring.trigger_callbacks(); | ||||
| 
 | ||||
|         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|         while !queue.is_empty() { | ||||
|             let (token, ret) = queue.pop_front().unwrap(); | ||||
|             match token { | ||||
|                 Token::Accept => { | ||||
|                     println!("[ECALL] accept"); | ||||
| 
 | ||||
|                     accept.count += 1; | ||||
| 
 | ||||
|                     let fd = ret; | ||||
| 
 | ||||
|                     let to_complete_token = Token::Poll { fd }; | ||||
|                     let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                     let slab_entry = handle_slab.vacant_entry(); | ||||
|                     let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                     let complete_fn = move |retval: i32| { | ||||
|                         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                         queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                         HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                     }; | ||||
| 
 | ||||
|                     let handle = | ||||
|                         unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|                     slab_entry.insert(handle); | ||||
|                 } | ||||
|                 Token::Poll { fd } => { | ||||
|                     let (buf_index, buf) = match bufpool.pop() { | ||||
|                         Some(buf_index) => (buf_index, &mut buf_alloc[buf_index]), | ||||
|                         None => { | ||||
|                             let buf = Box::new(unsafe { | ||||
|                                 std::slice::from_raw_parts_mut( | ||||
|                                     libc::ocall::malloc(2048) as *mut u8, | ||||
|                                     2048, | ||||
|                                 ) | ||||
|                             }); | ||||
|                             let buf_entry = buf_alloc.vacant_entry(); | ||||
|                             let buf_index = buf_entry.key(); | ||||
|                             (buf_index, buf_entry.insert(buf)) | ||||
|                         } | ||||
|                     }; | ||||
| 
 | ||||
|                     let to_complete_token = Token::Read { fd, buf_index }; | ||||
|                     let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                     let slab_entry = handle_slab.vacant_entry(); | ||||
|                     let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                     let complete_fn = move |retval: i32| { | ||||
|                         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                         queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                         HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                     }; | ||||
| 
 | ||||
|                     let handle = unsafe { | ||||
|                         ring.read( | ||||
|                             types::Fd(fd), | ||||
|                             buf.as_mut_ptr(), | ||||
|                             buf.len() as _, | ||||
|                             0, | ||||
|                             0, | ||||
|                             complete_fn, | ||||
|                         ) | ||||
|                     }; | ||||
| 
 | ||||
|                     slab_entry.insert(handle); | ||||
|                 } | ||||
|                 Token::Read { fd, buf_index } => { | ||||
|                     if ret == 0 { | ||||
|                         bufpool.push(buf_index); | ||||
| 
 | ||||
|                         println!("[ECALL] shutdown"); | ||||
| 
 | ||||
|                         unsafe { | ||||
|                             libc::ocall::close(fd); | ||||
|                         } | ||||
|                     } else { | ||||
|                         let len = ret as usize; | ||||
|                         let buf = &buf_alloc[buf_index]; | ||||
| 
 | ||||
|                         let to_complete_token = Token::Write { | ||||
|                             fd, | ||||
|                             buf_index, | ||||
|                             len, | ||||
|                             offset: 0, | ||||
|                         }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = unsafe { | ||||
|                             ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn) | ||||
|                         }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     } | ||||
|                 } | ||||
|                 Token::Write { | ||||
|                     fd, | ||||
|                     buf_index, | ||||
|                     offset, | ||||
|                     len, | ||||
|                 } => { | ||||
|                     let write_len = ret as usize; | ||||
| 
 | ||||
|                     if offset + write_len >= len { | ||||
|                         bufpool.push(buf_index); | ||||
| 
 | ||||
|                         let to_complete_token = Token::Poll { fd }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = | ||||
|                             unsafe { ring.poll_add(types::Fd(fd), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     } else { | ||||
|                         let offset = offset + write_len; | ||||
|                         let len = len - offset; | ||||
| 
 | ||||
|                         let buf = &buf_alloc[buf_index][offset..]; | ||||
| 
 | ||||
|                         let to_complete_token = Token::Write { | ||||
|                             fd, | ||||
|                             buf_index, | ||||
|                             offset, | ||||
|                             len, | ||||
|                         }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = unsafe { | ||||
|                             ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn) | ||||
|                         }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/libos/crates/io-uring-callback/examples/sgx/enclave/x86_64-unknown-linux-sgx.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										31
									
								
								src/libos/crates/io-uring-callback/examples/sgx/enclave/x86_64-unknown-linux-sgx.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| { | ||||
|   "arch": "x86_64", | ||||
|   "cpu": "x86-64", | ||||
|   "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", | ||||
|   "dynamic-linking": true, | ||||
|   "env": "sgx", | ||||
|   "exe-allocation-crate": "alloc_system", | ||||
|   "executables": true, | ||||
|   "has-elf-tls": true, | ||||
|   "has-rpath": true, | ||||
|   "linker-flavor": "gcc", | ||||
|   "linker-is-gnu": true, | ||||
|   "llvm-target": "x86_64-unknown-linux-gnu", | ||||
|   "max-atomic-width": 64, | ||||
|   "os": "linux", | ||||
|   "position-independent-executables": true, | ||||
|   "pre-link-args": { | ||||
|     "gcc": [ | ||||
|       "-Wl,--as-needed", | ||||
|       "-Wl,-z,noexecstack", | ||||
|       "-m64" | ||||
|     ] | ||||
|   }, | ||||
|   "relro-level": "full", | ||||
|   "stack-probes": true, | ||||
|   "target-c-int-width": "32", | ||||
|   "target-endian": "little", | ||||
|   "target-family": "unix", | ||||
|   "target-pointer-width": "64", | ||||
|   "vendor": "mesalock" | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| lib | ||||
							
								
								
									
										251
									
								
								src/libos/crates/io-uring-callback/examples/tcp_echo.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										251
									
								
								src/libos/crates/io-uring-callback/examples/tcp_echo.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,251 @@ | ||||
| use std::collections::VecDeque; | ||||
| use std::net::TcpListener; | ||||
| use std::os::unix::io::{AsRawFd, RawFd}; | ||||
| use std::ptr; | ||||
| use std::sync::Mutex; | ||||
| 
 | ||||
| use io_uring::opcode::types; | ||||
| use io_uring_callback::{Builder, IoHandle, IoUring}; | ||||
| use lazy_static::lazy_static; | ||||
| 
 | ||||
| lazy_static! { | ||||
|     static ref TOKEN_QUEUE: Mutex<VecDeque<(Token, i32)>> = Mutex::new(VecDeque::new()); | ||||
|     static ref HANDLE_SLAB: Mutex<slab::Slab<IoHandle>> = Mutex::new(slab::Slab::new()); | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| enum Token { | ||||
|     Accept, | ||||
|     Poll { | ||||
|         fd: RawFd, | ||||
|     }, | ||||
|     Read { | ||||
|         fd: RawFd, | ||||
|         buf_index: usize, | ||||
|     }, | ||||
|     Write { | ||||
|         fd: RawFd, | ||||
|         buf_index: usize, | ||||
|         offset: usize, | ||||
|         len: usize, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| pub struct AcceptCount { | ||||
|     fd: types::Fd, | ||||
|     count: usize, | ||||
| } | ||||
| 
 | ||||
| impl AcceptCount { | ||||
|     fn new(fd: RawFd, count: usize) -> AcceptCount { | ||||
|         AcceptCount { | ||||
|             fd: types::Fd(fd), | ||||
|             count: count, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn try_push_accept(&mut self, ring: &IoUring) { | ||||
|         while self.count > 0 { | ||||
|             let to_complete_token = Token::Accept; | ||||
|             let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|             let slab_entry = handle_slab.vacant_entry(); | ||||
|             let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|             let complete_fn = move |retval: i32| { | ||||
|                 let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                 queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                 HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|             }; | ||||
| 
 | ||||
|             let handle = | ||||
|                 unsafe { ring.accept(self.fd, ptr::null_mut(), ptr::null_mut(), 0, complete_fn) }; | ||||
| 
 | ||||
|             slab_entry.insert(handle); | ||||
| 
 | ||||
|             self.count -= 1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     let ring = Builder::new() | ||||
|         .setup_sqpoll(Some(500 /* ms */)) | ||||
|         .build(256) | ||||
|         .unwrap(); | ||||
|     let listener = TcpListener::bind(("127.0.0.1", 3456)).unwrap(); | ||||
| 
 | ||||
|     let mut bufpool = Vec::with_capacity(64); | ||||
|     let mut buf_alloc = slab::Slab::with_capacity(64); | ||||
| 
 | ||||
|     println!("listen {}", listener.local_addr().unwrap()); | ||||
| 
 | ||||
|     let mut accept = AcceptCount::new(listener.as_raw_fd(), 3); | ||||
| 
 | ||||
|     loop { | ||||
|         accept.try_push_accept(&ring); | ||||
| 
 | ||||
|         ring.poll_completions(0, 100); | ||||
| 
 | ||||
|         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|         while !queue.is_empty() { | ||||
|             let (token, ret) = queue.pop_front().unwrap(); | ||||
|             match token { | ||||
|                 Token::Accept => { | ||||
|                     println!("accept"); | ||||
| 
 | ||||
|                     accept.count += 1; | ||||
| 
 | ||||
|                     let fd = ret; | ||||
| 
 | ||||
|                     let to_complete_token = Token::Poll { fd }; | ||||
|                     let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                     let slab_entry = handle_slab.vacant_entry(); | ||||
|                     let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                     let complete_fn = move |retval: i32| { | ||||
|                         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                         queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                         HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                     }; | ||||
| 
 | ||||
|                     let handle = | ||||
|                         unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|                     slab_entry.insert(handle); | ||||
|                 } | ||||
|                 Token::Poll { fd } => { | ||||
|                     let (buf_index, buf) = match bufpool.pop() { | ||||
|                         Some(buf_index) => (buf_index, &mut buf_alloc[buf_index]), | ||||
|                         None => { | ||||
|                             let buf = vec![0u8; 2048].into_boxed_slice(); | ||||
|                             let buf_entry = buf_alloc.vacant_entry(); | ||||
|                             let buf_index = buf_entry.key(); | ||||
|                             (buf_index, buf_entry.insert(buf)) | ||||
|                         } | ||||
|                     }; | ||||
| 
 | ||||
|                     let to_complete_token = Token::Read { fd, buf_index }; | ||||
|                     let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                     let slab_entry = handle_slab.vacant_entry(); | ||||
|                     let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                     let complete_fn = move |retval: i32| { | ||||
|                         let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                         queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                         HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                     }; | ||||
| 
 | ||||
|                     let handle = unsafe { | ||||
|                         ring.read( | ||||
|                             types::Fd(fd), | ||||
|                             buf.as_mut_ptr(), | ||||
|                             buf.len() as _, | ||||
|                             0, | ||||
|                             0, | ||||
|                             complete_fn, | ||||
|                         ) | ||||
|                     }; | ||||
| 
 | ||||
|                     slab_entry.insert(handle); | ||||
|                 } | ||||
|                 Token::Read { fd, buf_index } => { | ||||
|                     if ret == 0 { | ||||
|                         bufpool.push(buf_index); | ||||
| 
 | ||||
|                         println!("shutdown"); | ||||
| 
 | ||||
|                         unsafe { | ||||
|                             libc::close(fd); | ||||
|                         } | ||||
|                     } else { | ||||
|                         let len = ret as usize; | ||||
|                         let buf = &buf_alloc[buf_index]; | ||||
| 
 | ||||
|                         let to_complete_token = Token::Write { | ||||
|                             fd, | ||||
|                             buf_index, | ||||
|                             len, | ||||
|                             offset: 0, | ||||
|                         }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = unsafe { | ||||
|                             ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn) | ||||
|                         }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     } | ||||
|                 } | ||||
|                 Token::Write { | ||||
|                     fd, | ||||
|                     buf_index, | ||||
|                     offset, | ||||
|                     len, | ||||
|                 } => { | ||||
|                     let write_len = ret as usize; | ||||
| 
 | ||||
|                     if offset + write_len >= len { | ||||
|                         bufpool.push(buf_index); | ||||
| 
 | ||||
|                         let to_complete_token = Token::Poll { fd }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = | ||||
|                             unsafe { ring.poll(types::Fd(fd), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     } else { | ||||
|                         let offset = offset + write_len; | ||||
|                         let len = len - offset; | ||||
| 
 | ||||
|                         let buf = &buf_alloc[buf_index][offset..]; | ||||
| 
 | ||||
|                         let to_complete_token = Token::Write { | ||||
|                             fd, | ||||
|                             buf_index, | ||||
|                             offset, | ||||
|                             len, | ||||
|                         }; | ||||
|                         let mut handle_slab = HANDLE_SLAB.lock().unwrap(); | ||||
|                         let slab_entry = handle_slab.vacant_entry(); | ||||
|                         let slab_key = slab_entry.key(); | ||||
| 
 | ||||
|                         let complete_fn = move |retval: i32| { | ||||
|                             let mut queue = TOKEN_QUEUE.lock().unwrap(); | ||||
|                             queue.push_back((to_complete_token, retval)); | ||||
| 
 | ||||
|                             HANDLE_SLAB.lock().unwrap().remove(slab_key); | ||||
|                         }; | ||||
| 
 | ||||
|                         let handle = unsafe { | ||||
|                             ring.write(types::Fd(fd), buf.as_ptr(), len as _, 0, 0, complete_fn) | ||||
|                         }; | ||||
| 
 | ||||
|                         slab_entry.insert(handle); | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/libos/crates/io-uring-callback/rust-toolchain
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								src/libos/crates/io-uring-callback/rust-toolchain
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| nightly-2022-02-23 | ||||
							
								
								
									
										192
									
								
								src/libos/crates/io-uring-callback/src/io_handle.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										192
									
								
								src/libos/crates/io-uring-callback/src/io_handle.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| use std::sync::Arc; | ||||
| cfg_if::cfg_if! { | ||||
|     if #[cfg(feature = "sgx")] { | ||||
|         use std::prelude::v1::*; | ||||
|         use spin::Mutex as Mutex; | ||||
|     } else { | ||||
|         use std::sync::Mutex; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// The handle to an I/O request pushed to the submission queue of an io_uring instance.
 | ||||
| #[derive(Debug)] | ||||
| #[repr(transparent)] | ||||
| pub struct IoHandle(pub(crate) Arc<IoToken>); | ||||
| 
 | ||||
| /// The state of an I/O request represented by an [`IoHandle`].
 | ||||
| /// If a request is in `Processed` or `Cancelled` state, means that the request is completed.
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||
| pub enum IoState { | ||||
|     /// The I/O request has been submitted.
 | ||||
|     Submitted, | ||||
|     /// The I/O request has been processed by the kernel and returns a value.
 | ||||
|     Processed(i32), | ||||
|     /// The I/O request is being cancelled.
 | ||||
|     Cancelling, | ||||
|     /// The I/O request has been cancelled by the kernel.
 | ||||
|     Cancelled, | ||||
| } | ||||
| 
 | ||||
| const CANCEL_RETVAL: i32 = -libc::ECANCELED; | ||||
| 
 | ||||
| impl IoHandle { | ||||
|     pub(crate) fn new(token: Arc<IoToken>) -> Self { | ||||
|         Self(token) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the state of the I/O request.
 | ||||
|     pub fn state(&self) -> IoState { | ||||
|         self.0.state() | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the return value of the I/O request if it is completed.
 | ||||
|     pub fn retval(&self) -> Option<i32> { | ||||
|         self.0.retval() | ||||
|     } | ||||
| 
 | ||||
|     /// Release a handle.
 | ||||
|     ///
 | ||||
|     /// Normally, a handle is not alloed to be dropped before the I/O is completed.
 | ||||
|     /// This helps discover memory safety problems due to potential misuse by users.
 | ||||
|     ///
 | ||||
|     /// But sometimes keeping handles can be pointless. This is when the `release`
 | ||||
|     /// method can help.  The release method explictly states that a handle is
 | ||||
|     /// useless and then drop it.
 | ||||
|     pub fn release(self) { | ||||
|         // Safety. The representation is transparent.
 | ||||
|         let token = unsafe { std::mem::transmute::<Self, Arc<IoToken>>(self) }; | ||||
|         drop(token); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Unpin for IoHandle {} | ||||
| 
 | ||||
| impl Drop for IoHandle { | ||||
|     fn drop(&mut self) { | ||||
|         // The user cannot drop a handle if the request isn't completed.
 | ||||
|         assert!(matches!( | ||||
|             self.state(), | ||||
|             IoState::Processed(_) | IoState::Cancelled | ||||
|         )); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A token representing an on-going I/O request.
 | ||||
| ///
 | ||||
| /// Tokens and handles are basically the same thing---an on-going I/O request. The main difference
 | ||||
| /// is that handles are used by users, while tokens are used internally.
 | ||||
| pub(crate) struct IoToken { | ||||
|     inner: Mutex<Inner>, | ||||
| } | ||||
| 
 | ||||
| impl IoToken { | ||||
|     pub fn new(completion_callback: impl FnOnce(i32) + Send + 'static, token_key: u64) -> Self { | ||||
|         let inner = Mutex::new(Inner::new(completion_callback, token_key)); | ||||
|         Self { inner } | ||||
|     } | ||||
| 
 | ||||
|     pub fn state(&self) -> IoState { | ||||
|         let inner = self.inner.lock(); | ||||
|         inner.state() | ||||
|     } | ||||
| 
 | ||||
|     pub fn retval(&self) -> Option<i32> { | ||||
|         let inner = self.inner.lock(); | ||||
|         inner.retval() | ||||
|     } | ||||
| 
 | ||||
|     pub fn complete(&self, retval: i32) { | ||||
|         // let mut inner = self.inner.lock().unwrap();
 | ||||
|         let mut inner = self.inner.lock(); | ||||
|         let callback = inner.complete(retval); | ||||
|         // Must release the lock before invoking the callback function.
 | ||||
|         // This avoids any deadlock if the IoHandle is accessed inside the callback by
 | ||||
|         // user.
 | ||||
|         drop(inner); | ||||
| 
 | ||||
|         (callback)(retval); | ||||
|     } | ||||
| 
 | ||||
|     /// Change the state from submited to cancelling.
 | ||||
|     /// If transition succeeds, return the token_key for following cancel operation.
 | ||||
|     pub fn transit_to_cancelling(&self) -> Result<u64, ()> { | ||||
|         // let mut inner = self.inner.lock().unwrap();
 | ||||
|         let mut inner = self.inner.lock(); | ||||
|         inner.transit_to_cancelling() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Debug for IoToken { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("IoToken") | ||||
|             .field("state", &self.state()) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct Inner { | ||||
|     state: IoState, | ||||
|     completion_callback: Option<Callback>, | ||||
|     token_key: u64, | ||||
| } | ||||
| 
 | ||||
| type Callback = Box<dyn FnOnce(i32) + Send + 'static>; | ||||
| 
 | ||||
| impl Inner { | ||||
|     pub fn new(completion_callback: impl FnOnce(i32) + Send + 'static, token_key: u64) -> Self { | ||||
|         let state = IoState::Submitted; | ||||
|         let completion_callback = Some(Box::new(completion_callback) as _); | ||||
|         Self { | ||||
|             state, | ||||
|             completion_callback, | ||||
|             token_key, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn complete(&mut self, retval: i32) -> Callback { | ||||
|         match self.state { | ||||
|             IoState::Submitted => { | ||||
|                 self.state = IoState::Processed(retval); | ||||
|             } | ||||
|             IoState::Cancelling => { | ||||
|                 if retval == CANCEL_RETVAL { | ||||
|                     // case 1: The request was cancelled successfully.
 | ||||
|                     self.state = IoState::Cancelled; | ||||
|                 } else { | ||||
|                     // case 2.1: The request was cancelled with error.
 | ||||
|                     // case 2.2: The request was not actually canceled.
 | ||||
|                     self.state = IoState::Processed(retval); | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 unreachable!("cannot do complete twice"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         self.completion_callback.take().unwrap() | ||||
|     } | ||||
| 
 | ||||
|     pub fn transit_to_cancelling(&mut self) -> Result<u64, ()> { | ||||
|         match self.state { | ||||
|             IoState::Submitted => { | ||||
|                 self.state = IoState::Cancelling; | ||||
|                 return Ok(self.token_key); | ||||
|             } | ||||
|             _ => { | ||||
|                 return Err(()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn retval(&self) -> Option<i32> { | ||||
|         match self.state { | ||||
|             IoState::Processed(retval) => Some(retval), | ||||
|             IoState::Cancelled => Some(CANCEL_RETVAL), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn state(&self) -> IoState { | ||||
|         self.state | ||||
|     } | ||||
| } | ||||
							
								
								
									
										812
									
								
								src/libos/crates/io-uring-callback/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										812
									
								
								src/libos/crates/io-uring-callback/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,812 @@ | ||||
| //! A more user-friendly io_uring crate.
 | ||||
| //!
 | ||||
| //! # Overview
 | ||||
| //!
 | ||||
| //! While the original [io_uring crate](https://github.com/tokio-rs/io-uring) exposes io_uring's API in Rust, it has
 | ||||
| //! one big shortcoming: users have to manually pop entries out of the completion queue and map those entries to
 | ||||
| //! user requests. It makes the APIs cumbersome to use.
 | ||||
| //!
 | ||||
| //! This crate provides more user-friend APIs with the following features:
 | ||||
| //!
 | ||||
| //! * Callback-based. On the completion of an I/O request, the corresponding user-registered
 | ||||
| //! callback will get invoked. No manual dispatching of I/O completions.
 | ||||
| //!
 | ||||
| //! * Async/await-ready. After submitting an I/O request, the user will get a handle that
 | ||||
| //! represents the on-going I/O request. The user can await the handle (as it is a `Future`).
 | ||||
| //!
 | ||||
| //! * Polling-based I/O. Both I/O submissions and completions can be easily done in polling mode.
 | ||||
| //!
 | ||||
| //! # Usage
 | ||||
| //!
 | ||||
| //! Use [`Builder`] to create a new instance of [`IoUring`].
 | ||||
| //!
 | ||||
| //! ```
 | ||||
| //! use io_uring_callback::{Builder, IoUring};
 | ||||
| //!
 | ||||
| //! let io_uring: IoUring = Builder::new().build(256).unwrap();
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! A number of I/O operations are supported, e.g., `read`, `write`, `fsync`, `sendmsg`,
 | ||||
| //! `recvmsg`, etc. Requests for such I/O operations can be pushed into the submission
 | ||||
| //! queue of the io_uring with the corresponding methods.
 | ||||
| //!
 | ||||
| //! ```
 | ||||
| //! # use io_uring_callback::{Builder};
 | ||||
| //! use io_uring_callback::{Fd, RwFlags};
 | ||||
| //!
 | ||||
| //! # let io_uring = Builder::new().build(256).unwrap();
 | ||||
| //! let fd = Fd(1); // use the stdout
 | ||||
| //! let msg = "hello world\0";
 | ||||
| //! let completion_callback = move |retval: i32| {
 | ||||
| //!     assert!(retval > 0);
 | ||||
| //! };
 | ||||
| //! let handle = unsafe {
 | ||||
| //!     io_uring.write(fd, msg.as_ptr(), msg.len() as u32, 0, RwFlags::default(), completion_callback)
 | ||||
| //! };
 | ||||
| //! # while handle.retval().is_none() {
 | ||||
| //! #    io_uring.wait_completions(1);
 | ||||
| //! # }
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! You have to two ways to get notified about the completion of I/O requests. The first
 | ||||
| //! is through the registered callback function and the second is by `await`ing the handle
 | ||||
| //! (which is a `Future`) obtained as a result of pushing I/O requests.
 | ||||
| //!
 | ||||
| //! After completing the I/O requests, Linux will push I/O responses into the completion queue of
 | ||||
| //! the io_uring instance. You need _periodically_ poll completions from the queue:
 | ||||
| //! ```no_run
 | ||||
| //! # use io_uring_callback::{Builder};
 | ||||
| //! # let io_uring = Builder::new().build(256).unwrap();
 | ||||
| //! let min_complete = 1;
 | ||||
| //! let polling_retries = 5000;
 | ||||
| //! io_uring.poll_completions(min_complete, polling_retries);
 | ||||
| //! ```
 | ||||
| //! which will trigger registered callbacks and wake up handles.
 | ||||
| //!
 | ||||
| //! When the I/O request is completed (the request is processed or cancelled by the kernel),
 | ||||
| //! `poll_completions` will trigger the user-registered callback.
 | ||||
| //!
 | ||||
| //! # I/O Handles
 | ||||
| //!
 | ||||
| //! After submitting an I/O request, the user will get as the return value
 | ||||
| //! an instance of [`IoHandle`], which represents the submitted I/O requests.
 | ||||
| //!
 | ||||
| //! So why bother keeping I/O handles? The reasons are three-fold.
 | ||||
| //!
 | ||||
| //! - First, as a future, `IoHandle` allows you to await on it, which is quite
 | ||||
| //! convenient if you happen to use io_uring with Rust's async/await.
 | ||||
| //! - Second, `IoHandle` makes it possible to _cancel_ on-going I/O requests.
 | ||||
| //! - Third, it makes the whole APIs less prone to memory safety issues. Recall that all I/O submitting
 | ||||
| //! methods (e.g., `write`, `accept`, etc.) are _unsafe_ as there are no guarantee that
 | ||||
| //! their arguments---like FDs or buffer pointers---are valid throughout the lifetime of
 | ||||
| //! an I/O request. What if an user accidentally releases the in-use resources associated with
 | ||||
| //! an on-going I/O request? I/O handles can detect such programming bugs as long as
 | ||||
| //! the handles are also released along with other in-use I/O resources (which is most likely).
 | ||||
| //! This is because when an `IoHandle` is dropped, we will panic if its state is neither
 | ||||
| //! processed (`IoState::Processed`) or canceled (`IoState::Canceled`). That is, dropping
 | ||||
| //! an `IoHandle` that is still in-use is forbidden.
 | ||||
| //!
 | ||||
| //! After pushing an I/O request into the submission queue, you will get an `IoHandle`.
 | ||||
| //! With this handle, you can cancel the I/O request.
 | ||||
| //! ```
 | ||||
| //! # use io_uring_callback::Builder;
 | ||||
| //! use io_uring_callback::{Timespec, TimeoutFlags};
 | ||||
| //!
 | ||||
| //! # let io_uring = Builder::new().build(256).unwrap();
 | ||||
| //! let tp = Timespec { tv_sec: 1, tv_nsec: 0, };
 | ||||
| //! let completion_callback = move |_retval: i32| {};
 | ||||
| //! let handle = unsafe {
 | ||||
| //!     io_uring.timeout(&tp as *const _, 0, TimeoutFlags::empty(), completion_callback)
 | ||||
| //! };
 | ||||
| //! unsafe { io_uring.cancel(&handle); }
 | ||||
| //! io_uring.wait_completions(1);
 | ||||
| //! ```
 | ||||
| 
 | ||||
| #![feature(get_mut_unchecked)] | ||||
| #![cfg_attr(feature = "sgx", no_std)] | ||||
| 
 | ||||
| #[cfg(feature = "sgx")] | ||||
| extern crate sgx_libc as libc; | ||||
| #[cfg(feature = "sgx")] | ||||
| extern crate sgx_tstd as std; | ||||
| 
 | ||||
| use core::sync::atomic::AtomicUsize; | ||||
| use std::sync::Arc; | ||||
| use std::{collections::HashMap, io}; | ||||
| cfg_if::cfg_if! { | ||||
|     if #[cfg(feature = "sgx")] { | ||||
|         use std::prelude::v1::*; | ||||
|         use spin::Mutex as Mutex; | ||||
|     } else { | ||||
|         use std::sync::Mutex; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| use atomic::Ordering; | ||||
| use io_uring::opcode; | ||||
| use io_uring::squeue::Entry as SqEntry; | ||||
| use io_uring::types; | ||||
| use slab::Slab; | ||||
| use spin::RwLock; | ||||
| use std::os::unix::prelude::RawFd; | ||||
| 
 | ||||
| use crate::io_handle::IoToken; | ||||
| 
 | ||||
| mod io_handle; | ||||
| 
 | ||||
| pub use crate::io_handle::{IoHandle, IoState}; | ||||
| pub use io_uring::types::{Fd, RwFlags, TimeoutFlags, Timespec}; | ||||
| pub type IoUringRef = Arc<IoUring>; | ||||
| 
 | ||||
| /// An io_uring instance.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// All I/O methods are based on the assumption that the resources (e.g., file descriptors, pointers, etc.)
 | ||||
| /// given in their arguments are valid before the completion of the async I/O.
 | ||||
| pub struct IoUring { | ||||
|     ring: io_uring::IoUring, | ||||
|     token_table: Mutex<Slab<Arc<IoToken>>>, | ||||
|     sq_lock: Mutex<()>, // For submission queue synchronization
 | ||||
|     fd_map: RwLock<HashMap<usize, AtomicUsize>>, // (key: fd, value: op num)
 | ||||
| } | ||||
| 
 | ||||
| impl Drop for IoUring { | ||||
|     fn drop(&mut self) { | ||||
|         // By the end of the life of the io_uring instance, its token table should have been emptied.
 | ||||
|         // This emptyness check prevents handles created by this io_uring become "dangling".
 | ||||
|         // That is, no user will ever hold a handle whose associated io_uring instance has
 | ||||
|         // been destroyed.
 | ||||
|         // let token_table = self.token_table.lock().unwrap();
 | ||||
|         let token_table = self.token_table.lock(); | ||||
|         assert!(token_table.len() == 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl IoUring { | ||||
|     /// The magic token_key for Cancel I/O request.
 | ||||
|     /// The magic token_key should be different from the token_table's keys.
 | ||||
|     const CANCEL_TOKEN_KEY: u64 = u64::MAX; | ||||
| 
 | ||||
|     /// Constructor for internal uses.
 | ||||
|     ///
 | ||||
|     /// Users should use `Builder` instead.
 | ||||
|     pub(crate) fn new(ring: io_uring::IoUring) -> Self { | ||||
|         let token_table = Mutex::new(Slab::new()); | ||||
|         let sq_lock = Mutex::new(()); | ||||
|         let fd_map = RwLock::new(HashMap::new()); | ||||
|         Self { | ||||
|             ring, | ||||
|             token_table, | ||||
|             sq_lock, | ||||
|             fd_map, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Get the raw io_uring instance for advanced usage.
 | ||||
|     pub fn raw(&self) -> &io_uring::IoUring { | ||||
|         &self.ring | ||||
|     } | ||||
| 
 | ||||
|     /// Push an accept request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn accept( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         addr: *mut libc::sockaddr, | ||||
|         addrlen: *mut libc::socklen_t, | ||||
|         flags: u32, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Accept::new(fd, addr, addrlen) | ||||
|             .flags(flags as i32) | ||||
|             .build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a connect request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn connect( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         addr: *const libc::sockaddr, | ||||
|         addrlen: libc::socklen_t, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Connect::new(fd, addr, addrlen).build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a poll request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn poll( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         flags: u32, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::PollAdd::new(fd, flags).build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a read request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn read( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         buf: *mut u8, | ||||
|         len: u32, | ||||
|         offset: libc::off_t, | ||||
|         flags: types::RwFlags, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Read::new(fd, buf, len) | ||||
|             .offset(offset) | ||||
|             .rw_flags(flags) | ||||
|             .build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a write request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn write( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         buf: *const u8, | ||||
|         len: u32, | ||||
|         offset: libc::off_t, | ||||
|         flags: types::RwFlags, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Write::new(fd, buf, len) | ||||
|             .offset(offset) | ||||
|             .rw_flags(flags) | ||||
|             .build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a readv request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn readv( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         iovec: *const libc::iovec, | ||||
|         len: u32, | ||||
|         offset: libc::off_t, | ||||
|         flags: types::RwFlags, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Readv::new(fd, iovec, len) | ||||
|             .offset(offset) | ||||
|             .rw_flags(flags) | ||||
|             .build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a writev request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn writev( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         iovec: *const libc::iovec, | ||||
|         len: u32, | ||||
|         offset: libc::off_t, | ||||
|         flags: types::RwFlags, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Writev::new(fd, iovec, len) | ||||
|             .offset(offset) | ||||
|             .rw_flags(flags) | ||||
|             .build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a recvmsg request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn recvmsg( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         msg: *mut libc::msghdr, | ||||
|         flags: u32, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::RecvMsg::new(fd, msg).flags(flags).build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a sendmsg request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn sendmsg( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         msg: *const libc::msghdr, | ||||
|         flags: u32, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::SendMsg::new(fd, msg).flags(flags).build(); | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a fsync request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn fsync( | ||||
|         &self, | ||||
|         fd: Fd, | ||||
|         datasync: bool, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = if datasync { | ||||
|             opcode::Fsync::new(fd) | ||||
|                 .flags(types::FsyncFlags::DATASYNC) | ||||
|                 .build() | ||||
|         } else { | ||||
|             opcode::Fsync::new(fd).build() | ||||
|         }; | ||||
|         self.op_fetch_add(fd.0 as usize, 1); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Push a timeout request into the submission queue of the io_uring.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// See the safety section of the `IoUring`.
 | ||||
|     pub unsafe fn timeout( | ||||
|         &self, | ||||
|         timespec: *const types::Timespec, | ||||
|         count: u32, | ||||
|         flags: types::TimeoutFlags, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         let entry = opcode::Timeout::new(timespec) | ||||
|             .count(count) | ||||
|             .flags(flags) | ||||
|             .build(); | ||||
|         self.push_entry(entry, callback) | ||||
|     } | ||||
| 
 | ||||
|     /// Poll new I/O completions in the completions queue of io_uring
 | ||||
|     /// and return the number of I/O completions.
 | ||||
|     ///
 | ||||
|     /// Upon receiving completed I/O, the corresponding user-registered callback functions
 | ||||
|     /// will get invoked and the `IoHandle` (as a `Future`) will become ready.
 | ||||
|     ///
 | ||||
|     /// The method guarantees at least the specified number of entries are
 | ||||
|     /// popped from the completion queue. To do so, it starts by polling the
 | ||||
|     /// completion queue for at most the specified number of retries.
 | ||||
|     /// If the number of completion entries popped so far does not reach the
 | ||||
|     /// the specified minimum value, then the method shall block
 | ||||
|     /// until new completions arrive. After getting unblocked, the method
 | ||||
|     /// repeats polling.
 | ||||
|     ///
 | ||||
|     /// If the user does not want to the method to block, set `min_complete`
 | ||||
|     /// to 0. If the user does not want to the method to busy polling, set
 | ||||
|     /// `polling_retries` to 0.
 | ||||
|     pub fn poll_completions(&self, min_complete: usize, polling_retries: usize) -> usize { | ||||
|         let mut cq = unsafe { self.ring.completion_shared() }; // Safety: Only polling thread is using the completion queue
 | ||||
|         let mut nr_complete = 0; | ||||
|         loop { | ||||
|             // Polling for at most a specified number of times
 | ||||
|             let mut nr_retries = 0; | ||||
|             while nr_retries <= polling_retries { | ||||
|                 // completetion queue must be synchoronized when loop for next entry.
 | ||||
|                 cq.sync(); | ||||
|                 if let Some(cqe) = cq.next() { | ||||
|                     let retval = cqe.result(); | ||||
|                     let token_key = cqe.user_data(); | ||||
| 
 | ||||
|                     if token_key != IoUring::CANCEL_TOKEN_KEY { | ||||
|                         let io_token = { | ||||
|                             let token_idx = token_key as usize; | ||||
|                             let mut token_table = self.token_table.lock(); | ||||
|                             token_table.remove(token_idx) | ||||
|                         }; | ||||
| 
 | ||||
|                         io_token.complete(retval); | ||||
|                         nr_complete += 1; | ||||
|                     } | ||||
|                 } else { | ||||
|                     nr_retries += 1; | ||||
|                     std::hint::spin_loop(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if nr_complete >= min_complete { | ||||
|                 return nr_complete; | ||||
|             } | ||||
| 
 | ||||
|             // Wait until at least one new completion entry arrives
 | ||||
|             let _ = self.ring.submit_and_wait(1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Wait for at least the specified number of I/O completions.
 | ||||
|     pub fn wait_completions(&self, min_complete: usize) -> usize { | ||||
|         self.poll_completions(min_complete, 10) | ||||
|     } | ||||
| 
 | ||||
|     unsafe fn push(&self, entry: SqEntry) { | ||||
|         // Push the entry into the submission queue
 | ||||
|         // No other `SubmissionQueue`s may exist when calling submission_shared(). Thus must lock here.
 | ||||
|         // Since the loop below should be very quick, acquire lock here.
 | ||||
|         let sq_guard = self.sq_lock.lock(); | ||||
|         loop { | ||||
|             if self.ring.submission_shared().push(&entry).is_err() { | ||||
|                 if self.ring.enter(1, 1, 0, None).is_err() { | ||||
|                     panic!("sq broken"); | ||||
|                 } | ||||
|             } else { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         drop(sq_guard); | ||||
| 
 | ||||
|         // Make sure Linux is aware of the new submission
 | ||||
|         if let Err(e) = self.ring.submit() { | ||||
|             panic!("submit failed, error: {}", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Push a submission entry to io_uring and return a corresponding handle.
 | ||||
|     //
 | ||||
|     // Safety. All resources referenced by the entry must be valid before its completion.
 | ||||
|     unsafe fn push_entry( | ||||
|         &self, | ||||
|         mut entry: SqEntry, | ||||
|         callback: impl FnOnce(i32) + Send + 'static, | ||||
|     ) -> IoHandle { | ||||
|         // Create the user-visible handle that is associated with the submission entry
 | ||||
|         let io_handle = { | ||||
|             // let mut token_table = self.token_table.lock().unwrap();
 | ||||
|             let mut token_table = self.token_table.lock(); | ||||
|             let token_slot = token_table.vacant_entry(); | ||||
|             let token_key = token_slot.key() as u64; | ||||
|             assert!(token_key != IoUring::CANCEL_TOKEN_KEY); | ||||
| 
 | ||||
|             let token = Arc::new(IoToken::new(callback, token_key)); | ||||
|             token_slot.insert(token.clone()); | ||||
|             let handle = IoHandle::new(token); | ||||
| 
 | ||||
|             // Associated entry with token, the latter of which is pointed to by handle.
 | ||||
|             entry = entry.user_data(token_key); | ||||
| 
 | ||||
|             handle | ||||
|         }; | ||||
| 
 | ||||
|         self.push(entry); | ||||
| 
 | ||||
|         io_handle | ||||
|     } | ||||
| 
 | ||||
|     fn op_fetch_add(&self, fd: usize, val: usize) -> usize { | ||||
|         let fd_map = self.fd_map.upgradeable_read(); | ||||
|         match fd_map.get(&fd) { | ||||
|             Some(ops_num) => ops_num.fetch_add(val, Ordering::Relaxed), | ||||
|             None => { | ||||
|                 let mut fd_map = fd_map.upgrade(); | ||||
|                 fd_map.insert(fd, AtomicUsize::new(val)); | ||||
|                 0 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn disattach_fd(&self, fd: usize) -> Option<AtomicUsize> { | ||||
|         let mut fd_map = self.fd_map.write(); | ||||
|         fd_map.remove(&fd) | ||||
|     } | ||||
| 
 | ||||
|     // Using the sum of the number of attached file descriptors (raw fd) as a measure of task load.
 | ||||
|     pub fn task_load(&self) -> usize { | ||||
|         let fd_map = self.fd_map.read(); | ||||
|         fd_map | ||||
|             .values() | ||||
|             .fold(0, |acc, val| acc + val.load(Ordering::Relaxed)) | ||||
|     } | ||||
| 
 | ||||
|     // The number of registered fd in this io_uring instance
 | ||||
|     pub fn registered_fds(&self) -> usize { | ||||
|         let fd_map = self.fd_map.read(); | ||||
|         fd_map.len() | ||||
|     } | ||||
| 
 | ||||
|     /// Cancel an ongoing I/O request.
 | ||||
|     ///
 | ||||
|     /// # safety
 | ||||
|     ///
 | ||||
|     /// The handle must be generated by this IoUring instance.
 | ||||
|     pub unsafe fn cancel(&self, handle: &IoHandle) { | ||||
|         let target_token_key = match handle.0.transit_to_cancelling() { | ||||
|             Ok(target_token_key) => target_token_key, | ||||
|             Err(_) => { | ||||
|                 return; | ||||
|             } | ||||
|         }; | ||||
|         let mut entry = opcode::AsyncCancel::new(target_token_key).build(); | ||||
|         entry = entry.user_data(IoUring::CANCEL_TOKEN_KEY); | ||||
| 
 | ||||
|         self.push(entry); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A builder for `IoUring`.
 | ||||
| pub struct Builder { | ||||
|     inner: io_uring::Builder, | ||||
| } | ||||
| 
 | ||||
| impl Builder { | ||||
|     /// Creates a `IoUring` builder.
 | ||||
|     pub fn new() -> Self { | ||||
|         let inner = io_uring::IoUring::builder(); | ||||
|         Self { inner } | ||||
|     } | ||||
| 
 | ||||
|     /// When this flag is specified, a kernel thread is created to perform submission queue polling.
 | ||||
|     /// An io_uring instance configured in this way enables an application to issue I/O
 | ||||
|     /// without ever context switching into the kernel.
 | ||||
|     pub fn setup_sqpoll(&mut self, idle: u32) -> &mut Self { | ||||
|         self.inner.setup_sqpoll(idle); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     pub fn setup_attach_wq(&mut self, fd: RawFd) -> &mut Self { | ||||
|         self.inner.setup_attach_wq(fd); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// If this flag is specified,
 | ||||
|     /// then the poll thread will be bound to the cpu set in the value.
 | ||||
|     /// This flag is only meaningful when [Builder::setup_sqpoll] is enabled.
 | ||||
|     pub fn setup_sqpoll_cpu(&mut self, n: u32) -> &mut Self { | ||||
|         self.inner.setup_sqpoll_cpu(n); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Create the completion queue with struct `io_uring_params.cq_entries` entries.
 | ||||
|     /// The value must be greater than entries, and may be rounded up to the next power-of-two.
 | ||||
|     pub fn setup_cqsize(&mut self, n: u32) -> &mut Self { | ||||
|         self.inner.setup_cqsize(n); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Build a [IoUring].
 | ||||
|     #[inline] | ||||
|     pub fn build(&self, entries: u32) -> io::Result<IoUring> { | ||||
|         let io_uring_inner = self.inner.build(entries)?; | ||||
|         let io_uring = IoUring::new(io_uring_inner); | ||||
|         Ok(io_uring) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::fs::File; | ||||
|     use std::io::{IoSlice, IoSliceMut, Write}; | ||||
|     use std::os::unix::io::{AsRawFd, FromRawFd}; | ||||
|     use std::thread; | ||||
|     use std::time::Duration; | ||||
|     use std::time::Instant; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_builder() { | ||||
|         let _io_uring = Builder::new().setup_sqpoll(1000).build(256).unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_new() { | ||||
|         let _io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_writev_readv() { | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let fd = tempfile::tempfile().unwrap(); | ||||
|         let fd = Fd(fd.as_raw_fd()); | ||||
| 
 | ||||
|         let text = b"1234"; | ||||
|         let text2 = b"5678"; | ||||
|         let mut output = vec![0; text.len()]; | ||||
|         let mut output2 = vec![0; text2.len()]; | ||||
| 
 | ||||
|         let w_iovecs = vec![IoSlice::new(text), IoSlice::new(text2)]; | ||||
|         let r_iovecs = vec![IoSliceMut::new(&mut output), IoSliceMut::new(&mut output2)]; | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
|         let handle = unsafe { | ||||
|             io_uring.writev( | ||||
|                 fd, | ||||
|                 w_iovecs.as_ptr().cast(), | ||||
|                 w_iovecs.len() as _, | ||||
|                 0, | ||||
|                 0, | ||||
|                 complete_fn, | ||||
|             ) | ||||
|         }; | ||||
| 
 | ||||
|         io_uring.wait_completions(1); | ||||
|         let retval = handle.retval().unwrap(); | ||||
|         assert_eq!(retval, (text.len() + text2.len()) as i32); | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
|         let handle = unsafe { | ||||
|             io_uring.readv( | ||||
|                 fd, | ||||
|                 r_iovecs.as_ptr().cast(), | ||||
|                 r_iovecs.len() as _, | ||||
|                 0, | ||||
|                 0, | ||||
|                 complete_fn, | ||||
|             ) | ||||
|         }; | ||||
| 
 | ||||
|         io_uring.wait_completions(1); | ||||
|         let retval = handle.retval().unwrap(); | ||||
|         assert_eq!(retval, (text.len() + text2.len()) as i32); | ||||
|         assert_eq!(&output, text); | ||||
|         assert_eq!(&output2, text2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_poll() { | ||||
|         let mut fd = unsafe { | ||||
|             let fd = libc::eventfd(0, libc::EFD_CLOEXEC); | ||||
|             assert!(fd != -1); | ||||
|             File::from_raw_fd(fd) | ||||
|         }; | ||||
| 
 | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
|         let handle = unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|         thread::sleep(Duration::from_millis(100)); | ||||
|         assert_eq!(io_uring.poll_completions(0, 10000), 0); | ||||
| 
 | ||||
|         fd.write(&0x1u64.to_ne_bytes()).unwrap(); | ||||
|         io_uring.wait_completions(1); | ||||
|         assert_eq!(handle.retval().unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_cancel_poll() { | ||||
|         let mut fd = unsafe { | ||||
|             let fd = libc::eventfd(0, libc::EFD_CLOEXEC); | ||||
|             assert!(fd != -1); | ||||
|             File::from_raw_fd(fd) | ||||
|         }; | ||||
| 
 | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
|         let poll_handle = | ||||
|             unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|         unsafe { | ||||
|             io_uring.cancel(&poll_handle); | ||||
|         } | ||||
| 
 | ||||
|         thread::sleep(Duration::from_millis(100)); | ||||
| 
 | ||||
|         fd.write(&0x1u64.to_ne_bytes()).unwrap(); | ||||
|         io_uring.wait_completions(1); | ||||
| 
 | ||||
|         assert_eq!(poll_handle.retval().unwrap(), -libc::ECANCELED); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_cancel_poll_failed() { | ||||
|         let mut fd = unsafe { | ||||
|             let fd = libc::eventfd(0, libc::EFD_CLOEXEC); | ||||
|             assert!(fd != -1); | ||||
|             File::from_raw_fd(fd) | ||||
|         }; | ||||
| 
 | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
|         let poll_handle = | ||||
|             unsafe { io_uring.poll(Fd(fd.as_raw_fd()), libc::POLLIN as _, complete_fn) }; | ||||
| 
 | ||||
|         fd.write(&0x1u64.to_ne_bytes()).unwrap(); | ||||
|         io_uring.wait_completions(1); | ||||
| 
 | ||||
|         unsafe { | ||||
|             io_uring.cancel(&poll_handle); | ||||
|         } | ||||
| 
 | ||||
|         thread::sleep(Duration::from_millis(100)); | ||||
|         assert_eq!(poll_handle.retval().unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_timeout() { | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let start = Instant::now(); | ||||
|         let secs = 1; | ||||
|         let timespec = types::Timespec::new().sec(secs).nsec(0); | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
| 
 | ||||
|         let handle = unsafe { | ||||
|             io_uring.timeout( | ||||
|                 ×pec as *const _, | ||||
|                 0, | ||||
|                 types::TimeoutFlags::empty(), | ||||
|                 complete_fn, | ||||
|             ) | ||||
|         }; | ||||
|         io_uring.wait_completions(1); | ||||
| 
 | ||||
|         assert_eq!(handle.retval().unwrap(), -libc::ETIME); | ||||
|         assert_eq!(start.elapsed().as_secs(), secs as u64); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_cancel_timeout() { | ||||
|         let io_uring = IoUring::new(io_uring::IoUring::new(256).unwrap()); | ||||
| 
 | ||||
|         let start = Instant::now(); | ||||
|         let secs = 1; | ||||
|         let timespec = types::Timespec::new().sec(secs).nsec(0); | ||||
| 
 | ||||
|         let complete_fn = move |_retval: i32| {}; | ||||
| 
 | ||||
|         let handle = unsafe { | ||||
|             io_uring.timeout( | ||||
|                 ×pec as *const _, | ||||
|                 0, | ||||
|                 types::TimeoutFlags::empty(), | ||||
|                 complete_fn, | ||||
|             ) | ||||
|         }; | ||||
| 
 | ||||
|         unsafe { | ||||
|             io_uring.cancel(&handle); | ||||
|         } | ||||
| 
 | ||||
|         io_uring.wait_completions(1); | ||||
| 
 | ||||
|         assert_eq!(handle.retval().unwrap(), -libc::ECANCELED); | ||||
|         assert_eq!(start.elapsed().as_secs(), 0); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/libos/crates/keyable-arc/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/libos/crates/keyable-arc/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| [package] | ||||
| name = "keyable-arc" | ||||
| version = "0.1.0" | ||||
| authors = ["Tate, Hongliang Tian <tate.thl@antgroup.com>"] | ||||
| edition = "2021" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| 
 | ||||
							
								
								
									
										348
									
								
								src/libos/crates/keyable-arc/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										348
									
								
								src/libos/crates/keyable-arc/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,348 @@ | ||||
| //! Same as the standard `Arc`, except that it can be used as the key type of a hash table.
 | ||||
| //!
 | ||||
| //! # Motivation
 | ||||
| //!
 | ||||
| //! A type `K` is _keyable_ if it can be used as the key type for a hash map. Specifically,
 | ||||
| //! according to the document of `std::collections::HashMap`, the type `K` must satisfy
 | ||||
| //! the following properties.
 | ||||
| //!
 | ||||
| //! 1. It implements the `Eq` and `Hash` traits.
 | ||||
| //! 2. The two values of `k1` and `k2` of type `K` equal to each other,
 | ||||
| //! if and only if their hash values equal to each other.
 | ||||
| //! 3. The hashes of a value of `k` of type `K` cannot change while it
 | ||||
| //! is in a map.
 | ||||
| //!
 | ||||
| //! Sometimes we want to use `Arc<T>` as the key type for a hash map but cannot do so
 | ||||
| //! since `T` does not satisfy the properties above. For example, a lot of types
 | ||||
| //! do not or cannot implemennt the `Eq` trait. This is when `KeyableArc<T>` can come
 | ||||
| //! to your aid.
 | ||||
| //!
 | ||||
| //! # Overview
 | ||||
| //!
 | ||||
| //! For any type `T`, `KeyableArc<T>` satisfies all the properties to be keyable.
 | ||||
| //! This can be achieved easily and efficiently as we can simply use the address
 | ||||
| //! of the data (of `T` type) of a `KeyableArc<T>` object in the heap to determine the
 | ||||
| //! equality and hash of the `KeyableArc<T>` object. As the address won't change for
 | ||||
| //! an immutable `KeyableArc<T>` object, the hash and equality also stay the same.
 | ||||
| //!
 | ||||
| //! This crate is `#[no_std]` compatible, but requires the `alloc` crate.
 | ||||
| //!
 | ||||
| //! # Usage
 | ||||
| //!
 | ||||
| //! Here is a basic example to how that `KeyableArc<T>` is keyable even when `T`
 | ||||
| //! is not.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use std::collections::HashMap;
 | ||||
| //! use std::sync::Arc;
 | ||||
| //! use keyable_arc::KeyableArc;
 | ||||
| //!
 | ||||
| //! struct Dummy; // Does not implement Eq and Hash
 | ||||
| //!
 | ||||
| //! let map: HashMap<KeyableArc<Dummy>, String> = HashMap::new();
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! `KeyableArc` is a reference counter-based smart pointer, just like `Arc`.
 | ||||
| //! So you can use `KeyableArc` the same way you would use `Arc`.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
 | ||||
| //! use keyable_arc::KeyableArc;
 | ||||
| //!
 | ||||
| //! let key_arc0 = KeyableArc::new(AtomicU64::new(0));
 | ||||
| //! let key_arc1 = key_arc0.clone();
 | ||||
| //! assert!(key_arc0.load(Relaxed) == 0 && key_arc1.load(Relaxed) == 0);
 | ||||
| //!
 | ||||
| //! key_arc0.fetch_add(1, Relaxed);
 | ||||
| //! assert!(key_arc0.load(Relaxed) == 1 && key_arc1.load(Relaxed) == 1);
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! # Differences from `Arc<T>`
 | ||||
| //!
 | ||||
| //! Notice how `KeyableArc` differs from standard smart pointers in determining equality?
 | ||||
| //! Two `KeyableArc` objects are considered different even when their data have the same
 | ||||
| //! value.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use keyable_arc::KeyableArc;
 | ||||
| //!
 | ||||
| //! let key_arc0 = KeyableArc::new(0);
 | ||||
| //! let key_arc1 = key_arc0.clone();
 | ||||
| //! assert!(key_arc0 == key_arc1);
 | ||||
| //! assert!(*key_arc0 == *key_arc1);
 | ||||
| //!
 | ||||
| //! let key_arc1 = KeyableArc::new(0);
 | ||||
| //! assert!(key_arc0 != key_arc1);
 | ||||
| //! assert!(*key_arc0 == *key_arc1);
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! `KeyableArc<T>` is simply a wrapper of `Arc<T>. So converting between them
 | ||||
| //! through the `From` and `Into` traits is zero cost.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use std::sync::Arc;
 | ||||
| //! use keyable_arc::KeyableArc;
 | ||||
| //!
 | ||||
| //! let key_arc: KeyableArc<u32> = Arc::new(0).into();
 | ||||
| //! let arc: Arc<u32> = KeyableArc::new(0).into();
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! # The weak version
 | ||||
| //!
 | ||||
| //! `KeyableWeak<T>` is the weak version of `KeyableArc<T>`, just like `Weak<T>` is
 | ||||
| //! that of `Arc<T>`. And of course, `KeyableWeak<T>` is also _keyable_ for any
 | ||||
| //! type `T`.
 | ||||
| 
 | ||||
| // TODO: Add `KeyableBox<T>` or other keyable versions of smart pointers.
 | ||||
| // If this is needed in the future, this crate should be renamed to `keyable`.
 | ||||
| 
 | ||||
| // TODO: Add the missing methods offered by `Arc` or `Weak` but not their
 | ||||
| // keyable counterparts.
 | ||||
| 
 | ||||
| #![cfg_attr(not(test), no_std)] | ||||
| #![feature(coerce_unsized)] | ||||
| #![feature(unsize)] | ||||
| 
 | ||||
| extern crate alloc; | ||||
| 
 | ||||
| use alloc::sync::{Arc, Weak}; | ||||
| use core::borrow::Borrow; | ||||
| use core::convert::AsRef; | ||||
| use core::fmt; | ||||
| use core::hash::{Hash, Hasher}; | ||||
| use core::marker::Unsize; | ||||
| use core::ops::{CoerceUnsized, Deref}; | ||||
| 
 | ||||
| /// Same as the standard `Arc`, except that it can be used as the key type of a hash table.
 | ||||
| #[repr(transparent)] | ||||
| pub struct KeyableArc<T: ?Sized>(Arc<T>); | ||||
| 
 | ||||
| impl<T> KeyableArc<T> { | ||||
|     /// Constructs a new instance of `KeyableArc<T>`.
 | ||||
|     #[inline] | ||||
|     pub fn new(data: T) -> Self { | ||||
|         Self(Arc::new(data)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> KeyableArc<T> { | ||||
|     /// Returns a raw pointer to the object `T` pointed to by this `KeyableArc<T>`.
 | ||||
|     #[inline] | ||||
|     pub fn as_ptr(this: &Self) -> *const T { | ||||
|         Arc::as_ptr(&this.0) | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a new `KeyableWeak` pointer to this allocation.
 | ||||
|     pub fn downgrade(this: &Self) -> KeyableWeak<T> { | ||||
|         Arc::downgrade(&this.0).into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Deref for KeyableArc<T> { | ||||
|     type Target = T; | ||||
| 
 | ||||
|     #[inline] | ||||
|     fn deref(&self) -> &T { | ||||
|         &*self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> AsRef<T> for KeyableArc<T> { | ||||
|     #[inline] | ||||
|     fn as_ref(&self) -> &T { | ||||
|         &**self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Borrow<T> for KeyableArc<T> { | ||||
|     #[inline] | ||||
|     fn borrow(&self) -> &T { | ||||
|         &**self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> From<Arc<T>> for KeyableArc<T> { | ||||
|     #[inline] | ||||
|     fn from(arc: Arc<T>) -> Self { | ||||
|         Self(arc) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Into<Arc<T>> for KeyableArc<T> { | ||||
|     #[inline] | ||||
|     fn into(self) -> Arc<T> { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> PartialEq for KeyableArc<T> { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Eq for KeyableArc<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized> Hash for KeyableArc<T> { | ||||
|     fn hash<H: Hasher>(&self, s: &mut H) { | ||||
|         Arc::as_ptr(&self.0).hash(s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Clone for KeyableArc<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + fmt::Debug> fmt::Debug for KeyableArc<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         fmt::Debug::fmt(&**self, f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //=========================================================
 | ||||
| // The weak version
 | ||||
| //=========================================================
 | ||||
| 
 | ||||
| /// The weak counterpart of `KeyableArc<T>`, similar to `Weak<T>`.
 | ||||
| ///
 | ||||
| /// `KeyableWeak<T>` is also _keyable_ for any type `T` just like
 | ||||
| /// `KeyableArc<T>`.
 | ||||
| #[repr(transparent)] | ||||
| pub struct KeyableWeak<T: ?Sized>(Weak<T>); | ||||
| 
 | ||||
| impl<T> KeyableWeak<T> { | ||||
|     /// Constructs a new `KeyableWeak<T>`, without allocating any memory.
 | ||||
|     /// Calling `upgrade` on the return value always gives `None`.
 | ||||
|     #[inline] | ||||
|     pub fn new() -> Self { | ||||
|         Self(Weak::new()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a raw pointer to the object `T` pointed to by this `KeyableWeak<T>`.
 | ||||
|     ///
 | ||||
|     /// The pointer is valid only if there are some strong references.
 | ||||
|     /// The pointer may be dangling, unaligned or even null otherwise.
 | ||||
|     #[inline] | ||||
|     pub fn as_ptr(&self) -> *const T { | ||||
|         self.0.as_ptr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> KeyableWeak<T> { | ||||
|     /// Attempts to upgrade the Weak pointer to an Arc,
 | ||||
|     /// delaying dropping of the inner value if successful.
 | ||||
|     ///
 | ||||
|     /// Returns None if the inner value has since been dropped.
 | ||||
|     #[inline] | ||||
|     pub fn upgrade(&self) -> Option<KeyableArc<T>> { | ||||
|         self.0.upgrade().map(|arc| arc.into()) | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the number of strong pointers pointing to this allocation.
 | ||||
|     #[inline] | ||||
|     pub fn strong_count(&self) -> usize { | ||||
|         self.0.strong_count() | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the number of weak pointers pointing to this allocation.
 | ||||
|     #[inline] | ||||
|     pub fn weak_count(&self) -> usize { | ||||
|         self.0.weak_count() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T> Hash for KeyableWeak<T> { | ||||
|     fn hash<H: Hasher>(&self, s: &mut H) { | ||||
|         self.0.as_ptr().hash(s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> From<Weak<T>> for KeyableWeak<T> { | ||||
|     #[inline] | ||||
|     fn from(weak: Weak<T>) -> Self { | ||||
|         Self(weak) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Into<Weak<T>> for KeyableWeak<T> { | ||||
|     #[inline] | ||||
|     fn into(self) -> Weak<T> { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> PartialEq for KeyableWeak<T> { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.0.as_ptr() == other.0.as_ptr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> PartialOrd for KeyableWeak<T> { | ||||
|     fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||||
|         Some(self.cmp(other)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Eq for KeyableWeak<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized> Ord for KeyableWeak<T> { | ||||
|     fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||||
|         self.0.as_ptr().cmp(&other.0.as_ptr()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + fmt::Debug> fmt::Debug for KeyableWeak<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "(KeyableWeak)") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Enabling type coercing, e.g., converting from `KeyableArc<T>` to `KeyableArc<dyn S>`,
 | ||||
| // where `T` implements `S`.
 | ||||
| impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<KeyableArc<U>> for KeyableArc<T> {} | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn downgrade_and_upgrade() { | ||||
|         let arc = KeyableArc::new(1); | ||||
|         let weak = KeyableArc::downgrade(&arc); | ||||
|         assert!(arc.clone() == weak.upgrade().unwrap()); | ||||
|         assert!(weak == KeyableArc::downgrade(&arc)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn debug_format() { | ||||
|         println!("{:?}", KeyableArc::new(1u32)); | ||||
|         println!("{:?}", KeyableWeak::<u32>::new()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn use_as_key() { | ||||
|         use std::collections::HashMap; | ||||
| 
 | ||||
|         let mut map: HashMap<KeyableArc<u32>, u32> = HashMap::new(); | ||||
|         let key = KeyableArc::new(1); | ||||
|         let val = 1; | ||||
|         map.insert(key.clone(), val); | ||||
|         assert!(map.get(&key) == Some(&val)); | ||||
|         assert!(map.remove(&key) == Some(val)); | ||||
|         assert!(map.keys().count() == 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn as_trait_object() { | ||||
|         trait DummyTrait {} | ||||
|         struct DummyStruct; | ||||
|         impl DummyTrait for DummyStruct {} | ||||
| 
 | ||||
|         let arc_struct = KeyableArc::new(DummyStruct); | ||||
|         let arc_dyn0: KeyableArc<dyn DummyTrait> = arc_struct.clone(); | ||||
|         let arc_dyn1: KeyableArc<dyn DummyTrait> = arc_struct.clone(); | ||||
|         assert!(arc_dyn0 == arc_dyn1); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/libos/crates/object-id/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								src/libos/crates/object-id/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| [package] | ||||
| name = "object-id" | ||||
| version = "0.1.0" | ||||
| authors = ["Tate, Hongliang Tian <tate.thl@antgroup.com>"] | ||||
| edition = "2021" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
							
								
								
									
										62
									
								
								src/libos/crates/object-id/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										62
									
								
								src/libos/crates/object-id/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| //! Assign a unique and immutable ID.
 | ||||
| //!
 | ||||
| //! Some types do not have a natural implementation for `PartialEq` or `Hash`.
 | ||||
| //! In such cases, it can be convenient to assign an unique ID for each instance
 | ||||
| //! of such types and use the ID to implement `PartialEq` or `Hash`.
 | ||||
| //!
 | ||||
| //! An ID have a length of 64-bit.
 | ||||
| 
 | ||||
| #![cfg_attr(not(any(test, doctest)), no_std)] | ||||
| 
 | ||||
| use core::sync::atomic::{AtomicU64, Ordering}; | ||||
| 
 | ||||
| /// A unique id.
 | ||||
| #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] | ||||
| #[repr(transparent)] | ||||
| pub struct ObjectId(u64); | ||||
| 
 | ||||
| impl ObjectId { | ||||
|     /// Create a new unique ID.
 | ||||
|     pub fn new() -> Self { | ||||
|         static NEXT_ID: AtomicU64 = AtomicU64::new(1); | ||||
|         let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); | ||||
| 
 | ||||
|         // Make sure that we can detect the overflow of id even in face of
 | ||||
|         // (extremely) concurrent addition on NEXT_ID.
 | ||||
|         assert!(id <= u64::max_value() / 2); | ||||
| 
 | ||||
|         Self(id) | ||||
|     } | ||||
| 
 | ||||
|     /// Return a special "null" ID.
 | ||||
|     ///
 | ||||
|     /// Note that no ID created by `ObjectId::new()` will be equivalent to the
 | ||||
|     /// null ID.
 | ||||
|     pub const fn null() -> Self { | ||||
|         Self(0) | ||||
|     } | ||||
| 
 | ||||
|     /// Get the ID value as `u64`.
 | ||||
|     pub const fn get(&self) -> u64 { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn unique() { | ||||
|         let id0 = ObjectId::new(); | ||||
|         let id1 = ObjectId::new(); | ||||
|         assert!(id0 != id1); | ||||
|         assert!(id0.get() < id1.get()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn non_null() { | ||||
|         let id0 = ObjectId::new(); | ||||
|         assert!(id0 != ObjectId::null()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								src/libos/crates/sgx-untrusted-alloc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								src/libos/crates/sgx-untrusted-alloc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| target/ | ||||
| Cargo.lock | ||||
							
								
								
									
										22
									
								
								src/libos/crates/sgx-untrusted-alloc/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										22
									
								
								src/libos/crates/sgx-untrusted-alloc/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| [package] | ||||
| name = "sgx-untrusted-alloc" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [features] | ||||
| default = ["libc"] | ||||
| sgx = ["sgx_types", "sgx_tstd", "sgx_trts", "sgx_libc"] | ||||
| 
 | ||||
| [dependencies] | ||||
| cfg-if = "1.0.0" | ||||
| libc = { version = "0.2", optional = true } | ||||
| lazy_static = { version = "1.4.0", features = ["spin_no_std"] } | ||||
| intrusive-collections = "0.9" | ||||
| log = "0.4" | ||||
| spin = "0.7" | ||||
| errno = { path = "../errno" } | ||||
| 
 | ||||
| sgx_types = { path = "../../../../deps/rust-sgx-sdk/sgx_types", optional = true } | ||||
| sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] } | ||||
| sgx_trts = { path = "../../../../deps/rust-sgx-sdk/sgx_trts", optional = true } | ||||
| sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true } | ||||
							
								
								
									
										10
									
								
								src/libos/crates/sgx-untrusted-alloc/README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/libos/crates/sgx-untrusted-alloc/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| # sgx-untrusted-alloc | ||||
| 
 | ||||
| untrusted memory allocator for SGX. | ||||
| 
 | ||||
| ## Usage | ||||
| To use sgx-untrusted-alloc, place the following line under the `[dependencies]` section in your `Cargo.toml`: | ||||
| 
 | ||||
| ``` | ||||
| sgx-untrusted-alloc = { path = "your_path/sgx-untrusted-alloc" } | ||||
| ``` | ||||
							
								
								
									
										190
									
								
								src/libos/crates/sgx-untrusted-alloc/src/box_.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										190
									
								
								src/libos/crates/sgx-untrusted-alloc/src/box_.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | ||||
| use crate::untrusted_allocator::Allocator; | ||||
| use std::fmt::Debug; | ||||
| use std::mem::{align_of, size_of}; | ||||
| use std::ops::{Deref, DerefMut}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| lazy_static! { | ||||
|     static ref UNTRUSTED_MEM_INSTANCE: Allocator = Allocator::new(); | ||||
| } | ||||
| 
 | ||||
| use crate::MaybeUntrusted; | ||||
| 
 | ||||
| /// A memory location on the heap in untrusted memory.
 | ||||
| ///
 | ||||
| /// `UntrustedBox<T>` Behaves similar to the standard `Box<T>`, except that
 | ||||
| /// it requires that the type bound of `T: MaybeUntrusted`. This is a safety
 | ||||
| /// measure to avoid potential misuses.
 | ||||
| pub struct UntrustedBox<T: ?Sized> { | ||||
|     ptr: NonNull<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T> Debug for UntrustedBox<T> | ||||
| where | ||||
|     T: ?Sized + Debug, | ||||
| { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("Untrusted T") | ||||
|             .field("T", unsafe { &self.ptr.as_ref() }) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: MaybeUntrusted> UntrustedBox<T> { | ||||
|     /// Creates a value of `T` on the heap in untrusted memory.
 | ||||
|     pub fn new(val: T) -> Self { | ||||
|         let mut new_self = Self::new_uninit(); | ||||
|         *new_self = val; | ||||
|         new_self | ||||
|     } | ||||
| 
 | ||||
|     /// Creates an _uninitialized_ value of `T` on the heap in untrusted memory.
 | ||||
|     pub fn new_uninit() -> Self { | ||||
|         let ptr = { | ||||
|             let raw_ptr = unsafe { | ||||
|                 UNTRUSTED_MEM_INSTANCE | ||||
|                     .alloc(size_of::<T>(), None) | ||||
|                     .expect("memory allocation failure") | ||||
|             } as *mut T; | ||||
|             assert!(raw_ptr != std::ptr::null_mut()); | ||||
|             assert!((raw_ptr as usize) % align_of::<T>() == 0); | ||||
|             NonNull::new(raw_ptr).unwrap() | ||||
|         }; | ||||
|         Self { ptr } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: MaybeUntrusted + Copy> UntrustedBox<[T]> { | ||||
|     /// Creates a slice of `T` on the heap in untrusted memory.
 | ||||
|     ///
 | ||||
|     /// Note that the pointer and length of the slice is still kept in trusted memory;
 | ||||
|     /// only the pointer refers to untrusted memory. Thus, there is no risk of buffer
 | ||||
|     /// overflow.
 | ||||
|     pub fn new_slice(slice: &[T]) -> Self { | ||||
|         let mut uninit_slice = Self::new_uninit_slice(slice.len()); | ||||
|         uninit_slice.copy_from_slice(slice); | ||||
|         uninit_slice | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: MaybeUntrusted> UntrustedBox<[T]> { | ||||
|     /// Creates an uninitialized slice of `T` on the heap in untrusted memory.
 | ||||
|     pub fn new_uninit_slice(len: usize) -> Self { | ||||
|         let ptr = { | ||||
|             let total_bytes = size_of::<T>() * len; | ||||
|             let raw_slice_ptr = unsafe { | ||||
|                 UNTRUSTED_MEM_INSTANCE | ||||
|                     .alloc(total_bytes, None) | ||||
|                     .expect("memory allocation failure") | ||||
|             } as *mut T; | ||||
|             assert!(raw_slice_ptr != std::ptr::null_mut()); | ||||
|             assert!((raw_slice_ptr as usize) % align_of::<T>() == 0); | ||||
|             let untrusted_slice = unsafe { std::slice::from_raw_parts_mut(raw_slice_ptr, len) }; | ||||
|             // For DST types like slice, NonNull is now a fat pointer.
 | ||||
|             NonNull::new(untrusted_slice as _).unwrap() | ||||
|         }; | ||||
|         Self { ptr } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> UntrustedBox<T> { | ||||
|     /// Gets an immutable pointer of the value on the untrusted memory.
 | ||||
|     pub fn as_ptr(&self) -> *const T { | ||||
|         self.ptr.as_ptr() | ||||
|     } | ||||
| 
 | ||||
|     /// Gets a mutable pointer of the value on the untrusted memory.
 | ||||
|     pub fn as_mut_ptr(&self) -> *mut T { | ||||
|         self.ptr.as_ptr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Drop for UntrustedBox<T> { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { | ||||
|             UNTRUSTED_MEM_INSTANCE.free(self.as_mut_ptr() as *mut u8); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> Deref for UntrustedBox<T> { | ||||
|     type Target = T; | ||||
| 
 | ||||
|     fn deref(&self) -> &T { | ||||
|         unsafe { self.ptr.as_ref() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> DerefMut for UntrustedBox<T> { | ||||
|     fn deref_mut(&mut self) -> &mut T { | ||||
|         unsafe { self.ptr.as_mut() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: MaybeUntrusted + Default> Default for UntrustedBox<T> { | ||||
|     fn default() -> Self { | ||||
|         Self::new(T::default()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: MaybeUntrusted + Clone> Clone for UntrustedBox<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self::new(self.deref().clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| unsafe impl<T: ?Sized + Send> Send for UntrustedBox<T> {} | ||||
| unsafe impl<T: ?Sized + Sync> Sync for UntrustedBox<T> {} | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     struct Point { | ||||
|         x: usize, | ||||
|         y: usize, | ||||
|     } | ||||
|     unsafe impl MaybeUntrusted for Point {} | ||||
| 
 | ||||
|     #[test] | ||||
|     fn with_i32() { | ||||
|         let mut untrusted_i32 = UntrustedBox::new(0i32); | ||||
|         assert!(*untrusted_i32 == 0); | ||||
|         *untrusted_i32 = 1; | ||||
|         assert!(*untrusted_i32 == 1); | ||||
|         drop(untrusted_i32); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn with_point() { | ||||
|         let mut untrusted_point = UntrustedBox::new(Point { x: 0, y: 0 }); | ||||
|         assert!(untrusted_point.x == 0 && untrusted_point.y == 0); | ||||
|         untrusted_point.x += 10; | ||||
|         untrusted_point.y += 20; | ||||
|         assert!(untrusted_point.x == 10 && untrusted_point.y == 20); | ||||
|         drop(untrusted_point); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn with_array() { | ||||
|         let mut untrusted_array = UntrustedBox::new([0u8, 1, 2, 3]); | ||||
|         untrusted_array | ||||
|             .iter() | ||||
|             .enumerate() | ||||
|             .for_each(|(pos, i)| assert!(pos as u8 == *i)); | ||||
| 
 | ||||
|         for i in untrusted_array.iter_mut() { | ||||
|             *i = 0; | ||||
|         } | ||||
|         untrusted_array.iter().for_each(|i| assert!(*i == 0)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn with_slice() { | ||||
|         let len = 4; | ||||
|         let mut untrusted_slice: UntrustedBox<[i32]> = UntrustedBox::new_uninit_slice(len); | ||||
|         assert!(untrusted_slice.len() == len); | ||||
|         untrusted_slice[1] = 123; | ||||
|         assert!(untrusted_slice[1] == 123); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										95
									
								
								src/libos/crates/sgx-untrusted-alloc/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										95
									
								
								src/libos/crates/sgx-untrusted-alloc/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| //! Allocation and access of _untrusted_ memory in a _safe_ way.
 | ||||
| //!
 | ||||
| //! # Usage
 | ||||
| //!
 | ||||
| //! ## Basics
 | ||||
| //!
 | ||||
| //! Suppose you have a data structure named `AcceptReq`
 | ||||
| //! ```rust
 | ||||
| //! struct AcceptReq {
 | ||||
| //!     addr: libc::sockaddr_storage,
 | ||||
| //!     addr_len: libc::socklen_t,
 | ||||
| //! }
 | ||||
| //! ```
 | ||||
| //! which is intended to be used as an untrusted buffer shared
 | ||||
| //! with the host OS to pass arguments of the accept system call.
 | ||||
| //! And we assume that this buffer must be present during the lifetime
 | ||||
| //! of a listening socket. So it must be allocated on the heap in
 | ||||
| //! untrusted memory. So how to do it?
 | ||||
| //!
 | ||||
| //! With this crate, it takes two steps.
 | ||||
| //!
 | ||||
| //! 1. Implement the [`MaybeUntrusted`] marker trait for the data structure.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use sgx_untrusted_alloc::MaybeUntrusted;
 | ||||
| //! # struct AcceptReq;
 | ||||
| //!
 | ||||
| //! unsafe impl MaybeUntrusted for AcceptReq { }
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! By implementing this trait, you are claiming: "I am fully aware of the
 | ||||
| //! security risks in communicating with the host through _untrusted,
 | ||||
| //! shared data structures_. I know that an attacker may peek or tamper with
 | ||||
| //! the data structure at any possible timing or in an arbitrary way.
 | ||||
| //! I will be very careful. And I am good to go."
 | ||||
| //!
 | ||||
| //! 2. You can now allocate the data structure in untrusted heap with
 | ||||
| //! [`UntrustedBox`], which is similar to the standard `Box` albeit for the
 | ||||
| //! untrusted memory.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! # use sgx_untrusted_alloc::MaybeUntrusted;
 | ||||
| //! # struct AcceptReq;
 | ||||
| //! # unsafe impl MaybeUntrusted for AcceptReq { }
 | ||||
| //! #
 | ||||
| //! use sgx_untrusted_alloc::UntrustedBox;
 | ||||
| //!
 | ||||
| //! let accept_req: UntrustedBox<AcceptReq> = UntrustedBox::new_uninit();
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! Note that the convenient constructor method `UntrustedBox::<T>::new_uninit`
 | ||||
| //! creates an _uninitialized_ instance of `T` on untrusted heap.
 | ||||
| //! Alternatively, you can create an _initialized_ instance with `UntrustedBox::new`.
 | ||||
| //!
 | ||||
| //! ## Arrays and slices
 | ||||
| //!
 | ||||
| //! You can also use `UntrustedBox` to allocate arrays (`[T; N]`) or
 | ||||
| //! slices (`[T]`) on untrusted heap as long as the trait bound of `T: MaybeUntrusted`
 | ||||
| //! is held.
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use sgx_untrusted_alloc::{MaybeUntrusted, UntrustedBox};
 | ||||
| //!
 | ||||
| //! let untrusted_array: UntrustedBox<[u8; 4]> = UntrustedBox::new_uninit();
 | ||||
| //!
 | ||||
| //! let untrusted_slice: UntrustedBox<[u8]> = UntrustedBox::new_uninit_slice(4);
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! Both `untrusted_array` and `untrusted_slice` above consist of four `u8` integers.
 | ||||
| 
 | ||||
| #![cfg_attr(feature = "sgx", no_std)] | ||||
| #![feature(linked_list_remove)] | ||||
| 
 | ||||
| #[cfg(feature = "sgx")] | ||||
| extern crate sgx_libc as libc; | ||||
| #[cfg(feature = "sgx")] | ||||
| extern crate sgx_tstd as std; | ||||
| 
 | ||||
| #[macro_use] | ||||
| extern crate alloc; | ||||
| #[macro_use] | ||||
| extern crate lazy_static; | ||||
| extern crate intrusive_collections; | ||||
| #[macro_use] | ||||
| extern crate log; | ||||
| extern crate spin; | ||||
| 
 | ||||
| mod maybe_untrusted; | ||||
| pub use maybe_untrusted::MaybeUntrusted; | ||||
| 
 | ||||
| mod box_; | ||||
| pub use box_::UntrustedBox; | ||||
| 
 | ||||
| mod prelude; | ||||
| mod untrusted_allocator; | ||||
							
								
								
									
										130
									
								
								src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										130
									
								
								src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,130 @@ | ||||
| use std::cell::Cell; | ||||
| 
 | ||||
| /// A marker trait for types that can be used in untrusted memory
 | ||||
| /// in a relatively safe way.
 | ||||
| ///
 | ||||
| /// # Overview
 | ||||
| ///
 | ||||
| /// Not all types are created equal: some of them are difficult---
 | ||||
| /// if not impossible---to be used safely when residing in untrusted memory.
 | ||||
| /// One obvious class is heap-backed Rust containers, like `Vec<T>`.
 | ||||
| /// If such types were put into untrusted memory, then an attacker could manipulate
 | ||||
| /// the internal pointer. Enums, in general, cannot be used safely in untrusted
 | ||||
| /// memory since an attacker could break Rust compiler's assumption on its memory
 | ||||
| /// representation, thus causing undefined behaviors. Surprisingly, even a primitive
 | ||||
| /// type like `bool` is dangerous to use in untrusted memory. This is also
 | ||||
| /// due to Rust compiler's strong assumption on memory representation.
 | ||||
| ///
 | ||||
| /// Here is a list of core types that implements `MaybeUntrusted`:
 | ||||
| /// * Primitive types: `u8`, `u16`, `u32`, `u64`, `usize` and their signed counterparts;
 | ||||
| /// * Pointer types: `*const T` and `*mut T`, where `T: MaybeUntrusted`;
 | ||||
| /// * Array types: `[T; N]`, where `T: MaybeUntrusted` and 1<= `N` <= 32;
 | ||||
| /// * Core types: `Cell<T>`, where `T: MaybeUntrusted`.
 | ||||
| /// * Libc types: Most C-style structs defined in libc can have `MaybeUntrusted` implemented.
 | ||||
| /// But since there are simply too many of them yet most of them are irrelevant for our usage,
 | ||||
| /// it is more reasonable to implement `MaybeUntrusted` on demand. Currently, the list of libc
 | ||||
| /// types that have implemented `MaybeUntrusted` is pretty short:
 | ||||
| ///     * `sockaddr_storage`.
 | ||||
| ///
 | ||||
| /// For user-defined types, the `MaybeUntrusted` trait should be implemented with discretion.
 | ||||
| /// A good rule of thumb is only implementing `MaybeUntrusted` for C-style structs.
 | ||||
| ///
 | ||||
| /// # Must implement `Sized`
 | ||||
| ///
 | ||||
| /// The `MaybeUntrusted` trait requires `Sized`. In other words, it wouldn't be safe to
 | ||||
| /// store any DST values in untrusted memory since the length part of DST may be tampered by
 | ||||
| /// attacker, leading to buffer overflow.
 | ||||
| ///
 | ||||
| /// # Safe to be uninitialized
 | ||||
| ///
 | ||||
| /// Another implicit property of `MaybeUntrusted` types is that unlike most Rust types it is
 | ||||
| /// perfectly fine to instantiate _uninitialized_ values for a `MaybeUntrusted` type. After all,
 | ||||
| /// our thread model assumes an attacker that may tamper with the values of `MaybeUntrusted`
 | ||||
| /// types in an arbitrary way. The code that handles `MaybeUntrusted` types must be robust
 | ||||
| /// enough to deal with any possible values, including uninitialized ones.
 | ||||
| ///
 | ||||
| /// As a result, `MaybeUntrusted` comes with two convenient constructor methods that creates
 | ||||
| /// an instance whose value is uninitialized or zeroed, respectively.
 | ||||
| ///
 | ||||
| /// # Good to have `Copy`
 | ||||
| ///
 | ||||
| /// As an effective defense against against TOCTOU attacks, a common practice is to---before
 | ||||
| /// actually using a value of `MaybeUntrusted`---first copy the value into trusted memory and
 | ||||
| /// validate its value. Thus, most types that implement `MaybeUntrusted` should also implement
 | ||||
| /// `Copy`.
 | ||||
| use std::mem::MaybeUninit; | ||||
| 
 | ||||
| pub unsafe trait MaybeUntrusted: Sized { | ||||
|     fn uninit() -> Self { | ||||
|         unsafe { MaybeUninit::uninit().assume_init() } | ||||
|     } | ||||
| 
 | ||||
|     fn zeroed() -> Self { | ||||
|         unsafe { MaybeUninit::zeroed().assume_init() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_maybe_untrusted { | ||||
|     ($($type:ty),*) => { | ||||
|         $( | ||||
|             unsafe impl MaybeUntrusted for $type {} | ||||
|         )* | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_maybe_untrusted! { | ||||
|     // Primitive types
 | ||||
|     u8, | ||||
|     u16, | ||||
|     u32, | ||||
|     u64, | ||||
|     usize, | ||||
|     i8, | ||||
|     i16, | ||||
|     i32, | ||||
|     i64, | ||||
|     isize, | ||||
| 
 | ||||
|     // Libc types
 | ||||
|     libc::sockaddr_storage, | ||||
|     libc::iovec | ||||
| } | ||||
| 
 | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for *const T {} | ||||
| 
 | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for *mut T {} | ||||
| 
 | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 1] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 2] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 3] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 4] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 5] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 6] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 7] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 8] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 9] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 10] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 11] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 12] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 13] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 14] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 15] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 16] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 17] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 18] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 19] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 20] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 21] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 22] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 23] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 24] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 25] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 26] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 27] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 28] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 29] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 30] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 31] {} | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for [T; 32] {} | ||||
| 
 | ||||
| unsafe impl<T: MaybeUntrusted> MaybeUntrusted for Cell<T> {} | ||||
							
								
								
									
										5
									
								
								src/libos/crates/sgx-untrusted-alloc/src/prelude.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								src/libos/crates/sgx-untrusted-alloc/src/prelude.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| // Convenient reexports for internal uses.
 | ||||
| pub(crate) use errno::prelude::*; | ||||
| pub(crate) use std::boxed::Box; | ||||
| pub(crate) use std::fmt; | ||||
| pub(crate) use std::vec::Vec; | ||||
| @ -0,0 +1,163 @@ | ||||
| // Implements free space management for memory.
 | ||||
| // Currently only use simple vector as the base structure.
 | ||||
| //
 | ||||
| // Basically use address-ordered first fit to find free ranges.
 | ||||
| use super::*; | ||||
| use std::cmp::Ordering; | ||||
| 
 | ||||
| static INITIAL_SIZE: usize = 100; | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| pub struct VMFreeSpaceManager { | ||||
|     free_manager: Vec<VMRange>, // Address-ordered first fit
 | ||||
| } | ||||
| 
 | ||||
| impl VMFreeSpaceManager { | ||||
|     pub fn new(initial_free_range: VMRange) -> Self { | ||||
|         let mut free_manager = Vec::with_capacity(INITIAL_SIZE); | ||||
|         free_manager.push(initial_free_range); | ||||
| 
 | ||||
|         VMFreeSpaceManager { | ||||
|             free_manager: free_manager, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn free_size(&self) -> usize { | ||||
|         self.free_manager | ||||
|             .iter() | ||||
|             .fold(0, |acc, free_range| acc + free_range.size()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_free_range_internal(&mut self, size: usize, align: usize) -> Result<VMRange> { | ||||
|         // Record the minimal free range that satisfies the constraints
 | ||||
|         let mut result_free_range: Option<VMRange> = None; | ||||
|         let mut result_idx: Option<usize> = None; | ||||
|         let free_list = &self.free_manager; | ||||
| 
 | ||||
|         for (idx, free_range) in free_list.iter().enumerate() { | ||||
|             let free_range = { | ||||
|                 if free_range.size() < size { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 // Check alignment
 | ||||
|                 let start = align_up(free_range.start(), align); | ||||
|                 let end = start + size; | ||||
|                 if free_range.end() < end { | ||||
|                     continue; | ||||
|                 } | ||||
|                 unsafe { VMRange::from_unchecked(start, end) } | ||||
|             }; | ||||
| 
 | ||||
|             result_free_range = Some(free_range); | ||||
|             result_idx = Some(idx); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if result_free_range.is_none() { | ||||
|             return_errno!(ENOMEM, "not enough memory"); | ||||
|         } | ||||
| 
 | ||||
|         let index = result_idx.unwrap(); | ||||
|         let result_free_range = result_free_range.unwrap(); | ||||
| 
 | ||||
|         self.free_list_update_range(index, result_free_range); | ||||
|         return Ok(result_free_range); | ||||
|     } | ||||
| 
 | ||||
|     fn free_list_update_range(&mut self, index: usize, range: VMRange) { | ||||
|         let free_list = &mut self.free_manager; | ||||
|         let ranges_after_subtraction = free_list[index].subtract(&range); | ||||
|         debug_assert!(ranges_after_subtraction.len() <= 2); | ||||
|         if ranges_after_subtraction.len() == 0 { | ||||
|             free_list.remove(index); | ||||
|             return; | ||||
|         } | ||||
|         free_list[index] = ranges_after_subtraction[0]; | ||||
|         if ranges_after_subtraction.len() == 2 { | ||||
|             free_list.insert(index + 1, ranges_after_subtraction[1]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_range_back_to_free_manager(&mut self, dirty_range: &VMRange) -> Result<()> { | ||||
|         let free_list = &mut self.free_manager; | ||||
| 
 | ||||
|         // If the free list is empty, insert the dirty range and it's done.
 | ||||
|         if free_list.is_empty() { | ||||
|             free_list.push(*dirty_range); | ||||
|             return Ok(()); | ||||
|         } | ||||
| 
 | ||||
|         let dirty_range_start = dirty_range.start(); | ||||
|         let dirty_range_end = dirty_range.end(); | ||||
| 
 | ||||
|         // If the dirty range is before the first free range or after the last free range
 | ||||
|         let head_range = &mut free_list[0]; | ||||
|         match dirty_range_end.cmp(&head_range.start()) { | ||||
|             Ordering::Equal => { | ||||
|                 head_range.set_start(dirty_range_start); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|             Ordering::Less => { | ||||
|                 free_list.insert(0, *dirty_range); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|             _ => (), | ||||
|         } | ||||
| 
 | ||||
|         let tail_range = free_list.last_mut().unwrap(); | ||||
|         match dirty_range_start.cmp(&tail_range.end()) { | ||||
|             Ordering::Equal => { | ||||
|                 tail_range.set_end(dirty_range_end); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|             Ordering::Greater => { | ||||
|                 free_list.push(*dirty_range); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|             _ => (), | ||||
|         } | ||||
| 
 | ||||
|         // The dirty range must be between some two ranges.
 | ||||
|         debug_assert!(free_list.len() >= 2); | ||||
|         let mut idx = 0; | ||||
| 
 | ||||
|         while idx < free_list.len() - 1 { | ||||
|             let left_range = free_list[idx]; | ||||
|             let right_range = free_list[idx + 1]; | ||||
| 
 | ||||
|             if left_range.end() <= dirty_range_start && dirty_range_end <= right_range.start() { | ||||
|                 match ( | ||||
|                     dirty_range.is_contiguous_with(&left_range), | ||||
|                     dirty_range.is_contiguous_with(&right_range), | ||||
|                 ) { | ||||
|                     (true, true) => { | ||||
|                         let left_range = &mut free_list[idx]; | ||||
|                         left_range.set_end(right_range.end()); | ||||
|                         free_list.remove(idx + 1); | ||||
|                     } | ||||
|                     (true, false) => { | ||||
|                         let left_range = &mut free_list[idx]; | ||||
|                         left_range.set_end(dirty_range_end); | ||||
|                     } | ||||
|                     (false, true) => { | ||||
|                         let right_range = &mut free_list[idx + 1]; | ||||
|                         right_range.set_start(dirty_range_start); | ||||
|                     } | ||||
|                     (false, false) => { | ||||
|                         free_list.insert(idx + 1, *dirty_range); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             idx += 1; | ||||
|         } | ||||
|         return Ok(()); | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_free_range(&self, request_range: &VMRange) -> bool { | ||||
|         self.free_manager | ||||
|             .iter() | ||||
|             .any(|free_range| free_range.is_superset_of(request_range)) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,111 @@ | ||||
| use super::*; | ||||
| 
 | ||||
| use crate::prelude::*; | ||||
| 
 | ||||
| mod free_space_manager; | ||||
| mod vm_area; | ||||
| mod vm_chunk_manager; | ||||
| mod vm_range; | ||||
| mod vm_util; | ||||
| 
 | ||||
| const PAGE_SIZE: usize = 4096; | ||||
| 
 | ||||
| // The address of a block returned by malloc or realloc in GNU systems is always a multiple of eight (or sixteen on 64-bit systems).
 | ||||
| const DEFAULT_ALIGNMENT: usize = 16; | ||||
| 
 | ||||
| const DEFAULT_CHUNK_SIZE: usize = 16 * 1024 * 1024; // 16MB
 | ||||
| 
 | ||||
| use vm_range::*; | ||||
| use vm_util::*; | ||||
| 
 | ||||
| use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; | ||||
| use spin::Mutex; | ||||
| use std::collections::LinkedList; | ||||
| use vm_chunk_manager::ChunkManager; | ||||
| 
 | ||||
| pub struct Allocator { | ||||
|     inner: Mutex<LinkedList<ChunkManager>>, // Use linked list for fast insert/delete.
 | ||||
| } | ||||
| 
 | ||||
| impl Allocator { | ||||
|     // Initiate a untrusted memory allocator with default size.
 | ||||
|     pub fn new() -> Self { | ||||
|         let total_bytes = DEFAULT_CHUNK_SIZE; | ||||
| 
 | ||||
|         let inner = { | ||||
|             let mut new_list = LinkedList::new(); | ||||
|             let chunk_manager = ChunkManager::new(total_bytes) | ||||
|                 .expect("Creating untrusted allocator instance failure"); | ||||
|             new_list.push_back(chunk_manager); | ||||
|             Mutex::new(new_list) | ||||
|         }; | ||||
| 
 | ||||
|         Self { inner } | ||||
|     } | ||||
| 
 | ||||
|     // Allocate a block of memory and return the start address in c style.
 | ||||
|     // Use exactly like malloc from libc.
 | ||||
|     pub unsafe fn alloc(&self, size: usize, align: Option<usize>) -> Result<*mut u8> { | ||||
|         let align = align.unwrap_or(DEFAULT_ALIGNMENT); | ||||
| 
 | ||||
|         // find a free range in the chunk list
 | ||||
|         for chunk_manager in self.inner.lock().iter_mut() { | ||||
|             if size > *chunk_manager.free_size() { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             if let Ok(addr) = chunk_manager.alloc(size, align) { | ||||
|                 return Ok(addr as *mut u8); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // if no free range found, create a new chunk and allocate from it.
 | ||||
|         let start_addr = self.alloc_from_new_chunk(size, align)?; | ||||
|         Ok(start_addr as *mut u8) | ||||
|     } | ||||
| 
 | ||||
|     // Free the memory block with specified start address
 | ||||
|     // For memory allocated with alloc, free must be called.
 | ||||
|     // Use exactly like free from libc.
 | ||||
|     pub unsafe fn free(&self, addr: *mut u8) { | ||||
|         let mut chunk_list = self.inner.lock(); | ||||
|         let (idx, chunk) = chunk_list | ||||
|             .iter_mut() | ||||
|             .enumerate() | ||||
|             .find(|(_, chunk)| chunk.contains(addr as usize)) | ||||
|             .expect("free failure"); | ||||
| 
 | ||||
|         chunk.free(addr as usize).expect("free failure"); | ||||
| 
 | ||||
|         // Keep at most one empty chunk in the list. And free other empty chunks:
 | ||||
|         // If the chunk is empty, and all other chunks are in use, push the chunk to the front of the list.
 | ||||
|         // If the chunk is empty and the front chunk is also empty, just remove this chunk.
 | ||||
|         // If this is the last chunk, just keep it.
 | ||||
|         if chunk.is_empty() && chunk_list.len() > 1 { | ||||
|             let empty_chunk = chunk_list.remove(idx); | ||||
|             if !chunk_list.front().unwrap().is_empty() { | ||||
|                 chunk_list.push_front(empty_chunk); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn alloc_from_new_chunk(&self, size: usize, align: usize) -> Result<usize> { | ||||
|         let total_bytes = size.max(DEFAULT_CHUNK_SIZE); | ||||
| 
 | ||||
|         let mut new_chunk = ChunkManager::new(total_bytes)?; | ||||
|         let ret_addr = new_chunk.alloc(size, align)?; | ||||
| 
 | ||||
|         self.inner.lock().push_front(new_chunk); // Add chunk to the list head to be iterated earlier.
 | ||||
|         Ok(ret_addr) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drop for Allocator { | ||||
|     fn drop(&mut self) { | ||||
|         debug!("[untrusted alloc] Drop allocator"); | ||||
|         self.inner.lock().iter().for_each(|chunk| { | ||||
|             assert!(chunk.is_empty() == true); | ||||
|             assert!(chunk.is_free_range(chunk.range())); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,63 @@ | ||||
| use super::vm_range::VMRange; | ||||
| use super::*; | ||||
| use std::ops::Deref; | ||||
| 
 | ||||
| use intrusive_collections::rbtree::Link; | ||||
| use intrusive_collections::{intrusive_adapter, KeyAdapter}; | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct VMArea { | ||||
|     range: VMRange, | ||||
| } | ||||
| 
 | ||||
| impl VMArea { | ||||
|     pub fn new(range: VMRange) -> Self { | ||||
|         Self { range } | ||||
|     } | ||||
| 
 | ||||
|     pub fn range(&self) -> &VMRange { | ||||
|         &self.range | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Deref for VMArea { | ||||
|     type Target = VMRange; | ||||
| 
 | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         &self.range | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct VMAObj { | ||||
|     link: Link, | ||||
|     vma: VMArea, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for VMAObj { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "{:?}", self.vma) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // key adapter for RBTree which is sorted by the start of vma ranges
 | ||||
| intrusive_adapter!(pub VMAAdapter = Box<VMAObj>: VMAObj { link : Link }); | ||||
| impl<'a> KeyAdapter<'a> for VMAAdapter { | ||||
|     type Key = usize; | ||||
|     fn get_key(&self, vma_obj: &'a VMAObj) -> usize { | ||||
|         vma_obj.vma.range().start() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl VMAObj { | ||||
|     pub fn new_vma_obj(vma: VMArea) -> Box<Self> { | ||||
|         Box::new(Self { | ||||
|             link: Link::new(), | ||||
|             vma, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn vma(&self) -> &VMArea { | ||||
|         &self.vma | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,158 @@ | ||||
| use super::*; | ||||
| 
 | ||||
| use super::free_space_manager::VMFreeSpaceManager as FreeRangeManager; | ||||
| use super::vm_area::*; | ||||
| 
 | ||||
| use intrusive_collections::rbtree::RBTree; | ||||
| use intrusive_collections::Bound; | ||||
| 
 | ||||
| use libc::c_void; | ||||
| cfg_if::cfg_if! { | ||||
|     if #[cfg(feature = "sgx")] { | ||||
|         use libc::ocall::{mmap, munmap}; | ||||
|     } else { | ||||
|         use libc::{mmap, munmap}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Memory chunk manager.
 | ||||
| ///
 | ||||
| /// Chunk is the memory unit for the allocator.
 | ||||
| /// ChunkManager is implemented basically with two data structures: a red-black tree to track vmas in use and a FreeRangeManager to track
 | ||||
| /// ranges which are free.
 | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| pub struct ChunkManager { | ||||
|     range: VMRange, | ||||
|     free_size: usize, | ||||
|     vmas: RBTree<VMAAdapter>, | ||||
|     free_manager: FreeRangeManager, | ||||
| } | ||||
| 
 | ||||
| #[allow(dead_code)] | ||||
| impl ChunkManager { | ||||
|     pub fn new(total_size: usize) -> Result<Self> { | ||||
|         let start_address = { | ||||
|             let addr = unsafe { | ||||
|                 mmap( | ||||
|                     0 as *mut _, | ||||
|                     total_size, | ||||
|                     PROT_READ | PROT_WRITE, | ||||
|                     MAP_PRIVATE | MAP_ANONYMOUS, | ||||
|                     0, | ||||
|                     0, | ||||
|                 ) | ||||
|             }; | ||||
| 
 | ||||
|             if addr == libc::MAP_FAILED { | ||||
|                 return_errno!(ENOMEM, "allocate new chunk failed"); | ||||
|             } | ||||
| 
 | ||||
|             let addr = addr as usize; | ||||
|             assert!(addr.checked_add(total_size).is_some()); | ||||
|             addr | ||||
|         }; | ||||
| 
 | ||||
|         let range = VMRange::new(start_address, start_address + total_size)?; | ||||
|         let vmas = RBTree::new(VMAAdapter::new()); | ||||
|         debug!( | ||||
|             "[untrusted alloc] create a new mem chunk, range = {:?}", | ||||
|             range | ||||
|         ); | ||||
|         Ok(ChunkManager { | ||||
|             range, | ||||
|             free_size: range.size(), | ||||
|             vmas, | ||||
|             free_manager: FreeRangeManager::new(range.clone()), | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn range(&self) -> &VMRange { | ||||
|         &self.range | ||||
|     } | ||||
| 
 | ||||
|     pub fn vmas(&self) -> &RBTree<VMAAdapter> { | ||||
|         &self.vmas | ||||
|     } | ||||
| 
 | ||||
|     pub fn free_size(&self) -> &usize { | ||||
|         &self.free_size | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.vmas.iter().count() == 0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn alloc(&mut self, size: usize, align: usize) -> Result<usize> { | ||||
|         // Find and allocate a new range for this request
 | ||||
|         let new_range = self.free_manager.find_free_range_internal(size, align)?; | ||||
|         let new_addr = new_range.start(); | ||||
|         let new_vma = VMArea::new(new_range); | ||||
|         self.free_size -= new_vma.size(); | ||||
| 
 | ||||
|         debug!("[untrusted alloc] malloc range = {:?}", new_vma.range()); | ||||
|         self.vmas.insert(VMAObj::new_vma_obj(new_vma)); | ||||
|         Ok(new_addr) | ||||
|     } | ||||
| 
 | ||||
|     pub fn free(&mut self, addr: usize) -> Result<()> { | ||||
|         if addr == 0 { | ||||
|             return Ok(()); | ||||
|         } | ||||
| 
 | ||||
|         let mut vmas_cursor = self.vmas.find_mut(&addr); | ||||
|         if vmas_cursor.is_null() { | ||||
|             return_errno!(EINVAL, "no vma related was found"); | ||||
|         } | ||||
| 
 | ||||
|         let vma_obj = vmas_cursor.remove().unwrap(); | ||||
|         let vma = vma_obj.vma(); | ||||
|         debug!("[untrusted alloc] free range = {:?}", vma.range()); | ||||
|         self.free_manager | ||||
|             .add_range_back_to_free_manager(vma.range())?; | ||||
|         self.free_size += vma.size(); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_used_mem_region(&self, addr: usize) -> Result<VMRange> { | ||||
|         let vma = self.vmas.upper_bound(Bound::Included(&addr)); | ||||
|         if vma.is_null() { | ||||
|             return_errno!(ESRCH, "no mmap regions that contains the address"); | ||||
|         } | ||||
|         let vma = vma.get().unwrap().vma(); | ||||
|         if !vma.contains(addr) { | ||||
|             return_errno!(ESRCH, "no mmap regions that contains the address"); | ||||
|         } | ||||
| 
 | ||||
|         return Ok(vma.range().clone()); | ||||
|     } | ||||
| 
 | ||||
|     pub fn usage_percentage(&self) -> f32 { | ||||
|         let total_size = self.range.size(); | ||||
|         let mut used_size = 0; | ||||
|         self.vmas | ||||
|             .iter() | ||||
|             .for_each(|vma_obj| used_size += vma_obj.vma().size()); | ||||
| 
 | ||||
|         return used_size as f32 / total_size as f32; | ||||
|     } | ||||
| 
 | ||||
|     // Returns whether the requested range is free
 | ||||
|     pub fn is_free_range(&self, request_range: &VMRange) -> bool { | ||||
|         self.free_manager.is_free_range(request_range) | ||||
|     } | ||||
| 
 | ||||
|     pub fn contains(&self, addr: usize) -> bool { | ||||
|         self.range.contains(addr) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drop for ChunkManager { | ||||
|     fn drop(&mut self) { | ||||
|         debug_assert!(self.is_empty() == true); | ||||
|         debug_assert!(self.free_size == self.range.size()); | ||||
|         debug_assert!(self.free_manager.free_size() == self.range.size()); | ||||
|         let ret = unsafe { munmap(self.range().start as *mut c_void, self.range().size()) }; | ||||
|         assert!(ret == 0); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,159 @@ | ||||
| use super::*; | ||||
| 
 | ||||
| #[derive(Clone, Copy, Default, Eq, PartialEq, Hash)] | ||||
| pub struct VMRange { | ||||
|     pub(super) start: usize, | ||||
|     pub(super) end: usize, | ||||
| } | ||||
| 
 | ||||
| #[allow(dead_code)] | ||||
| impl VMRange { | ||||
|     pub fn new(start: usize, end: usize) -> Result<VMRange> { | ||||
|         if start > end { | ||||
|             return_errno!(EINVAL, "invalid start end address"); | ||||
|         } | ||||
|         Ok(VMRange { | ||||
|             start: start, | ||||
|             end: end, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn new_with_size(start: usize, size: usize) -> Result<VMRange> { | ||||
|         Self::new(start, start + size) | ||||
|     } | ||||
| 
 | ||||
|     pub fn new_empty(start: usize) -> Result<VMRange> { | ||||
|         if start % PAGE_SIZE != 0 { | ||||
|             return_errno!(EINVAL, "invalid start or end"); | ||||
|         } | ||||
|         Ok(VMRange { | ||||
|             start: start, | ||||
|             end: start, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub unsafe fn from_unchecked(start: usize, end: usize) -> VMRange { | ||||
|         debug_assert!(start <= end); | ||||
|         VMRange { | ||||
|             start: start, | ||||
|             end: end, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn start(&self) -> usize { | ||||
|         self.start | ||||
|     } | ||||
| 
 | ||||
|     pub fn end(&self) -> usize { | ||||
|         self.end | ||||
|     } | ||||
| 
 | ||||
|     pub fn size(&self) -> usize { | ||||
|         self.end - self.start | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_start(&mut self, new_start: usize) { | ||||
|         debug_assert!(new_start <= self.end); | ||||
|         self.start = new_start; | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_end(&mut self, new_end: usize) { | ||||
|         debug_assert!(new_end >= self.start); | ||||
|         self.end = new_end; | ||||
|     } | ||||
| 
 | ||||
|     pub fn empty(&self) -> bool { | ||||
|         self.start == self.end | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_superset_of(&self, other: &VMRange) -> bool { | ||||
|         self.start() <= other.start() && other.end() <= self.end() | ||||
|     } | ||||
| 
 | ||||
|     pub fn contains(&self, addr: usize) -> bool { | ||||
|         self.start() <= addr && addr < self.end() | ||||
|     } | ||||
| 
 | ||||
|     // Returns whether two ranges have non-empty interesection.
 | ||||
|     pub fn overlap_with(&self, other: &VMRange) -> bool { | ||||
|         let intersection_start = self.start().max(other.start()); | ||||
|         let intersection_end = self.end().min(other.end()); | ||||
|         intersection_start < intersection_end | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_contiguous_with(&self, other: &VMRange) -> bool { | ||||
|         self.start == other.end || self.end == other.start | ||||
|     } | ||||
| 
 | ||||
|     // Returns a set of ranges by subtracting self with the other.
 | ||||
|     //
 | ||||
|     // Post-condition: the returned ranges have non-zero sizes.
 | ||||
|     pub fn subtract(&self, other: &VMRange) -> Vec<VMRange> { | ||||
|         if self.size() == 0 { | ||||
|             return vec![]; | ||||
|         } | ||||
| 
 | ||||
|         let intersection = match self.intersect(other) { | ||||
|             None => return vec![*self], | ||||
|             Some(intersection) => intersection, | ||||
|         }; | ||||
| 
 | ||||
|         let self_start = self.start(); | ||||
|         let self_end = self.end(); | ||||
|         let inter_start = intersection.start(); | ||||
|         let inter_end = intersection.end(); | ||||
|         debug_assert!(self_start <= inter_start); | ||||
|         debug_assert!(inter_end <= self_end); | ||||
| 
 | ||||
|         match (self_start < inter_start, inter_end < self_end) { | ||||
|             (false, false) => Vec::new(), | ||||
|             (false, true) => unsafe { vec![VMRange::from_unchecked(inter_end, self_end)] }, | ||||
|             (true, false) => unsafe { vec![VMRange::from_unchecked(self_start, inter_start)] }, | ||||
|             (true, true) => unsafe { | ||||
|                 vec![ | ||||
|                     VMRange::from_unchecked(self_start, inter_start), | ||||
|                     VMRange::from_unchecked(inter_end, self_end), | ||||
|                 ] | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Returns an non-empty intersection if where is any
 | ||||
|     pub fn intersect(&self, other: &VMRange) -> Option<VMRange> { | ||||
|         let intersection_start = self.start().max(other.start()); | ||||
|         let intersection_end = self.end().min(other.end()); | ||||
|         if intersection_start >= intersection_end { | ||||
|             return None; | ||||
|         } | ||||
|         unsafe { | ||||
|             Some(VMRange::from_unchecked( | ||||
|                 intersection_start, | ||||
|                 intersection_end, | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub unsafe fn as_slice(&self) -> &[u8] { | ||||
|         let buf_ptr = self.start() as *const u8; | ||||
|         let buf_size = self.size() as usize; | ||||
|         std::slice::from_raw_parts(buf_ptr, buf_size) | ||||
|     } | ||||
| 
 | ||||
|     pub unsafe fn as_slice_mut(&self) -> &mut [u8] { | ||||
|         let buf_ptr = self.start() as *mut u8; | ||||
|         let buf_size = self.size() as usize; | ||||
|         std::slice::from_raw_parts_mut(buf_ptr, buf_size) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for VMRange { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!( | ||||
|             f, | ||||
|             "VMRange {{ start: 0x{:x?}, end: 0x{:x?}, size: 0x{:x?} }}", | ||||
|             self.start, | ||||
|             self.end, | ||||
|             self.size() | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| pub fn align_down(addr: usize, align: usize) -> usize { | ||||
|     debug_assert!(align.is_power_of_two()); | ||||
|     addr & !(align - 1) | ||||
| } | ||||
| 
 | ||||
| pub fn align_up(addr: usize, align: usize) -> usize { | ||||
|     debug_assert!(align.is_power_of_two()); | ||||
|     align_down(addr + (align - 1), align) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user