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