rkyv/validation/shared/
validator.rs1use core::{any::TypeId, error::Error, fmt, hash::BuildHasherDefault};
5#[cfg(feature = "std")]
6use std::collections::hash_map;
7
8#[cfg(not(feature = "std"))]
9use hashbrown::hash_map;
10use rancor::{fail, Source};
11
12use crate::{
13 erased::{ErasedPtr, Metadata},
14 hash::FxHasher64,
15 validation::{shared::ValidationState, SharedContext},
16};
17
18#[derive(Debug)]
19struct SharedValidationState {
20 type_id: TypeId,
21 metadata: Metadata,
22 is_finished: bool,
23}
24
25#[derive(Debug, Default)]
27pub struct SharedValidator {
28 shared: hash_map::HashMap<
29 usize,
30 SharedValidationState,
31 BuildHasherDefault<FxHasher64>,
32 >,
33}
34
35impl SharedValidator {
36 #[inline]
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 #[inline]
44 pub fn with_capacity(capacity: usize) -> Self {
45 Self {
46 shared: hash_map::HashMap::with_capacity_and_hasher(
47 capacity,
48 Default::default(),
49 ),
50 }
51 }
52}
53
54#[derive(Debug)]
55struct TypeMismatch {
56 previous: TypeId,
57 current: TypeId,
58}
59
60impl fmt::Display for TypeMismatch {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(
63 f,
64 "the same memory region has been claimed as two different types: \
65 {:?} and {:?}",
66 self.previous, self.current,
67 )
68 }
69}
70
71impl Error for TypeMismatch {}
72
73#[derive(Debug)]
74struct MetadataMismatch;
75
76impl fmt::Display for MetadataMismatch {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(
79 f,
80 "the same memory region has been claimed as the same type with
81 different pointer metadata (e.g. slice length)",
82 )
83 }
84}
85
86impl Error for MetadataMismatch {}
87
88#[derive(Debug)]
89struct NotStarted;
90
91impl fmt::Display for NotStarted {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "shared pointer was not started validation")
94 }
95}
96
97impl Error for NotStarted {}
98
99#[derive(Debug)]
100struct AlreadyFinished;
101
102impl fmt::Display for AlreadyFinished {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "shared pointer was already finished validation")
105 }
106}
107
108impl Error for AlreadyFinished {}
109
110impl<E: Source> SharedContext<E> for SharedValidator {
111 fn start_shared(
112 &mut self,
113 shared_type_id: TypeId,
114 ptr: ErasedPtr,
115 metadata_is_eq: unsafe fn(Metadata, Metadata) -> bool,
116 ) -> Result<ValidationState, E> {
117 match self.shared.entry(ptr.data_address() as usize) {
118 hash_map::Entry::Vacant(vacant) => {
119 vacant.insert(SharedValidationState {
120 type_id: shared_type_id,
121 metadata: ptr.metadata(),
122 is_finished: false,
123 });
124 Ok(ValidationState::Started)
125 }
126 hash_map::Entry::Occupied(occupied) => {
127 let state = occupied.get();
128 if state.type_id != shared_type_id {
129 fail!(TypeMismatch {
130 previous: state.type_id,
131 current: shared_type_id,
132 });
133 }
134
135 let is_same_metadata =
136 unsafe { metadata_is_eq(ptr.metadata(), state.metadata) };
137 if !is_same_metadata {
138 fail!(MetadataMismatch);
139 }
140
141 if !state.is_finished {
142 Ok(ValidationState::Pending)
143 } else {
144 Ok(ValidationState::Finished)
145 }
146 }
147 }
148 }
149
150 fn finish_shared(
151 &mut self,
152 _shared_type_id: TypeId,
153 ptr: ErasedPtr,
154 ) -> Result<(), E> {
155 match self.shared.entry(ptr.data_address() as usize) {
156 hash_map::Entry::Vacant(_) => fail!(NotStarted),
157 hash_map::Entry::Occupied(mut occupied) => {
158 let state = occupied.get_mut();
159
160 if state.is_finished {
161 fail!(AlreadyFinished);
162 }
163
164 state.is_finished = true;
165 Ok(())
166 }
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 #[cfg(not(any(
174 feature = "pointer_width_16",
175 feature = "pointer_width_64"
176 )))]
177 #[test]
178 fn conflicting_metadata() {
179 use rancor::Error;
180
181 use crate::{
182 alloc::rc::Rc, api::high::access, util::Align, Archive, Serialize,
183 };
184
185 #[expect(dead_code)]
186 #[derive(Archive, Serialize)]
187 #[rkyv(crate, derive(Debug))]
188 struct Test {
189 a: Rc<[u8]>,
190 b: Rc<[u8]>,
191 }
192
193 let synthetic_buf = Align([
195 1u8, 2u8, 3u8, 4u8, 0xfc, 0xff, 0xff, 0xff, 4u8, 0u8, 0u8, 0u8, 0xf4, 0xff, 0xff, 0xff, 2u8, 0u8, 0u8, 0u8, ]);
203
204 let result = access::<ArchivedTest, Error>(&*synthetic_buf);
205 assert_source!(result.unwrap_err(), super::MetadataMismatch);
206 }
207}