Skip to main content

nitro_enclaves/launch/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use nix::errno::Errno;
4use std::{fmt, io};
5
6const NE_ERR_VCPU_ALREADY_USED: i32 = 256;
7const NE_ERR_VCPU_NOT_IN_CPU_POOL: i32 = 257;
8const NE_ERR_VCPU_INVALID_CPU_CORE: i32 = 258;
9const NE_ERR_INVALID_MEM_REGION_SIZE: i32 = 259;
10const NE_ERR_INVALID_MEM_REGION_ADDR: i32 = 260;
11const NE_ERR_UNALIGNED_MEM_REGION_ADDR: i32 = 261;
12const NE_ERR_MEM_REGION_ALREADY_USED: i32 = 262;
13const NE_ERR_MEM_NOT_HUGE_PAGE: i32 = 263;
14const NE_ERR_MEM_DIFFERENT_NUMA_NODE: i32 = 264;
15const NE_ERR_MEM_MAX_REGIONS: i32 = 265;
16const NE_ERR_NO_MEM_REGIONS_ADDED: i32 = 266;
17const NE_ERR_NO_VCPUS_ADDED: i32 = 267;
18const NE_ERR_ENCLAVE_MEM_MIN_SIZE: i32 = 268;
19const NE_ERR_FULL_CORES_NOT_USED: i32 = 269;
20const NE_ERR_NOT_IN_INIT_STATE: i32 = 270;
21const NE_ERR_INVALID_VCPU: i32 = 271;
22const NE_ERR_NO_CPUS_AVAIL_IN_POOL: i32 = 272;
23const NE_ERR_INVALID_PAGE_SIZE: i32 = 273;
24const NE_ERR_INVALID_FLAG_VALUE: i32 = 274;
25const NE_ERR_INVALID_ENCLAVE_CID: i32 = 275;
26
27/// Error that may occur during the launch process.
28#[derive(Debug)]
29pub enum LaunchError {
30    /// /dev/nitro_enclaves ioctl error.
31    Ioctl(IoctlError),
32
33    /// Memory initialization error.
34    MemInit(MemInitError),
35
36    /// Error occuring when randomly-generating an enclave CID.
37    CidInvalid,
38}
39
40impl LaunchError {
41    /// Error on ioctl, return an IoctlError from errno.
42    pub fn ioctl_err_from_errno() -> Self {
43        Self::Ioctl(IoctlError::from_errno())
44    }
45}
46
47impl From<nix::errno::Errno> for LaunchError {
48    fn from(_e: nix::errno::Errno) -> Self {
49        LaunchError::Ioctl(IoctlError::from_errno())
50    }
51}
52
53impl fmt::Display for LaunchError {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        let msg = match self {
56            Self::Ioctl(e) => format!("ioctl error: {e}"),
57            Self::MemInit(e) => format!("memory initialization error: {e}"),
58            Self::CidInvalid => "the value of the provided CID is invalid".to_string(),
59        };
60
61        write!(f, "{}", msg)
62    }
63}
64
65/// Error that may occur when issuing /dev/nitro_enclaves ioctls.
66#[derive(Debug)]
67pub enum IoctlError {
68    /// copy_to_user() failure.
69    CopyToUser,
70
71    /// Memory allocation failure for internal bookkeeping variables.
72    InternalMemAllocation,
73
74    /// Current task mm is not the same as the one that created the enclave.
75    DifferentTaskMm,
76
77    /// The value of the provided flag is invalid.
78    InvalidFlagValue,
79
80    /// No nitro enclave CPU pool set or no CPUs available in the pool.
81    NoCpusAvailInPool,
82
83    /// The enclave is not in "init" (not yet started) state.
84    NotInInitState,
85
86    /// The provided vCPU is already used.
87    VcpuAlreadyUsed,
88
89    /// The provided vCPU is not available in the NE CPU pool.
90    VcpuNotInCpuPool,
91
92    /// The core id of the provided vCPU is invalid or out of range.
93    VcpuInvalidCpuCore,
94
95    /// The provided vCPU is not in the available CPUs range.
96    InvalidVcpu,
97
98    /// Invalid physical memory region(s) (e.g. unaligned addresses).
99    InvalidPhysicalMemRegion,
100
101    /// The memory size of the region is not a multiple of 2 MiB.
102    InvalidMemRegionSize,
103
104    /// Invalid user space address given.
105    InvalidMemRegionAddr,
106
107    /// Unaligned user space address given.
108    UnalignedMemRegionAddr,
109
110    /// The memory region is already used.
111    MemRegionAlreadyUsed,
112
113    /// The memory region is not backed by huge pages.
114    MemNotHugePage,
115
116    /// The memory region is not from the same NUMA node as the CPUs.
117    MemDifferentNumaNode,
118
119    /// The number of memory regions set for the enclave reached maximum.
120    MemMaxRegions,
121
122    /// The memory region is not backed by pages multiple of 2 MiB.
123    InvalidPageSize,
124
125    /// No memory regions are set.
126    NoMemRegionsAdded,
127
128    /// No vCPUs are set.
129    NoVcpusAdded,
130
131    /// Full core(s) not set for the enclave.
132    FullCoresNotUsed,
133
134    /// Enclave memory is less than minimum memory size (64 MiB).
135    EnclaveMemMinSize,
136
137    /// The provided enclave CID is invalid.
138    InvalidEnclaveCid,
139
140    /// Unknown.
141    Unknown(Errno),
142}
143
144impl IoctlError {
145    /// Parse an error from errno.
146    pub fn from_errno() -> Self {
147        Self::from(Errno::last())
148    }
149}
150
151impl From<Errno> for IoctlError {
152    fn from(err: Errno) -> Self {
153        match err {
154            // Standard error codes.
155            Errno::EFAULT => Self::CopyToUser,
156            Errno::ENOMEM => Self::InternalMemAllocation,
157            Errno::EIO => Self::DifferentTaskMm,
158            Errno::EINVAL => Self::InvalidPhysicalMemRegion,
159            _ => {
160                let raw = err as i32;
161                match raw {
162                    // Nitro-specific error codes.
163                    NE_ERR_VCPU_ALREADY_USED => Self::VcpuAlreadyUsed,
164                    NE_ERR_VCPU_NOT_IN_CPU_POOL => Self::VcpuNotInCpuPool,
165                    NE_ERR_VCPU_INVALID_CPU_CORE => Self::VcpuInvalidCpuCore,
166                    NE_ERR_INVALID_MEM_REGION_SIZE => Self::InvalidMemRegionSize,
167                    NE_ERR_INVALID_MEM_REGION_ADDR => Self::InvalidMemRegionAddr,
168                    NE_ERR_UNALIGNED_MEM_REGION_ADDR => Self::UnalignedMemRegionAddr,
169                    NE_ERR_MEM_REGION_ALREADY_USED => Self::MemRegionAlreadyUsed,
170                    NE_ERR_MEM_NOT_HUGE_PAGE => Self::MemNotHugePage,
171                    NE_ERR_MEM_DIFFERENT_NUMA_NODE => Self::MemDifferentNumaNode,
172                    NE_ERR_MEM_MAX_REGIONS => Self::MemMaxRegions,
173                    NE_ERR_NO_MEM_REGIONS_ADDED => Self::NoMemRegionsAdded,
174                    NE_ERR_NO_VCPUS_ADDED => Self::NoVcpusAdded,
175                    NE_ERR_ENCLAVE_MEM_MIN_SIZE => Self::EnclaveMemMinSize,
176                    NE_ERR_FULL_CORES_NOT_USED => Self::FullCoresNotUsed,
177                    NE_ERR_NOT_IN_INIT_STATE => Self::NotInInitState,
178                    NE_ERR_INVALID_VCPU => Self::InvalidVcpu,
179                    NE_ERR_NO_CPUS_AVAIL_IN_POOL => Self::NoCpusAvailInPool,
180                    NE_ERR_INVALID_PAGE_SIZE => Self::InvalidPageSize,
181                    NE_ERR_INVALID_FLAG_VALUE => Self::InvalidFlagValue,
182                    NE_ERR_INVALID_ENCLAVE_CID => Self::InvalidEnclaveCid,
183                    _ => Self::Unknown(err),
184                }
185            }
186        }
187    }
188}
189
190impl fmt::Display for IoctlError {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        let msg = match self {
193            Self::CopyToUser => "unable to copy data to/from userspace".to_string(),
194            Self::InternalMemAllocation => {
195                "memory allocation failure for internal bookkeeping variables".to_string()
196            }
197            Self::DifferentTaskMm => {
198                "current task mm is not the same as the one that created the enclave".to_string()
199            }
200            Self::InvalidFlagValue => "the value of the provided flag is invalid".to_string(),
201            Self::NoCpusAvailInPool => {
202                "no nitro enclave CPU pool set or no CPUs available in the pool".to_string()
203            }
204            Self::NotInInitState => "not in init (not yet started) state".to_string(),
205            Self::VcpuAlreadyUsed => "the provided vCPU is already used".to_string(),
206            Self::VcpuNotInCpuPool => {
207                "the provided vCPU is not available in the NE CPU pool".to_string()
208            }
209            Self::VcpuInvalidCpuCore => {
210                "the core id of the provided vCPU is invalid or out of range".to_string()
211            }
212            Self::InvalidVcpu => "the provided vCPU is not in the available CPUs range".to_string(),
213            Self::InvalidPhysicalMemRegion => {
214                "invalid physical memory region(s) (e.g. unaligned addresses)".to_string()
215            }
216            Self::InvalidMemRegionSize => {
217                "the memory size of the region is not a multiple of 2 MiB".to_string()
218            }
219            Self::InvalidMemRegionAddr => "invalid user space address given".to_string(),
220            Self::UnalignedMemRegionAddr => "unaligned user space address given".to_string(),
221            Self::MemRegionAlreadyUsed => "the memory region is already used".to_string(),
222            Self::MemNotHugePage => "the memory region is not backed by huge pages".to_string(),
223            Self::MemDifferentNumaNode => {
224                "the memory region is not from the same NUMA node as the CPUs".to_string()
225            }
226            Self::MemMaxRegions => {
227                "the number of memory regions set for the enclave reached maximum".to_string()
228            }
229            Self::InvalidPageSize => {
230                "the memory region is not backed by pages multiple of 2 MiB".to_string()
231            }
232            Self::NoMemRegionsAdded => "no memory regions are set".to_string(),
233            Self::NoVcpusAdded => "no vCPUs are set".to_string(),
234            Self::FullCoresNotUsed => "full core(s) not set for the enclave".to_string(),
235            Self::EnclaveMemMinSize => {
236                "enclave memory is less than minimum memory size (64 MiB)".to_string()
237            }
238            Self::InvalidEnclaveCid => "the provided enclave CID is invalid".to_string(),
239            Self::Unknown(e) => format!("unknown error: {e}"),
240        };
241
242        write!(f, "{}", msg)
243    }
244}
245
246/// Error that may occur when allocating and configuring enclave memory.
247#[derive(Debug)]
248pub enum MemInitError {
249    /// A valid combination of hugepages could not be found for the requested size.
250    NoHugePageFound,
251
252    /// Unable to retrieve image metadata.
253    ImageMetadata(io::Error),
254
255    /// Unable to rewind image to beginning of image file.
256    ImageRewind(io::Error),
257
258    /// Unable to write total image file to memory regions.
259    ImageWriteIncomplete,
260
261    /// Unable to read bytes from image file.
262    ImageRead(io::Error),
263
264    /// Overflow when checking if memory region write was greater than image offset.
265    OffsetCheckOverflow,
266
267    /// Overflow when calculating end of image region in guest memory.
268    ImagePlacementOverflow,
269}
270
271impl fmt::Display for MemInitError {
272    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273        let msg = match self {
274            Self::NoHugePageFound => {
275                "a valid combination of hugepages could not be found for the requested size"
276                    .to_string()
277            }
278            Self::ImageMetadata(e) => format!("unable to retrieve image metadata: {e}"),
279            Self::ImageRewind(e) => {
280                format!("unable to rewind image to beginning of image file: {e}")
281            }
282            Self::ImageWriteIncomplete => {
283                "unable to write total image file to memory regions".to_string()
284            }
285            Self::ImageRead(e) => format!("unable to read bytes from image file: {e}"),
286            Self::OffsetCheckOverflow => {
287                "overflow when checking if memory region write was greater than image offset"
288                    .to_string()
289            }
290            Self::ImagePlacementOverflow => {
291                "overflow when calculating end of image region in guest memory".to_string()
292            }
293        };
294
295        write!(f, "{}", msg)
296    }
297}