Coder Social home page Coder Social logo

serde_traitobject's Introduction

serde_traitobject

Crates.io MIT / Apache 2.0 licensed Build Status

๐Ÿ“– Docs | ๐Ÿ’ฌ Chat

Serializable and deserializable trait objects.

This library enables the serialization and deserialization of trait objects so they can be sent between other processes running the same binary.

For example, if you have multiple forks of a process, or the same binary running on each of a cluster of machines, this library lets you send trait objects between them.

Any trait can be made (de)serializable when made into a trait object by adding this crate's Serialize and Deserialize traits as supertraits:

trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize {
    fn my_method(&self);
}

#[derive(Serialize, Deserialize)]
struct Message {
    #[serde(with = "serde_traitobject")]
    message: Box<dyn MyTrait>,
}

// Woohoo, `Message` is now serializable!

And that's it! The two traits are automatically implemented for all T: serde::Serialize and all T: serde::de::DeserializeOwned, so as long as all implementors of your trait are themselves serializable then you're good to go.

There are two ways to (de)serialize your trait object:

  • Apply the #[serde(with = "serde_traitobject")] field attribute, which instructs serde to use this crate's serialize and deserialize functions;
  • The Box, Rc and Arc structs, which are simple wrappers around their stdlib counterparts that automatically handle (de)serialization without needing the above annotation;

Additionally, there are several convenience traits implemented that extend their stdlib counterparts:

These are automatically implemented on all implementors of their stdlib counterparts that also implement serde::Serialize and serde::de::DeserializeOwned.

use std::any::Any;
use serde_traitobject as s;

#[derive(Serialize, Deserialize, Debug)]
struct MyStruct {
    foo: String,
    bar: usize,
}

let my_struct = MyStruct {
    foo: String::from("abc"),
    bar: 123,
};

let erased: s::Box<dyn s::Any> = s::Box::new(my_struct);

let serialized = serde_json::to_string(&erased).unwrap();
let deserialized: s::Box<dyn s::Any> = serde_json::from_str(&serialized).unwrap();

let downcast: Box<MyStruct> = Box::<dyn Any>::downcast(deserialized.into_any()).unwrap();

println!("{:?}", downcast);
// MyStruct { foo: "abc", bar: 123 }

Security

This crate works by wrapping the vtable pointer with relative::Vtable such that it can safely be sent between processes.

This approach is not yet secure against malicious actors. However, if we assume non-malicious actors and typical (static or dynamic) linking conditions, then it's not unreasonable to consider it sound.

See here for ongoing work in rustc to make this safe and secure.

Validation

Three things are serialized alongside the vtable pointer for the purpose of validation:

  • the build_id (128 bits);
  • the type_id of the trait object (64 bits);
  • the type_id of the concrete type (64 bits).

At some point in Rust's future, I think it would be great if the latter could be used to safely look up and create a trait object. As it is, that functionality doesn't exist yet, so what this crate does instead is serialize the vtable pointer (relative to a static base), and do as much validity checking as it reasonably can before it can be used and potentially invoke UB.

The first two are checked for validity before usage of the vtable pointer. The build_id ensures that the vtable pointer came from an invocation of an identically laid out binary1. The type_id ensures that the trait object being deserialized is the same type as the trait object that was serialized. They ensure that under non-malicious conditions, attempts to deserialize invalid data return an error rather than UB. The type_id of the concrete type is used as a sanity check that panics if it differs from the type_id of the concrete type to be deserialized.

Regarding collisions, the 128 bit build_id colliding is sufficiently unlikely that it can be relied upon to never occur. The 64 bit type_id colliding is possible, see rust-lang/rust#10389, though exceedingly unlikely to occur in practise.

The vtable pointer is (de)serialized as a usize relative to the vtable pointer of this static trait object. This enables it to work under typical dynamic linking conditions, where the absolute vtable addresses can differ across invocations of the same binary, but relative addresses remain constant.

