1#![allow(missing_docs)]
18
19use crate::encode::{hash_image, NodeRec};
20use crate::error::{Error, Result};
21use crate::ir::{NodeId, ProvValue, Provenance};
22use crate::layout::{
23 align8, dialect_hash, read_u32, read_u64, Header, Limits, Meta, Section, BLOB_ENTRY_SIZE,
24 EXT_DIR_SIZE, FIELD_SIZE, FLAG_ANNOTATIONS, FLAG_CANONICAL, FLAG_EXTENSIONS, FLAG_PROVENANCE,
25 FLAG_SECRETS, HEADER_SIZE, NODE_FLAG_0, NODE_FLAG_1, NODE_FLAG_KNOWN, NODE_RECORD_SIZE,
26 PROV_ENTRY_SIZE, PROV_HEADER_SIZE, SECTION_COUNT,
27};
28use crate::unit::{UnitTag, UNIT_NAMED};
29
30#[derive(Debug, Clone, Copy)]
32pub struct Validated<'a> {
33 buf: &'a [u8],
34 header: Header,
35 ends: [u64; SECTION_COUNT],
36 limits: Limits,
37}
38
39impl<'a> Validated<'a> {
40 pub fn new(buf: &'a [u8], opts: crate::layout::DecodeOpts) -> Result<Self> {
42 opts.limits.check()?;
43 let header = Header::parse(buf)?;
44 if header.dialect_hash != dialect_hash() {
45 return Err(Error::DialectMismatch(header.dialect_hash));
46 }
47 let limits = opts.limits;
48 if buf.len() as u64 > limits.max_total_bytes {
49 return Err(Error::Limit {
50 kind: "total bytes",
51 limit: limits.max_total_bytes,
52 });
53 }
54 validate_counts(&header, limits)?;
55 let ends = validate_layout(buf, &header)?;
56 let validated = Validated {
57 buf,
58 header,
59 ends,
60 limits,
61 };
62
63 if header.flags & FLAG_ANNOTATIONS != 0 && validated.span(Section::Annotations).count == 0 {
64 return Err(Error::Annotation {
65 id: 0,
66 reason: "annotations flag set with an empty table",
67 });
68 }
69 if header.flags & FLAG_ANNOTATIONS == 0 && validated.span(Section::Annotations).count != 0 {
70 return Err(Error::Annotation {
71 id: 0,
72 reason: "annotations present without the flag",
73 });
74 }
75 if header.flags & FLAG_PROVENANCE != 0 && validated.span(Section::Provenance).count == 0 {
76 return Err(Error::Provenance("provenance flag with an empty block"));
77 }
78 if header.flags & FLAG_PROVENANCE == 0 && validated.span(Section::Provenance).count != 0 {
79 return Err(Error::Provenance("provenance block without the flag"));
80 }
81 if header.flags & FLAG_EXTENSIONS != 0 && validated.span(Section::Extensions).count == 0 {
82 return Err(Error::Extension {
83 tag: 0,
84 reason: "extensions flag with an empty directory",
85 });
86 }
87 if header.flags & FLAG_EXTENSIONS == 0 && validated.span(Section::Extensions).count != 0 {
88 return Err(Error::Extension {
89 tag: 0,
90 reason: "extension blocks without the flag",
91 });
92 }
93 if header.flags & FLAG_SECRETS != 0 && validated.span(Section::Secrets).count == 0 {
94 return Err(Error::SecretEntry(0));
95 }
96 if header.flags & FLAG_SECRETS == 0 && validated.span(Section::Secrets).count != 0 {
97 return Err(Error::SecretEntry(0));
98 }
99
100 validated.check_strings()?;
101 validated.check_units(limits)?;
102 validated.check_types(limits)?;
103 validated.check_numbers()?;
104 validated.check_hashes()?;
105 validated.check_secrets(limits)?;
106 validated.check_blobs(limits)?;
107 validated.check_nodes(limits)?;
108 validated.check_fields()?;
109 validated.check_parts()?;
110 validated.check_edges()?;
111 validated.check_annotations()?;
112 validated.check_provenance(limits)?;
113 validated.check_extensions()?;
114 validated.check_reachability(limits)?;
115 validated.check_depth(limits)?;
116
117 if opts.verify_hash {
118 let computed = hash_image(buf)?;
119 if computed != header.content_hash {
120 return Err(Error::HashMismatch);
121 }
122 }
123 if opts.verify_canonical && header.flags & FLAG_CANONICAL != 0 {
124 let module = crate::decode::decode_validated(&validated)?;
125 let image = crate::encode::encode_with(
126 &module,
127 crate::layout::EncodeOpts {
128 write_hash: true,
129 mark_canonical: true,
130 streamed: header.flags & crate::layout::FLAG_STREAMED != 0,
131 },
132 )?;
133 if image != buf {
134 return Err(Error::NotCanonical);
135 }
136 }
137 Ok(validated)
138 }
139
140 pub fn buf(&self) -> &'a [u8] {
141 self.buf
142 }
143
144 pub fn limits_snapshot(&self) -> Limits {
146 self.limits
147 }
148
149 pub fn header(&self) -> &Header {
150 &self.header
151 }
152
153 pub fn limits_span(&self, section: Section) -> (u32, u32) {
154 let span = self.header.span(section);
155 (span.offset, span.count)
156 }
157
158 pub fn span(&self, section: Section) -> crate::layout::SectionSpan {
159 self.header.span(section)
160 }
161
162 pub fn section_len(&self, section: Section) -> u64 {
164 self.ends[section as usize] - self.header.span(section).offset as u64
165 }
166
167 pub fn section(&self, section: Section) -> &'a [u8] {
168 let (offset, _) = self.limits_span(section);
169 let end = self.ends[section as usize];
170 &self.buf[offset as usize..end as usize]
171 }
172
173 pub fn meta(&self) -> Meta {
175 let header = &self.header;
176 Meta {
177 flags: header.flags,
178 major: header.major,
179 minor: header.minor,
180 dialect_hash: header.dialect_hash,
181 content_hash: header.content_hash,
182 total_size: header.total_size,
183 nodes: header.span(Section::Nodes).count,
184 edges: header.span(Section::Edges).count,
185 strings: header.span(Section::Strings).count,
186 string_bytes: self.string_heap_len(),
187 blobs: header.span(Section::Blobs).count,
188 blob_bytes: self.blob_heap_len(),
189 annotations: header.span(Section::Annotations).count,
190 units: header.span(Section::Units).count,
191 types: header.span(Section::Types).count,
192 secrets: header.span(Section::Secrets).count,
193 extensions: header.span(Section::Extensions).count,
194 }
195 }
196
197 pub fn node_count(&self) -> u32 {
198 self.header.span(Section::Nodes).count
199 }
200
201 pub(crate) fn node(&self, id: NodeId) -> Result<NodeRec> {
203 let count = self.node_count();
204 if id.0 >= count {
205 return Err(Error::NodeRef(id.0));
206 }
207 let offset =
208 self.header.span(Section::Nodes).offset as usize + id.index() * NODE_RECORD_SIZE;
209 let rec = &self.buf[offset..offset + NODE_RECORD_SIZE];
210 Ok(NodeRec {
211 kind: rec[0],
212 flags: rec[1],
213 aux: u32::from_le_bytes([rec[4], rec[5], rec[6], rec[7]]),
214 a: u64::from_le_bytes(rec[8..16].try_into().expect("8 bytes")),
215 b: u64::from_le_bytes(rec[16..24].try_into().expect("8 bytes")),
216 c: u64::from_le_bytes(rec[24..32].try_into().expect("8 bytes")),
217 })
218 }
219
220 pub fn child(&self, index: u32) -> Result<u32> {
222 let count = self.header.span(Section::Children).count;
223 if index >= count {
224 return Err(Error::Node {
225 id: index,
226 reason: "child index out of range",
227 });
228 }
229 let offset = self.header.span(Section::Children).offset as usize + index as usize * 4;
230 read_u32(self.buf, offset)
231 }
232
233 pub fn field(&self, index: u32) -> Result<(u32, NodeId)> {
235 let count = self.header.span(Section::Fields).count;
236 if index >= count {
237 return Err(Error::Node {
238 id: index,
239 reason: "field index out of range",
240 });
241 }
242 let offset =
243 self.header.span(Section::Fields).offset as usize + index as usize * FIELD_SIZE;
244 let name = read_u32(self.buf, offset)?;
245 let node = read_u32(self.buf, offset + 8)?;
246 Ok((name, NodeId(node)))
247 }
248
249 pub fn part(&self, index: u32) -> Result<(u32, u32)> {
251 let count = self.header.span(Section::Parts).count;
252 if index >= count {
253 return Err(Error::Node {
254 id: index,
255 reason: "part index out of range",
256 });
257 }
258 let offset = self.header.span(Section::Parts).offset as usize + index as usize * 8;
259 Ok((read_u32(self.buf, offset)?, read_u32(self.buf, offset + 4)?))
260 }
261
262 pub fn edge(&self, index: u32) -> Result<(u32, u32)> {
264 let count = self.header.span(Section::Edges).count;
265 if index >= count {
266 return Err(Error::Edge {
267 from: index,
268 to: index,
269 });
270 }
271 let offset = self.header.span(Section::Edges).offset as usize + index as usize * 8;
272 Ok((read_u32(self.buf, offset)?, read_u32(self.buf, offset + 4)?))
273 }
274
275 pub fn string_heap_len(&self) -> u32 {
277 let count = self.header.span(Section::Strings).count;
278 if count == 0 {
279 return 0;
280 }
281 let offset = self.header.span(Section::Strings).offset as usize + count as usize * 4;
282 read_u32(self.buf, offset).unwrap_or(0)
283 }
284
285 pub fn string(&self, id: u32) -> Result<&'a str> {
287 let count = self.header.span(Section::Strings).count;
288 if id >= count {
289 return Err(Error::StringRef(id));
290 }
291 let table = self.header.span(Section::Strings).offset as usize;
292 let start = read_u32(self.buf, table + id as usize * 4)?;
293 let end = read_u32(self.buf, table + id as usize * 4 + 4)?;
294 let heap = table + (count as usize + 1) * 4;
295 let bytes = &self.buf[heap + start as usize..heap + end as usize];
296 std::str::from_utf8(bytes).map_err(|_| Error::Utf8 {
297 offset: (heap + start as usize) as u64,
298 })
299 }
300
301 pub fn blob_heap_len(&self) -> u32 {
303 let count = self.header.span(Section::Blobs).count;
304 if count == 0 {
305 return 0;
306 }
307 let table = self.header.span(Section::Blobs).offset as usize;
308 let last = table + (count as usize - 1) * BLOB_ENTRY_SIZE;
309 let off = read_u32(self.buf, last + 32).unwrap_or(0);
310 let len = read_u32(self.buf, last + 36).unwrap_or(0);
311 off.saturating_add(len)
312 }
313
314 pub fn blob_entry(&self, id: u32) -> Result<([u8; 32], u32, u32)> {
316 let count = self.header.span(Section::Blobs).count;
317 if id >= count {
318 return Err(Error::Blob {
319 id,
320 reason: "index out of range",
321 });
322 }
323 let offset =
324 self.header.span(Section::Blobs).offset as usize + id as usize * BLOB_ENTRY_SIZE;
325 let mut hash = [0u8; 32];
326 hash.copy_from_slice(&self.buf[offset..offset + 32]);
327 Ok((
328 hash,
329 read_u32(self.buf, offset + 32)?,
330 read_u32(self.buf, offset + 36)?,
331 ))
332 }
333
334 pub fn blob(&self, id: u32) -> Result<&'a [u8]> {
336 let count = self.header.span(Section::Blobs).count;
337 let (_, off, len) = self.blob_entry(id)?;
338 let heap =
339 self.header.span(Section::Blobs).offset as usize + count as usize * BLOB_ENTRY_SIZE;
340 let start = heap + off as usize;
341 let end = start + len as usize;
342 if end > self.ends[Section::Blobs as usize] as usize {
343 return Err(Error::Blob {
344 id,
345 reason: "payload out of range",
346 });
347 }
348 Ok(&self.buf[start..end])
349 }
350
351 pub fn annotation(&self, index: u32) -> Result<(u32, u32, u32, u32)> {
353 let count = self.header.span(Section::Annotations).count;
354 if index >= count {
355 return Err(Error::Annotation {
356 id: index,
357 reason: "index out of range",
358 });
359 }
360 let offset = self.header.span(Section::Annotations).offset as usize + index as usize * 16;
361 Ok((
362 read_u32(self.buf, offset)?,
363 read_u32(self.buf, offset + 4)?,
364 read_u32(self.buf, offset + 8)?,
365 read_u32(self.buf, offset + 12)?,
366 ))
367 }
368
369 pub fn unit(&self, id: u32) -> Result<UnitTag> {
371 let count = self.header.span(Section::Units).count;
372 if id >= count {
373 return Err(Error::UnitRef(id));
374 }
375 let offset = self.header.span(Section::Units).offset as usize + id as usize * 8;
376 let disc = read_u32(self.buf, offset)?;
377 let name = read_u32(self.buf, offset + 4)?;
378 if disc == UNIT_NAMED {
379 Ok(UnitTag::Named(self.string(name)?.to_string()))
380 } else {
381 UnitTag::from_discriminant(disc).ok_or(Error::UnknownUnit(disc))
382 }
383 }
384
385 pub fn type_entry(&self, id: u32) -> Result<(u32, u32, u32, u32)> {
387 let count = self.header.span(Section::Types).count;
388 if id >= count {
389 return Err(Error::Type {
390 id,
391 reason: "index out of range",
392 });
393 }
394 let offset = self.header.span(Section::Types).offset as usize + id as usize * 16;
395 Ok((
396 read_u32(self.buf, offset)?,
397 read_u32(self.buf, offset + 4)?,
398 read_u32(self.buf, offset + 8)?,
399 read_u32(self.buf, offset + 12)?,
400 ))
401 }
402
403 pub fn aux(&self, index: u32) -> Result<(u32, u32)> {
405 let count = self.header.span(Section::Aux).count;
406 if index >= count {
407 return Err(Error::Node {
408 id: index,
409 reason: "aux index out of range",
410 });
411 }
412 let offset = self.header.span(Section::Aux).offset as usize + index as usize * 8;
413 Ok((read_u32(self.buf, offset)?, read_u32(self.buf, offset + 4)?))
414 }
415
416 pub fn number(&self, index: u32) -> Result<i64> {
418 let count = self.header.span(Section::Numbers).count;
419 if index >= count {
420 return Err(Error::Node {
421 id: index,
422 reason: "number index out of range",
423 });
424 }
425 let offset = self.header.span(Section::Numbers).offset as usize + index as usize * 8;
426 Ok(read_u64(self.buf, offset)? as i64)
427 }
428
429 pub fn secret(&self, id: u32) -> Result<&'a [u8]> {
431 let count = self.header.span(Section::Secrets).count;
432 if id >= count {
433 return Err(Error::SecretEntry(id));
434 }
435 let table = self.header.span(Section::Secrets).offset as usize;
436 let off = read_u32(self.buf, table + id as usize * 8)?;
437 let len = read_u32(self.buf, table + id as usize * 8 + 4)?;
438 let heap = table + count as usize * 8;
439 let start = heap + off as usize;
440 let end = start + len as usize;
441 if end > self.ends[Section::Secrets as usize] as usize {
442 return Err(Error::SecretEntry(id));
443 }
444 Ok(&self.buf[start..end])
445 }
446
447 pub fn hash_entry(&self, index: u32) -> Result<[u8; 32]> {
449 let count = self.header.span(Section::Hashes).count;
450 if index >= count {
451 return Err(Error::Provenance("hash index out of range"));
452 }
453 let offset = self.header.span(Section::Hashes).offset as usize + index as usize * 32;
454 let mut hash = [0u8; 32];
455 hash.copy_from_slice(&self.buf[offset..offset + 32]);
456 Ok(hash)
457 }
458
459 pub fn provenance(&self) -> Result<Option<Provenance>> {
461 let len = self.header.span(Section::Provenance).count as usize;
462 if len == 0 {
463 return Ok(None);
464 }
465 let start = self.header.span(Section::Provenance).offset as usize;
466 if len < PROV_HEADER_SIZE {
467 return Err(Error::Provenance("block shorter than its header"));
468 }
469 let block = &self.buf[start..start + len];
470 let version = read_u32(block, 0)?;
471 if version != 1 {
472 return Err(Error::Provenance("unsupported provenance version"));
473 }
474 let count = read_u32(block, 4)?;
475 let timestamp_ms = read_u64(block, 8)?;
476 let read_sid = |offset: usize| -> Result<Option<String>> {
477 let id = read_u32(block, offset)?;
478 if id == u32::MAX {
479 Ok(None)
480 } else {
481 Ok(Some(self.string(id)?.to_string()))
482 }
483 };
484 let producer = read_sid(16)?;
485 let model = read_sid(20)?;
486 let tool = read_sid(24)?;
487 let flags = read_u32(block, 28)?;
488 if flags & !1 != 0 {
489 return Err(Error::Provenance("unknown provenance flags"));
490 }
491 let parent = if flags & 1 != 0 {
492 let mut hash = [0u8; 32];
493 hash.copy_from_slice(&block[32..64]);
494 Some(hash)
495 } else {
496 if block[32..64].iter().any(|byte| *byte != 0) {
497 return Err(Error::Provenance("parent hash without the flag"));
498 }
499 None
500 };
501 if block[64..PROV_HEADER_SIZE].iter().any(|byte| *byte != 0) {
502 return Err(Error::Provenance("non-zero provenance reserved bytes"));
503 }
504 let mut entries = Vec::with_capacity(count.min(1024) as usize);
505 for index in 0..count as usize {
506 let offset = PROV_HEADER_SIZE + index * PROV_ENTRY_SIZE;
507 if offset + PROV_ENTRY_SIZE > block.len() {
508 return Err(Error::Provenance("entry exceeds block"));
509 }
510 let kind = read_u32(block, offset)?;
511 let key = self.string(read_u32(block, offset + 4)?)?.to_string();
512 let a = read_u32(block, offset + 8)?;
513 let b = read_u32(block, offset + 12)?;
514 if kind > 3 {
515 return Err(Error::Provenance("reserved provenance entry kind"));
518 }
519 if b != 0 {
520 return Err(Error::Provenance("non-zero entry reserved word"));
521 }
522 let value = match kind {
523 0 => ProvValue::Text(self.string(a)?.to_string()),
524 1 => ProvValue::Int(self.number(a)?),
525 2 => {
526 if a > 1 {
527 return Err(Error::Provenance("flag entry is not 0/1"));
528 }
529 ProvValue::Flag(a == 1)
530 }
531 3 => ProvValue::Hash(self.hash_entry(a)?),
532 _ => unreachable!("kind checked above"),
533 };
534 entries.push(crate::ir::ProvEntry { key, value });
535 }
536 if PROV_HEADER_SIZE + count as usize * PROV_ENTRY_SIZE != block.len() {
537 return Err(Error::Provenance("trailing bytes in provenance block"));
538 }
539 Ok(Some(Provenance {
540 producer,
541 timestamp_ms,
542 model,
543 tool,
544 parent,
545 entries,
546 }))
547 }
548
549 pub fn root(&self) -> Result<Option<NodeId>> {
551 let count = self.header.span(Section::Bindings).count;
552 if count == 0 {
553 return Err(Error::Offset {
554 section: "bindings",
555 offset: self.header.span(Section::Bindings).offset as u64,
556 });
557 }
558 let offset = self.header.span(Section::Bindings).offset as usize;
559 let root = read_u32(self.buf, offset)?;
560 if root == u32::MAX {
561 Ok(None)
562 } else if root < self.node_count() {
563 Ok(Some(NodeId(root)))
564 } else {
565 Err(Error::NodeRef(root))
566 }
567 }
568
569 pub fn bindings(&self) -> Result<Vec<(&'a str, NodeId)>> {
571 let count = self.header.span(Section::Bindings).count;
572 if count == 0 {
573 return Err(Error::Offset {
574 section: "bindings",
575 offset: self.header.span(Section::Bindings).offset as u64,
576 });
577 }
578 let base = self.header.span(Section::Bindings).offset as usize;
579 let mut out = Vec::with_capacity(count.saturating_sub(1) as usize);
580 for index in 1..count {
581 let offset = base + index as usize * 8;
582 let name = read_u32(self.buf, offset)?;
583 let node = read_u32(self.buf, offset + 4)?;
584 if node >= self.node_count() {
585 return Err(Error::NodeRef(node));
586 }
587 out.push((self.string(name)?, NodeId(node)));
588 }
589 Ok(out)
590 }
591
592 fn check_bindings(&self) -> Result<()> {
593 let count = self.header.span(Section::Bindings).count;
594 if count == 0 {
595 return Err(Error::Offset {
596 section: "bindings",
597 offset: self.header.span(Section::Bindings).offset as u64,
598 });
599 }
600 let base = self.header.span(Section::Bindings).offset as usize;
601 if read_u32(self.buf, base + 4)? != 0 {
602 return Err(Error::ReservedNonZero {
603 section: "bindings",
604 offset: (base + 4) as u64,
605 });
606 }
607 if count as u64 > self.limits.max_nodes as u64 {
608 return Err(Error::Limit {
609 kind: "bindings",
610 limit: self.limits.max_nodes as u64,
611 });
612 }
613 let mut previous: Option<&str> = None;
614 for (name, _) in self.bindings()? {
615 if let Some(previous) = previous {
616 if previous >= name {
617 return Err(Error::DuplicateBinding);
618 }
619 }
620 previous = Some(name);
621 }
622 Ok(())
623 }
624
625 fn check_strings(&self) -> Result<()> {
628 let count = self.header.span(Section::Strings).count;
629 if count == 0 {
630 return Ok(());
631 }
632 let table = self.header.span(Section::Strings).offset as usize;
633 let heap_len = self.string_heap_len();
634 if heap_len as u64 > self.limits_snapshot().max_string_bytes as u64 {
635 return Err(Error::Limit {
636 kind: "string bytes",
637 limit: self.limits_snapshot().max_string_bytes as u64,
638 });
639 }
640 let heap_len = self.string_heap_len();
641 let heap = table + (count as usize + 1) * 4;
642 if heap + heap_len as usize > self.ends[Section::Strings as usize] as usize {
643 return Err(Error::Offset {
644 section: "strings",
645 offset: table as u64,
646 });
647 }
648 let mut previous: Option<&[u8]> = None;
649 let mut last_end = 0u32;
650 for id in 0..count {
651 let start = read_u32(self.buf, table + id as usize * 4)?;
652 let end = read_u32(self.buf, table + id as usize * 4 + 4)?;
653 if start != last_end || end < start || end > heap_len {
654 return Err(Error::Offset {
655 section: "strings",
656 offset: (heap + start as usize) as u64,
657 });
658 }
659 last_end = end;
660 let bytes = &self.buf[heap + start as usize..heap + end as usize];
661 std::str::from_utf8(bytes).map_err(|_| Error::Utf8 {
662 offset: (heap + start as usize) as u64,
663 })?;
664 if let Some(previous) = previous {
665 if previous >= bytes {
666 return Err(Error::Offset {
667 section: "strings",
668 offset: (heap + start as usize) as u64,
669 });
670 }
671 }
672 previous = Some(bytes);
673 }
674 if last_end != heap_len {
675 return Err(Error::Offset {
676 section: "strings",
677 offset: (heap + last_end as usize) as u64,
678 });
679 }
680 Ok(())
681 }
682
683 fn check_units(&self, limits: Limits) -> Result<()> {
684 let count = self.header.span(Section::Units).count;
685 if count > limits.max_units {
686 return Err(Error::Limit {
687 kind: "units",
688 limit: limits.max_units as u64,
689 });
690 }
691 let mut previous: Option<(u32, u32)> = None;
692 for id in 0..count {
693 let offset = self.header.span(Section::Units).offset as usize + id as usize * 8;
694 let disc = read_u32(self.buf, offset)?;
695 let name = read_u32(self.buf, offset + 4)?;
696 if disc == UNIT_NAMED {
697 if name >= self.header.span(Section::Strings).count {
698 return Err(Error::UnitRef(name));
699 }
700 if self.string(name)?.is_empty() {
701 return Err(Error::UnitRef(name));
702 }
703 } else {
704 if UnitTag::from_discriminant(disc).is_none() {
705 return Err(Error::UnknownUnit(disc));
706 }
707 if name != 0 {
708 return Err(Error::UnitRef(name));
709 }
710 }
711 if let Some(previous) = previous {
712 if previous >= (disc, name) {
713 return Err(Error::Offset {
714 section: "units",
715 offset: offset as u64,
716 });
717 }
718 }
719 previous = Some((disc, name));
720 }
721 Ok(())
722 }
723
724 fn check_types(&self, limits: Limits) -> Result<()> {
725 let count = self.header.span(Section::Types).count;
726 if count > limits.max_types {
727 return Err(Error::Limit {
728 kind: "types",
729 limit: limits.max_types as u64,
730 });
731 }
732 let aux_count = self.header.span(Section::Aux).count;
733 let mut previous: Option<(u32, u32, u32, u32)> = None;
734 for id in 0..count {
735 let offset = self.header.span(Section::Types).offset as usize + id as usize * 16;
736 let code = read_u32(self.buf, offset)?;
737 let flags = read_u32(self.buf, offset + 4)?;
738 let start = read_u32(self.buf, offset + 8)?;
739 let len = read_u32(self.buf, offset + 12)?;
740 if code > 11 {
741 return Err(Error::Type {
742 id,
743 reason: "unknown type code",
744 });
745 }
746 if code != 7 && flags != 0 {
747 return Err(Error::Type {
748 id,
749 reason: "flags on a non-table type",
750 });
751 }
752 if code == 7 && flags > 1 {
753 return Err(Error::Type {
754 id,
755 reason: "table closed flag must be 0 or 1",
756 });
757 }
758 if start > aux_count || len > aux_count - start {
759 return Err(Error::Type {
760 id,
761 reason: "aux range out of bounds",
762 });
763 }
764 for index in start..start + len {
765 let (tag, value) = self.aux(index)?;
766 match (code, tag) {
767 (8, crate::layout::AUX_TYPE_ENUM_ITEM) => {
768 if value >= self.header.span(Section::Strings).count {
769 return Err(Error::StringRef(value));
770 }
771 }
772 (7, crate::layout::AUX_TYPE_TABLE) => {
773 if value > 1 {
774 return Err(Error::Type {
775 id,
776 reason: "closed flag must be 0 or 1",
777 });
778 }
779 }
780 _ => {
781 return Err(Error::Type {
782 id,
783 reason: "unexpected aux record",
784 })
785 }
786 }
787 }
788 if code == 8 && len == 0 {
789 return Err(Error::Type {
790 id,
791 reason: "enum type without members",
792 });
793 }
794 if let Some(previous) = previous {
795 if previous >= (code, flags, start, len) {
796 return Err(Error::Offset {
797 section: "types",
798 offset: offset as u64,
799 });
800 }
801 }
802 previous = Some((code, flags, start, len));
803 }
804 Ok(())
805 }
806
807 fn check_numbers(&self) -> Result<()> {
808 let count = self.header.span(Section::Numbers).count;
809 let base = self.header.span(Section::Numbers).offset as usize;
810 let mut previous: Option<i64> = None;
811 for index in 0..count {
812 let value = read_u64(self.buf, base + index as usize * 8)? as i64;
813 if let Some(previous) = previous {
814 if previous >= value {
815 return Err(Error::Offset {
816 section: "numbers",
817 offset: (base + index as usize * 8) as u64,
818 });
819 }
820 }
821 previous = Some(value);
822 }
823 Ok(())
824 }
825
826 fn check_hashes(&self) -> Result<()> {
827 let count = self.header.span(Section::Hashes).count;
828 if count as u64 > self.limits.max_nodes as u64 {
829 return Err(Error::Limit {
830 kind: "hashes",
831 limit: self.limits.max_nodes as u64,
832 });
833 }
834 let base = self.header.span(Section::Hashes).offset as usize;
835 let mut previous: Option<[u8; 32]> = None;
836 for index in 0..count {
837 let mut hash = [0u8; 32];
838 hash.copy_from_slice(
839 &self.buf[base + index as usize * 32..base + (index as usize + 1) * 32],
840 );
841 if let Some(previous) = previous {
842 if previous >= hash {
843 return Err(Error::Offset {
844 section: "hashes",
845 offset: (base + index as usize * 32) as u64,
846 });
847 }
848 }
849 previous = Some(hash);
850 }
851 Ok(())
852 }
853
854 fn check_secrets(&self, limits: Limits) -> Result<()> {
855 let count = self.header.span(Section::Secrets).count;
856 if count > limits.max_secrets {
857 return Err(Error::Limit {
858 kind: "secrets",
859 limit: limits.max_secrets as u64,
860 });
861 }
862 let table = self.header.span(Section::Secrets).offset as usize;
863 let heap = table + count as usize * 8;
864 let end = self.ends[Section::Secrets as usize] as usize;
865 let mut cursor = 0u32;
866 for id in 0..count {
867 let off = read_u32(self.buf, table + id as usize * 8)?;
868 let len = read_u32(self.buf, table + id as usize * 8 + 4)?;
869 if off != cursor || heap + off as usize + len as usize > end {
870 return Err(Error::SecretEntry(id));
871 }
872 cursor = cursor.checked_add(len).ok_or(Error::SecretEntry(id))?;
873 }
874 if cursor as u64 > limits.max_secret_bytes as u64 {
875 return Err(Error::Limit {
876 kind: "secret bytes",
877 limit: limits.max_secret_bytes as u64,
878 });
879 }
880 Ok(())
881 }
882
883 fn check_blobs(&self, limits: Limits) -> Result<()> {
884 let count = self.header.span(Section::Blobs).count;
885 if count > limits.max_blobs {
886 return Err(Error::Limit {
887 kind: "blobs",
888 limit: limits.max_blobs as u64,
889 });
890 }
891 let table = self.header.span(Section::Blobs).offset as usize;
892 let heap = table + count as usize * BLOB_ENTRY_SIZE;
893 let end = self.ends[Section::Blobs as usize] as usize;
894 let mut cursor = 0u32;
895 let mut previous: Option<([u8; 32], u32, u32)> = None;
896 for id in 0..count {
897 let offset = table + id as usize * BLOB_ENTRY_SIZE;
898 let mut hash = [0u8; 32];
899 hash.copy_from_slice(&self.buf[offset..offset + 32]);
900 let off = read_u32(self.buf, offset + 32)?;
901 let len = read_u32(self.buf, offset + 36)?;
902 if off != cursor || heap + off as usize + len as usize > end {
903 return Err(Error::Blob {
904 id,
905 reason: "payload out of range",
906 });
907 }
908 cursor = cursor.checked_add(len).ok_or(Error::Blob {
909 id,
910 reason: "payload heap overflows",
911 })?;
912 if let Some(previous) = previous {
913 if previous >= (hash, off, len) {
914 return Err(Error::Offset {
915 section: "blobs",
916 offset: offset as u64,
917 });
918 }
919 }
920 previous = Some((hash, off, len));
921 }
922 if cursor > limits.max_blob_bytes {
923 return Err(Error::Limit {
924 kind: "blob bytes",
925 limit: limits.max_blob_bytes as u64,
926 });
927 }
928 Ok(())
929 }
930
931 fn check_nodes(&self, limits: Limits) -> Result<()> {
932 let count = self.node_count();
933 if count > limits.max_nodes {
934 return Err(Error::Limit {
935 kind: "nodes",
936 limit: limits.max_nodes as u64,
937 });
938 }
939 let child_count = self.header.span(Section::Children).count;
940 let field_count = self.header.span(Section::Fields).count;
941 let part_count = self.header.span(Section::Parts).count;
942 let string_count = self.header.span(Section::Strings).count;
943 let unit_count = self.header.span(Section::Units).count;
944 let type_count = self.header.span(Section::Types).count;
945 let aux_count = self.header.span(Section::Aux).count;
946 let blob_count = self.header.span(Section::Blobs).count;
947 let secret_count = self.header.span(Section::Secrets).count;
948 for index in 0..count {
949 let id = NodeId(index);
950 let rec = self.node(id)?;
951 if rec.kind > 24 {
952 return Err(Error::Node {
953 id: index,
954 reason: "unknown node kind",
955 });
956 }
957 if rec.flags & !NODE_FLAG_KNOWN != 0 {
958 return Err(Error::Node {
959 id: index,
960 reason: "reserved node flag set",
961 });
962 }
963 let node = |value: u64| -> Result<()> {
964 if value >= count as u64 {
965 Err(Error::NodeRef(value as u32))
966 } else {
967 Ok(())
968 }
969 };
970 let range = |start: u64, len: u64, total: u32, what: &'static str| -> Result<()> {
971 if start > total as u64 || len > total as u64 - start {
972 return Err(Error::Node {
973 id: index,
974 reason: what,
975 });
976 }
977 Ok(())
978 };
979 let string = |value: u32| -> Result<()> {
980 if value >= string_count {
981 Err(Error::StringRef(value))
982 } else {
983 Ok(())
984 }
985 };
986 let zero = |what: &'static str| -> Result<()> {
987 if rec.aux != 0 || rec.a != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
988 return Err(Error::Node {
989 id: index,
990 reason: what,
991 });
992 }
993 Ok(())
994 };
995 match rec.kind {
996 0 => zero("unused fields in Null node")?,
997 1 => {
998 if rec.a > 1 || rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
999 return Err(Error::Node {
1000 id: index,
1001 reason: "invalid Bool node",
1002 });
1003 }
1004 }
1005 2 => {
1006 if rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1007 return Err(Error::Node {
1008 id: index,
1009 reason: "unused fields in Int node",
1010 });
1011 }
1012 }
1013 3 => {
1014 let value = f64::from_bits(rec.a);
1015 if rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1016 return Err(Error::Node {
1017 id: index,
1018 reason: "unused fields in Float node",
1019 });
1020 }
1021 if value.is_nan() && !crate::canonical::is_canonical_nan(value) {
1022 return Err(Error::Node {
1023 id: index,
1024 reason: "non-canonical NaN",
1025 });
1026 }
1027 if value == 0.0 && value.is_sign_negative() {
1028 return Err(Error::Node {
1029 id: index,
1030 reason: "negative zero must be canonicalized",
1031 });
1032 }
1033 }
1034 4 | 13 => {
1035 string(rec.aux)?;
1036 if rec.a != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1037 return Err(Error::Node {
1038 id: index,
1039 reason: "unused fields in string node",
1040 });
1041 }
1042 }
1043 5 => {
1044 if rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1045 return Err(Error::Node {
1046 id: index,
1047 reason: "unused fields in Duration node",
1048 });
1049 }
1050 }
1051 6 => {
1052 node(rec.aux as u64)?;
1053 if rec.a >= unit_count as u64 {
1054 return Err(Error::UnitRef(rec.a as u32));
1055 }
1056 if rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1057 return Err(Error::Node {
1058 id: index,
1059 reason: "unused fields in Quantity node",
1060 });
1061 }
1062 }
1063 7 => {
1064 range(rec.a, rec.b, child_count, "array range out of bounds")?;
1065 if rec.aux != 0 || rec.c != 0 || rec.flags != 0 {
1066 return Err(Error::Node {
1067 id: index,
1068 reason: "unused fields in Array node",
1069 });
1070 }
1071 for offset in rec.a..rec.a + rec.b {
1072 let child = self.child(offset as u32)?;
1073 node(child as u64)?;
1074 }
1075 }
1076 8 | 20 => {
1077 range(rec.a, rec.b, field_count, "field range out of bounds")?;
1078 if rec.aux != 0 || rec.c != 0 {
1079 return Err(Error::Node {
1080 id: index,
1081 reason: "unused fields in table node",
1082 });
1083 }
1084 if rec.kind == 8 && rec.flags != 0 {
1085 return Err(Error::Node {
1086 id: index,
1087 reason: "flags on a data table",
1088 });
1089 }
1090 if rec.kind == 20 && rec.flags & !NODE_FLAG_0 != 0 {
1091 return Err(Error::Node {
1092 id: index,
1093 reason: "unknown schema-table flag",
1094 });
1095 }
1096 let mut previous: Option<u32> = None;
1097 for offset in rec.a..rec.a + rec.b {
1098 let (name, node_id) = self.field(offset as u32)?;
1099 string(name)?;
1100 node(node_id.0 as u64)?;
1101 if let Some(previous) = previous {
1102 if previous >= name {
1103 return Err(Error::Node {
1104 id: index,
1105 reason: "table fields not sorted by key",
1106 });
1107 }
1108 }
1109 previous = Some(name);
1110 }
1111 }
1112 9 => {
1113 if rec.a >= secret_count as u64 {
1114 return Err(Error::SecretEntry(rec.a as u32));
1115 }
1116 if rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1117 return Err(Error::Node {
1118 id: index,
1119 reason: "unused fields in Secret node",
1120 });
1121 }
1122 }
1123 10 | 11 | 21 => {
1124 node(rec.a)?;
1125 if rec.aux != 0 || rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1126 return Err(Error::Node {
1127 id: index,
1128 reason: "unused fields in reference node",
1129 });
1130 }
1131 }
1132 12 => {
1133 string(rec.aux)?;
1134 if rec.a != u32::MAX as u64 {
1135 node(rec.a)?;
1136 }
1137 if rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1138 return Err(Error::Node {
1139 id: index,
1140 reason: "unused fields in Env node",
1141 });
1142 }
1143 }
1144 14 => {
1145 range(rec.a, rec.b, part_count, "part range out of bounds")?;
1146 if rec.aux != 0 || rec.c != 0 || rec.flags != 0 {
1147 return Err(Error::Node {
1148 id: index,
1149 reason: "unused fields in Interpolate node",
1150 });
1151 }
1152 for offset in rec.a..rec.a + rec.b {
1153 let (tag, value) = self.part(offset as u32)?;
1154 match tag {
1155 crate::layout::PART_LITERAL => string(value)?,
1156 crate::layout::PART_EXPR => node(value as u64)?,
1157 _ => {
1158 return Err(Error::Node {
1159 id: index,
1160 reason: "unknown interpolation part tag",
1161 })
1162 }
1163 }
1164 }
1165 }
1166 15 => {
1167 if crate::ir::BinOp::from_code(rec.aux).is_none() {
1168 return Err(Error::Node {
1169 id: index,
1170 reason: "unknown binary operator",
1171 });
1172 }
1173 node(rec.a)?;
1174 node(rec.b)?;
1175 if rec.c != 0 || rec.flags != 0 {
1176 return Err(Error::Node {
1177 id: index,
1178 reason: "unused fields in Binary node",
1179 });
1180 }
1181 }
1182 16 => {
1183 if crate::ir::UnOp::from_code(rec.aux).is_none() {
1184 return Err(Error::Node {
1185 id: index,
1186 reason: "unknown unary operator",
1187 });
1188 }
1189 node(rec.a)?;
1190 if rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1191 return Err(Error::Node {
1192 id: index,
1193 reason: "unused fields in Unary node",
1194 });
1195 }
1196 }
1197 17 => {
1198 node(rec.a)?;
1199 if rec.aux > crate::layout::CONSTRAINT_KIND_CUSTOM {
1200 return Err(Error::Node {
1201 id: index,
1202 reason: "unknown constraint kind",
1203 });
1204 }
1205 range(rec.b, rec.c, aux_count, "constraint aux out of range")?;
1206 if rec.flags != 0 {
1207 return Err(Error::Node {
1208 id: index,
1209 reason: "flags on a constraint node",
1210 });
1211 }
1212 let mut seen_kinds = Vec::new();
1213 for offset in rec.b..rec.b + rec.c {
1214 let (tag, value) = self.aux(offset as u32)?;
1215 seen_kinds.push(tag);
1216 match tag {
1217 crate::layout::AUX_CONSTRAINT_MIN
1218 | crate::layout::AUX_CONSTRAINT_MAX => {
1219 if value >= self.header.span(Section::Numbers).count {
1220 return Err(Error::Node {
1221 id: index,
1222 reason: "constraint bound out of range",
1223 });
1224 }
1225 }
1226 crate::layout::AUX_CONSTRAINT_ENUM_ITEM
1227 | crate::layout::AUX_CONSTRAINT_PATTERN
1228 | crate::layout::AUX_CONSTRAINT_CUSTOM => string(value)?,
1229 _ => {
1230 return Err(Error::Node {
1231 id: index,
1232 reason: "unknown constraint aux tag",
1233 })
1234 }
1235 }
1236 }
1237 let expected_matches = match rec.aux {
1238 crate::layout::CONSTRAINT_KIND_MIN_MAX => seen_kinds.iter().all(|tag| {
1239 *tag == crate::layout::AUX_CONSTRAINT_MIN
1240 || *tag == crate::layout::AUX_CONSTRAINT_MAX
1241 }),
1242 crate::layout::CONSTRAINT_KIND_ENUM => seen_kinds
1243 .iter()
1244 .all(|tag| *tag == crate::layout::AUX_CONSTRAINT_ENUM_ITEM),
1245 crate::layout::CONSTRAINT_KIND_PATTERN => {
1246 seen_kinds == [crate::layout::AUX_CONSTRAINT_PATTERN]
1247 }
1248 crate::layout::CONSTRAINT_KIND_ODD => seen_kinds.is_empty(),
1249 _ => seen_kinds == [crate::layout::AUX_CONSTRAINT_CUSTOM],
1250 };
1251 if !expected_matches {
1252 return Err(Error::Node {
1253 id: index,
1254 reason: "constraint payload does not match its kind",
1255 });
1256 }
1257 if rec.aux == crate::layout::CONSTRAINT_KIND_ENUM && rec.c == 0 {
1258 return Err(Error::Node {
1259 id: index,
1260 reason: "empty enum constraint",
1261 });
1262 }
1263 }
1264 18 => {
1265 if rec.aux > crate::layout::PATCH_KIND_RENAME {
1266 return Err(Error::Node {
1267 id: index,
1268 reason: "unknown patch kind",
1269 });
1270 }
1271 range(rec.b, rec.c, aux_count, "patch aux out of range")?;
1272 if rec.a != 0 || rec.flags != 0 {
1273 return Err(Error::Node {
1274 id: index,
1275 reason: "unused fields in Patch node",
1276 });
1277 }
1278 let mut tags = Vec::new();
1279 for offset in rec.b..rec.b + rec.c {
1280 let (tag, value) = self.aux(offset as u32)?;
1281 tags.push(tag);
1282 match tag {
1283 crate::layout::AUX_PATCH_PATH | crate::layout::AUX_PATCH_RENAME_TO => {
1284 string(value)?
1285 }
1286 crate::layout::AUX_PATCH_VALUE => node(value as u64)?,
1287 _ => {
1288 return Err(Error::Node {
1289 id: index,
1290 reason: "unknown patch aux tag",
1291 })
1292 }
1293 }
1294 }
1295 let ok = match rec.aux {
1296 crate::layout::PATCH_KIND_SET => {
1297 tags == [
1298 crate::layout::AUX_PATCH_PATH,
1299 crate::layout::AUX_PATCH_VALUE,
1300 ]
1301 }
1302 crate::layout::PATCH_KIND_REMOVE => tags == [crate::layout::AUX_PATCH_PATH],
1303 _ => {
1304 tags == [
1305 crate::layout::AUX_PATCH_PATH,
1306 crate::layout::AUX_PATCH_RENAME_TO,
1307 ]
1308 }
1309 };
1310 if !ok {
1311 return Err(Error::Node {
1312 id: index,
1313 reason: "patch payload does not match its kind",
1314 });
1315 }
1316 }
1317 19 => {
1318 if rec.aux >= type_count {
1319 return Err(Error::Type {
1320 id: rec.aux,
1321 reason: "schema field references a missing type",
1322 });
1323 }
1324 range(rec.b, rec.c, aux_count, "schema aux out of range")?;
1325 if rec.a != 0 || rec.flags != 0 {
1326 return Err(Error::Node {
1327 id: index,
1328 reason: "unused fields in SchemaField node",
1329 });
1330 }
1331 for offset in rec.b..rec.b + rec.c {
1332 let (tag, value) = self.aux(offset as u32)?;
1333 match tag {
1334 crate::layout::AUX_SCHEMA_MIN
1335 | crate::layout::AUX_SCHEMA_MAX
1336 | crate::layout::AUX_SCHEMA_MIN_LEN
1337 | crate::layout::AUX_SCHEMA_MAX_LEN
1338 | crate::layout::AUX_SCHEMA_PATTERN
1339 | crate::layout::AUX_SCHEMA_DEFAULT => node(value as u64)?,
1340 crate::layout::AUX_SCHEMA_DEPRECATED => string(value)?,
1341 _ => {
1342 return Err(Error::Node {
1343 id: index,
1344 reason: "unknown schema aux tag",
1345 })
1346 }
1347 }
1348 }
1349 }
1350 22 => {
1351 if rec.aux > 2 {
1352 return Err(Error::Node {
1353 id: index,
1354 reason: "unknown semantic tag",
1355 });
1356 }
1357 node(rec.a)?;
1358 if rec.b != 0 || rec.c != 0 || rec.flags != 0 {
1359 return Err(Error::Node {
1360 id: index,
1361 reason: "unused fields in Semantic node",
1362 });
1363 }
1364 }
1365 23 => {
1366 if rec.a >= blob_count as u64 {
1367 return Err(Error::Blob {
1368 id: rec.a as u32,
1369 reason: "node references a missing blob",
1370 });
1371 }
1372 string(rec.aux)?;
1373 if rec.c != 0 {
1374 return Err(Error::Node {
1375 id: index,
1376 reason: "unused field in Blob node",
1377 });
1378 }
1379 if rec.flags & !(NODE_FLAG_0 | NODE_FLAG_1) != 0 {
1380 return Err(Error::Node {
1381 id: index,
1382 reason: "unknown blob flag",
1383 });
1384 }
1385 let (hash, _, len) = self.blob_entry(rec.a as u32)?;
1386 let tainted = rec.flags & NODE_FLAG_0 != 0;
1387 let has_payload = rec.flags & NODE_FLAG_1 != 0;
1388 if tainted && has_payload {
1389 return Err(Error::Blob {
1390 id: rec.a as u32,
1391 reason: "tainted blob must not carry a payload",
1392 });
1393 }
1394 let size = rec.b;
1395 if has_payload {
1396 let payload = self.blob(rec.a as u32)?;
1397 if payload.len() as u64 != size {
1398 return Err(Error::Blob {
1399 id: rec.a as u32,
1400 reason: "payload length disagrees with size",
1401 });
1402 }
1403 let mut hasher = blake3::Hasher::new();
1404 hasher.update(payload);
1405 if hasher.finalize().as_bytes() != &hash {
1406 return Err(Error::Blob {
1407 id: rec.a as u32,
1408 reason: "payload hash mismatch",
1409 });
1410 }
1411 } else if len != 0 {
1412 return Err(Error::Blob {
1413 id: rec.a as u32,
1414 reason: "payload present without the flag",
1415 });
1416 }
1417 }
1418 24 => {
1419 node(rec.a)?;
1420 node(rec.b)?;
1421 if rec.aux != 0 || rec.c != 0 || rec.flags != 0 {
1422 return Err(Error::Node {
1423 id: index,
1424 reason: "unused fields in ContentAddressed node",
1425 });
1426 }
1427 }
1428 _ => unreachable!("kind checked above"),
1429 }
1430 }
1431 Ok(())
1432 }
1433
1434 fn check_fields(&self) -> Result<()> {
1435 let count = self.header.span(Section::Fields).count;
1436 let base = self.header.span(Section::Fields).offset as usize;
1437 for index in 0..count {
1438 let offset = base + index as usize * FIELD_SIZE;
1439 if read_u32(self.buf, offset + 4)? != 0 || read_u32(self.buf, offset + 12)? != 0 {
1440 return Err(Error::ReservedNonZero {
1441 section: "fields",
1442 offset: (offset + 4) as u64,
1443 });
1444 }
1445 }
1446 Ok(())
1447 }
1448
1449 fn check_parts(&self) -> Result<()> {
1450 let count = self.header.span(Section::Parts).count;
1451 let base = self.header.span(Section::Parts).offset as usize;
1452 for index in 0..count {
1453 let tag = read_u32(self.buf, base + index as usize * 8)?;
1454 if tag > crate::layout::PART_EXPR {
1455 return Err(Error::Node {
1456 id: index,
1457 reason: "unknown part tag",
1458 });
1459 }
1460 }
1461 Ok(())
1462 }
1463
1464 fn check_edges(&self) -> Result<()> {
1465 let count = self.header.span(Section::Edges).count;
1466 let node_count = self.node_count();
1467 let base = self.header.span(Section::Edges).offset as usize;
1468 let mut previous: Option<(u32, u32)> = None;
1469 let mut edges = Vec::with_capacity(count.min(1 << 20) as usize);
1470 for index in 0..count {
1471 let from = read_u32(self.buf, base + index as usize * 8)?;
1472 let to = read_u32(self.buf, base + index as usize * 8 + 4)?;
1473 if from >= node_count || to >= node_count || from == to {
1474 return Err(Error::Edge { from, to });
1475 }
1476 if let Some(previous) = previous {
1477 if previous >= (from, to) {
1478 return Err(Error::Edge { from, to });
1479 }
1480 }
1481 previous = Some((from, to));
1482 edges.push((from, to));
1483 }
1484 let mut derived: Vec<(u32, u32)> = Vec::new();
1487 let mut scratch: Vec<u32> = Vec::new();
1488 for index in 0..node_count {
1489 let rec = self.node(NodeId(index))?;
1490 self.node_children_into(&rec, &mut scratch)?;
1491 for child in &scratch {
1492 derived.push((*child, index));
1493 }
1494 }
1495 derived.sort_unstable();
1496 derived.dedup();
1497 if derived != edges {
1498 return Err(Error::Edge { from: 0, to: 0 });
1499 }
1500 Ok(())
1501 }
1502
1503 pub(crate) fn node_children_into(&self, rec: &NodeRec, out: &mut Vec<u32>) -> Result<()> {
1509 out.clear();
1510 match rec.kind {
1511 6 => out.push(rec.aux),
1512 7 => {
1513 for offset in rec.a..rec.a + rec.b {
1514 out.push(self.child(offset as u32)?);
1515 }
1516 }
1517 8 | 20 => {
1518 for offset in rec.a..rec.a + rec.b {
1519 out.push(self.field(offset as u32)?.1 .0);
1520 }
1521 }
1522 10 | 11 | 21 | 22 => out.push(rec.a as u32),
1523 12 => {
1524 if rec.a != u32::MAX as u64 {
1525 out.push(rec.a as u32);
1526 }
1527 }
1528 14 => {
1529 for offset in rec.a..rec.a + rec.b {
1530 let (tag, value) = self.part(offset as u32)?;
1531 if tag == crate::layout::PART_EXPR {
1532 out.push(value);
1533 }
1534 }
1535 }
1536 15 => {
1537 out.push(rec.a as u32);
1538 out.push(rec.b as u32);
1539 }
1540 16 | 17 => out.push(rec.a as u32),
1541 18 => {
1542 for offset in rec.b..rec.b + rec.c {
1543 let (tag, value) = self.aux(offset as u32)?;
1544 if tag == crate::layout::AUX_PATCH_VALUE {
1545 out.push(value);
1546 }
1547 }
1548 }
1549 19 => {
1550 for offset in rec.b..rec.b + rec.c {
1551 let (tag, value) = self.aux(offset as u32)?;
1552 if tag != crate::layout::AUX_SCHEMA_DEPRECATED {
1553 out.push(value);
1554 }
1555 }
1556 }
1557 24 => {
1558 out.push(rec.a as u32);
1559 out.push(rec.b as u32);
1560 }
1561 _ => {}
1562 }
1563 Ok(())
1564 }
1565
1566 pub(crate) fn node_children(&self, _index: u32, rec: &NodeRec) -> Result<Vec<u32>> {
1568 let mut out = Vec::new();
1569 match rec.kind {
1570 6 => out.push(rec.aux),
1571 7 => {
1572 for offset in rec.a..rec.a + rec.b {
1573 out.push(self.child(offset as u32)?);
1574 }
1575 }
1576 8 | 20 => {
1577 for offset in rec.a..rec.a + rec.b {
1578 out.push(self.field(offset as u32)?.1 .0);
1579 }
1580 }
1581 10 | 11 | 21 | 22 => out.push(rec.a as u32),
1582 12 => {
1583 if rec.a != u32::MAX as u64 {
1584 out.push(rec.a as u32);
1585 }
1586 }
1587 14 => {
1588 for offset in rec.a..rec.a + rec.b {
1589 let (tag, value) = self.part(offset as u32)?;
1590 if tag == crate::layout::PART_EXPR {
1591 out.push(value);
1592 }
1593 }
1594 }
1595 15 => {
1596 out.push(rec.a as u32);
1597 out.push(rec.b as u32);
1598 }
1599 16 => out.push(rec.a as u32),
1600 17 => out.push(rec.a as u32),
1601 18 => {
1602 for offset in rec.b..rec.b + rec.c {
1603 let (tag, value) = self.aux(offset as u32)?;
1604 if tag == crate::layout::AUX_PATCH_VALUE {
1605 out.push(value);
1606 }
1607 }
1608 }
1609 19 => {
1610 for offset in rec.b..rec.b + rec.c {
1611 let (tag, value) = self.aux(offset as u32)?;
1612 if tag != crate::layout::AUX_SCHEMA_DEPRECATED {
1613 out.push(value);
1614 }
1615 }
1616 }
1617 24 => {
1618 out.push(rec.a as u32);
1619 out.push(rec.b as u32);
1620 }
1621 _ => {}
1622 }
1623 Ok(out)
1624 }
1625
1626 fn check_annotations(&self) -> Result<()> {
1627 let count = self.header.span(Section::Annotations).count;
1628 let node_count = self.node_count();
1629 let mut previous: Option<(u32, u32, u32, u32)> = None;
1630 for index in 0..count {
1631 let (node, kind, a, b) = self.annotation(index)?;
1632 if node >= node_count {
1633 return Err(Error::Annotation {
1634 id: index,
1635 reason: "annotation subject out of range",
1636 });
1637 }
1638 match kind {
1639 crate::layout::ANN_KIND_INTENT => {
1640 if a >= self.header.span(Section::Strings).count || b != 0 {
1641 return Err(Error::Annotation {
1642 id: index,
1643 reason: "invalid intent annotation",
1644 });
1645 }
1646 }
1647 crate::layout::ANN_KIND_CONSTRAINT => {
1648 if a >= self.header.span(Section::Strings).count || b >= node_count {
1649 return Err(Error::Annotation {
1650 id: index,
1651 reason: "invalid constraint annotation",
1652 });
1653 }
1654 }
1655 crate::layout::ANN_KIND_AI_IMMUTABLE => {
1656 if a > 1 || b != 0 {
1657 return Err(Error::Annotation {
1658 id: index,
1659 reason: "invalid immutability annotation",
1660 });
1661 }
1662 }
1663 _ => {
1664 return Err(Error::Annotation {
1665 id: index,
1666 reason: "unknown annotation kind",
1667 })
1668 }
1669 }
1670 if let Some(previous) = previous {
1671 if previous >= (node, kind, a, b) {
1672 return Err(Error::Annotation {
1673 id: index,
1674 reason: "annotations not sorted",
1675 });
1676 }
1677 }
1678 previous = Some((node, kind, a, b));
1679 }
1680 Ok(())
1681 }
1682
1683 fn check_provenance(&self, limits: Limits) -> Result<()> {
1684 let len = self.header.span(Section::Provenance).count as usize;
1685 if len == 0 {
1686 return Ok(());
1687 }
1688 let start = self.header.span(Section::Provenance).offset as usize;
1689 let count = read_u32(self.buf, start + 4)?;
1690 if count > limits.max_prov_entries {
1691 return Err(Error::Limit {
1692 kind: "provenance entries",
1693 limit: limits.max_prov_entries as u64,
1694 });
1695 }
1696 let prov = self.provenance()?;
1697 if let Some(prov) = prov {
1698 let mut previous: Option<&str> = None;
1699 for entry in &prov.entries {
1700 if let Some(previous) = previous {
1701 if previous >= entry.key.as_str() {
1702 return Err(Error::Provenance("entries not sorted by key"));
1703 }
1704 }
1705 previous = Some(entry.key.as_str());
1706 }
1707 if let Some(text) = &prov.producer {
1708 if text.is_empty() {
1709 return Err(Error::Provenance("empty producer"));
1710 }
1711 }
1712 }
1713 Ok(())
1714 }
1715
1716 fn check_extensions(&self) -> Result<()> {
1717 let count = self.header.span(Section::Extensions).count;
1718 if count == 0 {
1719 return Ok(());
1720 }
1721 let start = self.header.span(Section::Extensions).offset as u64;
1722 let end = self.ends[Section::Extensions as usize];
1723 let dir_end = start + count as u64 * EXT_DIR_SIZE as u64;
1724 if dir_end > end {
1725 return Err(Error::Offset {
1726 section: "extensions",
1727 offset: start,
1728 });
1729 }
1730 let mut previous_tag: Option<u64> = None;
1731 let mut previous_end = dir_end;
1732 for index in 0..count {
1733 let offset = start as usize + index as usize * EXT_DIR_SIZE;
1734 let tag = read_u64(self.buf, offset)?;
1735 let payload_offset = read_u32(self.buf, offset + 8)? as u64;
1736 let payload_len = read_u32(self.buf, offset + 12)? as u64;
1737 if let Some(previous) = previous_tag {
1738 if previous >= tag {
1739 return Err(Error::Extension {
1740 tag,
1741 reason: "extension tags not sorted",
1742 });
1743 }
1744 }
1745 previous_tag = Some(tag);
1746 if payload_offset < align8(previous_end) || payload_offset + payload_len > end {
1747 return Err(Error::Extension {
1748 tag,
1749 reason: "payload out of range",
1750 });
1751 }
1752 previous_end = payload_offset + payload_len;
1753 }
1754 if align8(previous_end) != end {
1755 return Err(Error::Extension {
1756 tag: 0,
1757 reason: "extension section has trailing bytes",
1758 });
1759 }
1760 Ok(())
1761 }
1762
1763 fn check_reachability(&self, limits: Limits) -> Result<()> {
1764 self.check_bindings()?;
1765 let count = self.node_count();
1766 if count == 0 {
1767 return Ok(());
1768 }
1769 let mut roots = Vec::new();
1770 if let Some(root) = self.root()? {
1771 roots.push(root.0);
1772 }
1773 for (_, id) in self.bindings()? {
1774 roots.push(id.0);
1775 }
1776 for index in 0..self.header.span(Section::Annotations).count {
1777 let (node, kind, _, b) = self.annotation(index)?;
1778 roots.push(node);
1779 if kind == crate::layout::ANN_KIND_CONSTRAINT {
1780 roots.push(b);
1781 }
1782 }
1783 roots.sort_unstable();
1784 roots.dedup();
1785 let mut seen = vec![false; count as usize];
1786 let mut stack: Vec<u32> = roots;
1787 let mut scratch: Vec<u32> = Vec::new();
1788 let mut visited = 0u32;
1789 while let Some(node) = stack.pop() {
1790 if seen[node as usize] {
1791 continue;
1792 }
1793 seen[node as usize] = true;
1794 visited += 1;
1795 let rec = self.node(NodeId(node))?;
1796 self.node_children_into(&rec, &mut scratch)?;
1797 for child in &scratch {
1798 if !seen[*child as usize] {
1799 stack.push(*child);
1800 }
1801 }
1802 }
1803 if visited != count {
1804 return Err(Error::Node {
1805 id: (0..count).find(|id| !seen[*id as usize]).unwrap_or(0),
1806 reason: "unreachable node",
1807 });
1808 }
1809 let _ = limits;
1810 Ok(())
1811 }
1812
1813 fn check_depth(&self, limits: Limits) -> Result<()> {
1814 if limits.max_depth == 0 {
1815 return Ok(());
1816 }
1817 let count = self.node_count();
1818 if count == 0 {
1819 return Ok(());
1820 }
1821 let mut depth = vec![0u32; count as usize];
1822 let mut scratch: Vec<u32> = Vec::new();
1823 let mut has_parent = vec![false; count as usize];
1827 for node in 0..count {
1828 let rec = self.node(NodeId(node))?;
1829 self.node_children_into(&rec, &mut scratch)?;
1830 for child in &scratch {
1831 has_parent[*child as usize] = true;
1832 }
1833 }
1834 let mut stack: Vec<(u32, u32)> = Vec::new();
1835 for node in 0..count {
1836 if !has_parent[node as usize] {
1837 depth[node as usize] = 1;
1838 stack.push((node, 0));
1839 }
1840 }
1841 while let Some((node, cursor)) = stack.pop() {
1842 let rec = self.node(NodeId(node))?;
1843 self.node_children_into(&rec, &mut scratch)?;
1844 if cursor as usize >= scratch.len() {
1845 continue;
1846 }
1847 stack.push((node, cursor + 1));
1848 let child = scratch[cursor as usize];
1849 let candidate = depth[node as usize] + 1;
1850 if candidate > limits.max_depth {
1851 return Err(Error::Depth {
1852 limit: limits.max_depth,
1853 });
1854 }
1855 if candidate > depth[child as usize] {
1856 depth[child as usize] = candidate;
1857 stack.push((child, 0));
1858 }
1859 }
1860 Ok(())
1861 }
1862}
1863
1864fn validate_counts(header: &Header, limits: Limits) -> Result<()> {
1866 let checks: [(Section, u32, u32); SECTION_COUNT] = [
1867 (
1868 Section::Nodes,
1869 header.span(Section::Nodes).count,
1870 limits.max_nodes,
1871 ),
1872 (
1873 Section::Children,
1874 header.span(Section::Children).count,
1875 limits.max_children,
1876 ),
1877 (
1878 Section::Fields,
1879 header.span(Section::Fields).count,
1880 limits.max_fields,
1881 ),
1882 (
1883 Section::Parts,
1884 header.span(Section::Parts).count,
1885 limits.max_parts,
1886 ),
1887 (
1888 Section::Edges,
1889 header.span(Section::Edges).count,
1890 limits.max_edges,
1891 ),
1892 (
1893 Section::Strings,
1894 header.span(Section::Strings).count,
1895 limits.max_strings,
1896 ),
1897 (
1898 Section::Blobs,
1899 header.span(Section::Blobs).count,
1900 limits.max_blobs,
1901 ),
1902 (
1903 Section::Annotations,
1904 header.span(Section::Annotations).count,
1905 limits.max_annotations,
1906 ),
1907 (
1908 Section::Units,
1909 header.span(Section::Units).count,
1910 limits.max_units,
1911 ),
1912 (
1913 Section::Types,
1914 header.span(Section::Types).count,
1915 limits.max_types,
1916 ),
1917 (
1918 Section::Aux,
1919 header.span(Section::Aux).count,
1920 limits.max_aux,
1921 ),
1922 (
1923 Section::Numbers,
1924 header.span(Section::Numbers).count,
1925 limits.max_numbers,
1926 ),
1927 (
1928 Section::Secrets,
1929 header.span(Section::Secrets).count,
1930 limits.max_secrets,
1931 ),
1932 (
1933 Section::Provenance,
1934 header.span(Section::Provenance).count,
1935 u32::MAX,
1936 ),
1937 (
1938 Section::Hashes,
1939 header.span(Section::Hashes).count,
1940 u32::MAX,
1941 ),
1942 (
1943 Section::Extensions,
1944 header.span(Section::Extensions).count,
1945 limits.max_extensions,
1946 ),
1947 (
1948 Section::Bindings,
1949 header.span(Section::Bindings).count,
1950 u32::MAX,
1951 ),
1952 ];
1953 for (section, count, limit) in checks {
1954 if count > limit {
1955 return Err(Error::Limit {
1956 kind: section.name(),
1957 limit: limit as u64,
1958 });
1959 }
1960 }
1961 Ok(())
1962}
1963
1964fn validate_layout(buf: &[u8], header: &Header) -> Result<[u64; SECTION_COUNT]> {
1967 if header.total_size > buf.len() as u64 {
1969 return Err(Error::Truncated {
1970 needed: header.total_size,
1971 found: buf.len() as u64,
1972 });
1973 }
1974 if header.total_size < buf.len() as u64 {
1975 return Err(Error::TrailingBytes {
1976 at: header.total_size,
1977 });
1978 }
1979 let mut ends = [0u64; SECTION_COUNT];
1980 let mut cursor = HEADER_SIZE as u64;
1981 for section in Section::ALL {
1982 let span = header.span(section);
1983 if span.offset as u64 != align8(cursor) {
1984 return Err(Error::Offset {
1985 section: section.name(),
1986 offset: span.offset as u64,
1987 });
1988 }
1989 if span.offset as u64 > buf.len() as u64 {
1990 return Err(Error::Truncated {
1991 needed: span.offset as u64,
1992 found: buf.len() as u64,
1993 });
1994 }
1995 for offset in cursor..span.offset as u64 {
1997 if buf[offset as usize] != 0 {
1998 return Err(Error::Padding { offset });
1999 }
2000 }
2001 let end = section_end(buf, header, section, span.offset as u64, span.count)?;
2002 if end > header.total_size || end > buf.len() as u64 {
2003 return Err(Error::Count {
2006 field: section.name(),
2007 value: span.count as u64,
2008 });
2009 }
2010 ends[section as usize] = end;
2011 cursor = end;
2012 }
2013 if header.total_size != align8(cursor) {
2014 return Err(Error::Offset {
2015 section: "total-size",
2016 offset: header.total_size,
2017 });
2018 }
2019 if header.total_size != buf.len() as u64 {
2020 if header.total_size < buf.len() as u64 {
2021 return Err(Error::TrailingBytes {
2022 at: header.total_size,
2023 });
2024 }
2025 return Err(Error::Truncated {
2026 needed: header.total_size,
2027 found: buf.len() as u64,
2028 });
2029 }
2030 Ok(ends)
2031}
2032
2033fn section_end(
2034 buf: &[u8],
2035 _header: &Header,
2036 section: Section,
2037 offset: u64,
2038 count: u32,
2039) -> Result<u64> {
2040 let end = match section {
2041 Section::Strings => {
2042 if count == 0 {
2043 offset
2044 } else {
2045 let table = offset as usize;
2046 let heap_len = read_u32(buf, table + count as usize * 4)? as u64;
2047 offset + (count as u64 + 1) * 4 + heap_len
2048 }
2049 }
2050 Section::Blobs => {
2051 let table = offset as usize;
2052 let mut heap = 0u64;
2053 for index in 0..count {
2054 let len = read_u32(buf, table + index as usize * BLOB_ENTRY_SIZE + 36)? as u64;
2055 heap = heap.checked_add(len).ok_or(Error::Blob {
2056 id: index,
2057 reason: "payload heap overflows",
2058 })?;
2059 }
2060 offset + count as u64 * BLOB_ENTRY_SIZE as u64 + heap
2061 }
2062 Section::Provenance => offset + count as u64,
2063 Section::Secrets => {
2064 let table = offset as usize;
2065 let mut heap = 0u64;
2066 for index in 0..count {
2067 let len = read_u32(buf, table + index as usize * 8 + 4)? as u64;
2068 heap = heap.checked_add(len).ok_or(Error::SecretEntry(index))?;
2069 }
2070 offset + count as u64 * 8 + heap
2071 }
2072 Section::Extensions => {
2073 if count == 0 {
2074 offset
2075 } else {
2076 let mut max_end = offset + count as u64 * EXT_DIR_SIZE as u64;
2077 for index in 0..count {
2078 let dir = offset as usize + index as usize * EXT_DIR_SIZE;
2079 let payload_offset = read_u32(buf, dir + 8)? as u64;
2080 let payload_len = read_u32(buf, dir + 12)? as u64;
2081 max_end = max_end.max(payload_offset + payload_len);
2082 }
2083 max_end
2084 }
2085 }
2086 Section::Nodes => offset + count as u64 * NODE_RECORD_SIZE as u64,
2087 Section::Children => offset + count as u64 * 4,
2088 Section::Fields => offset + count as u64 * FIELD_SIZE as u64,
2089 Section::Parts => offset + count as u64 * 8,
2090 Section::Edges => offset + count as u64 * 8,
2091 Section::Annotations => offset + count as u64 * 16,
2092 Section::Units => offset + count as u64 * 8,
2093 Section::Types => offset + count as u64 * 16,
2094 Section::Aux => offset + count as u64 * 8,
2095 Section::Numbers => offset + count as u64 * 8,
2096 Section::Hashes => offset + count as u64 * 32,
2097 Section::Bindings => offset + count as u64 * 8,
2098 };
2099 Ok(end)
2100}
2101
2102pub fn validate(buf: &[u8]) -> Result<Meta> {
2104 Validated::new(buf, crate::layout::DecodeOpts::default()).map(|validated| validated.meta())
2105}
2106
2107pub fn validate_with(buf: &[u8], opts: crate::layout::DecodeOpts) -> Result<Meta> {
2109 Validated::new(buf, opts).map(|validated| validated.meta())
2110}
2111
2112pub fn validate_canonical(buf: &[u8], limits: Limits) -> Result<Meta> {
2115 let opts = crate::layout::DecodeOpts {
2116 limits,
2117 verify_hash: true,
2118 verify_canonical: true,
2119 };
2120 let validated = Validated::new(buf, opts)?;
2121 let module = crate::decode::decode_validated(&validated)?;
2122 let image = crate::encode::encode_with(
2123 &module,
2124 crate::layout::EncodeOpts {
2125 write_hash: true,
2126 mark_canonical: true,
2127 streamed: validated.header.flags & crate::layout::FLAG_STREAMED != 0,
2128 },
2129 )?;
2130 if image != buf {
2131 return Err(Error::NotCanonical);
2132 }
2133 Ok(validated.meta())
2134}
2135
2136pub fn provenance_hash_of(prov: Option<&Provenance>) -> Option<[u8; 32]> {
2138 prov.and_then(|prov| prov.parent)
2139}
2140
2141pub fn is_hash_entry(entry: &crate::ir::ProvEntry) -> bool {
2143 matches!(entry.value, ProvValue::Hash(_))
2144}