Skip to content

Commit a74972c

Browse files
committed
Rework Module deserialize methods
Rework the deserialize/deserialize_checked methods for module deserialization. * Make deserialize() methods use artifact validation * Make checked methods unsafe again, because loading executable memory is inherently unsafe * Rename methods that skip artifact validation to deserialize_unchecked Closes #3727
1 parent a8bddf7 commit a74972c

File tree

12 files changed

+138
-70
lines changed

12 files changed

+138
-70
lines changed

lib/api/src/engine.rs

+39-6
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,57 @@ impl Engine {
135135
))
136136
}
137137

138-
#[deprecated(since = "3.2.0")]
139138
#[cfg(all(feature = "sys", not(target_arch = "wasm32")))]
140-
/// Deserializes a WebAssembly module
139+
/// Deserializes a WebAssembly module which was previously serialized with
140+
/// `Module::serialize`.
141+
///
142+
/// NOTE: you should almost always prefer [`Self::deserialize`].
141143
///
142144
/// # Safety
145+
/// See [`Artifact::deserialize_unchecked`].
146+
pub unsafe fn deserialize_unchecked(
147+
&self,
148+
bytes: &[u8],
149+
) -> Result<Arc<Artifact>, DeserializeError> {
150+
Ok(Arc::new(Artifact::deserialize_unchecked(&self.0, bytes)?))
151+
}
152+
153+
#[cfg(all(feature = "sys", not(target_arch = "wasm32")))]
154+
/// Deserializes a WebAssembly module which was previously serialized with
155+
/// `Module::serialize`.
143156
///
144-
/// The serialized content must represent a serialized WebAssembly module.
157+
/// # Safety
158+
/// See [`Artifact::deserialize`].
145159
pub unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<Artifact>, DeserializeError> {
146160
Ok(Arc::new(Artifact::deserialize(&self.0, bytes)?))
147161
}
148162

149-
#[deprecated(since = "3.2.0")]
150163
#[cfg(all(feature = "sys", not(target_arch = "wasm32")))]
151-
/// Deserializes a WebAssembly module from a path
164+
/// Load a serialized WebAssembly module from a file and deserialize it.
165+
///
166+
/// NOTE: you should almost always prefer [`Self::deserialize_from_file`].
152167
///
153168
/// # Safety
169+
/// See [`Artifact::deserialize_unchecked`].
170+
pub unsafe fn deserialize_from_file_unchecked(
171+
&self,
172+
file_ref: &Path,
173+
) -> Result<Arc<Artifact>, DeserializeError> {
174+
let mut file = std::fs::File::open(file_ref)?;
175+
let mut buffer = Vec::new();
176+
// read the whole file
177+
file.read_to_end(&mut buffer)?;
178+
Ok(Arc::new(Artifact::deserialize_unchecked(
179+
&self.0,
180+
buffer.as_slice(),
181+
)?))
182+
}
183+
184+
#[cfg(all(feature = "sys", not(target_arch = "wasm32")))]
185+
/// Load a serialized WebAssembly module from a file and deserialize it.
154186
///
155-
/// The file's content must represent a serialized WebAssembly module.
187+
/// # Safety
188+
/// See [`Artifact::deserialize`].
156189
pub unsafe fn deserialize_from_file(
157190
&self,
158191
file_ref: &Path,

lib/api/src/js/module.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ impl Module {
227227
));
228228
}
229229

230-
pub unsafe fn deserialize(
230+
pub unsafe fn deserialize_unchecked(
231231
_engine: &impl AsEngineRef,
232232
_bytes: impl IntoBytes,
233233
) -> Result<Self, DeserializeError> {
@@ -239,7 +239,7 @@ impl Module {
239239
return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string()));
240240
}
241241

242-
pub fn deserialize_checked(
242+
pub unsafe fn deserialize(
243243
_engine: &impl AsEngineRef,
244244
_bytes: impl IntoBytes,
245245
) -> Result<Self, DeserializeError> {
@@ -251,20 +251,20 @@ impl Module {
251251
return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string()));
252252
}
253253

