/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tiff-0.10.3/src/decoder/cycles.rs
Line | Count | Source |
1 | | use crate::{tags::IfdPointer, TiffError, TiffFormatError}; |
2 | | use std::collections::HashMap; |
3 | | |
4 | | /// The IFD structure of a TIFF file should be limited to a forest of entries, and a tree when we |
5 | | /// only consider having a primary IFD and its children. There is up to one primary child of a node |
6 | | /// that is the `next` entry in an IFD itself. Since we offer iteration over this chain we want to |
7 | | /// detect when a new edge would introduce a cycle, such that callers can be guaranteed to |
8 | | /// terminate iteration at some point even in malicious images. |
9 | | /// |
10 | | /// However, we are not necessarily visiting the IFDs in their topological order. |
11 | | /// |
12 | | /// Since we only consider one child, we run union find to assign each pointer to a known chain. |
13 | | /// Then, when we insert a new edge we check if they belong to the same component. |
14 | | #[derive(Default, Debug)] |
15 | | pub struct IfdCycles { |
16 | | /// The root of each component union. |
17 | | component_union: HashMap<ComponentId, ComponentId>, |
18 | | links: HashMap<IfdPointer, u64>, |
19 | | chains: HashMap<IfdPointer, ComponentId>, |
20 | | } |
21 | | |
22 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
23 | | struct ComponentId(u64); |
24 | | |
25 | | impl IfdCycles { |
26 | 3.98k | pub fn new() -> Self { |
27 | 3.98k | IfdCycles::default() |
28 | 3.98k | } |
29 | | |
30 | 3.65k | pub fn insert_next( |
31 | 3.65k | &mut self, |
32 | 3.65k | from: IfdPointer, |
33 | 3.65k | to: Option<IfdPointer>, |
34 | 3.65k | ) -> Result<bool, TiffError> { |
35 | 3.65k | let to_offset = to.map_or(0, |p| p.0); |
36 | | |
37 | | // For some reason we got |
38 | 3.65k | match self.links.get(&from) { |
39 | 0 | Some(existing) if *existing == to_offset => return Ok(false), |
40 | | // We got here twice with two different reads of the same IFD. That is .. unusual and |
41 | | // we interpret it as a cycle. |
42 | 0 | Some(_) => return Err(TiffError::FormatError(TiffFormatError::CycleInOffsets)), |
43 | 3.65k | None => self.links.insert(from, to_offset), |
44 | | }; |
45 | | |
46 | 3.65k | self.ensure_node(from); |
47 | | |
48 | 3.65k | if let Some(to) = to { |
49 | 3.06k | self.ensure_node(to); |
50 | | |
51 | 3.06k | let parent = self.nominal_component(from); |
52 | 3.06k | let child = self.nominal_component(to); |
53 | | |
54 | 3.06k | if parent == child { |
55 | 2 | return Err(TiffError::FormatError(TiffFormatError::CycleInOffsets)); |
56 | 3.05k | } |
57 | | |
58 | 3.05k | self.component_union.insert(child, parent); |
59 | 591 | } |
60 | | |
61 | 3.64k | Ok(true) |
62 | 3.65k | } |
63 | | |
64 | 6.71k | fn ensure_node(&mut self, ifd: IfdPointer) { |
65 | 6.71k | self.chains.entry(ifd).or_insert_with(|| { |
66 | 6.70k | let id = ComponentId(self.component_union.len() as u64); |
67 | 6.70k | self.component_union.insert(id, id); |
68 | 6.70k | id |
69 | 6.70k | }); |
70 | 6.71k | } |
71 | | |
72 | 6.12k | fn nominal_component(&mut self, node: IfdPointer) -> ComponentId { |
73 | 6.12k | let id = self.chains[&node]; |
74 | | |
75 | 6.12k | let nomimal = { |
76 | 6.12k | let mut iter = id; |
77 | | |
78 | | loop { |
79 | 6.12k | let parent = self.component_union[&iter]; |
80 | | |
81 | 6.12k | if parent == iter { |
82 | 6.12k | break parent; |
83 | 0 | } |
84 | | |
85 | 0 | iter = parent; |
86 | | } |
87 | | }; |
88 | | |
89 | 6.12k | if nomimal != id { |
90 | | // Compress. |
91 | 0 | let mut iter = id; |
92 | | |
93 | | loop { |
94 | 0 | let parent = self.component_union[&iter]; |
95 | | |
96 | 0 | if parent == iter { |
97 | 0 | break; |
98 | 0 | } |
99 | | |
100 | 0 | self.component_union.insert(iter, nomimal); |
101 | 0 | iter = parent; |
102 | | } |
103 | 6.12k | } |
104 | | |
105 | 6.12k | nomimal |
106 | 6.12k | } |
107 | | } |
108 | | |
109 | | #[test] |
110 | | fn cycles_are_detected() { |
111 | | let mut cycles = IfdCycles::new(); |
112 | | |
113 | | cycles |
114 | | .insert_next(IfdPointer(0x20), Some(IfdPointer(0x800))) |
115 | | .expect("non-existing link is valid"); |
116 | | |
117 | | cycles |
118 | | .insert_next(IfdPointer(0x800), Some(IfdPointer(0x20))) |
119 | | .expect_err("cycle must be detected"); |
120 | | } |
121 | | |
122 | | #[test] |
123 | | fn reflective_cycle() { |
124 | | let mut cycles = IfdCycles::new(); |
125 | | |
126 | | cycles |
127 | | .insert_next(IfdPointer(0x20), Some(IfdPointer(0x20))) |
128 | | .expect_err("self-referential cycle must be detected"); |
129 | | } |
130 | | |
131 | | #[test] |
132 | | fn late_cycle() { |
133 | | let mut cycles = IfdCycles::new(); |
134 | | |
135 | | cycles |
136 | | .insert_next(IfdPointer(0x20), Some(IfdPointer(0x40))) |
137 | | .expect("non-existing link is valid"); |
138 | | |
139 | | cycles |
140 | | .insert_next(IfdPointer(0x60), Some(IfdPointer(0x80))) |
141 | | .expect("non-existing link is valid"); |
142 | | cycles |
143 | | .insert_next(IfdPointer(0x80), Some(IfdPointer(0x20))) |
144 | | .expect("non-existing link is valid"); |
145 | | |
146 | | cycles |
147 | | .insert_next(IfdPointer(0x40), Some(IfdPointer(0x60))) |
148 | | .expect_err("non-existing link is valid"); |
149 | | } |
150 | | |
151 | | #[test] |
152 | | fn odd_cycle() { |
153 | | let mut cycles = IfdCycles::new(); |
154 | | |
155 | | cycles |
156 | | .insert_next(IfdPointer(0x20), Some(IfdPointer(0x40))) |
157 | | .expect("non-existing link is valid"); |
158 | | |
159 | | cycles |
160 | | .insert_next(IfdPointer(0x60), Some(IfdPointer(0x80))) |
161 | | .expect("non-existing link is valid"); |
162 | | cycles |
163 | | .insert_next(IfdPointer(0x80), Some(IfdPointer(0x20))) |
164 | | .expect("non-existing link is valid"); |
165 | | |
166 | | cycles |
167 | | .insert_next(IfdPointer(0x40), Some(IfdPointer(0x80))) |
168 | | .expect_err("non-existing link is valid"); |
169 | | } |