wgpu_core/
pipeline_cache.rs

1use thiserror::Error;
2use wgt::{
3    error::{ErrorType, WebGpuError},
4    AdapterInfo,
5};
6
7pub const HEADER_LENGTH: usize = size_of::<PipelineCacheHeader>();
8
9#[derive(Debug, PartialEq, Eq, Clone, Error)]
10#[non_exhaustive]
11pub enum PipelineCacheValidationError {
12    #[error("The pipeline cache data was truncated")]
13    Truncated,
14    #[error("The pipeline cache data was longer than recorded")]
15    // TODO: Is it plausible that this would happen
16    Extended,
17    #[error("The pipeline cache data was corrupted (e.g. the hash didn't match)")]
18    Corrupted,
19    #[error("The pipeline cacha data was out of date and so cannot be safely used")]
20    Outdated,
21    #[error("The cache data was created for a different device")]
22    DeviceMismatch,
23    #[error("Pipeline cacha data was created for a future version of wgpu")]
24    Unsupported,
25}
26
27impl PipelineCacheValidationError {
28    /// Could the error have been avoided?
29    /// That is, is there a mistake in user code interacting with the cache
30    pub fn was_avoidable(&self) -> bool {
31        match self {
32            PipelineCacheValidationError::DeviceMismatch => true,
33            PipelineCacheValidationError::Truncated
34            | PipelineCacheValidationError::Unsupported
35            | PipelineCacheValidationError::Extended
36            // It's unusual, but not implausible, to be downgrading wgpu
37            | PipelineCacheValidationError::Outdated
38            | PipelineCacheValidationError::Corrupted => false,
39        }
40    }
41}
42
43impl WebGpuError for PipelineCacheValidationError {
44    fn webgpu_error_type(&self) -> ErrorType {
45        ErrorType::Validation
46    }
47}
48
49/// Validate the data in a pipeline cache
50pub fn validate_pipeline_cache<'d>(
51    cache_data: &'d [u8],
52    adapter: &AdapterInfo,
53    validation_key: [u8; 16],
54) -> Result<&'d [u8], PipelineCacheValidationError> {
55    let adapter_key = adapter_key(adapter)?;
56    let Some((header, remaining_data)) = PipelineCacheHeader::read(cache_data) else {
57        return Err(PipelineCacheValidationError::Truncated);
58    };
59    if header.magic != MAGIC {
60        return Err(PipelineCacheValidationError::Corrupted);
61    }
62    if header.header_version != HEADER_VERSION {
63        return Err(PipelineCacheValidationError::Outdated);
64    }
65    if header.cache_abi != ABI {
66        return Err(PipelineCacheValidationError::Outdated);
67    }
68    if header.backend != adapter.backend as u8 {
69        return Err(PipelineCacheValidationError::DeviceMismatch);
70    }
71    if header.adapter_key != adapter_key {
72        return Err(PipelineCacheValidationError::DeviceMismatch);
73    }
74    if header.validation_key != validation_key {
75        // If the validation key is wrong, that means that this device has changed
76        // in a way where the cache won't be compatible since the cache was made,
77        // so it is outdated
78        return Err(PipelineCacheValidationError::Outdated);
79    }
80    let data_size: usize = header
81        .data_size
82        .try_into()
83        // If the data was previously more than 4GiB, and we're still on a 32 bit system (ABI check, above)
84        // Then the data must be corrupted
85        .map_err(|_| PipelineCacheValidationError::Corrupted)?;
86    if remaining_data.len() < data_size {
87        return Err(PipelineCacheValidationError::Truncated);
88    }
89    if remaining_data.len() > data_size {
90        return Err(PipelineCacheValidationError::Extended);
91    }
92    if header.hash_space != HASH_SPACE_VALUE {
93        return Err(PipelineCacheValidationError::Corrupted);
94    }
95    Ok(remaining_data)
96}
97
98pub fn add_cache_header(
99    in_region: &mut [u8],
100    data: &[u8],
101    adapter: &AdapterInfo,
102    validation_key: [u8; 16],
103) {
104    assert_eq!(in_region.len(), HEADER_LENGTH);
105    let header = PipelineCacheHeader {
106        adapter_key: adapter_key(adapter)
107            .expect("Called add_cache_header for an adapter which doesn't support cache data. This is a wgpu internal bug"),
108        backend: adapter.backend as u8,
109        cache_abi: ABI,
110        magic: MAGIC,
111        header_version: HEADER_VERSION,
112        validation_key,
113        hash_space: HASH_SPACE_VALUE,
114        data_size: data
115            .len()
116            .try_into()
117            .expect("Cache larger than u64::MAX bytes"),
118    };
119    header.write(in_region);
120}
121
122const MAGIC: [u8; 8] = *b"WGPUPLCH";
123const HEADER_VERSION: u32 = 1;
124const ABI: u32 = size_of::<*const ()>() as u32;
125
126/// The value used to fill [`PipelineCacheHeader::hash_space`]
127///
128/// If we receive reports of pipeline cache data corruption which is not otherwise caught
129/// on a real device, it would be worth modifying this
130///
131/// Note that wgpu does not protect against malicious writes to e.g. a file used
132/// to store a pipeline cache.
133/// That is the responsibility of the end application, such as by using a
134/// private space.
135const HASH_SPACE_VALUE: u64 = 0xFEDCBA9_876543210;
136
137#[repr(C)]
138#[derive(PartialEq, Eq)]
139struct PipelineCacheHeader {
140    /// The magic header to ensure that we have the right file format
141    /// Has a value of MAGIC, as above
142    magic: [u8; 8],
143    // /// The total size of this header, in bytes
144    // header_size: u32,
145    /// The version of this wgpu header
146    /// Should be equal to HEADER_VERSION above
147    ///
148    /// This must always be the second item, after the value above
149    header_version: u32,
150    /// The number of bytes in the pointers of this ABI, because some drivers
151    /// have previously not distinguished between their 32 bit and 64 bit drivers
152    /// leading to Vulkan data corruption
153    cache_abi: u32,
154    /// The id for the backend in use, from [wgt::Backend]
155    backend: u8,
156    /// The key which identifiers the device/adapter.
157    /// This is used to validate that this pipeline cache (probably) was produced for
158    /// the expected device.
159    /// On Vulkan: it is a combination of vendor ID and device ID
160    adapter_key: [u8; 15],
161    /// A key used to validate that this device is still compatible with the cache
162    ///
163    /// This should e.g. contain driver version and/or intermediate compiler versions
164    validation_key: [u8; 16],
165    /// The length of the data which is sent to/recieved from the backend
166    data_size: u64,
167    /// Space reserved for a hash of the data in future
168    ///
169    /// We assume that your cache storage system will be relatively robust, and so
170    /// do not validate this hash
171    ///
172    /// Therefore, this will always have a value of [`HASH_SPACE_VALUE`]
173    hash_space: u64,
174}
175
176impl PipelineCacheHeader {
177    fn read(data: &[u8]) -> Option<(PipelineCacheHeader, &[u8])> {
178        let mut reader = Reader {
179            data,
180            total_read: 0,
181        };
182        let magic = reader.read_array()?;
183        let header_version = reader.read_u32()?;
184        let cache_abi = reader.read_u32()?;
185        let backend = reader.read_byte()?;
186        let adapter_key = reader.read_array()?;
187        let validation_key = reader.read_array()?;
188        let data_size = reader.read_u64()?;
189        let data_hash = reader.read_u64()?;
190
191        assert_eq!(reader.total_read, size_of::<PipelineCacheHeader>());
192
193        Some((
194            PipelineCacheHeader {
195                magic,
196                header_version,
197                cache_abi,
198                backend,
199                adapter_key,
200                validation_key,
201                data_size,
202                hash_space: data_hash,
203            },
204            reader.data,
205        ))
206    }
207
208    fn write(&self, into: &mut [u8]) -> Option<()> {
209        let mut writer = Writer { data: into };
210        writer.write_array(&self.magic)?;
211        writer.write_u32(self.header_version)?;
212        writer.write_u32(self.cache_abi)?;
213        writer.write_byte(self.backend)?;
214        writer.write_array(&self.adapter_key)?;
215        writer.write_array(&self.validation_key)?;
216        writer.write_u64(self.data_size)?;
217        writer.write_u64(self.hash_space)?;
218
219        assert_eq!(writer.data.len(), 0);
220        Some(())
221    }
222}
223
224fn adapter_key(adapter: &AdapterInfo) -> Result<[u8; 15], PipelineCacheValidationError> {
225    match adapter.backend {
226        wgt::Backend::Vulkan => {
227            // If these change size, the header format needs to change
228            // We set the type explicitly so this won't compile in that case
229            let v: [u8; 4] = adapter.vendor.to_be_bytes();
230            let d: [u8; 4] = adapter.device.to_be_bytes();
231            let adapter = [
232                255, 255, 255, v[0], v[1], v[2], v[3], d[0], d[1], d[2], d[3], 255, 255, 255, 255,
233            ];
234            Ok(adapter)
235        }
236        _ => Err(PipelineCacheValidationError::Unsupported),
237    }
238}
239
240struct Reader<'a> {
241    data: &'a [u8],
242    total_read: usize,
243}
244
245impl<'a> Reader<'a> {
246    fn read_byte(&mut self) -> Option<u8> {
247        let res = *self.data.first()?;
248        self.total_read += 1;
249        self.data = &self.data[1..];
250        Some(res)
251    }
252    fn read_array<const N: usize>(&mut self) -> Option<[u8; N]> {
253        // Only greater than because we're indexing fenceposts, not items
254        if N > self.data.len() {
255            return None;
256        }
257        let (start, data) = self.data.split_at(N);
258        self.total_read += N;
259        self.data = data;
260        Some(start.try_into().expect("off-by-one-error in array size"))
261    }
262
263    // fn read_u16(&mut self) -> Option<u16> {
264    //     self.read_array().map(u16::from_be_bytes)
265    // }
266    fn read_u32(&mut self) -> Option<u32> {
267        self.read_array().map(u32::from_be_bytes)
268    }
269    fn read_u64(&mut self) -> Option<u64> {
270        self.read_array().map(u64::from_be_bytes)
271    }
272}
273
274struct Writer<'a> {
275    data: &'a mut [u8],
276}
277
278impl<'a> Writer<'a> {
279    fn write_byte(&mut self, byte: u8) -> Option<()> {
280        self.write_array(&[byte])
281    }
282    fn write_array<const N: usize>(&mut self, array: &[u8; N]) -> Option<()> {
283        // Only greater than because we're indexing fenceposts, not items
284        if N > self.data.len() {
285            return None;
286        }
287        let data = core::mem::take(&mut self.data);
288        let (start, data) = data.split_at_mut(N);
289        self.data = data;
290        start.copy_from_slice(array);
291        Some(())
292    }
293
294    // fn write_u16(&mut self, value: u16) -> Option<()> {
295    //     self.write_array(&value.to_be_bytes())
296    // }
297    fn write_u32(&mut self, value: u32) -> Option<()> {
298        self.write_array(&value.to_be_bytes())
299    }
300    fn write_u64(&mut self, value: u64) -> Option<()> {
301        self.write_array(&value.to_be_bytes())
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use alloc::{string::String, vec::Vec};
308    use wgt::AdapterInfo;
309
310    use crate::pipeline_cache::{PipelineCacheValidationError as E, HEADER_LENGTH};
311
312    use super::ABI;
313
314    // Assert the correct size
315    const _: [(); HEADER_LENGTH] = [(); 64];
316
317    const ADAPTER: AdapterInfo = AdapterInfo {
318        name: String::new(),
319        vendor: 0x0002_FEED,
320        device: 0xFEFE_FEFE,
321        device_type: wgt::DeviceType::Other,
322        device_pci_bus_id: String::new(),
323        driver: String::new(),
324        driver_info: String::new(),
325        backend: wgt::Backend::Vulkan,
326        subgroup_min_size: 32,
327        subgroup_max_size: 32,
328        transient_saves_memory: true,
329        limit_bucket: None,
330    };
331
332    // IMPORTANT: If these tests fail, then you MUST increment HEADER_VERSION
333    const VALIDATION_KEY: [u8; 16] = u128::to_be_bytes(0xFFFFFFFF_FFFFFFFF_88888888_88888888);
334    #[test]
335    fn written_header() {
336        let mut result = [0; HEADER_LENGTH];
337        super::add_cache_header(&mut result, &[], &ADAPTER, VALIDATION_KEY);
338        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
339            *b"WGPUPLCH",                                 // MAGIC
340            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
341            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
342            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
343            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
344            0x88888888_88888888u64.to_be_bytes(),         // Validation key
345            0x0u64.to_be_bytes(),                         // Data size
346            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
347        ];
348        let expected = cache.into_iter().flatten().collect::<Vec<u8>>();
349
350        assert_eq!(result.as_slice(), expected.as_slice());
351    }
352
353    #[test]
354    fn valid_data() {
355        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
356            *b"WGPUPLCH",                                 // MAGIC
357            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
358            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
359            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
360            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
361            0x88888888_88888888u64.to_be_bytes(),         // Validation key
362            0x0u64.to_be_bytes(),                         // Data size
363            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
364        ];
365        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
366        let expected: &[u8] = &[];
367        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
368        assert_eq!(validation_result, Ok(expected));
369    }
370    #[test]
371    fn invalid_magic() {
372        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
373            *b"NOT_WGPU",                                 // (Wrong) MAGIC
374            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
375            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
376            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
377            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
378            0x88888888_88888888u64.to_be_bytes(),         // Validation key
379            0x0u64.to_be_bytes(),                         // Data size
380            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
381        ];
382        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
383        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
384        assert_eq!(validation_result, Err(E::Corrupted));
385    }
386
387    #[test]
388    fn wrong_version() {
389        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
390            *b"WGPUPLCH",                                 // MAGIC
391            [0, 0, 0, 2, 0, 0, 0, ABI as u8],             // (wrong) Version and ABI
392            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
393            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
394            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
395            0x88888888_88888888u64.to_be_bytes(),         // Validation key
396            0x0u64.to_be_bytes(),                         // Data size
397            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
398        ];
399        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
400        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
401        assert_eq!(validation_result, Err(E::Outdated));
402    }
403    #[test]
404    fn wrong_abi() {
405        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
406            *b"WGPUPLCH", // MAGIC
407            // a 14 bit ABI is improbable
408            [0, 0, 0, 1, 0, 0, 0, 14],            // Version and (wrong) ABI
409            [1, 255, 255, 255, 0, 2, 0xFE, 0xED], // Backend and Adapter key
410            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
411            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(), // Validation key
412            0x88888888_88888888u64.to_be_bytes(), // Validation key
413            0x0u64.to_be_bytes(),                 // Data size
414            0xFEDCBA9_876543210u64.to_be_bytes(), // Header
415        ];
416        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
417        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
418        assert_eq!(validation_result, Err(E::Outdated));
419    }
420
421    #[test]
422    fn wrong_backend() {
423        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
424            *b"WGPUPLCH",                                 // MAGIC
425            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
426            [2, 255, 255, 255, 0, 2, 0xFE, 0xED],         // (wrong) Backend and Adapter key
427            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
428            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
429            0x88888888_88888888u64.to_be_bytes(),         // Validation key
430            0x0u64.to_be_bytes(),                         // Data size
431            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
432        ];
433        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
434        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
435        assert_eq!(validation_result, Err(E::DeviceMismatch));
436    }
437    #[test]
438    fn wrong_adapter() {
439        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
440            *b"WGPUPLCH",                                 // MAGIC
441            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
442            [1, 255, 255, 255, 0, 2, 0xFE, 0x00],         // Backend and (wrong) Adapter key
443            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
444            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
445            0x88888888_88888888u64.to_be_bytes(),         // Validation key
446            0x0u64.to_be_bytes(),                         // Data size
447            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
448        ];
449        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
450        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
451        assert_eq!(validation_result, Err(E::DeviceMismatch));
452    }
453    #[test]
454    fn wrong_validation() {
455        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
456            *b"WGPUPLCH",                                 // MAGIC
457            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
458            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
459            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
460            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
461            0x88888888_00000000u64.to_be_bytes(),         // (wrong) Validation key
462            0x0u64.to_be_bytes(),                         // Data size
463            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
464        ];
465        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
466        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
467        assert_eq!(validation_result, Err(E::Outdated));
468    }
469    #[test]
470    fn too_little_data() {
471        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
472            *b"WGPUPLCH",                                 // MAGIC
473            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
474            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
475            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
476            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
477            0x88888888_88888888u64.to_be_bytes(),         // Validation key
478            0x064u64.to_be_bytes(),                       // Data size
479            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
480        ];
481        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
482        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
483        assert_eq!(validation_result, Err(E::Truncated));
484    }
485    #[test]
486    fn not_no_data() {
487        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
488            *b"WGPUPLCH",                                 // MAGIC
489            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
490            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
491            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
492            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
493            0x88888888_88888888u64.to_be_bytes(),         // Validation key
494            100u64.to_be_bytes(),                         // Data size
495            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
496        ];
497        let cache = cache
498            .into_iter()
499            .flatten()
500            .chain(core::iter::repeat_n(0u8, 100))
501            .collect::<Vec<u8>>();
502        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
503        let expected: &[u8] = &[0; 100];
504        assert_eq!(validation_result, Ok(expected));
505    }
506    #[test]
507    fn too_much_data() {
508        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
509            *b"WGPUPLCH",                                 // MAGIC
510            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
511            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
512            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
513            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
514            0x88888888_88888888u64.to_be_bytes(),         // Validation key
515            0x064u64.to_be_bytes(),                       // Data size
516            0xFEDCBA9_876543210u64.to_be_bytes(),         // Hash
517        ];
518        let cache = cache
519            .into_iter()
520            .flatten()
521            .chain(core::iter::repeat_n(0u8, 200))
522            .collect::<Vec<u8>>();
523        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
524        assert_eq!(validation_result, Err(E::Extended));
525    }
526    #[test]
527    fn wrong_hash() {
528        let cache: [[u8; 8]; HEADER_LENGTH / 8] = [
529            *b"WGPUPLCH",                                 // MAGIC
530            [0, 0, 0, 1, 0, 0, 0, ABI as u8],             // Version and ABI
531            [1, 255, 255, 255, 0, 2, 0xFE, 0xED],         // Backend and Adapter key
532            [0xFE, 0xFE, 0xFE, 0xFE, 255, 255, 255, 255], // Backend and Adapter key
533            0xFFFFFFFF_FFFFFFFFu64.to_be_bytes(),         // Validation key
534            0x88888888_88888888u64.to_be_bytes(),         // Validation key
535            0x0u64.to_be_bytes(),                         // Data size
536            0x00000000_00000000u64.to_be_bytes(),         // Hash
537        ];
538        let cache = cache.into_iter().flatten().collect::<Vec<u8>>();
539        let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY);
540        assert_eq!(validation_result, Err(E::Corrupted));
541    }
542}