254-
pub unsafe fn deserialize_from_file(
254+
pub unsafe fn deserialize_from_file_unchecked(
255255
engine: &impl AsEngineRef,
256256
path: impl AsRef<Path>,
257257
) -> Result<Self, DeserializeError> {
258258
let bytes = std::fs::read(path.as_ref())?;
259259
Self::deserialize(engine, bytes)
260260
}
261261

262-
pub fn deserialize_from_file_checked(
262+
pub unsafe fn deserialize_from_file(
263263
engine: &impl AsEngineRef,
264264
path: impl AsRef<Path>,
265265
) -> Result<Self, DeserializeError> {
266266
let bytes = std::fs::read(path.as_ref())?;
267-
Self::deserialize_checked(engine, bytes)
267+
Self::deserialize(engine, bytes)
268268
}
269269

270270
pub fn set_name(&mut self, name: &str) -> bool {

lib/api/src/jsc/module.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -183,35 +183,35 @@ impl Module {
183183
));
184184
}
185185

186-
pub unsafe fn deserialize(
186+
pub unsafe fn deserialize_unchecked(
187187
_engine: &impl AsEngineRef,
188188
_bytes: impl IntoBytes,
189189
) -> Result<Self, DeserializeError> {
190190
return Self::from_binary(_engine, &_bytes.into_bytes())
191191
.map_err(|e| DeserializeError::Compiler(e));
192192
}
193193

194-
pub fn deserialize_checked(
194+
pub unsafe fn deserialize(
195195
_engine: &impl AsEngineRef,
196196
_bytes: impl IntoBytes,
197197
) -> Result<Self, DeserializeError> {
198198
unimplemented!();
199199
}
200200

201-
pub unsafe fn deserialize_from_file(
201+
pub unsafe fn deserialize_from_file_unchecked(
202202
engine: &impl AsEngineRef,
203203
path: impl AsRef<Path>,
204204
) -> Result<Self, DeserializeError> {
205205
let bytes = std::fs::read(path.as_ref())?;
206-
Self::deserialize(engine, bytes)
206+
Self::deserialize_unchecked(engine, bytes)
207207
}
208208

209-
pub fn deserialize_from_file_checked(
209+
pub unsafe fn deserialize_from_file(
210210
engine: &impl AsEngineRef,
211211
path: impl AsRef<Path>,
212212
) -> Result<Self, DeserializeError> {
213213
let bytes = std::fs::read(path.as_ref())?;
214-
Self::deserialize_checked(engine, bytes)
214+
Self::deserialize(engine, bytes)
215215
}
216216

217217
pub fn set_name(&mut self, name: &str) -> bool {

lib/api/src/module.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ impl Module {
219219
Ok(())
220220
}
221221

222-
/// Deserializes a serialized Module binary into a `Module`.
222+
/// Deserializes a serialized module binary into a `Module`.
223223
///
224-
/// Note: You should usually prefer the safe [`Module::deserialize_checked`].
224+
/// Note: You should usually prefer the safer [`Module::deserialize`].
225225
///
226226
/// # Important
227227
///
@@ -250,11 +250,13 @@ impl Module {
250250
/// # Ok(())
251251
/// # }
252252
/// ```
253-
pub unsafe fn deserialize(
253+
pub unsafe fn deserialize_unchecked(
254254
engine: &impl AsEngineRef,
255255
bytes: impl IntoBytes,
256256
) -> Result<Self, DeserializeError> {
257-
Ok(Self(module_imp::Module::deserialize(engine, bytes)?))
257+
Ok(Self(module_imp::Module::deserialize_unchecked(
258+
engine, bytes,
259+
)?))
258260
}
259261

260262
/// Deserializes a serialized Module binary into a `Module`.
@@ -272,17 +274,21 @@ impl Module {
272274
/// # use wasmer::*;
273275
/// # fn main() -> anyhow::Result<()> {
274276
/// # let mut store = Store::default();
275-
/// let module = Module::deserialize_checked(&store, serialized_data)?;
277+
/// let module = Module::deserialize(&store, serialized_data)?;
276278
/// # Ok(())
277279
/// # }
278280
/// ```
279-
pub fn deserialize_checked(
281+
///
282+
/// # Safety
283+
/// This function is inherently **unsafe**, because it loads executable code
284+
/// into memory.
285+
/// The loaded bytes must be trusted to contain a valid artifact previously
286+
/// built with [`Self::serialize`].
287+
pub unsafe fn deserialize(
280288
engine: &impl AsEngineRef,
281289
bytes: impl IntoBytes,
282290
) -> Result<Self, DeserializeError> {
283-
Ok(Self(module_imp::Module::deserialize_checked(
284-
engine, bytes,
285-
)?))
291+
Ok(Self(module_imp::Module::deserialize(engine, bytes)?))
286292
}
287293

288294
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
@@ -298,37 +304,43 @@ impl Module {
298304
/// # Ok(())
299305
/// # }
300306
/// ```
301-
pub fn deserialize_from_file_checked(
307+
///
308+
/// # Safety
309+
///
310+
/// See [`Self::deserialize`].
311+
pub unsafe fn deserialize_from_file(
302312
engine: &impl AsEngineRef,
303313
path: impl AsRef<Path>,
304314
) -> Result<Self, DeserializeError> {
305-
Ok(Self(module_imp::Module::deserialize_from_file_checked(
315+
Ok(Self(module_imp::Module::deserialize_from_file(
306316
engine, path,
307317
)?))
308318
}
309319

310320
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
311321
/// > Note: the module has to be serialized before with the `serialize` method.
312322
///
323+
/// You should usually prefer the safer [`Module::deserialize_from_file`].
324+
///
313325
/// # Safety
314326
///
315-
/// Please check [`Module::deserialize`].
327+
/// Please check [`Module::deserialize_unchecked`].
316328
///
317329
/// # Usage
318330
///
319331
/// ```ignore
320332
/// # use wasmer::*;
321333
/// # let mut store = Store::default();
322334
/// # fn main() -> anyhow::Result<()> {
323-
/// let module = Module::deserialize_from_file(&store, path)?;
335+
/// let module = Module::deserialize_from_file_unchecked(&store, path)?;
324336
/// # Ok(())
325337
/// # }
326338
/// ```
327-
pub unsafe fn deserialize_from_file(
339+
pub unsafe fn deserialize_from_file_unchecked(
328340
engine: &impl AsEngineRef,
329341
path: impl AsRef<Path>,
330342
) -> Result<Self, DeserializeError> {
331-
Ok(Self(module_imp::Module::deserialize_from_file(
343+
Ok(Self(module_imp::Module::deserialize_from_file_unchecked(
332344
engine, path,
333345
)?))
334346
}

lib/api/src/sys/module.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl Module {
6969
self.artifact.serialize().map(|bytes| bytes.into())
7070
}
7171

72-
pub unsafe fn deserialize(
72+
pub unsafe fn deserialize_unchecked(
7373
engine: &impl AsEngineRef,
7474
bytes: impl IntoBytes,
7575
) -> Result<Self, DeserializeError> {
@@ -78,20 +78,16 @@ impl Module {
7878
Ok(Self::from_artifact(artifact))
7979
}
8080

81-
pub fn deserialize_checked(
81+
pub unsafe fn deserialize(
8282
engine: &impl AsEngineRef,
8383
bytes: impl IntoBytes,
8484
) -> Result<Self, DeserializeError> {
8585
let bytes = bytes.into_bytes();
86-
let artifact = engine
87-
.as_engine_ref()
88-
.engine()
89-
.0
90-
.deserialize_checked(&bytes)?;
86+
let artifact = engine.as_engine_ref().engine().0.deserialize(&bytes)?;
9187
Ok(Self::from_artifact(artifact))
9288
}
9389

94-
pub unsafe fn deserialize_from_file(
90+
pub unsafe fn deserialize_from_file_unchecked(
9591
engine: &impl AsEngineRef,
9692
path: impl AsRef<Path>,
9793
) -> Result<Self, DeserializeError> {
@@ -103,15 +99,15 @@ impl Module {
10399
Ok(Self::from_artifact(artifact))
104100
}
105101

106-
pub fn deserialize_from_file_checked(
102+
pub unsafe fn deserialize_from_file(
107103
engine: &impl AsEngineRef,
108104
path: impl AsRef<Path>,
109105
) -> Result<Self, DeserializeError> {
110106
let artifact = engine
111107
.as_engine_ref()
112108
.engine()
113109
.0
114-
.deserialize_from_file_checked(path.as_ref())?;
110+
.deserialize_from_file(path.as_ref())?;
115111
Ok(Self::from_artifact(artifact))
116112
}
117113

lib/cache/src/filesystem.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl Cache for FileSystemCache {
103103
key.to_string()
104104
};
105105
let path = self.path.join(filename);
106-
let ret = Module::deserialize_from_file_checked(engine, path.clone());
106+
let ret = Module::deserialize_from_file(engine, path.clone());
107107
if ret.is_err() {
108108
// If an error occurs while deserializing then we can not trust it anymore
109109
// so delete the cache file

lib/cli/src/commands/run.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ impl RunWithPathBuf {
472472
if wasmer_compiler::Artifact::is_deserializable(&contents) {
473473
let engine = wasmer_compiler::EngineBuilder::headless();
474474
let store = Store::new(engine);
475-
let module = Module::deserialize_from_file_checked(&store, &self.path)?;
475+
let module = unsafe { Module::deserialize_from_file(&store, &self.path)? };
476476
return Ok((store, module));
477477
}
478478
let (store, compiler_type) = self.store.get_store()?;

lib/compiler/src/engine/artifact.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,15 @@ impl Artifact {
143143
))
144144
}
145145

146-
/// Deserialize a ArtifactBuild
146+
/// Deserialize a serialized artifact.
147147
///
148148
/// # Safety
149-
/// This function is unsafe because rkyv reads directly without validating
150-
/// the data.
151-
pub fn deserialize_checked(engine: &Engine, bytes: &[u8]) -> Result<Self, DeserializeError> {
149+
/// This function loads executable code into memory.
150+
/// You must trust the loaded bytes to be valid for the chosen engine and
151+
/// for the host CPU architecture.
152+
/// In contrast to [`Self::deserialize_unchecked`] the artifact layout is
153+
/// validated, which increases safety.
154+
pub unsafe fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<Self, DeserializeError> {
152155
if !ArtifactBuild::is_deserializable(bytes) {
153156
return Err(DeserializeError::Incompatible(
154157
"Magic header not found".to_string(),
@@ -161,18 +164,25 @@ impl Artifact {
161164
let metadata_slice = Self::get_byte_slice(bytes, MetadataHeader::LEN, bytes.len())?;
162165
let metadata_slice = Self::get_byte_slice(metadata_slice, 0, metadata_len)?;
163166

164-
let serializable = SerializableModule::deserialize_checked(metadata_slice)?;
167+
let serializable = SerializableModule::deserialize(metadata_slice)?;
165168
let artifact = ArtifactBuild::from_serializable(serializable);
166169
let mut inner_engine = engine.inner_mut();
167170
Self::from_parts(&mut inner_engine, artifact, engine.target())
168171
.map_err(DeserializeError::Compiler)
169172
}
170173

171-
/// Deserialize a ArtifactBuild
174+
/// Deserialize a serialized artifact.
175+
///
176+
/// NOTE: You should prefer [`Self::deserialize`].
172177
///
173178
/// # Safety
174-
/// This function is unsafe because rkyv reads directly without validating the data.
175-
pub unsafe fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<Self, DeserializeError> {
179+
/// See [`Self::deserialize`].
180+
/// In contrast to the above, this function skips artifact layout validation,
181+
/// which increases the risk of loading invalid artifacts.
182+
pub unsafe fn deserialize_unchecked(
183+
engine: &Engine,
184+
bytes: &[u8],
185+
) -> Result<Self, DeserializeError> {
176186
if !ArtifactBuild::is_deserializable(bytes) {
177187
let static_artifact = Self::deserialize_object(engine, bytes);
178188
match static_artifact {

0 commit comments

Comments
 (0)