18次发布
0.2.8 | 2022年11月25日 |
---|---|
0.2.7 | 2020年7月14日 |
0.2.6 | 2020年6月25日 |
0.2.4 | 2019年11月24日 |
0.1.0 | 2018年7月23日 |
#806 in 编码
每月422次下载
用于 9 个crate(直接使用5个)
62KB
1K SLoC
serde_traitobject
可序列化和反序列化的特例对象。
该库实现了特例对象的序列化和反序列化,以便它们可以在运行相同二进制文件的其他进程之间传输。
例如,如果您有多个进程的分支,或者集群中每台机器上运行相同的二进制文件,该库允许您在它们之间传输特例对象。
任何特例都可以通过将其作为超特例添加此crate的#[serde)]
和 Deserialize 特性,使其可序列化和反序列化
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!
就是这样!这两个特性自动应用于所有 T: serde::Serialize
和所有 T: serde::de::DeserializeOwned
,只要您的特例的所有实现者本身都是可序列化的,那么您就可以开始了。
有两种方式来(反)序列化您的特例对象
- 应用
#[serde)]
字段属性,它指示serde使用此crate的 serialize 和 deserialize 函数; - Box、Rc 和 Arc 结构体,它们是它们stdlib对应结构的简单包装,能够自动处理序列化和反序列化,无需上述注解;
此外,还实现了几个方便的特质,它们扩展了它们的stdlib对应结构
这些特质会自动实现所有同时实现 serde::Serialize
和 serde::de::DeserializeOwned
的stdlib对应结构的实现者。
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 }
安全性
此crate通过使用relative::Vtable
包装vtable指针,使其可以在进程间安全地传递。
这种方法目前还不能抵御恶意行为者。然而,如果我们假设非恶意行为者和典型的(静态或动态)链接条件,那么将其视为合理的也是可以的。
有关在rustc
中进行的使这一方法安全的工作,请参阅此处。
验证
为了验证目的,随vtable指针序列化了三件事情
- 构建ID(128位);
- 特质对象的
type_id
(64位); - 具体类型的
type_id
(64位)。
在Rust的未来某个时刻,我认为如果能够使用后者安全地查找和创建特质对象将非常棒。但目前,该功能尚不存在,因此这个crate所做的是序列化vtable指针(相对于静态基),并在它被使用和可能引发UB之前进行尽可能多的有效性检查。
前两项在使用vtable指针之前会进行有效性检查。构建ID确保vtable指针来自具有相同布局的二进制文件的调用1。类型ID确保正在反序列化的特质对象与序列化的特质对象相同类型。它们确保在非恶意条件下,尝试反序列化无效数据会返回错误而不是UB。具体类型的类型ID用作健康检查,如果它与要反序列化的具体类型的类型ID不同,则会导致panic。
关于冲突,128位的构建ID发生冲突的可能性极低,可以信赖它永远不会发生。64位的类型ID发生冲突是可能的,参见rust-lang/rust#10389,但在实践中发生的可能性极低。
vtable指针作为相对于此静态特质对象的usize进行(反)序列化。这使得它可以在典型的动态链接条件下工作,在这种情况下,相同的二进制文件的绝对vtable地址可能因调用而异,但相对地址保持不变。
总的来说,据我所知,存在三个安全漏洞。
- 一个恶意用户拥有二进制文件的副本,可以轻易地构造一个通过验证的
build_id
和type_id
,从而控制跳转位置。 - 序列化vtable指针的数据损坏,但不是用于验证的
build_id
或type_id
,导致跳转到任意地址。未来版本可以通过使用密码来修复,使得损坏只影响vtable指针的可能性极小,通过在序列化和反序列化时混合vtable指针和验证组件。 - 动态链接条件,其中相对地址(vtable - 静态vtable)在不同二进制文件的调用之间不同。我确信这是可能的,但我没有遇到过这种情况,因此无法评论其普遍性。
1我认为这个要求并不严格必要,因为 type_id
应该包含所有可能影响安全性的信息(特质方法、调用约定等),但包含它是为了以防万一;提供更有用的错误信息;并降低冲突的可能性。
注意
该crate目前需要Rust nightly版。
许可协议
许可协议为以下之一
- Apache License,版本2.0,(LICENSE-APACHE.txt 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可协议 (LICENSE-MIT.txt 或 http://opensource.org/licenses/MIT)
任选其一。
除非你明确表示,否则根据 Apache-2.0 许可协议定义的,你提交的任何旨在包含在作品中的贡献,都应如上双许可,不附加任何额外条款或条件。
依赖
~0.8–1.7MB
~31K SLoC