• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use std::fs::File;
5 use std::mem;
6 
7 use base::AsRawDescriptor;
8 #[cfg(windows)]
9 use base::CloseNotifier;
10 use base::Event;
11 use base::RawDescriptor;
12 use base::ReadNotifier;
13 use base::INVALID_DESCRIPTOR;
14 use zerocopy::FromBytes;
15 use zerocopy::Immutable;
16 use zerocopy::IntoBytes;
17 
18 use crate::backend::VhostUserMemoryRegionInfo;
19 use crate::backend::VringConfigData;
20 use crate::into_single_file;
21 use crate::message::*;
22 use crate::Connection;
23 use crate::Error as VhostUserError;
24 use crate::FrontendReq;
25 use crate::Result as VhostUserResult;
26 use crate::Result;
27 
28 /// Client for a vhost-user device. The API is a thin abstraction over the vhost-user protocol.
29 pub struct BackendClient {
30     connection: Connection<FrontendReq>,
31     // Cached virtio features from the backend.
32     virtio_features: u64,
33     // Cached acked virtio features from the driver.
34     acked_virtio_features: u64,
35     // Cached vhost-user protocol features.
36     acked_protocol_features: u64,
37 }
38 
39 impl BackendClient {
40     /// Create a new instance.
new(connection: Connection<FrontendReq>) -> Self41     pub fn new(connection: Connection<FrontendReq>) -> Self {
42         BackendClient {
43             connection,
44             virtio_features: 0,
45             acked_virtio_features: 0,
46             acked_protocol_features: 0,
47         }
48     }
49 
50     /// Get a bitmask of supported virtio/vhost features.
get_features(&mut self) -> Result<u64>51     pub fn get_features(&mut self) -> Result<u64> {
52         let hdr = self.send_request_header(FrontendReq::GET_FEATURES, None)?;
53         let val = self.recv_reply::<VhostUserU64>(&hdr)?;
54         self.virtio_features = val.value;
55         Ok(self.virtio_features)
56     }
57 
58     /// Inform the vhost subsystem which features to enable.
59     /// This should be a subset of supported features from get_features().
set_features(&mut self, features: u64) -> Result<()>60     pub fn set_features(&mut self, features: u64) -> Result<()> {
61         let val = VhostUserU64::new(features);
62         let hdr = self.send_request_with_body(FrontendReq::SET_FEATURES, &val, None)?;
63         self.acked_virtio_features = features & self.virtio_features;
64         self.wait_for_ack(&hdr)
65     }
66 
67     /// Set the current process as the owner of the vhost backend.
68     /// This must be run before any other vhost commands.
set_owner(&self) -> Result<()>69     pub fn set_owner(&self) -> Result<()> {
70         let hdr = self.send_request_header(FrontendReq::SET_OWNER, None)?;
71         self.wait_for_ack(&hdr)
72     }
73 
74     /// Used to be sent to request disabling all rings
75     /// This is no longer used.
reset_owner(&self) -> Result<()>76     pub fn reset_owner(&self) -> Result<()> {
77         let hdr = self.send_request_header(FrontendReq::RESET_OWNER, None)?;
78         self.wait_for_ack(&hdr)
79     }
80 
81     /// Set the memory map regions on the backend so it can translate the vring
82     /// addresses. In the ancillary data there is an array of file descriptors
set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>83     pub fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
84         if regions.is_empty() || regions.len() > MAX_ATTACHED_FD_ENTRIES {
85             return Err(VhostUserError::InvalidParam(
86                 "set_mem_table: regions empty or exceed max allowed regions per req.",
87             ));
88         }
89 
90         let mut ctx = VhostUserMemoryContext::new();
91         for region in regions.iter() {
92             if region.memory_size == 0 || region.mmap_handle == INVALID_DESCRIPTOR {
93                 return Err(VhostUserError::InvalidParam(
94                     "set_mem_table: invalid memory region",
95                 ));
96             }
97 
98             let reg = VhostUserMemoryRegion {
99                 guest_phys_addr: region.guest_phys_addr,
100                 memory_size: region.memory_size,
101                 user_addr: region.userspace_addr,
102                 mmap_offset: region.mmap_offset,
103             };
104             ctx.append(&reg, region.mmap_handle);
105         }
106 
107         let body = VhostUserMemory::new(ctx.regions.len() as u32);
108         let hdr = self.send_request_with_payload(
109             FrontendReq::SET_MEM_TABLE,
110             &body,
111             ctx.regions.as_bytes(),
112             Some(ctx.fds.as_slice()),
113         )?;
114         self.wait_for_ack(&hdr)
115     }
116 
117     /// Set base address for page modification logging.
set_log_base(&self, base: u64, fd: Option<RawDescriptor>) -> Result<()>118     pub fn set_log_base(&self, base: u64, fd: Option<RawDescriptor>) -> Result<()> {
119         let val = VhostUserU64::new(base);
120 
121         let should_have_fd =
122             self.acked_protocol_features & VhostUserProtocolFeatures::LOG_SHMFD.bits() != 0;
123         if should_have_fd != fd.is_some() {
124             return Err(VhostUserError::InvalidParam("set_log_base: FD is missing"));
125         }
126 
127         let _ = self.send_request_with_body(
128             FrontendReq::SET_LOG_BASE,
129             &val,
130             fd.as_ref().map(std::slice::from_ref),
131         )?;
132 
133         Ok(())
134     }
135 
136     /// Specify an event file descriptor to signal on log write.
set_log_fd(&self, fd: RawDescriptor) -> Result<()>137     pub fn set_log_fd(&self, fd: RawDescriptor) -> Result<()> {
138         let fds = [fd];
139         let hdr = self.send_request_header(FrontendReq::SET_LOG_FD, Some(&fds))?;
140         self.wait_for_ack(&hdr)
141     }
142 
143     /// Set the number of descriptors in the vring.
set_vring_num(&self, queue_index: usize, num: u16) -> Result<()>144     pub fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
145         let val = VhostUserVringState::new(queue_index as u32, num.into());
146         let hdr = self.send_request_with_body(FrontendReq::SET_VRING_NUM, &val, None)?;
147         self.wait_for_ack(&hdr)
148     }
149 
150     /// Set the addresses for a given vring.
set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()>151     pub fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
152         if config_data.flags & !(VhostUserVringAddrFlags::all().bits()) != 0 {
153             return Err(VhostUserError::InvalidParam(
154                 "set_vring_addr: unsupported vring flags",
155             ));
156         }
157 
158         let val = VhostUserVringAddr::from_config_data(queue_index as u32, config_data);
159         let hdr = self.send_request_with_body(FrontendReq::SET_VRING_ADDR, &val, None)?;
160         self.wait_for_ack(&hdr)
161     }
162 
163     /// Set the first index to look for available descriptors.
164     // TODO: b/331466964 - Arguments and message format are wrong for packed queues.
set_vring_base(&self, queue_index: usize, base: u16) -> Result<()>165     pub fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
166         let val = VhostUserVringState::new(queue_index as u32, base.into());
167         let hdr = self.send_request_with_body(FrontendReq::SET_VRING_BASE, &val, None)?;
168         self.wait_for_ack(&hdr)
169     }
170 
171     /// Get the available vring base offset.
172     // TODO: b/331466964 - Return type is wrong for packed queues.
get_vring_base(&self, queue_index: usize) -> Result<u32>173     pub fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
174         let req = VhostUserVringState::new(queue_index as u32, 0);
175         let hdr = self.send_request_with_body(FrontendReq::GET_VRING_BASE, &req, None)?;
176         let reply = self.recv_reply::<VhostUserVringState>(&hdr)?;
177         Ok(reply.num)
178     }
179 
180     /// Set the event to trigger when buffers have been used by the host.
181     ///
182     /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
183     /// is set when there is no file descriptor in the ancillary data. This signals that polling
184     /// will be used instead of waiting for the call.
set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()>185     pub fn set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()> {
186         let hdr = self.send_fd_for_vring(
187             FrontendReq::SET_VRING_CALL,
188             queue_index,
189             event.as_raw_descriptor(),
190         )?;
191         self.wait_for_ack(&hdr)
192     }
193 
194     /// Set the event that will be signaled by the guest when buffers are available for the host to
195     /// process.
196     ///
197     /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
198     /// is set when there is no file descriptor in the ancillary data. This signals that polling
199     /// should be used instead of waiting for a kick.
set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()>200     pub fn set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()> {
201         let hdr = self.send_fd_for_vring(
202             FrontendReq::SET_VRING_KICK,
203             queue_index,
204             event.as_raw_descriptor(),
205         )?;
206         self.wait_for_ack(&hdr)
207     }
208 
209     /// Set the event that will be signaled by the guest when error happens.
210     ///
211     /// Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag
212     /// is set when there is no file descriptor in the ancillary data.
set_vring_err(&self, queue_index: usize, event: &Event) -> Result<()>213     pub fn set_vring_err(&self, queue_index: usize, event: &Event) -> Result<()> {
214         let hdr = self.send_fd_for_vring(
215             FrontendReq::SET_VRING_ERR,
216             queue_index,
217             event.as_raw_descriptor(),
218         )?;
219         self.wait_for_ack(&hdr)
220     }
221 
222     /// Front-end and back-end negotiate a channel over which to transfer the back-end’s internal
223     /// state during migration.
224     ///
225     /// Requires VHOST_USER_PROTOCOL_F_DEVICE_STATE to be negotiated.
set_device_state_fd( &self, transfer_direction: VhostUserTransferDirection, migration_phase: VhostUserMigrationPhase, fd: &impl AsRawDescriptor, ) -> Result<Option<File>>226     pub fn set_device_state_fd(
227         &self,
228         transfer_direction: VhostUserTransferDirection,
229         migration_phase: VhostUserMigrationPhase,
230         fd: &impl AsRawDescriptor,
231     ) -> Result<Option<File>> {
232         if self.acked_protocol_features & VhostUserProtocolFeatures::DEVICE_STATE.bits() == 0 {
233             return Err(VhostUserError::InvalidOperation);
234         }
235         // Send request.
236         let req = DeviceStateTransferParameters {
237             transfer_direction: match transfer_direction {
238                 VhostUserTransferDirection::Save => 0,
239                 VhostUserTransferDirection::Load => 1,
240             },
241             migration_phase: match migration_phase {
242                 VhostUserMigrationPhase::Stopped => 0,
243             },
244         };
245         let hdr = self.send_request_with_body(
246             FrontendReq::SET_DEVICE_STATE_FD,
247             &req,
248             Some(&[fd.as_raw_descriptor()]),
249         )?;
250         // Receive reply.
251         let (reply, files) = self.recv_reply_with_files::<VhostUserU64>(&hdr)?;
252         let has_err = reply.value & 0xff != 0;
253         let invalid_fd = reply.value & 0x100 != 0;
254         if has_err {
255             return Err(VhostUserError::BackendInternalError);
256         }
257         match (invalid_fd, files.len()) {
258             (true, 0) => Ok(None),
259             (false, 1) => Ok(files.into_iter().next()),
260             _ => Err(VhostUserError::IncorrectFds),
261         }
262     }
263 
264     /// After transferring the back-end’s internal state during migration, check whether the
265     /// back-end was able to successfully fully process the state.
check_device_state(&self) -> Result<()>266     pub fn check_device_state(&self) -> Result<()> {
267         if self.acked_protocol_features & VhostUserProtocolFeatures::DEVICE_STATE.bits() == 0 {
268             return Err(VhostUserError::InvalidOperation);
269         }
270         let hdr = self.send_request_header(FrontendReq::CHECK_DEVICE_STATE, None)?;
271         let reply = self.recv_reply::<VhostUserU64>(&hdr)?;
272         if reply.value != 0 {
273             return Err(VhostUserError::BackendInternalError);
274         }
275         Ok(())
276     }
277 
278     /// Get the protocol feature bitmask from the underlying vhost implementation.
get_protocol_features(&self) -> Result<VhostUserProtocolFeatures>279     pub fn get_protocol_features(&self) -> Result<VhostUserProtocolFeatures> {
280         if self.virtio_features & 1 << VHOST_USER_F_PROTOCOL_FEATURES == 0 {
281             return Err(VhostUserError::InvalidOperation);
282         }
283         let hdr = self.send_request_header(FrontendReq::GET_PROTOCOL_FEATURES, None)?;
284         let val = self.recv_reply::<VhostUserU64>(&hdr)?;
285         Ok(VhostUserProtocolFeatures::from_bits_truncate(val.value))
286     }
287 
288     /// Enable protocol features in the underlying vhost implementation.
set_protocol_features(&mut self, features: VhostUserProtocolFeatures) -> Result<()>289     pub fn set_protocol_features(&mut self, features: VhostUserProtocolFeatures) -> Result<()> {
290         if self.virtio_features & 1 << VHOST_USER_F_PROTOCOL_FEATURES == 0 {
291             return Err(VhostUserError::InvalidOperation);
292         }
293         if features.contains(VhostUserProtocolFeatures::SHARED_MEMORY_REGIONS)
294             && !features.contains(VhostUserProtocolFeatures::BACKEND_REQ)
295         {
296             return Err(VhostUserError::FeatureMismatch);
297         }
298         let val = VhostUserU64::new(features.bits());
299         let hdr = self.send_request_with_body(FrontendReq::SET_PROTOCOL_FEATURES, &val, None)?;
300         // Don't wait for ACK here because the protocol feature negotiation process hasn't been
301         // completed yet.
302         self.acked_protocol_features = features.bits();
303         self.wait_for_ack(&hdr)
304     }
305 
306     /// Query how many queues the backend supports.
get_queue_num(&self) -> Result<u64>307     pub fn get_queue_num(&self) -> Result<u64> {
308         if !self.is_feature_mq_available() {
309             return Err(VhostUserError::InvalidOperation);
310         }
311 
312         let hdr = self.send_request_header(FrontendReq::GET_QUEUE_NUM, None)?;
313         let val = self.recv_reply::<VhostUserU64>(&hdr)?;
314         if val.value > VHOST_USER_MAX_VRINGS {
315             return Err(VhostUserError::InvalidMessage);
316         }
317         Ok(val.value)
318     }
319 
320     /// Signal backend to enable or disable corresponding vring.
321     ///
322     /// Backend must not pass data to/from the ring until ring is enabled by
323     /// VHOST_USER_SET_VRING_ENABLE with parameter 1, or after it has been
324     /// disabled by VHOST_USER_SET_VRING_ENABLE with parameter 0.
set_vring_enable(&self, queue_index: usize, enable: bool) -> Result<()>325     pub fn set_vring_enable(&self, queue_index: usize, enable: bool) -> Result<()> {
326         // set_vring_enable() is supported only when PROTOCOL_FEATURES has been enabled.
327         if self.acked_virtio_features & 1 << VHOST_USER_F_PROTOCOL_FEATURES == 0 {
328             return Err(VhostUserError::InvalidOperation);
329         }
330 
331         let val = VhostUserVringState::new(queue_index as u32, enable.into());
332         let hdr = self.send_request_with_body(FrontendReq::SET_VRING_ENABLE, &val, None)?;
333         self.wait_for_ack(&hdr)
334     }
335 
336     /// Fetch the contents of the virtio device configuration space.
get_config( &self, offset: u32, size: u32, flags: VhostUserConfigFlags, buf: &[u8], ) -> Result<(VhostUserConfig, VhostUserConfigPayload)>337     pub fn get_config(
338         &self,
339         offset: u32,
340         size: u32,
341         flags: VhostUserConfigFlags,
342         buf: &[u8],
343     ) -> Result<(VhostUserConfig, VhostUserConfigPayload)> {
344         let body = VhostUserConfig::new(offset, size, flags);
345         if !body.is_valid() {
346             return Err(VhostUserError::InvalidParam(
347                 "get_config: VhostUserConfig is invalid",
348             ));
349         }
350 
351         // depends on VhostUserProtocolFeatures::CONFIG
352         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
353             return Err(VhostUserError::InvalidOperation);
354         }
355 
356         // vhost-user spec states that:
357         // "Request payload: virtio device config space"
358         // "Reply payload: virtio device config space"
359         let hdr = self.send_request_with_payload(FrontendReq::GET_CONFIG, &body, buf, None)?;
360         let (body_reply, buf_reply, rfds) =
361             self.recv_reply_with_payload::<VhostUserConfig>(&hdr)?;
362         if !rfds.is_empty() {
363             return Err(VhostUserError::InvalidMessage);
364         } else if body_reply.size == 0 {
365             return Err(VhostUserError::BackendInternalError);
366         } else if body_reply.size != body.size
367             || body_reply.size as usize != buf.len()
368             || body_reply.offset != body.offset
369         {
370             return Err(VhostUserError::InvalidMessage);
371         }
372 
373         Ok((body_reply, buf_reply))
374     }
375 
376     /// Change the virtio device configuration space. It also can be used for live migration on the
377     /// destination host to set readonly configuration space fields.
set_config(&self, offset: u32, flags: VhostUserConfigFlags, buf: &[u8]) -> Result<()>378     pub fn set_config(&self, offset: u32, flags: VhostUserConfigFlags, buf: &[u8]) -> Result<()> {
379         let body = VhostUserConfig::new(
380             offset,
381             buf.len()
382                 .try_into()
383                 .map_err(VhostUserError::InvalidCastToInt)?,
384             flags,
385         );
386         if !body.is_valid() {
387             return Err(VhostUserError::InvalidParam(
388                 "set_config: VhostUserConfig is invalid",
389             ));
390         }
391 
392         // depends on VhostUserProtocolFeatures::CONFIG
393         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
394             return Err(VhostUserError::InvalidOperation);
395         }
396 
397         let hdr = self.send_request_with_payload(FrontendReq::SET_CONFIG, &body, buf, None)?;
398         self.wait_for_ack(&hdr)
399     }
400 
401     /// Setup backend communication channel.
set_backend_req_fd(&self, fd: &dyn AsRawDescriptor) -> Result<()>402     pub fn set_backend_req_fd(&self, fd: &dyn AsRawDescriptor) -> Result<()> {
403         if self.acked_protocol_features & VhostUserProtocolFeatures::BACKEND_REQ.bits() == 0 {
404             return Err(VhostUserError::InvalidOperation);
405         }
406         let fds = [fd.as_raw_descriptor()];
407         let hdr = self.send_request_header(FrontendReq::SET_BACKEND_REQ_FD, Some(&fds))?;
408         self.wait_for_ack(&hdr)
409     }
410 
411     /// Retrieve shared buffer for inflight I/O tracking.
get_inflight_fd( &self, inflight: &VhostUserInflight, ) -> Result<(VhostUserInflight, File)>412     pub fn get_inflight_fd(
413         &self,
414         inflight: &VhostUserInflight,
415     ) -> Result<(VhostUserInflight, File)> {
416         if self.acked_protocol_features & VhostUserProtocolFeatures::INFLIGHT_SHMFD.bits() == 0 {
417             return Err(VhostUserError::InvalidOperation);
418         }
419 
420         let hdr = self.send_request_with_body(FrontendReq::GET_INFLIGHT_FD, inflight, None)?;
421         let (inflight, files) = self.recv_reply_with_files::<VhostUserInflight>(&hdr)?;
422 
423         match into_single_file(files) {
424             Some(file) => Ok((inflight, file)),
425             None => Err(VhostUserError::IncorrectFds),
426         }
427     }
428 
429     /// Set shared buffer for inflight I/O tracking.
set_inflight_fd(&self, inflight: &VhostUserInflight, fd: RawDescriptor) -> Result<()>430     pub fn set_inflight_fd(&self, inflight: &VhostUserInflight, fd: RawDescriptor) -> Result<()> {
431         if self.acked_protocol_features & VhostUserProtocolFeatures::INFLIGHT_SHMFD.bits() == 0 {
432             return Err(VhostUserError::InvalidOperation);
433         }
434 
435         if inflight.mmap_size == 0
436             || inflight.num_queues == 0
437             || inflight.queue_size == 0
438             || fd == INVALID_DESCRIPTOR
439         {
440             return Err(VhostUserError::InvalidParam(
441                 "set_inflight_fd: invalid fd or params",
442             ));
443         }
444 
445         let hdr =
446             self.send_request_with_body(FrontendReq::SET_INFLIGHT_FD, inflight, Some(&[fd]))?;
447         self.wait_for_ack(&hdr)
448     }
449 
450     /// Query the maximum amount of memory slots supported by the backend.
get_max_mem_slots(&self) -> Result<u64>451     pub fn get_max_mem_slots(&self) -> Result<u64> {
452         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() == 0
453         {
454             return Err(VhostUserError::InvalidOperation);
455         }
456 
457         let hdr = self.send_request_header(FrontendReq::GET_MAX_MEM_SLOTS, None)?;
458         let val = self.recv_reply::<VhostUserU64>(&hdr)?;
459 
460         Ok(val.value)
461     }
462 
463     /// Add a new guest memory mapping for vhost to use.
add_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()>464     pub fn add_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()> {
465         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() == 0
466         {
467             return Err(VhostUserError::InvalidOperation);
468         }
469 
470         if region.memory_size == 0 || region.mmap_handle == INVALID_DESCRIPTOR {
471             return Err(VhostUserError::InvalidParam(
472                 "add_mem_region: region empty or mmap handle invalid",
473             ));
474         }
475 
476         let body = VhostUserSingleMemoryRegion::new(
477             region.guest_phys_addr,
478             region.memory_size,
479             region.userspace_addr,
480             region.mmap_offset,
481         );
482         let fds = [region.mmap_handle];
483         let hdr = self.send_request_with_body(FrontendReq::ADD_MEM_REG, &body, Some(&fds))?;
484         self.wait_for_ack(&hdr)
485     }
486 
487     /// Remove a guest memory mapping from vhost.
remove_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()>488     pub fn remove_mem_region(&self, region: &VhostUserMemoryRegionInfo) -> Result<()> {
489         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() == 0
490         {
491             return Err(VhostUserError::InvalidOperation);
492         }
493         if region.memory_size == 0 {
494             return Err(VhostUserError::InvalidParam(
495                 "remove_mem_region: cannot remove zero sized region",
496             ));
497         }
498 
499         let body = VhostUserSingleMemoryRegion::new(
500             region.guest_phys_addr,
501             region.memory_size,
502             region.userspace_addr,
503             region.mmap_offset,
504         );
505         let hdr = self.send_request_with_body(FrontendReq::REM_MEM_REG, &body, None)?;
506         self.wait_for_ack(&hdr)
507     }
508 
509     /// Gets the shared memory regions used by the device.
get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>>510     pub fn get_shared_memory_regions(&self) -> Result<Vec<VhostSharedMemoryRegion>> {
511         let hdr = self.send_request_header(FrontendReq::GET_SHARED_MEMORY_REGIONS, None)?;
512         let (body_reply, buf_reply, rfds) = self.recv_reply_with_payload::<VhostUserU64>(&hdr)?;
513         let struct_size = mem::size_of::<VhostSharedMemoryRegion>();
514         if !rfds.is_empty() || buf_reply.len() != body_reply.value as usize * struct_size {
515             return Err(VhostUserError::InvalidMessage);
516         }
517         let mut regions = Vec::new();
518         let mut offset = 0;
519         for _ in 0..body_reply.value {
520             regions.push(
521                 // Can't fail because the input is the correct size.
522                 VhostSharedMemoryRegion::read_from_bytes(
523                     &buf_reply[offset..(offset + struct_size)],
524                 )
525                 .unwrap(),
526             );
527             offset += struct_size;
528         }
529         Ok(regions)
530     }
531 
send_request_header( &self, code: FrontendReq, fds: Option<&[RawDescriptor]>, ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>>532     fn send_request_header(
533         &self,
534         code: FrontendReq,
535         fds: Option<&[RawDescriptor]>,
536     ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
537         let hdr = self.new_request_header(code, 0);
538         self.connection.send_header_only_message(&hdr, fds)?;
539         Ok(hdr)
540     }
541 
send_request_with_body<T: IntoBytes + Immutable>( &self, code: FrontendReq, msg: &T, fds: Option<&[RawDescriptor]>, ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>>542     fn send_request_with_body<T: IntoBytes + Immutable>(
543         &self,
544         code: FrontendReq,
545         msg: &T,
546         fds: Option<&[RawDescriptor]>,
547     ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
548         let hdr = self.new_request_header(code, mem::size_of::<T>() as u32);
549         self.connection.send_message(&hdr, msg, fds)?;
550         Ok(hdr)
551     }
552 
send_request_with_payload<T: IntoBytes + Immutable>( &self, code: FrontendReq, msg: &T, payload: &[u8], fds: Option<&[RawDescriptor]>, ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>>553     fn send_request_with_payload<T: IntoBytes + Immutable>(
554         &self,
555         code: FrontendReq,
556         msg: &T,
557         payload: &[u8],
558         fds: Option<&[RawDescriptor]>,
559     ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
560         if let Some(fd_arr) = fds {
561             if fd_arr.len() > MAX_ATTACHED_FD_ENTRIES {
562                 return Err(VhostUserError::InvalidParam(
563                     "send_request_with_payload: too many FDs supplied with message",
564                 ));
565             }
566         }
567         let len = mem::size_of::<T>()
568             .checked_add(payload.len())
569             .ok_or(VhostUserError::OversizedMsg)?;
570         let hdr = self.new_request_header(
571             code,
572             len.try_into().map_err(VhostUserError::InvalidCastToInt)?,
573         );
574         self.connection
575             .send_message_with_payload(&hdr, msg, payload, fds)?;
576         Ok(hdr)
577     }
578 
send_fd_for_vring( &self, code: FrontendReq, queue_index: usize, fd: RawDescriptor, ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>>579     fn send_fd_for_vring(
580         &self,
581         code: FrontendReq,
582         queue_index: usize,
583         fd: RawDescriptor,
584     ) -> VhostUserResult<VhostUserMsgHeader<FrontendReq>> {
585         // Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag.
586         // This flag is set when there is no file descriptor in the ancillary data. This signals
587         // that polling will be used instead of waiting for the call.
588         let msg = VhostUserU64::new(queue_index as u64);
589         let hdr = self.new_request_header(code, mem::size_of::<VhostUserU64>() as u32);
590         self.connection.send_message(&hdr, &msg, Some(&[fd]))?;
591         Ok(hdr)
592     }
593 
recv_reply<T: Sized + FromBytes + IntoBytes + Default + VhostUserMsgValidator>( &self, hdr: &VhostUserMsgHeader<FrontendReq>, ) -> VhostUserResult<T>594     fn recv_reply<T: Sized + FromBytes + IntoBytes + Default + VhostUserMsgValidator>(
595         &self,
596         hdr: &VhostUserMsgHeader<FrontendReq>,
597     ) -> VhostUserResult<T> {
598         if hdr.is_reply() {
599             return Err(VhostUserError::InvalidParam(
600                 "recv_reply: header is not a reply",
601             ));
602         }
603         let (reply, body, rfds) = self.connection.recv_message::<T>()?;
604         if !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
605             return Err(VhostUserError::InvalidMessage);
606         }
607         Ok(body)
608     }
609 
recv_reply_with_files<T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator>( &self, hdr: &VhostUserMsgHeader<FrontendReq>, ) -> VhostUserResult<(T, Vec<File>)>610     fn recv_reply_with_files<T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator>(
611         &self,
612         hdr: &VhostUserMsgHeader<FrontendReq>,
613     ) -> VhostUserResult<(T, Vec<File>)> {
614         if hdr.is_reply() {
615             return Err(VhostUserError::InvalidParam(
616                 "with_files: expected a reply, but the header is not marked as a reply",
617             ));
618         }
619 
620         let (reply, body, files) = self.connection.recv_message::<T>()?;
621         if !reply.is_reply_for(hdr) || !body.is_valid() {
622             return Err(VhostUserError::InvalidMessage);
623         }
624         Ok((body, files))
625     }
626 
recv_reply_with_payload< T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator, >( &self, hdr: &VhostUserMsgHeader<FrontendReq>, ) -> VhostUserResult<(T, Vec<u8>, Vec<File>)>627     fn recv_reply_with_payload<
628         T: Sized + IntoBytes + FromBytes + Default + VhostUserMsgValidator,
629     >(
630         &self,
631         hdr: &VhostUserMsgHeader<FrontendReq>,
632     ) -> VhostUserResult<(T, Vec<u8>, Vec<File>)> {
633         if hdr.is_reply() {
634             return Err(VhostUserError::InvalidParam(
635                 "with_payload: expected a reply, but the header is not marked as a reply",
636             ));
637         }
638 
639         let (reply, body, buf, files) = self.connection.recv_message_with_payload::<T>()?;
640         if !reply.is_reply_for(hdr) || !files.is_empty() || !body.is_valid() {
641             return Err(VhostUserError::InvalidMessage);
642         }
643 
644         Ok((body, buf, files))
645     }
646 
wait_for_ack(&self, hdr: &VhostUserMsgHeader<FrontendReq>) -> VhostUserResult<()>647     fn wait_for_ack(&self, hdr: &VhostUserMsgHeader<FrontendReq>) -> VhostUserResult<()> {
648         if self.acked_protocol_features & VhostUserProtocolFeatures::REPLY_ACK.bits() == 0
649             || !hdr.is_need_reply()
650         {
651             return Ok(());
652         }
653 
654         let (reply, body, rfds) = self.connection.recv_message::<VhostUserU64>()?;
655         if !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
656             return Err(VhostUserError::InvalidMessage);
657         }
658         if body.value != 0 {
659             return Err(VhostUserError::BackendInternalError);
660         }
661         Ok(())
662     }
663 
is_feature_mq_available(&self) -> bool664     fn is_feature_mq_available(&self) -> bool {
665         self.acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0
666     }
667 
668     #[inline]
new_request_header( &self, request: FrontendReq, size: u32, ) -> VhostUserMsgHeader<FrontendReq>669     fn new_request_header(
670         &self,
671         request: FrontendReq,
672         size: u32,
673     ) -> VhostUserMsgHeader<FrontendReq> {
674         VhostUserMsgHeader::new(request, 0x1, size)
675     }
676 }
677 
678 #[cfg(windows)]
679 impl CloseNotifier for BackendClient {
get_close_notifier(&self) -> &dyn AsRawDescriptor680     fn get_close_notifier(&self) -> &dyn AsRawDescriptor {
681         self.connection.0.get_close_notifier()
682     }
683 }
684 
685 impl ReadNotifier for BackendClient {
get_read_notifier(&self) -> &dyn AsRawDescriptor686     fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
687         self.connection.0.get_read_notifier()
688     }
689 }
690 
691 // TODO(b/221882601): likely need pairs of RDs and/or SharedMemory to represent mmaps on Windows.
692 /// Context object to pass guest memory configuration to BackendClient::set_mem_table().
693 struct VhostUserMemoryContext {
694     regions: VhostUserMemoryPayload,
695     fds: Vec<RawDescriptor>,
696 }
697 
698 impl VhostUserMemoryContext {
699     /// Create a context object.
new() -> Self700     pub fn new() -> Self {
701         VhostUserMemoryContext {
702             regions: VhostUserMemoryPayload::new(),
703             fds: Vec::new(),
704         }
705     }
706 
707     /// Append a user memory region and corresponding RawDescriptor into the context object.
append(&mut self, region: &VhostUserMemoryRegion, fd: RawDescriptor)708     pub fn append(&mut self, region: &VhostUserMemoryRegion, fd: RawDescriptor) {
709         self.regions.push(*region);
710         self.fds.push(fd);
711     }
712 }
713 
714 #[cfg(test)]
715 mod tests {
716     use base::INVALID_DESCRIPTOR;
717     use tempfile::tempfile;
718 
719     use super::*;
720 
721     const BUFFER_SIZE: usize = 0x1001;
722     const INVALID_PROTOCOL_FEATURE: u64 = 1 << 63;
723 
create_pair() -> (BackendClient, Connection<FrontendReq>)724     fn create_pair() -> (BackendClient, Connection<FrontendReq>) {
725         let (client_connection, server_connection) = Connection::pair().unwrap();
726         let backend_client = BackendClient::new(client_connection);
727         (backend_client, server_connection)
728     }
729 
730     #[test]
create_backend_client()731     fn create_backend_client() {
732         let (backend_client, peer) = create_pair();
733 
734         assert!(backend_client.connection.as_raw_descriptor() != INVALID_DESCRIPTOR);
735         // Send two messages continuously
736         backend_client.set_owner().unwrap();
737         backend_client.reset_owner().unwrap();
738 
739         let (hdr, rfds) = peer.recv_header().unwrap();
740         assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
741         assert_eq!(hdr.get_size(), 0);
742         assert_eq!(hdr.get_version(), 0x1);
743         assert!(rfds.is_empty());
744 
745         let (hdr, rfds) = peer.recv_header().unwrap();
746         assert_eq!(hdr.get_code(), Ok(FrontendReq::RESET_OWNER));
747         assert_eq!(hdr.get_size(), 0);
748         assert_eq!(hdr.get_version(), 0x1);
749         assert!(rfds.is_empty());
750     }
751 
752     #[test]
test_features()753     fn test_features() {
754         let (mut backend_client, peer) = create_pair();
755 
756         backend_client.set_owner().unwrap();
757         let (hdr, rfds) = peer.recv_header().unwrap();
758         assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
759         assert_eq!(hdr.get_size(), 0);
760         assert_eq!(hdr.get_version(), 0x1);
761         assert!(rfds.is_empty());
762 
763         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
764         let msg = VhostUserU64::new(0x15);
765         peer.send_message(&hdr, &msg, None).unwrap();
766         let features = backend_client.get_features().unwrap();
767         assert_eq!(features, 0x15u64);
768         let (_hdr, rfds) = peer.recv_header().unwrap();
769         assert!(rfds.is_empty());
770 
771         let hdr = VhostUserMsgHeader::new(FrontendReq::SET_FEATURES, 0x4, 8);
772         let msg = VhostUserU64::new(0x15);
773         peer.send_message(&hdr, &msg, None).unwrap();
774         backend_client.set_features(0x15).unwrap();
775         let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
776         assert!(rfds.is_empty());
777         let val = msg.value;
778         assert_eq!(val, 0x15);
779 
780         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
781         let msg = 0x15u32;
782         peer.send_message(&hdr, &msg, None).unwrap();
783         assert!(backend_client.get_features().is_err());
784     }
785 
786     #[test]
test_protocol_features()787     fn test_protocol_features() {
788         let (mut backend_client, peer) = create_pair();
789 
790         backend_client.set_owner().unwrap();
791         let (hdr, rfds) = peer.recv_header().unwrap();
792         assert_eq!(hdr.get_code(), Ok(FrontendReq::SET_OWNER));
793         assert!(rfds.is_empty());
794 
795         assert!(backend_client.get_protocol_features().is_err());
796         assert!(backend_client
797             .set_protocol_features(VhostUserProtocolFeatures::all())
798             .is_err());
799 
800         let vfeatures = 0x15 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
801         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0x4, 8);
802         let msg = VhostUserU64::new(vfeatures);
803         peer.send_message(&hdr, &msg, None).unwrap();
804         let features = backend_client.get_features().unwrap();
805         assert_eq!(features, vfeatures);
806         let (_hdr, rfds) = peer.recv_header().unwrap();
807         assert!(rfds.is_empty());
808 
809         backend_client.set_features(vfeatures).unwrap();
810         let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
811         assert!(rfds.is_empty());
812         let val = msg.value;
813         assert_eq!(val, vfeatures);
814 
815         let pfeatures = VhostUserProtocolFeatures::all();
816         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_PROTOCOL_FEATURES, 0x4, 8);
817         // Unknown feature bits should be ignored.
818         let msg = VhostUserU64::new(pfeatures.bits() | INVALID_PROTOCOL_FEATURE);
819         peer.send_message(&hdr, &msg, None).unwrap();
820         let features = backend_client.get_protocol_features().unwrap();
821         assert_eq!(features, pfeatures);
822         let (_hdr, rfds) = peer.recv_header().unwrap();
823         assert!(rfds.is_empty());
824 
825         backend_client.set_protocol_features(pfeatures).unwrap();
826         let (_hdr, msg, rfds) = peer.recv_message::<VhostUserU64>().unwrap();
827         assert!(rfds.is_empty());
828         let val = msg.value;
829         assert_eq!(val, pfeatures.bits());
830 
831         let hdr = VhostUserMsgHeader::new(FrontendReq::SET_PROTOCOL_FEATURES, 0x4, 8);
832         let msg = VhostUserU64::new(pfeatures.bits());
833         peer.send_message(&hdr, &msg, None).unwrap();
834         assert!(backend_client.get_protocol_features().is_err());
835     }
836 
837     #[test]
test_backend_client_set_config_negative()838     fn test_backend_client_set_config_negative() {
839         let (mut backend_client, _peer) = create_pair();
840         let buf = vec![0x0; BUFFER_SIZE];
841 
842         backend_client
843             .set_config(0x100, VhostUserConfigFlags::WRITABLE, &buf[0..4])
844             .unwrap_err();
845 
846         backend_client.virtio_features = 0xffff_ffff;
847         backend_client.acked_virtio_features = 0xffff_ffff;
848         backend_client.acked_protocol_features = 0xffff_ffff;
849 
850         backend_client
851             .set_config(0, VhostUserConfigFlags::WRITABLE, &buf[0..4])
852             .unwrap();
853         backend_client
854             .set_config(
855                 VHOST_USER_CONFIG_SIZE,
856                 VhostUserConfigFlags::WRITABLE,
857                 &buf[0..4],
858             )
859             .unwrap_err();
860         backend_client
861             .set_config(0x1000, VhostUserConfigFlags::WRITABLE, &buf[0..4])
862             .unwrap_err();
863         backend_client
864             .set_config(
865                 0x100,
866                 VhostUserConfigFlags::from_bits_retain(0xffff_ffff),
867                 &buf[0..4],
868             )
869             .unwrap_err();
870         backend_client
871             .set_config(VHOST_USER_CONFIG_SIZE, VhostUserConfigFlags::WRITABLE, &buf)
872             .unwrap_err();
873         backend_client
874             .set_config(VHOST_USER_CONFIG_SIZE, VhostUserConfigFlags::WRITABLE, &[])
875             .unwrap_err();
876     }
877 
create_pair2() -> (BackendClient, Connection<FrontendReq>)878     fn create_pair2() -> (BackendClient, Connection<FrontendReq>) {
879         let (mut backend_client, peer) = create_pair();
880 
881         backend_client.virtio_features = 0xffff_ffff;
882         backend_client.acked_virtio_features = 0xffff_ffff;
883         backend_client.acked_protocol_features = 0xffff_ffff;
884 
885         (backend_client, peer)
886     }
887 
888     #[test]
test_backend_client_get_config_negative0()889     fn test_backend_client_get_config_negative0() {
890         let (backend_client, peer) = create_pair2();
891         let buf = vec![0x0; BUFFER_SIZE];
892 
893         let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
894         let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
895         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
896             .unwrap();
897         assert!(backend_client
898             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
899             .is_ok());
900 
901         hdr.set_code(FrontendReq::GET_FEATURES);
902         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
903             .unwrap();
904         assert!(backend_client
905             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
906             .is_err());
907         hdr.set_code(FrontendReq::GET_CONFIG);
908     }
909 
910     #[test]
test_backend_client_get_config_negative1()911     fn test_backend_client_get_config_negative1() {
912         let (backend_client, peer) = create_pair2();
913         let buf = vec![0x0; BUFFER_SIZE];
914 
915         let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
916         let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
917         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
918             .unwrap();
919         assert!(backend_client
920             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
921             .is_ok());
922 
923         hdr.set_reply(false);
924         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
925             .unwrap();
926         assert!(backend_client
927             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
928             .is_err());
929     }
930 
931     #[test]
test_backend_client_get_config_negative2()932     fn test_backend_client_get_config_negative2() {
933         let (backend_client, peer) = create_pair2();
934         let buf = vec![0x0; BUFFER_SIZE];
935 
936         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
937         let msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
938         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
939             .unwrap();
940         assert!(backend_client
941             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
942             .is_ok());
943     }
944 
945     #[test]
test_backend_client_get_config_negative3()946     fn test_backend_client_get_config_negative3() {
947         let (backend_client, peer) = create_pair2();
948         let buf = vec![0x0; BUFFER_SIZE];
949 
950         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
951         let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
952         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
953             .unwrap();
954         assert!(backend_client
955             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
956             .is_ok());
957 
958         msg.offset = 0;
959         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
960             .unwrap();
961         assert!(backend_client
962             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
963             .is_err());
964     }
965 
966     #[test]
test_backend_client_get_config_negative4()967     fn test_backend_client_get_config_negative4() {
968         let (backend_client, peer) = create_pair2();
969         let buf = vec![0x0; BUFFER_SIZE];
970 
971         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
972         let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
973         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
974             .unwrap();
975         assert!(backend_client
976             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
977             .is_ok());
978 
979         msg.offset = 0x101;
980         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
981             .unwrap();
982         assert!(backend_client
983             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
984             .is_err());
985     }
986 
987     #[test]
test_backend_client_get_config_negative5()988     fn test_backend_client_get_config_negative5() {
989         let (backend_client, peer) = create_pair2();
990         let buf = vec![0x0; BUFFER_SIZE];
991 
992         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
993         let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
994         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
995             .unwrap();
996         assert!(backend_client
997             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
998             .is_ok());
999 
1000         msg.offset = (BUFFER_SIZE) as u32;
1001         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
1002             .unwrap();
1003         assert!(backend_client
1004             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
1005             .is_err());
1006     }
1007 
1008     #[test]
test_backend_client_get_config_negative6()1009     fn test_backend_client_get_config_negative6() {
1010         let (backend_client, peer) = create_pair2();
1011         let buf = vec![0x0; BUFFER_SIZE];
1012 
1013         let hdr = VhostUserMsgHeader::new(FrontendReq::GET_CONFIG, 0x4, 16);
1014         let mut msg = VhostUserConfig::new(0x100, 4, VhostUserConfigFlags::empty());
1015         peer.send_message_with_payload(&hdr, &msg, &buf[0..4], None)
1016             .unwrap();
1017         assert!(backend_client
1018             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
1019             .is_ok());
1020 
1021         msg.size = 6;
1022         peer.send_message_with_payload(&hdr, &msg, &buf[0..6], None)
1023             .unwrap();
1024         assert!(backend_client
1025             .get_config(0x100, 4, VhostUserConfigFlags::WRITABLE, &buf[0..4])
1026             .is_err());
1027     }
1028 
1029     #[test]
test_maset_set_mem_table_failure()1030     fn test_maset_set_mem_table_failure() {
1031         let (backend_client, _peer) = create_pair2();
1032 
1033         // set_mem_table() with 0 regions is invalid
1034         backend_client.set_mem_table(&[]).unwrap_err();
1035 
1036         // set_mem_table() with more than MAX_ATTACHED_FD_ENTRIES is invalid
1037         let files: Vec<File> = (0..MAX_ATTACHED_FD_ENTRIES + 1)
1038             .map(|_| tempfile().unwrap())
1039             .collect();
1040         let tables: Vec<VhostUserMemoryRegionInfo> = files
1041             .iter()
1042             .map(|f| VhostUserMemoryRegionInfo {
1043                 guest_phys_addr: 0,
1044                 memory_size: 0x100000,
1045                 userspace_addr: 0x800000,
1046                 mmap_offset: 0,
1047                 mmap_handle: f.as_raw_descriptor(),
1048             })
1049             .collect();
1050         backend_client.set_mem_table(&tables).unwrap_err();
1051     }
1052 }
1053