starnix_types/
user_buffer.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::PAGE_SIZE;
6use smallvec::SmallVec;
7use starnix_uapi::errors::{Errno, errno, error};
8use starnix_uapi::user_address::{UserAddress, UserAddress32, UserRef};
9use std::sync::LazyLock;
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
11
12pub type UserBuffers = SmallVec<[UserBuffer; 1]>;
13pub type UserBuffers32 = SmallVec<[UserBuffer32; 1]>;
14
15/// Matches iovec_t.
16#[derive(
17    Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, KnownLayout, FromBytes, Immutable,
18)]
19#[repr(C)]
20pub struct UserBuffer {
21    pub address: UserAddress,
22    pub length: usize,
23}
24
25pub static MAX_RW_COUNT: LazyLock<usize> = LazyLock::new(|| ((1 << 31) - *PAGE_SIZE) as usize);
26/// Matches compat_iovec_t.
27#[derive(
28    Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, KnownLayout, FromBytes, Immutable,
29)]
30#[repr(C)]
31#[repr(packed)]
32pub struct UserBuffer32 {
33    pub address: UserAddress32,
34    pub length: u32,
35}
36
37impl From<UserBuffer32> for UserBuffer {
38    fn from(ub32: UserBuffer32) -> Self {
39        UserBuffer { address: ub32.address.into(), length: ub32.length as usize }
40    }
41}
42
43impl UserBuffer {
44    pub fn cap_buffers_to_max_rw_count(
45        max_address: UserAddress,
46        buffers: &mut UserBuffers,
47    ) -> Result<usize, Errno> {
48        // Linux checks all buffers for plausibility, even those past the MAX_RW_COUNT threshold.
49        for buffer in buffers.iter() {
50            if buffer.address > max_address
51                || buffer.address.checked_add(buffer.length).ok_or_else(|| errno!(EINVAL))?
52                    > max_address
53            {
54                return error!(EFAULT);
55            }
56        }
57        let max_rw_count = *MAX_RW_COUNT;
58        let mut total: usize = 0;
59        let mut offset = 0;
60        while offset < buffers.len() {
61            total = total.checked_add(buffers[offset].length).ok_or_else(|| errno!(EINVAL))?;
62            if total >= max_rw_count {
63                buffers[offset].length -= total - max_rw_count;
64                total = max_rw_count;
65                buffers.truncate(offset + 1);
66                break;
67            }
68            offset += 1;
69        }
70        Ok(total)
71    }
72
73    pub fn advance(&mut self, length: usize) -> Result<(), Errno> {
74        self.address = self.address.checked_add(length).ok_or_else(|| errno!(EINVAL))?;
75        self.length = self.length.checked_sub(length).ok_or_else(|| errno!(EINVAL))?;
76        Ok(())
77    }
78
79    /// Returns whether the buffer address is 0 and its length is 0.
80    pub fn is_null(&self) -> bool {
81        self.address.is_null() && self.is_empty()
82    }
83
84    /// Returns whether the buffer length is 0.
85    pub fn is_empty(&self) -> bool {
86        self.length == 0
87    }
88
89    /// Whether the given address and length is contained within this buffer.
90    pub fn contains(&self, address: UserAddress, length: usize) -> bool {
91        if let (Some(limit), Some(self_limit)) =
92            (address.checked_add(length), self.address.checked_add(self.length))
93        {
94            address >= self.address && limit <= self_limit
95        } else {
96            false
97        }
98    }
99}
100
101impl<T> TryInto<UserRef<T>> for UserBuffer {
102    type Error = Errno;
103
104    /// Returns EINVAL if the buffer is too small for the type.
105    fn try_into(self) -> Result<UserRef<T>, Errno> {
106        if self.length < std::mem::size_of::<T>() {
107            return error!(EINVAL);
108        }
109        Ok(UserRef::new(self.address))
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use smallvec::smallvec;
117
118    #[::fuchsia::test]
119    fn test_cap_buffers_to_max_rw_count_buffer_begin_past_max_address() {
120        let mut buffers =
121            smallvec![UserBuffer { address: UserAddress::const_from(50), length: 10 }];
122        assert_eq!(
123            error!(EFAULT),
124            UserBuffer::cap_buffers_to_max_rw_count(UserAddress::const_from(40), &mut buffers),
125        );
126    }
127
128    #[::fuchsia::test]
129    fn test_cap_buffers_to_max_rw_count_buffer_end_past_max_address() {
130        let mut buffers =
131            smallvec![UserBuffer { address: UserAddress::const_from(50), length: 10 }];
132        assert_eq!(
133            error!(EFAULT),
134            UserBuffer::cap_buffers_to_max_rw_count(UserAddress::const_from(55), &mut buffers),
135        );
136    }
137
138    #[::fuchsia::test]
139    fn test_cap_buffers_to_max_rw_count_buffer_overflow_u64() {
140        let mut buffers =
141            smallvec![UserBuffer { address: UserAddress::const_from(u64::MAX - 10), length: 20 }];
142        assert_eq!(
143            error!(EINVAL),
144            UserBuffer::cap_buffers_to_max_rw_count(
145                UserAddress::const_from(u64::MAX),
146                &mut buffers
147            ),
148        );
149    }
150
151    #[::fuchsia::test]
152    fn test_cap_buffers_to_max_rw_count_shorten_buffer() {
153        let mut buffers = smallvec![UserBuffer {
154            address: UserAddress::const_from(0),
155            length: *MAX_RW_COUNT + 10
156        }];
157        let total = UserBuffer::cap_buffers_to_max_rw_count(
158            UserAddress::const_from(u64::MAX),
159            &mut buffers,
160        )
161        .unwrap();
162        assert_eq!(total, *MAX_RW_COUNT);
163        assert_eq!(
164            buffers.as_slice(),
165            &[UserBuffer { address: UserAddress::const_from(0), length: *MAX_RW_COUNT }]
166        );
167    }
168
169    #[::fuchsia::test]
170    fn test_cap_buffers_to_max_rw_count_drop_buffer() {
171        let mut buffers = smallvec![
172            UserBuffer { address: UserAddress::const_from(0), length: *MAX_RW_COUNT },
173            UserBuffer { address: UserAddress::const_from(1 << 33), length: 20 }
174        ];
175        let total = UserBuffer::cap_buffers_to_max_rw_count(
176            UserAddress::const_from(u64::MAX),
177            &mut buffers,
178        )
179        .unwrap();
180        assert_eq!(total, *MAX_RW_COUNT);
181        assert_eq!(
182            buffers.as_slice(),
183            &[UserBuffer { address: UserAddress::const_from(0), length: *MAX_RW_COUNT }]
184        );
185    }
186
187    #[::fuchsia::test]
188    fn test_cap_buffers_to_max_rw_count_drop_and_shorten_buffer() {
189        let mut buffers = smallvec![
190            UserBuffer { address: UserAddress::const_from(0), length: *MAX_RW_COUNT - 10 },
191            UserBuffer { address: UserAddress::const_from(1 << 33), length: 20 },
192            UserBuffer { address: UserAddress::const_from(2 << 33), length: 20 }
193        ];
194        let total = UserBuffer::cap_buffers_to_max_rw_count(
195            UserAddress::const_from(u64::MAX),
196            &mut buffers,
197        )
198        .unwrap();
199        assert_eq!(total, *MAX_RW_COUNT);
200        assert_eq!(
201            buffers.as_slice(),
202            &[
203                UserBuffer { address: UserAddress::const_from(0), length: *MAX_RW_COUNT - 10 },
204                UserBuffer { address: UserAddress::const_from(1 << 33), length: 10 },
205            ]
206        );
207    }
208}