fn do_random_request_parse()

in src/devices/src/virtio/block/request.rs [802:958]


    fn do_random_request_parse(
        sparsity: u64,
        data_len: u32,
        sector: u64,
        request_type: RequestType,
        virtio_request_id: u32,
        coins_arr: &[bool],
    ) -> (Result<Request, Error>, GuestMemoryMmap, Queue) {
        let coins = &mut coins_arr.iter();

        // Randomize descriptor addresses. Assumed page size as max buffer len.
        let base_addr = sparsity & 0x0000_FFFF_FFFF_F000; // 48 bit base, page aligned.
        let max_desc_len = 0x1000;

        // First addr starts at page base + 1.
        let req_type_addr = GuestAddress(base_addr).checked_add(0x1000).unwrap();

        // Use first 4 bits of randomness to shift the gap size between this descriptor
        // and the next one.
        let mut next_desc_dist = max_desc_len + (0x1000 << (sparsity & 0xF));
        let data_addr = req_type_addr.checked_add(next_desc_dist).unwrap();

        // Use next 4 bits of randomness to shift gap size between this descriptor
        // and the next one.
        next_desc_dist = max_desc_len + (0x1000 << ((sparsity & 0xF0) >> 4));
        let status_addr = data_addr.checked_add(next_desc_dist).unwrap();

        let mem_end = status_addr.checked_add(max_desc_len).unwrap();
        let mem: GuestMemoryMmap = create_anon_guest_memory(
            &[(
                GuestAddress(base_addr),
                (mem_end.0 - base_addr).try_into().unwrap(),
            )],
            false,
        )
        .unwrap();

        let mut rq = RequestVirtQueue::new(GuestAddress(base_addr), &mem);
        let q = rq.vq.create_queue();

        // Make sure that data_len is a multiple of 512
        // and that 512 <= data_len <= (4096 + 512).
        let valid_data_len = ((data_len & 4096) | (SECTOR_SIZE as u32 - 1)) + 1;
        let sectors_len = u64::from(valid_data_len) / SECTOR_SIZE;
        // Craft a random request with the randomized parameters.
        let mut request = Request {
            r#type: request_type,
            data_len: valid_data_len,
            status_addr,
            sector: sector & (NUM_DISK_SECTORS - sectors_len),
            data_addr,
        };
        let request_header = RequestHeader::new(virtio_request_id, request.sector);

        rq.set_hdr_desc(
            req_type_addr.0,
            max_desc_len as u32,
            VIRTQ_DESC_F_NEXT,
            request_header,
        );
        // Flush requests have no data desc.
        if request.r#type == RequestType::Flush {
            request.data_addr = GuestAddress(0);
            request.data_len = 0;
            rq.mut_hdr_desc()
                .next
                .set(RequestVirtQueue::STATUS_DESC as u16);
        } else {
            rq.set_data_desc(
                request.data_addr.0,
                request.data_len,
                request_type_flags(request.r#type),
            )
        }
        rq.set_status_desc(request.status_addr.0, 1, VIRTQ_DESC_F_WRITE);

        // Flip a coin - should we generate a valid request or an error.
        if *coins.next().unwrap() {
            return (Ok(request), mem, q);
        }

        // This is the initial correct value.
        let data_desc_flags = &rq.mut_data_desc().flags;

        // Flip coin - corrupt the status desc len.
        if *coins.next().unwrap() {
            rq.mut_status_desc().len.set(0);
            return (Err(Error::DescriptorLengthTooSmall), mem, q);
        }

        // Flip coin - corrupt data desc next flag.
        // Exception: flush requests do not have data desc.
        if *coins.next().unwrap() && request.r#type != RequestType::Flush {
            data_desc_flags.set(data_desc_flags.get() & !VIRTQ_DESC_F_NEXT);
            return (Err(Error::DescriptorChainTooShort), mem, q);
        }

        // Flip coin - req type desc is write only.
        if *coins.next().unwrap() {
            let hdr_desc_flags = &rq.mut_hdr_desc().flags;
            hdr_desc_flags.set(hdr_desc_flags.get() | VIRTQ_DESC_F_WRITE);
            return (Err(Error::UnexpectedWriteOnlyDescriptor), mem, q);
        }

        // Corrupt data desc accessibility
        if *coins.next().unwrap() {
            match request.r#type {
                // Readonly buffer is writable.
                RequestType::Out => {
                    data_desc_flags.set(data_desc_flags.get() | VIRTQ_DESC_F_WRITE);
                    return (Err(Error::UnexpectedWriteOnlyDescriptor), mem, q);
                }
                // Writeable buffer is readonly.
                RequestType::In | RequestType::GetDeviceID => {
                    data_desc_flags.set(data_desc_flags.get() & !VIRTQ_DESC_F_WRITE);
                    return (Err(Error::UnexpectedReadOnlyDescriptor), mem, q);
                }
                _ => {}
            };
        }

        // Flip coin - Corrupt data_len
        if *coins.next().unwrap() {
            match request.r#type {
                RequestType::In | RequestType::Out => {
                    // data_len is not a multiple of 512
                    rq.mut_data_desc()
                        .len
                        .set(valid_data_len + (data_len % 511) + 1);
                    return (Err(Error::InvalidDataLength), mem, q);
                }
                RequestType::GetDeviceID => {
                    // data_len is < VIRTIO_BLK_ID_BYTES
                    rq.mut_data_desc()
                        .len
                        .set(data_len & (VIRTIO_BLK_ID_BYTES - 1));
                    return (Err(Error::InvalidDataLength), mem, q);
                }
                _ => {}
            };
        }

        // Flip coin - Corrupt sector
        if *coins.next().unwrap() {
            match request.r#type {
                RequestType::In | RequestType::Out => {
                    rq.mut_hdr().sector = (sector | NUM_DISK_SECTORS) + 1;
                    return (Err(Error::InvalidOffset), mem, q);
                }
                _ => {}
            };
        }

        // Simulate no status descriptor.
        rq.mut_hdr_desc().flags.set(0);
        (Err(Error::DescriptorChainTooShort), mem, q)
    }