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