Nugine 的个人博客关于

Unsafe Rust 随堂小测(三)

本卷总分为 100 分,时间 50 分钟。

开卷考试,资料限制为 Rust 官方文档、书籍和 Docs.rs 网站,禁止访问其他网站。

默认情况下,所有测试题仅可使用 Rust 标准库,禁止自行链接 C 标准库函数。

本卷不附带标准答案,完成后请自行订正。

本卷题号与第二卷连续。


第 11 题

类型 Buf 的 default_inplace 方法实现可能触发 UB,请指出原因并修复该方法。(20分)

const N: usize = 1024;

pub struct Buf {
    header: String,
    body: [u8; N],
}

impl Buf {
    /// FIXME: UB
    ///
    /// # Safety
    /// The pointer `p` must be valid for writes and be properly aligned.
    pub unsafe fn default_inplace(p: *mut Buf) {
        let header: *mut String = &mut (*p).header;
        header.write(String::default());

        let body: *mut [u8; N] = &mut (*p).body;
        body.write_bytes(0, 1);
    }
}

第 12 题

str_replace 函数为什么是不健全的?(10分)

请增加测试用例,使该函数触发 UB。(10分)

/// !!!unsound!!!
pub fn str_replace(s: &str, from: &str, to: &str) -> String {
    if s.len() < from.len() {
        return s.to_owned();
    }

    let (s, from, to) = (s.as_bytes(), from.as_bytes(), to.as_bytes());
    let mut ans: Vec<u8> = Vec::new();

    unsafe {
        if from.is_empty() {
            for &b in s {
                ans.extend_from_slice(to);
                ans.push(b);
            }
            ans.extend_from_slice(to);
        } else {
            let mut i = 0;
            let end = s.len() - from.len();
            while i <= end {
                let probe = s.get_unchecked(i..i + from.len());
                if probe == from {
                    ans.extend_from_slice(to);
                    i += from.len();
                } else {
                    ans.push(s[i]);
                    i += 1;
                }
            }
        }

        String::from_utf8(ans).unwrap_unchecked()
    }
}

#[test]
fn test_str_replace() {
    assert_eq!(str_replace("abc", "a", "b"), "bbc");
    assert_eq!(str_replace("abc", "", "b"), "babbbcb");
    assert_eq!(str_replace("abc", "a", ""), "bc");
}

第 13 题

以下是类型 DerefIterMut 的公开接口实现,其中存在什么问题?(10分)

请针对该类型的公开接口构造测试,使以下代码触发 UB。(10分)

use std::ops::DerefMut;

#[derive(Debug)]
pub struct DerefIterMut<'a, T> {
    slice: &'a mut [T],
    cur: usize,
}

impl<'a, T> DerefIterMut<'a, T> {
    pub fn new(slice: &'a mut [T]) -> Self {
        Self { slice, cur: 0 }
    }
}

impl<'a, T: DerefMut> Iterator for DerefIterMut<'a, T> {
    type Item = &'a mut T::Target;

    fn next(&mut self) -> Option<Self::Item> {
        let elem: &mut T = self.slice.get_mut(self.cur)?;
        let item: *mut T::Target = &mut **elem;
        self.cur += 1;
        unsafe { Some(&mut *item) }
    }
}

第 14 题

以下是类型 IntoIter 的公开接口实现,其中存在什么问题?(10分)

请针对该类型的各公开接口构造测试,使以下代码触发 UB。(10分)


use std::mem::ManuallyDrop;
use std::{ptr, slice};

#[derive(Clone)]
pub struct IntoIter<T> {
    boxed_slice: Box<[ManuallyDrop<T>]>,
    cur: usize,
}

impl<T> IntoIter<T> {
    pub fn new(boxed_slice: Box<[T]>) -> Self {
        let boxed_slice: Box<[ManuallyDrop<T>]> = unsafe {
            let mut b = ManuallyDrop::new(boxed_slice);
            let data: *mut ManuallyDrop<T> = b.as_mut_ptr().cast();
            let len: usize = b.len();
            Box::from_raw(slice::from_raw_parts_mut(data, len))
        };
        Self {
            boxed_slice,
            cur: 0,
        }
    }
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let item: ManuallyDrop<T> = ptr::read(self.boxed_slice.get(self.cur)?);
            self.cur += 1;
            Some(ManuallyDrop::into_inner(item))
        }
    }
}

impl<T> Drop for IntoIter<T> {
    fn drop(&mut self) {
        unsafe {
            let data: *mut T = self.boxed_slice.as_mut_ptr().add(self.cur).cast();
            let len: usize = self.boxed_slice.len() - self.cur;
            let slice: *mut [T] = slice::from_raw_parts_mut(data, len);
            ptr::drop_in_place(slice);
        }
    }
}

第 15 题

以下是类型 Month 的公开接口实现,其中存在什么问题?(10分)

请针对该类型的公开接口构造测试,使以下代码触发 UB。(10分)

本题测试函数允许使用任意第三方库,但禁止一切系统调用。


use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Month(u8);

impl Month {
    pub fn new(month: u8) -> Self {
        assert!((1..=12).contains(&month));
        Self(month)
    }

    pub fn name(self) -> &'static str {
        static NAME: &[&str; 12] = &[
            "January",
            "February",
            "March",
            "April",
            "May",
            "June",
            "July",
            "August",
            "September",
            "October",
            "November",
            "December",
        ];
        unsafe { NAME.get_unchecked((self.0 - 1) as usize) }
    }
}

Unsafe Rust 随堂小测第一季到此结束,总共三卷,300 分,时间 120 分钟。

每丢一个得分点,在实际项目中就有可能爆出相应的漏洞,各位同学有信心拿满分吗?

发布于 2022-06-24地址: GitHub, 知乎