AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 76979096
Accepted
444RS
444RS
Asked: 2023-08-26 00:41:38 +0800 CST2023-08-26 00:41:38 +0800 CST 2023-08-26 00:41:38 +0800 CST

这个不安全的 Rust 代码有什么问题,所以它可以在 Windows 上运行,但不能在 Ubuntu 上运行?

  • 772

您好,我知道代码可以完全编写,没有任何不安全的代码,但我正在研究并学习“幕后”的工作原理。

回到主题,我编写了一段不安全的 Rust 代码,在我看来,它应该可以正常工作,没有任何问题。

这是定义:

pub struct Container {
    inner: Pin<Box<String>>,
    half_a: *const str,
    half_b: *const str,
}

impl Container {
    const SEPARATOR: char = '-';

    pub fn new(input: impl AsRef<str>) -> Option<Self> {
        let input = input.as_ref();
        if input.is_empty() {
            return None
        }

        // Making sure the value is never moved in the memory
        let inner = Box::pin(input.to_string());

        let separator_index = inner.find(Container::SEPARATOR)?;
        let inner_ref = &**inner;

        let half_a = &inner_ref[0..separator_index];
        let half_b = &inner_ref[separator_index+1..];

        // Check if the structure definition is met (populated values + only one separator)
        if half_a.is_empty() || half_b.is_empty() || half_b.contains(Container::SEPARATOR) {
            return None;
        }

        Some(Self {
            half_a: half_a as *const str,
            half_b: half_b as *const str,
            inner,
        })
    }
    
    pub fn get_half_a(&self) -> &str {
        unsafe {
            &*self.half_a
        }
    }

    pub fn get_half_b(&self) -> &str {
        unsafe {
            &*self.half_b
        }
    }
}

总之,它接受任何可以表示为 str 引用的输入,在堆上创建输入的固定克隆,获取指向该值的两半的地址并将其作为结构返回。

现在当我做测试时:

let valid = Container::new("first-second").unwrap();
assert_eq!(valid.get_half_a(), "first");
assert_eq!(valid.get_half_b(), "second");

它应该运行而不会出现任何恐慌,事实上这就是 Windows 上发生的情况。它多次编译和运行没有任何问题,但是当它在 Ubuntu 上运行时,我收到一条错误,显示地址不再指向内存中的有效位置:

 thread 'tests::types::container' panicked at 'assertion failed: `(left == right)`
  left: `"�K\u{13}϶"`,
 right: `"first"`', research/src/tests/types.rs:77:5

这里可能有什么问题?我错过了什么? 我使用以下标志将这段代码作为 GitHub 操作运行runs-on: ubuntu-latest。

以下是 Playground 的 URL,显示此代码运行时没有任何问题: https://play.rust-lang.org/? version=stable&mode=debug&edition=2021&gist=d36b19de4d0fa05340191f5107029d75

我预计在不同的操作系统上运行此代码不会出现任何问题。

windows
  • 1 1 个回答
  • 72 Views

1 个回答

  • Voted
  1. Best Answer
    drewtato
    2023-08-26T03:37:29+08:002023-08-26T03:37:29+08:00

    更改Box<String>为Box<str>不会影响健全性,但会触发 MIRI。

    error: Undefined Behavior: trying to retag from <2563> for SharedReadOnly permission at alloc890[0x0], but that tag does not exist in the borrow stack for this location
      --> src/main.rs:41:18
       |
    41 |         unsafe { &*self.half_a }
       |                  ^^^^^^^^^^^^^
       |                  |
       |                  trying to retag from <2563> for SharedReadOnly permission at alloc890[0x0], but that tag does not exist in the borrow stack for this location
       |                  this error occurs as part of retag at alloc890[0x0..0x5]
       |
       = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
       = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
    help: <2563> was created by a SharedReadOnly retag at offsets [0x0..0x5]
      --> src/main.rs:34:21
       |
    34 |             half_a: half_a as *const str,
       |                     ^^^^^^
    help: <2563> was later invalidated at offsets [0x0..0xc] by a Unique retag (of a reference/box inside this compound value)
      --> src/main.rs:36:13
       |
    36 |             inner,
       |             ^^^^^
       = note: BACKTRACE (of the first span):
       = note: inside `Container::get_half_a` at src/main.rs:41:18: 41:31
    note: inside `main`
      --> src/main.rs:51:16
       |
    51 |     assert_eq!(valid.get_half_a(), "first");
       |                ^^^^^^^^^^^^^^^^^^
    

    这来自Box,不能使用别名。虽然从 派生指针通常没问题Box,但当您移动Box(通过返回Container)时,Rust 不再知道Box已派生自它的指针,并假设由于别名而通过指针进行的访问无效。

    这就是 MIRI 被触发的原因。但是,我不确定是什么导致了这种未定义的行为。您的测试结果表明确实如此,但无法告诉您原因。我的猜测是,Rust 决定inner一旦new返回就可以删除,因为它保证是唯一的。它甚至可能优化分配,以永远不会实际写入任何数据(您版本中的指针、长度和容量String),因为该数据永远不会被读取,这可以解释您的运行时错误。

    您可以通过仅存储指针并实现Drop. (操场)

    pub struct Container {
        inner: *mut str,
        half_a: *const str,
        half_b: *const str,
    }
    
    impl Drop for Container {
        fn drop(&mut self) {
            // SAFETY: Nothing references this value since it is being dropped,
            // and `half_a` and `half_b` are never read after this.
            unsafe { drop(Box::from_raw(self.inner)) }
        }
    }
    

    我认为Pin这里对健全性没有任何作用。Pin更多地用于处理公共接口。只要您不提供任何&mut对 的引用inner,就没有什么可防范的。Pin虽然您可能需要它作为内部保证,但您的实际保证比您根本无法使用该值更强大。

    • 3

相关问题

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve