linux_libc_auxv/
builder.rs

1/*
2MIT License
3
4Copyright (c) 2025 Philipp Schuster
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24use crate::util::get_null_index;
25use crate::{AuxVar, AuxVarRaw, AuxVarType};
26use aligned_vec::{ABox, AVec};
27use alloc::string::String;
28use alloc::vec::Vec;
29use core::ffi::CStr;
30
31/// Builder to create a stack layout as described by the [`StackLayoutRef`]
32/// type.
33///
34/// [`StackLayoutRef`]: crate::StackLayoutRef
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct StackLayoutBuilder<'a> {
37    argv: Vec<String>,
38    envv: Vec<String>,
39    auxv: Vec<AuxVar<'a>>,
40}
41
42impl<'a> StackLayoutBuilder<'a> {
43    /// Creates a mew bioƶder-
44    #[must_use]
45    pub const fn new() -> Self {
46        Self {
47            argv: vec![],
48            envv: vec![],
49            auxv: vec![],
50        }
51    }
52
53    /// Adds an argument to the builder.
54    ///
55    /// Adding a terminating NUL byte is not necessary. Interim NUL bytes are
56    /// prohibited.
57    pub fn add_argv(mut self, arg: impl Into<String>) -> Self {
58        let mut arg = arg.into();
59        if let Some(pos) = arg.find('\0') {
60            assert_eq!(
61                pos,
62                arg.len() - 1,
63                "strings must not contain interim NUL bytes"
64            );
65        }
66
67        if !arg.ends_with('\0') {
68            arg.push('\0');
69        }
70
71        self.argv.push(arg);
72        self
73    }
74
75    /// Adds an environment-variable to the builder.
76    ///
77    /// Adding a terminating NUL byte is not necessary. Interim NUL bytes are
78    /// prohibited.
79    ///
80    /// The value must follow the `key=value` syntax, where `value` may be
81    /// empty.
82    pub fn add_envv(mut self, env: impl Into<String>) -> Self {
83        let mut env = env.into();
84        if let Some(pos) = env.find('\0') {
85            assert_eq!(
86                pos,
87                env.len() - 1,
88                "strings must not contain interim NUL bytes"
89            );
90        }
91
92        if !env.ends_with('\0') {
93            env.push('\0');
94        }
95
96        // Check syntax
97        {
98            let (key, _value) = env
99                .split_once('=')
100                .expect("should have ENV var syntax (`key=value`)");
101            assert!(!key.is_empty());
102        }
103        self.envv.push(env);
104        self
105    }
106
107    /// Adds an [`AuxVar`] to the builder.
108    #[must_use]
109    pub fn add_auxv(mut self, aux: AuxVar<'a>) -> Self {
110        // Ignore, we do this automatically in the end.
111        if aux != AuxVar::Null {
112            self.auxv.push(aux);
113        }
114        self
115    }
116
117    /// Returns the size in bytes needed for the `argv` entries.
118    ///
119    /// This includes the terminating null entry.
120    fn calc_len_argv_entries(&self) -> usize {
121        (self.argv.len() + 1/* null */) * size_of::<usize>()
122    }
123
124    /// Returns the size in bytes needed for the `envv` entries.
125    ///
126    /// This includes the terminating null entry.
127    fn calc_len_envv_entries(&self) -> usize {
128        (self.envv.len() + 1/* null */) * size_of::<usize>()
129    }
130
131    /// Returns the size in bytes needed for the `auxv` entries.
132    ///
133    /// This includes the terminating null entry.
134    fn calc_len_auxv_entries(&self) -> usize {
135        (self.auxv.len() + 1/* NULL entry */) * size_of::<AuxVarRaw>()
136    }
137
138    fn _calc_len_data_cstr(strs: &[String]) -> usize {
139        strs.iter()
140            .map(|arg| arg.as_bytes())
141            .map(|bytes| CStr::from_bytes_until_nul(bytes).expect("should have NUL byte"))
142            .map(|cstr| cstr.count_bytes() + 1 /* NUL */)
143            .sum::<usize>()
144    }
145
146    /// Returns the size in bytes needed for the `argv` data area.
147    ///
148    /// This includes any terminating null entries or padding.
149    fn calc_len_argv_data(&self) -> usize {
150        Self::_calc_len_data_cstr(&self.argv)
151    }
152
153    /// Returns the size in bytes needed for the `envv` data area.
154    ///
155    /// This includes any terminating null entries or padding.
156    fn calc_len_envv_data(&self) -> usize {
157        Self::_calc_len_data_cstr(&self.envv)
158    }
159
160    /// Returns the size in bytes needed for the `auxv` data area.
161    ///
162    /// This includes any terminating null entries or padding.
163    fn calc_len_auxv_data(&self) -> usize {
164        self.auxv
165            .iter()
166            .map(|aux| {
167                match aux {
168                    AuxVar::Platform(v) => {
169                        v.count_bytes() + 1 /* NUL */
170                    }
171                    AuxVar::BasePlatform(v) => {
172                        v.count_bytes() + 1 /* NUL */
173                    }
174                    AuxVar::Random(v) => {
175                        assert_eq!(v.len(), 16);
176                        16 /* fixed size */
177                    }
178                    AuxVar::ExecFn(v) => {
179                        v.count_bytes() + 1 /* NUL */
180                    }
181                    _ => 0,
182                }
183            })
184            .sum::<usize>()
185    }
186
187    /// Returns the total size in bytes needed for the structure.
188    ///
189    /// This includes any null entries or padding.
190    fn calc_total_len(&self) -> usize {
191        size_of::<usize>() /* argc */ +
192            self.calc_len_argv_entries()
193            + self.calc_len_envv_entries()
194            + self.calc_len_auxv_entries()
195            + self.calc_len_argv_data()
196            + self.calc_len_envv_data()
197            + self.calc_len_auxv_data()
198    }
199
200    /// Builds the layout with heap-allocated memory.
201    ///
202    /// # Arguments
203    /// - `target_addr`: The address the stack layout in the target address space.
204    ///   This may be a user-space address of another process. If this is
205    ///   `None` then the address of the buffer will be used.
206    #[must_use]
207    pub fn build(mut self, target_addr: Option<usize>) -> ABox<[u8]> {
208        if Some(&AuxVar::Null) != self.auxv.last() {
209            self.auxv.push(AuxVar::Null);
210        }
211
212        // Zeroed buffer. Enables us to not write dedicated NULL entries into
213        // `argv` and `envv`.
214        let mut buffer = {
215            let len = self.calc_total_len();
216            let mut vec = AVec::<u8>::new(align_of::<usize>());
217            for _ in 0..len {
218                vec.push(0);
219            }
220            vec.into_boxed_slice()
221        };
222
223        // If this is None, this will cause that the process creating this
224        // can also parse the structure entirely without memory issues.
225        let target_addr = target_addr.unwrap_or(buffer.as_ptr() as usize);
226
227        let mut serializer = StackLayoutSerializer::new(
228            &mut buffer,
229            target_addr,
230            self.calc_len_argv_entries(),
231            self.calc_len_envv_entries(),
232            self.calc_len_auxv_entries(),
233            self.calc_len_argv_data(),
234            self.calc_len_envv_data(),
235            self.calc_len_auxv_data(),
236        );
237        serializer.write_argc(self.argv.len());
238
239        for arg in self.argv {
240            let c_str = CStr::from_bytes_until_nul(arg.as_bytes()).unwrap();
241            serializer.write_arg(c_str);
242        }
243        // Writing NULL entry not necessary, the buffer is already zeroed
244
245        for var in self.envv {
246            let c_str = CStr::from_bytes_until_nul(var.as_bytes()).unwrap();
247            serializer.write_env(c_str);
248        }
249        // Writing NULL entry not necessary, the buffer is already zeroed
250
251        for var in self.auxv {
252            serializer.write_aux(&var);
253        }
254
255        buffer
256    }
257}
258
259/// Serializer for [`StackLayoutBuilder`].
260///
261/// This type takes care of the _entry area_ and the _data area_ with respect
262/// to a given `target_addr` (base address in target address space).
263///
264/// All strings can contain a NUL byte already. If it is not present, the
265/// serializer will take care of that.
266struct StackLayoutSerializer<'a> {
267    buffer: &'a mut [u8],
268    // Offset in bytes for writes
269    offset_argv: usize,
270    // Offset in bytes for writes
271    offset_envv: usize,
272    // Offset in bytes for writes
273    offset_auxv: usize,
274    // Offset in bytes for writes
275    offset_argv_data: usize,
276    // Offset in bytes for writes
277    offset_envv_data: usize,
278    // Offset in bytes for writes
279    offset_auxv_data: usize,
280    target_addr: usize,
281}
282
283impl<'a> StackLayoutSerializer<'a> {
284    /// Creates a new builder.
285    ///
286    /// The `auxv` entries [`AuxVarType::Null`] will be added automatically.
287    ///
288    /// # Arguments
289    /// - `target_addr`: The address the stack layout in the target address space.
290    ///   This may be a user-space address of another process.
291    #[allow(clippy::too_many_arguments)]
292    fn new(
293        buffer: &'a mut [u8],
294        target_addr: usize,
295        len_argv_entries: usize,
296        len_envv_entries: usize,
297        len_auxv_entries: usize,
298        len_argv_data: usize,
299        len_envv_data: usize,
300        len_auxv_data: usize,
301    ) -> Self {
302        assert_eq!(buffer.as_ptr().align_offset(align_of::<usize>()), 0);
303
304        let total_size = size_of::<usize>() /* initial argc */ + len_argv_entries + len_envv_entries + len_auxv_entries
305            + len_argv_data + len_envv_data + len_auxv_data;
306        assert!(buffer.len() >= total_size);
307
308        // These offsets include any necessary NULL entries and NUL bytes.
309        let offset_argv = size_of::<usize>() /* initial argc */;
310        let offset_envv = offset_argv + len_argv_entries;
311        let offset_auxv = offset_envv + len_envv_entries;
312        // auxv data area comes first, then argv, then envv
313        let offset_auxv_data = offset_auxv + len_auxv_entries;
314        let offset_argv_data = offset_auxv_data + len_auxv_data;
315        let offset_envv_data = offset_argv_data + len_argv_data;
316
317        Self {
318            buffer,
319            offset_argv: size_of::<usize>(), /* argc */
320            offset_envv,
321            offset_auxv,
322            offset_argv_data,
323            offset_envv_data,
324            offset_auxv_data,
325            target_addr,
326        }
327    }
328
329    /// Performs sanity checks ensuring that no offset breaks its boundaries.
330    fn sanity_checks(&self) {
331        assert!(self.offset_argv <= self.offset_envv);
332        assert!(self.offset_envv <= self.offset_auxv);
333        assert!(self.offset_auxv <= self.offset_auxv_data);
334        assert!(self.offset_auxv_data <= self.offset_argv_data);
335        assert!(self.offset_argv_data <= self.offset_envv_data);
336        assert!(self.offset_envv_data <= self.buffer.len());
337    }
338
339    /// Writes bytes to the data area and updates the offset afterward.
340    const fn _write_data_area(buffer: &mut [u8], data: &[u8], data_area_offset: &mut usize) {
341        let src_ptr = data.as_ptr();
342        let dst_ptr = buffer.as_mut_ptr().cast::<u8>();
343        let dst_ptr = unsafe { dst_ptr.add(*data_area_offset) };
344        unsafe {
345            core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, data.len());
346        }
347        *data_area_offset += data.len();
348    }
349
350    /// Writes a null-terminated CStr into the structure, including the
351    /// pointer and the actual data.
352    const fn _write_cstr(
353        buffer: &mut [u8],
354        str: &CStr,
355        entry_offset: &mut usize,
356        data_area_offset: &mut usize,
357        target_addr: usize,
358    ) {
359        // The address where this will be reachable from a user-perspective.
360        let data_addr = target_addr + *data_area_offset;
361
362        // write entry
363        {
364            let src_ptr = buffer.as_mut_ptr().cast::<u8>();
365            let src_ptr = unsafe { src_ptr.add(*entry_offset) };
366            unsafe { core::ptr::write(src_ptr.cast::<usize>(), data_addr) }
367            *entry_offset += size_of::<usize>();
368        }
369
370        // write data
371        Self::_write_data_area(buffer, str.to_bytes(), data_area_offset);
372        // write NUL
373        Self::_write_data_area(buffer, &[0], data_area_offset);
374    }
375
376    /// Writes the `argc` value into the structure.
377    fn write_argc(&mut self, argc: usize) {
378        unsafe { core::ptr::write(self.buffer.as_mut_ptr().cast::<usize>(), argc) }
379
380        self.sanity_checks();
381    }
382
383    /// Writes an argument into the structure.
384    fn write_arg(&mut self, arg: &CStr) {
385        Self::_write_cstr(
386            self.buffer,
387            arg,
388            &mut self.offset_argv,
389            &mut self.offset_argv_data,
390            self.target_addr,
391        );
392        self.sanity_checks();
393    }
394
395    /// Writes an environmental variable into the structure.
396    fn write_env(&mut self, var: &CStr) {
397        Self::_write_cstr(
398            self.buffer,
399            var,
400            &mut self.offset_envv,
401            &mut self.offset_envv_data,
402            self.target_addr,
403        );
404
405        self.sanity_checks();
406    }
407
408    /// Writes an auxiliary variable into the auxiliary vector.
409    fn write_aux_immediate(&mut self, key: AuxVarType, val: usize) {
410        let ptr = self.buffer.as_mut_ptr().cast::<u8>();
411        let ptr = unsafe { ptr.add(self.offset_auxv) };
412        let value = AuxVarRaw::new(key, val);
413        unsafe { core::ptr::write(ptr.cast::<AuxVarRaw>(), value) }
414        self.offset_auxv += size_of::<AuxVarRaw>();
415    }
416
417    /// Writes the referenced data of an auxiliary vector into the
418    /// _auxv data area_.
419    fn write_aux_refdata(&mut self, key: AuxVarType, data: &[u8], add_nul_byte: bool) {
420        // The address where this will be reachable from a user-perspective.
421        let data_addr = self.target_addr + self.offset_auxv_data;
422        self.write_aux_immediate(key, data_addr);
423
424        // write data
425        Self::_write_data_area(self.buffer, data, &mut self.offset_auxv_data);
426
427        // add NUL byte if necessary
428        if add_nul_byte {
429            // assert: If there is a NUL byte in the data, it is only allowed at
430            // the very last position
431            if let Some(pos) = get_null_index(data) {
432                assert_eq!(
433                    pos,
434                    data.len() - 1,
435                    "strings must not contain interim NUL bytes"
436                );
437            }
438
439            if data.last().copied().unwrap() != 0 {
440                // write NUL
441                Self::_write_data_area(self.buffer, &[0], &mut self.offset_auxv_data);
442            }
443        }
444    }
445
446    /// Deconstructs a [`AuxVar`] and writes the corresponding [`AuxVarRaw`]
447    /// into the structure.
448    fn write_aux(&mut self, aux: &AuxVar<'a>) {
449        match aux {
450            AuxVar::Platform(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
451            AuxVar::BasePlatform(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
452            AuxVar::Random(v) => self.write_aux_refdata(aux.key(), v, false),
453            AuxVar::ExecFn(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
454            _ => self.write_aux_immediate(aux.key(), aux.value_raw()),
455        }
456
457        self.sanity_checks();
458    }
459}
460
461#[cfg(test)]
462mod tests {
463    use super::*;
464    use crate::StackLayoutRef;
465
466    #[test]
467    fn test_builder() {
468        let builder = StackLayoutBuilder::new()
469            .add_argv("first arg")
470            .add_argv("second arg")
471            .add_envv("var1=foo")
472            .add_envv("var2=bar")
473            .add_auxv(AuxVar::EGid(1_1337))
474            .add_auxv(AuxVar::Gid(2_1337))
475            .add_auxv(AuxVar::Random([
476                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
477            ]))
478            .add_auxv(AuxVar::Uid(3_1337))
479            .add_auxv(AuxVar::ExecFn(c"ExecFn as &CStr".into()))
480            .add_auxv(AuxVar::Platform("Platform as &str".into()))
481            .add_auxv(AuxVar::BasePlatform("Base Platform as &str".into()));
482        let layout = builder.build(None);
483
484        // now parse the layout
485        let layout = StackLayoutRef::new(layout.as_ref(), None);
486
487        assert_eq!(layout.argc(), 2);
488
489        // argv
490        {
491            assert_eq!(layout.argv_raw_iter().count(), 2);
492
493            // Just printing uncovers memory errors
494            // SAFETY: This was created for the address space of this process.
495            unsafe { layout.argv_iter() }
496                .enumerate()
497                .for_each(|(i, str)| eprintln!("  arg {i:>2}: {str:?}"));
498        }
499
500        // envv
501        {
502            assert_eq!(layout.envc(), 2);
503
504            // Just printing uncovers memory errors
505            // SAFETY: This was created for the address space of this process.
506            unsafe { layout.envv_iter() }
507                .enumerate()
508                .for_each(|(i, ptr)| eprintln!("  env {i:>2}: {ptr:?}"));
509        }
510
511        // auxv
512        {
513            assert_eq!(layout.auxvc(), 7);
514
515            // Just printing uncovers memory errors
516            // SAFETY: This was created for the address space of this process.
517            unsafe { layout.auxv_iter() }
518                .enumerate()
519                .for_each(|(i, aux)| eprintln!("  auxv {i:>2}: {aux:?}"));
520        }
521
522        // now test the strings match
523        let fn_get_at_string = |key: AuxVarType| {
524            unsafe { layout.auxv_iter() }
525                .find(|e| e.key() == key)
526                .map(|v| {
527                    v.value_payload_str()
528                        .cloned()
529                        .map(|s| s.into_string())
530                        .unwrap()
531                })
532                .unwrap()
533        };
534        let at_exec_fn = fn_get_at_string(AuxVarType::ExecFn);
535        assert_eq!(at_exec_fn, "ExecFn as &CStr");
536
537        let at_base_platform = fn_get_at_string(AuxVarType::BasePlatform);
538        assert_eq!(at_base_platform, "Base Platform as &str");
539    }
540}