1use alloc::vec::Vec;
10
11use crate::binary::leb128::{self, Cursor};
12use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[repr(u8)]
18pub enum SectionId {
19 Custom = 0,
20 Type = 1,
21 Import = 2,
22 Function = 3,
23 Table = 4,
24 Memory = 5,
25 Global = 6,
26 Export = 7,
27 Start = 8,
28 Element = 9,
29 Code = 10,
30 Data = 11,
31 DataCount = 12,
32}
33
34impl SectionId {
35 pub fn from_byte(byte: u8) -> Option<Self> {
37 match byte {
38 0 => Some(SectionId::Custom),
39 1 => Some(SectionId::Type),
40 2 => Some(SectionId::Import),
41 3 => Some(SectionId::Function),
42 4 => Some(SectionId::Table),
43 5 => Some(SectionId::Memory),
44 6 => Some(SectionId::Global),
45 7 => Some(SectionId::Export),
46 8 => Some(SectionId::Start),
47 9 => Some(SectionId::Element),
48 10 => Some(SectionId::Code),
49 11 => Some(SectionId::Data),
50 12 => Some(SectionId::DataCount),
51 _ => None,
52 }
53 }
54
55 pub fn name(self) -> &'static str {
57 match self {
58 SectionId::Custom => "custom",
59 SectionId::Type => "type",
60 SectionId::Import => "import",
61 SectionId::Function => "function",
62 SectionId::Table => "table",
63 SectionId::Memory => "memory",
64 SectionId::Global => "global",
65 SectionId::Export => "export",
66 SectionId::Start => "start",
67 SectionId::Element => "element",
68 SectionId::Code => "code",
69 SectionId::Data => "data",
70 SectionId::DataCount => "datacount",
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct RawSection<'a> {
78 pub id: SectionId,
80 pub offset: usize,
82 pub data: &'a [u8],
84}
85
86const WASM_MAGIC: [u8; 4] = [0x00, 0x61, 0x73, 0x6D];
88
89const WASM_VERSION: [u8; 4] = [0x01, 0x00, 0x00, 0x00];
91
92pub fn parse_preamble<'a>(cursor: &mut Cursor<'a>) -> Result<(), DecodeError> {
95 let magic = cursor.read_bytes(4).map_err(|_| DecodeError {
96 offset: ByteOffset(0),
97 context: DecodeContext::Magic,
98 kind: DecodeErrorKind::UnexpectedEof,
99 })?;
100
101 if magic != WASM_MAGIC {
102 return Err(DecodeError {
103 offset: ByteOffset(0),
104 context: DecodeContext::Magic,
105 kind: DecodeErrorKind::InvalidMagic,
106 });
107 }
108
109 let version = cursor.read_bytes(4).map_err(|_| DecodeError {
110 offset: ByteOffset(4),
111 context: DecodeContext::Version,
112 kind: DecodeErrorKind::UnexpectedEof,
113 })?;
114
115 if version != WASM_VERSION {
116 let found = u32::from_le_bytes([version[0], version[1], version[2], version[3]]);
117 return Err(DecodeError {
118 offset: ByteOffset(4),
119 context: DecodeContext::Version,
120 kind: DecodeErrorKind::UnsupportedVersion { found },
121 });
122 }
123
124 Ok(())
125}
126
127fn section_order(id: SectionId) -> u8 {
132 match id {
133 SectionId::Custom => 0,
134 SectionId::Type => 1,
135 SectionId::Import => 2,
136 SectionId::Function => 3,
137 SectionId::Table => 4,
138 SectionId::Memory => 5,
139 SectionId::Global => 6,
140 SectionId::Export => 7,
141 SectionId::Start => 8,
142 SectionId::Element => 9,
143 SectionId::Code => 10,
144 SectionId::Data => 11,
145 SectionId::DataCount => 12,
146 }
147}
148
149fn sections_in_valid_order(prev: SectionId, current: SectionId) -> bool {
150 let prev_order = section_order(prev);
151 let current_order = section_order(current);
152
153 current_order >= prev_order
154 || matches!(
155 (prev, current),
156 (SectionId::Element, SectionId::DataCount)
157 | (SectionId::DataCount, SectionId::Code)
158 | (SectionId::Data, SectionId::DataCount)
159 )
160}
161
162fn validate_custom_section_name(data: &[u8], base_offset: usize) -> Result<(), DecodeError> {
163 let mut cursor = Cursor::new(data);
164 let name_len = leb128::decode_u32(&mut cursor).map_err(|mut e| {
165 e.context = DecodeContext::SectionBody { id: 0 };
166 e.offset = ByteOffset(base_offset + e.offset.0);
167 e
168 })? as usize;
169
170 let name_offset = cursor.position();
171 let name = cursor.read_bytes(name_len).map_err(|_| DecodeError {
172 offset: ByteOffset(base_offset + name_offset),
173 context: DecodeContext::SectionBody { id: 0 },
174 kind: DecodeErrorKind::UnexpectedEof,
175 })?;
176
177 core::str::from_utf8(name).map_err(|_| DecodeError {
178 offset: ByteOffset(base_offset + name_offset),
179 context: DecodeContext::SectionBody { id: 0 },
180 kind: DecodeErrorKind::InvalidUtf8,
181 })?;
182
183 Ok(())
184}
185
186pub fn parse_sections<'a>(cursor: &mut Cursor<'a>) -> Result<Vec<RawSection<'a>>, DecodeError> {
187 let mut sections = Vec::new();
188 let mut last_non_custom: Option<(u8, u8)> = None;
189
190 while !cursor.is_empty() {
191 let id_offset = cursor.position();
192 let id_byte = cursor.read_byte().map_err(|_| DecodeError {
193 offset: ByteOffset(id_offset),
194 context: DecodeContext::SectionHeader,
195 kind: DecodeErrorKind::UnexpectedEof,
196 })?;
197
198 let id = SectionId::from_byte(id_byte).ok_or(DecodeError {
199 offset: ByteOffset(id_offset),
200 context: DecodeContext::SectionHeader,
201 kind: DecodeErrorKind::UnknownSectionId { id: id_byte },
202 })?;
203
204 let size = leb128::decode_u32(cursor).map_err(|mut e| {
205 e.context = DecodeContext::SectionHeader;
206 e
207 })?;
208
209 let content_offset = cursor.position();
210
211 if content_offset + size as usize > cursor.position() + cursor.remaining().len() {
212 return Err(DecodeError {
213 offset: ByteOffset(id_offset),
214 context: DecodeContext::SectionHeader,
215 kind: DecodeErrorKind::SectionOverflow,
216 });
217 }
218
219 if id != SectionId::Custom {
222 let current_order = section_order(id);
223 if let Some((prev_id, _prev_order)) = last_non_custom {
224 if id_byte == prev_id {
225 return Err(DecodeError {
226 offset: ByteOffset(id_offset),
227 context: DecodeContext::SectionHeader,
228 kind: DecodeErrorKind::DuplicateSection { id: id_byte },
229 });
230 }
231 if !sections_in_valid_order(
232 SectionId::from_byte(prev_id).expect("known section id"),
233 id,
234 ) {
235 return Err(DecodeError {
236 offset: ByteOffset(id_offset),
237 context: DecodeContext::SectionHeader,
238 kind: DecodeErrorKind::SectionOutOfOrder {
239 prev: prev_id,
240 current: id_byte,
241 },
242 });
243 }
244 }
245 last_non_custom = Some((id_byte, current_order));
246 }
247
248 let data = cursor.read_bytes(size as usize).map_err(|_| DecodeError {
249 offset: ByteOffset(content_offset),
250 context: DecodeContext::SectionBody { id: id_byte },
251 kind: DecodeErrorKind::SectionOverflow,
252 })?;
253
254 if id == SectionId::Custom {
255 validate_custom_section_name(data, content_offset)?;
256 }
257
258 sections.push(RawSection {
259 id,
260 offset: content_offset,
261 data,
262 });
263 }
264
265 Ok(sections)
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 const MINIMAL_MODULE: [u8; 8] = [
274 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
277
278 #[test]
279 fn parse_minimal_module_preamble() {
280 let mut cursor = Cursor::new(&MINIMAL_MODULE);
281 parse_preamble(&mut cursor).unwrap();
282 assert!(cursor.is_empty());
283 }
284
285 #[test]
286 fn reject_bad_magic() {
287 let data = [0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
288 let mut cursor = Cursor::new(&data);
289 let err = parse_preamble(&mut cursor).unwrap_err();
290 assert_eq!(err.kind, DecodeErrorKind::InvalidMagic);
291 }
292
293 #[test]
294 fn reject_bad_version() {
295 let data = [0x00, 0x61, 0x73, 0x6D, 0x02, 0x00, 0x00, 0x00];
296 let mut cursor = Cursor::new(&data);
297 let err = parse_preamble(&mut cursor).unwrap_err();
298 assert!(matches!(
299 err.kind,
300 DecodeErrorKind::UnsupportedVersion { found: 2 }
301 ));
302 }
303
304 #[test]
305 fn parse_empty_sections() {
306 let mut cursor = Cursor::new(&MINIMAL_MODULE);
307 parse_preamble(&mut cursor).unwrap();
308 let sections = parse_sections(&mut cursor).unwrap();
309 assert!(sections.is_empty());
310 }
311
312 #[test]
313 fn parse_single_type_section() {
314 let data = [
316 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0xAA, 0xBB, ];
322 let mut cursor = Cursor::new(&data);
323 parse_preamble(&mut cursor).unwrap();
324 let sections = parse_sections(&mut cursor).unwrap();
325
326 assert_eq!(sections.len(), 1);
327 assert_eq!(sections[0].id, SectionId::Type);
328 assert_eq!(sections[0].data, &[0xAA, 0xBB]);
329 }
330
331 #[test]
332 fn parse_multiple_sections_in_order() {
333 let data = [
334 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x03, 0x01, 0xEE, 0x07, 0x01, 0xDD, ];
340 let mut cursor = Cursor::new(&data);
341 parse_preamble(&mut cursor).unwrap();
342 let sections = parse_sections(&mut cursor).unwrap();
343
344 assert_eq!(sections.len(), 3);
345 assert_eq!(sections[0].id, SectionId::Type);
346 assert_eq!(sections[1].id, SectionId::Function);
347 assert_eq!(sections[2].id, SectionId::Export);
348 }
349
350 #[test]
351 fn reject_duplicate_section() {
352 let data = [
353 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x01, 0x01, 0xEE, ];
358 let mut cursor = Cursor::new(&data);
359 parse_preamble(&mut cursor).unwrap();
360 let err = parse_sections(&mut cursor).unwrap_err();
361 assert!(matches!(
362 err.kind,
363 DecodeErrorKind::DuplicateSection { id: 1 }
364 ));
365 }
366
367 #[test]
368 fn reject_out_of_order_sections() {
369 let data = [
370 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x03, 0x01, 0xFF, 0x01, 0x01, 0xEE, ];
375 let mut cursor = Cursor::new(&data);
376 parse_preamble(&mut cursor).unwrap();
377 let err = parse_sections(&mut cursor).unwrap_err();
378 assert!(matches!(
379 err.kind,
380 DecodeErrorKind::SectionOutOfOrder {
381 prev: 3,
382 current: 1
383 }
384 ));
385 }
386
387 #[test]
388 fn allow_data_count_before_code_and_data() {
389 let data = [
390 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x09, 0x01, 0xAA, 0x0C, 0x01, 0xBB, 0x0A, 0x01, 0xCC, 0x0B, 0x01, 0xDD, ];
397 let mut cursor = Cursor::new(&data);
398 parse_preamble(&mut cursor).unwrap();
399 let sections = parse_sections(&mut cursor).unwrap();
400
401 assert_eq!(sections.len(), 4);
402 assert_eq!(sections[0].id, SectionId::Element);
403 assert_eq!(sections[1].id, SectionId::DataCount);
404 assert_eq!(sections[2].id, SectionId::Code);
405 assert_eq!(sections[3].id, SectionId::Data);
406 }
407
408 #[test]
409 fn custom_sections_allowed_anywhere() {
410 let data = [
411 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0xAA, 0x00, 0x01, 0x00, 0x03, 0x01, 0xCC, 0x00, 0x01, 0x00, ];
419 let mut cursor = Cursor::new(&data);
420 parse_preamble(&mut cursor).unwrap();
421 let sections = parse_sections(&mut cursor).unwrap();
422
423 assert_eq!(sections.len(), 5);
424 assert_eq!(sections[0].id, SectionId::Custom);
425 assert_eq!(sections[1].id, SectionId::Type);
426 assert_eq!(sections[2].id, SectionId::Custom);
427 assert_eq!(sections[3].id, SectionId::Function);
428 assert_eq!(sections[4].id, SectionId::Custom);
429 }
430
431 #[test]
432 fn reject_section_overflow() {
433 let data = [
434 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x01, ];
438 let mut cursor = Cursor::new(&data);
439 parse_preamble(&mut cursor).unwrap();
440 let err = parse_sections(&mut cursor).unwrap_err();
441 assert!(matches!(err.kind, DecodeErrorKind::SectionOverflow));
442 }
443
444 #[test]
445 fn reject_invalid_utf8_custom_section_name() {
446 let data = [
447 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x80, ];
451 let mut cursor = Cursor::new(&data);
452 parse_preamble(&mut cursor).unwrap();
453 let err = parse_sections(&mut cursor).unwrap_err();
454 assert!(matches!(err.kind, DecodeErrorKind::InvalidUtf8));
455 assert!(matches!(err.context, DecodeContext::SectionBody { id: 0 }));
456 }
457}