All together this leaves, as far as I'm aware, three soundness holes:

  • A malicious user with a copy of the binary could trivially craft a build_id and type_id that pass validation and gives them control of where to jump to.
  • Data corruption of the serialized vtable pointer but not the build_id or type_id used for validation, resulting in a jump to an arbitrary address. This could be rectified in a future version of this library by using a cipher to make it vanishingly unlikely for corruptions to affect only the vtable pointer, by mixing the vtable pointer and validation components upon (de)serialization.
  • Dynamic linking conditions where the relative addresses (vtable - static vtable) are different across different invocations of the same binary. I'm sure this is possible, but it's not a scenario I've encountered so I can't speak to its commonness.

1I don't think this requirement is strictly necessary, as the type_id should include all information that could affect soundness (trait methods, calling conventions, etc), but it's included in case that doesn't hold in practise; to provide a more helpful error message; and to reduce the likelihood of collisions.

Note

This crate currently requires Rust nightly.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

serde_traitobject's People

Contributors

alecmocatta avatar dependabot-preview[bot] avatar mergify[bot] avatar rajasekarv avatar syntheticore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

serde_traitobject's Issues

Non nightly crate

Hello,

I wanna to know your point of view about your other crate metatype that requires nightly build.
Do you have other way to handle metatype features without nightly features ?
Just wanna to know is something with a feature can be recoded ?

Best,
Marc-Antoine

warning: the trait Any cannot be made into an object

This warning is spurious, as noted in the linked issue rust-lang/rust#51443 the lint is overly broad and in fact needn't fire for marker traits.

This could be fixed in rustc by adjusting the lint here: https://github.com/rust-lang/rust/blob/2f16be42dd64669b784559e98662d9712ce13fab/src/librustc/traits/object_safety.rs#L343

See also the commit that introdued the lint: rust-lang/rust@1453b3a

