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