warning: the trait `serde_traitobject::convenience::Any` cannot be made into an object
  |
  = note: `#[warn(where_clauses_object_safety)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
  = note: method `into_any_send` references the `Self` type in where clauses

rustc PR to fix this: rust-lang/rust#66122

I need s_to::Box into_raw, from_raw and from Box<dyn .. >

Hello. I need to use raw pointers for optimization(to avoid incrementation of ref counter). I need to serialize *const dyn MyTrait almost as Box. That is why I create Box via from_raw, and not I need to convert it to s_to::Box, serialize and later mem::forget

Compiler crashes when compiling on nightly

Crash on nightly-2023-04-23


error: internal compiler error: compiler/rustc_mir_transform/src/inline.rs:208:17: Closure arguments are not passed as a tuple                                                                                                                                     
                                                                                                                                                                                                                                                                   
thread 'rustc' panicked at 'Box<dyn Any>', /rustc/7f94b314cead7059a71a265a8b64905ef2511796/compiler/rustc_errors/src/lib.rs:1643:9                                                                                                                                 
stack backtrace:                                                                                                                                                                                                                                                   
   0:     0x7f1e2ba65f33 - std::backtrace_rs::backtrace::libunwind::trace::h527d8d64d53ade2d                                                                                                                                                                       
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5                                                                                                                  
   1:     0x7f1e2ba65f33 - std::backtrace_rs::backtrace::trace_unsynchronized::hfb55b01517dd6379                                                                                                                                                                   
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5                                                                                                                        
   2:     0x7f1e2ba65f33 - std::sys_common::backtrace::_print_fmt::hd134e914eea0bd97                                                                                                                                                                               
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/sys_common/backtrace.rs:65:5                                                                                                                                     
   3:     0x7f1e2ba65f33 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h1480db11ec399d77                                                                                                                                    
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/sys_common/backtrace.rs:44:22                                                                                                                                    
   4:     0x7f1e2bac6d4f - core::fmt::write::h67ec4c4171c92b26                                                                                                                                                                                                     
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/core/src/fmt/mod.rs:1247:17                                                                                                                                              
   5:     0x7f1e2ba58ed1 - std::io::Write::write_fmt::h3b12aef0fff2463b                                                                                                                                                                                            
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/io/mod.rs:1712:15                                                                                                                                                
   6:     0x7f1e2ba65d45 - std::sys_common::backtrace::_print::h584400135abdbd51                                                                                                                                                                                   
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/sys_common/backtrace.rs:47:5                                                                                                                                     
   7:     0x7f1e2ba65d45 - std::sys_common::backtrace::print::hce41d3c8bd91096b                                                                                                                                                                                    
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/sys_common/backtrace.rs:34:9                                                                                                                                     
   8:     0x7f1e2ba6884f - std::panicking::default_hook::{{closure}}::h2043b657a3791225                                                                                                                                                                            
   9:     0x7f1e2ba68507 - std::panicking::default_hook::h99252b8d3dd5719c                                                                                                                                                                                         
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/panicking.rs:293:9                                                                                                                                               
  10:     0x7f1e2ecdc915 - <rustc_driver_impl[d30cd2737d9d343a]::DEFAULT_HOOK::{closure#0}::{closure#0} as core[cc19a662f3570270]::ops::function::FnOnce<(&core[cc19a662f3570270]::panic::panic_info::PanicInfo,)>>::call_once::{shim:vtable#0}                    
  11:     0x7f1e2ba69005 - <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call::h222a2b674b9f4762                                                                                                                                                      
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/alloc/src/boxed.rs:1976:9                                                                                                                                                
  12:     0x7f1e2ba69005 - std::panicking::rust_panic_with_hook::h7f49b36bf7f8ff77                                                                                                                                                                                 
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/panicking.rs:704:13                                                                                                                                              
  13:     0x7f1e2f1d0931 - std[71a32ca0600a6a04]::panicking::begin_panic::<rustc_errors[6504f979c583f5cb]::ExplicitBug>::{closure#0}                                                                                                                               
  14:     0x7f1e2f1ceef6 - std[71a32ca0600a6a04]::sys_common::backtrace::__rust_end_short_backtrace::<std[71a32ca0600a6a04]::panicking::begin_panic<rustc_errors[6504f979c583f5cb]::ExplicitBug>::{closure#0}, !>                                                  
  15:     0x7f1e2f1b4816 - std[71a32ca0600a6a04]::panicking::begin_panic::<rustc_errors[6504f979c583f5cb]::ExplicitBug>                                                                                                                                            
  16:     0x7f1e2f2377a6 - <rustc_errors[6504f979c583f5cb]::HandlerInner>::bug::<&alloc[31b12c976b8d660]::string::String>                                                                                                                                          
  17:     0x7f1e2f237470 - <rustc_errors[6504f979c583f5cb]::Handler>::bug::<&alloc[31b12c976b8d660]::string::String>                                                                                                                                               
  18:     0x7f1e2f230acb - rustc_middle[bb083b0d5f0119e4]::util::bug::opt_span_bug_fmt::<rustc_span[9551eaa044f53f4f]::span_encoding::Span>::{closure#0}                                                                                                           
  19:     0x7f1e2f22e41a - rustc_middle[bb083b0d5f0119e4]::ty::context::tls::with_opt::<rustc_middle[bb083b0d5f0119e4]::util::bug::opt_span_bug_fmt<rustc_span[9551eaa044f53f4f]::span_encoding::Span>::{closure#0}, !>::{closure#0}                               
  20:     0x7f1e2f22e3ea - rustc_middle[bb083b0d5f0119e4]::ty::context::tls::with_context_opt::<rustc_middle[bb083b0d5f0119e4]::ty::context::tls::with_opt<rustc_middle[bb083b0d5f0119e4]::util::bug::opt_span_bug_fmt<rustc_span[9551eaa044f53f4f]::span_encoding:
:Span>::{closure#0}, !>::{closure#0}, !>                                                                                                                                                                                                                           
  21:     0x7f1e2cedac5d - rustc_middle[bb083b0d5f0119e4]::util::bug::bug_fmt                                                                                                                                                                                      
  22:     0x7f1e2dbff812 - <rustc_mir_transform[64351b74f6b43a55]::inline::Inliner>::try_inlining                                                                                                                                                                  
  23:     0x7f1e2dbf8e34 - <rustc_mir_transform[64351b74f6b43a55]::inline::Inliner>::process_blocks                                                                                                                                                                
  24:     0x7f1e2dbf871f - <rustc_mir_transform[64351b74f6b43a55]::inline::Inline as rustc_middle[bb083b0d5f0119e4]::mir::MirPass>::run_pass                                                                                                                       
  25:     0x7f1e2e0737c3 - rustc_mir_transform[64351b74f6b43a55]::optimized_mir                                                                                                                                                                                    
  26:     0x7f1e2e071950 - rustc_query_system[1a377a8b6f66ffa1]::query::plumbing::try_execute_query::<rustc_query_impl[676b5616e96b3d6b]::queries::optimized_mir, rustc_query_impl[676b5616e96b3d6b]::plumbing::QueryCtxt>                                         
  27:     0x7f1e2df4fddc - <rustc_metadata[9b16796d473eaa76]::rmeta::encoder::EncodeContext>::encode_crate_root                                                                                                                                                    
  28:     0x7f1e2dedb383 - rustc_metadata[9b16796d473eaa76]::rmeta::encoder::encode_metadata_impl                                                                                                                                                                  
  29:     0x7f1e2decfd61 - rustc_data_structures[c2b7e56340b9c22f]::sync::join::<rustc_metadata[9b16796d473eaa76]::rmeta::encoder::encode_metadata::{closure#0}, rustc_metadata[9b16796d473eaa76]::rmeta::encoder::encode_metadata::{closure#1}, (), ()>           
  30:     0x7f1e2decf98e - rustc_metadata[9b16796d473eaa76]::rmeta::encoder::encode_metadata                                                                                                                                                                       
  31:     0x7f1e2dece30d - rustc_metadata[9b16796d473eaa76]::fs::encode_and_write_metadata                                                                                                                                                                         
  32:     0x7f1e2dec7bc3 - rustc_interface[c2b70c9b1dae0906]::passes::start_codegen                                                                                                                                                                                
  33:     0x7f1e2dec2ce4 - <rustc_middle[bb083b0d5f0119e4]::ty::context::GlobalCtxt>::enter::<<rustc_interface[c2b70c9b1dae0906]::queries::Queries>::ongoing_codegen::{closure#0}::{closure#0}, core[cc19a662f3570270]::result::Result<alloc[31b12c976b8d660]::boxe
d::Box<dyn core[cc19a662f3570270]::any::Any>, rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>                                                                                                                                                                      
  34:     0x7f1e2dec08b8 - <rustc_interface[c2b70c9b1dae0906]::queries::Queries>::ongoing_codegen                                                                                                                                                                  
  35:     0x7f1e2dec009b - <rustc_interface[c2b70c9b1dae0906]::interface::Compiler>::enter::<rustc_driver_impl[d30cd2737d9d343a]::run_compiler::{closure#1}::{closure#2}, core[cc19a662f3570270]::result::Result<core[cc19a662f3570270]::option::Option<rustc_inter
face[c2b70c9b1dae0906]::queries::Linker>, rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>                                                                                                                                                                          
  36:     0x7f1e2debe08f - rustc_span[9551eaa044f53f4f]::set_source_map::<core[cc19a662f3570270]::result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>, rustc_interface[c2b70c9b1dae0906]::interface::run_compiler<core[cc19a662f3570270]::result::Res
ult<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>, rustc_driver_impl[d30cd2737d9d343a]::run_compiler::{closure#1}>::{closure#0}::{closure#0}>                                                                                                                 
  37:     0x7f1e2debd720 - std[71a32ca0600a6a04]::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface[c2b70c9b1dae0906]::util::run_in_thread_pool_with_globals<rustc_interface[c2b70c9b1dae0906]::interface::run_compiler<core[cc19a662f3570270]:
:result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>, rustc_driver_impl[d30cd2737d9d343a]::run_compiler::{closure#1}>::{closure#0}, core[cc19a662f3570270]::result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>::{closure#0}::{closure
#0}, core[cc19a662f3570270]::result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>                                                                                                                                                                    
  38:     0x7f1e2debd041 - <<std[71a32ca0600a6a04]::thread::Builder>::spawn_unchecked_<rustc_interface[c2b70c9b1dae0906]::util::run_in_thread_pool_with_globals<rustc_interface[c2b70c9b1dae0906]::interface::run_compiler<core[cc19a662f3570270]::result::Result<(
), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>, rustc_driver_impl[d30cd2737d9d343a]::run_compiler::{closure#1}>::{closure#0}, core[cc19a662f3570270]::result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[cc19a66
2f3570270]::result::Result<(), rustc_span[9551eaa044f53f4f]::ErrorGuaranteed>>::{closure#1} as core[cc19a662f3570270]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}                                                                                      
  39:     0x7f1e2ba733e5 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hf15d802f31f86225                                                                                                                                             
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/alloc/src/boxed.rs:1962:9                                                                                                                                                
  40:     0x7f1e2ba733e5 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hac564355b46c52d6                                                                                                                                             
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/alloc/src/boxed.rs:1962:9                                                                                                                                                
  41:     0x7f1e2ba733e5 - std::sys::unix::thread::Thread::new::thread_start::h86fb3aedb7811f07                                                                                                                                                                    
                               at /rustc/7f94b314cead7059a71a265a8b64905ef2511796/library/std/src/sys/unix/thread.rs:108:17                                                                                                                                        
  42:     0x7f1e2b92a609 - start_thread                                                                                                                                                                                                                            
                               at /build/glibc-SzIz7B/glibc-2.31/nptl/pthread_create.c:477:8                                                                                                                                                                       
  43:     0x7f1e2b84d133 - clone                                                                                                                                                                                                                                   
                               at /build/glibc-SzIz7B/glibc-2.31/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:95                                                                                                                                                 
  44:                0x0 - <unknown>                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                   
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md                                                                                                                           
                                                                                                                                                                                                                                                                   
note: rustc 1.71.0-nightly (7f94b314c 2023-04-23) running on x86_64-unknown-linux-gnu                                                                                                                                                                              
                                                                                                                                                                                                                                                                   
note: compiler flags: --crate-type lib -C opt-level=3 -C embed-bitcode=no                                                                                                                                                                                          
                                                                                                                                                                                                                                                                   
note: some of the compiler flags provided by cargo are hidden                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                   
query stack during panic:                                                                                                                                                                                                                                          
#0 [optimized_mir] optimizing MIR for `convenience::<impl at /home/shou/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_traitobject-0.2.8/src/convenience.rs:159:1: 159:51>::call_mut`                                                                  
end of query stack                                                                                                                                                                                                                                                 

Is this secure?

I just looked through the code a bit, and I was amazed when I saw that you're bypassing the need to register traits by actually making a virtual function call into the vtable pointer. I wouldn't have thought of that. My question is, is this secure, and is it safe?

It seems like attempting to deserialize a bad vtable pointer, intentionally or unintentionally, could allow the execution of arbitrary memory regions, which seems like both a massive security flaw, and unsafe in that it could cause undefined behavior. Am I wrong about this?

It seems like, unless there's some verification that I missed, this needs to be addressed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.