范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Rust社区RFC导读构建安全的IO

  动机
  最近Rust官方合并了一个 RFC ,通过引入I/O安全的概念和一套新的类型和特征,为AsRawFd和相关特质的用户提供关于其原始资源句柄的保证,从而弥补Rust中封装边界的漏洞。
  Rust 标准库提供了 I/O 安全性,保证程序持有私有的原始句柄(raw handle),其他部分无法访问它。但是 FromRawFd::from_raw_fd 是 Unsafe 的,所以在 Safe Rust中无法做到 File::from_raw(7) 这种事。 在这个文件描述符上面进行 I/O 操作,而这个文件描述符可能被程序的其他部分私自持有。
  但是,很多 API 通过接受 原始句柄 来进行 I/O 操作:pub fn do_some_io(input: &FD) -> io::Result<()> {     some_syscall(input.as_raw_fd()) } 复制代码
  AsRawFd并没有限制as_raw_fd的返回值,所以do_some_io最终可以在任意的RawFd值上进行 I/O 操作。甚至可以写do_some_io(&7),因为RawFd本身实现了AsRawFd。这可能会导致程序访问错误的资源。甚至通过创建在其他部分私有的句柄别名来打破封装边界,导致一些诡异的 远隔作用(Action at a distance)。
  远隔作用(Action at a distance)是一种程式设计中的反模式,是指程式某一部分的行为会广泛的受到程式其他部分指令的影响,而且要找到影响其他程式的指令很困难,甚至根本无法进行。
  在一些特殊的情况下,违反 I/O 安全甚至会导致内存安全。I/O 安全概念引入
  标准库中有一些类型和特质: RawFd(Unix) / RawHandle/RawSocket(Windows) ,它们代表原始的操作系统资源句柄。这些类型本身并不提供任何行为,而只是代表可以传递给底层操作系统API的标识符。
  这些原始句柄可以被认为是原始指针,具有类似的危险性。虽然获得一个原始指针是安全的,但是如果一个原始指针不是一个有效的指针,或者如果它超过了它所指向的内存的生命周期,那么解引用原始指针可能会调用未定义的行为。
  同样,通过AsRawFd::as_raw_fd和类似的方式获得一个原始句柄是安全的,但是如果它不是一个有效的句柄或者在其资源关闭后使用,使用它来做I/O可能会导致输出损坏、输入数据丢失或泄漏,或者违反封装边界。而在这两种情况下,影响可能是非局部的且影响到程序中其他不相关的部分。对原始指针危险的保护被称为内存安全,所以对原始句柄危险的保护被称为I/O安全。
  Rust的标准库也有一些高级类型,如File和TcpStream,它们是这些原始句柄的封装器,提供了操作系统API的高级接口。
  这些高级类型也实现了Unix-like平台上的FromRawFd和Windows上的FromRawHandle/FromRawSocket的特性,这些特性提供了包裹底层(low-level )值以产生上层(high-level)值的函数。这些函数是不安全的,因为它们无法保证I/O安全,类型系统并不限制传入的句柄。use std::fs::File; use std::os::unix::io::FromRawFd;  // Create a file. let file = File::open("data.txt")?;  // 从任意的整数值构造 file // 然而这种类型的检查在运行时可能无法识别一个合法存活的资源 // 或者它可能意外地在程序的其他地方被以别名方式封装处理(此处无法判断) // 这里添加  unsafe 块 是让调用者来避免上述危险 let forged = unsafe { File::from_raw_fd(7) };  // Obtain a copy of `file`"s inner raw handle. let raw_fd = file.as_raw_fd();  // Close `file`. drop(file);  // Open some unrelated file. let another = File::open("another.txt")?;  // 进一步使用 raw_fd ,也就是 file 的内部原始句柄,将超出操作系统与之相关的生命周期 // 这可能会导致它意外地与其他封装好的 file 实例发生别名,比如 another   // 因此,这里 unsafe 块是让调用者避免上述危险 let dangling = unsafe { File::from_raw_fd(raw_fd) }; 复制代码
  调用者必须确保传入from_raw_fd的值是明确地从操作系统返回的,而且from_raw_fd的返回值不会超过操作系统与句柄相关的生命周期。
  I/O 安全的概念虽然是新的,但它反映出了一个普遍的做法。 Rust 生态系统将会逐步支持 I/O 安全。I/O 安全 Rust 解决方案OwnedFd 和 BorrowedFd<"fd>
  这两种类型用于替代 RawFd ,对句柄值赋予所有权语义,代表句柄值的 拥有和借用。
  OwnedFd 拥有一个 fd ,会在析构的时候关闭它。BorrowedFd<"fd> 中的生命周期参数表示对这个 fd 的访问被借用多长时间。
  对于Windows来说,也有类似的类型,但都是Handle和Socket形式。
  类型
  类似于
  OwnedFd
  Box
  BorrowedFd<"a>
  &"a _
  RawFd
  *const _
  和其他类型相比,I/O 类型并不区分可变和不可变。操作系统资源可以在Rust的控制之外以各种方式共享,所以I/O可以被认为是使用内部可变性。AsFd、Into和From
  这三个概念是AsRawFd::as_raw_fd、IntoRawFd::into_raw_fd和FromRawFd::from_raw_fd的概念性替代,分别适用于大多数使用情况。它们以OwnedFd和BorrowedFd的方式工作,所以它们自动执行其I/O安全不变性。pub fn do_some_io(input: &FD) -> io::Result<()> {     some_syscall(input.as_fd()) } 复制代码
  使用这个类型,就会避免之前那个问题。由于AsFd只针对那些适当拥有或借用其文件描述符的类型实现,这个版本的do_some_io不必担心被传递假的或悬空的文件描述符。逐步采用
  I/O安全和新的类型和特性不需要立即被采用,可以逐步采用。首先,std为所有相关的std类型添加新的类型和特质,并提供impls。这是一个向后兼容的变化。之后,crate可以开始使用新的类型,并为它们自己的类型实现新的特质。这些变化将是很小的,而且是半兼容的,不需要特别的协调。一旦标准库和足够多的流行crate实现了新的特质,crate就可以按照自己的节奏开始使用新的特质作为接受通用参数时的边界。这些将是与semver不兼容的变化,尽管大多数切换到这些新特质的API的用户不需要任何改变。原型实现
  该 RFC 内容原型已经实现,参见 io-lifetimes 。
  Raw API
  This experimental API
  Raw*
  Borrowed* and Owned*
  AsRaw*
  As*
  IntoRaw*
  Into*
  FromRaw*
  From*trait 实现
  AsFd 转换为 原生 fd ,是带有生命周期参数的 BorrowedFd<"_>#[cfg(any(unix, target_os = "wasi"))] pub trait AsFd {     /// Borrows the file descriptor.     ///     /// # Example     ///     /// ```rust,no_run     /// # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]     /// use std::fs::File;     /// # use std::io;     /// use io_lifetimes::{AsFd, BorrowedFd};     ///     /// let mut f = File::open("foo.txt")?;     /// let borrowed_fd: BorrowedFd<"_> = f.as_fd();     /// # Ok::<(), io::Error>(())     /// ```     fn as_fd(&self) -> BorrowedFd<"_>; }  复制代码
  IntoFd从 原生 fd 转为 安全的 fd,是 OwnedFd#[cfg(any(unix, target_os = "wasi"))] pub trait IntoFd {     /// Consumes this object, returning the underlying file descriptor.     ///     /// # Example     ///     /// ```rust,no_run     /// # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]     /// use std::fs::File;     /// # use std::io;     /// use io_lifetimes::{IntoFd, OwnedFd};     ///     /// let f = File::open("foo.txt")?;     /// let owned_fd: OwnedFd = f.into_fd();     /// # Ok::<(), io::Error>(())     /// ```     fn into_fd(self) -> OwnedFd; } 复制代码
  FromFd 从原生 fd 构造 OwnedFd#[cfg(any(unix, target_os = "wasi"))] pub trait FromFd {     /// Constructs a new instance of `Self` from the given file descriptor.     ///     /// # Example     ///     /// ```rust,no_run     /// # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]     /// use std::fs::File;     /// # use std::io;     /// use io_lifetimes::{FromFd, IntoFd, OwnedFd};     ///     /// let f = File::open("foo.txt")?;     /// let owned_fd: OwnedFd = f.into_fd();     /// let f = File::from_fd(owned_fd);     /// # Ok::<(), io::Error>(())     /// ```     fn from_fd(owned: OwnedFd) -> Self;      /// Constructs a new instance of `Self` from the given file descriptor     /// converted from `into_owned`.     ///     /// # Example     ///     /// ```rust,no_run     /// # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]     /// use std::fs::File;     /// # use std::io;     /// use io_lifetimes::{FromFd, IntoFd};     ///     /// let f = File::open("foo.txt")?;     /// let f = File::from_into_fd(f);     /// # Ok::<(), io::Error>(())     /// ```     #[inline]     fn from_into_fd(into_owned: Owned) -> Self     where         Self: Sized,     {         Self::from_fd(into_owned.into_fd())     } } 复制代码
  上述为针对 Unix 平台的 trait,该库也包含 Windows 平台的相关 trait : AsHandle / AsSocket 、IntoHandle /IntoSocket 、FromHandle /FromSocket 。相关类型
  BorrowedFd<"fd>#[cfg(any(unix, target_os = "wasi"))] #[derive(Copy, Clone)] #[repr(transparent)] #[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))] // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a // 32-bit c_int. Below is -2, in two"s complement, but that only works out // because c_int is 32 bits. #[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] pub struct BorrowedFd<"fd> {     fd: RawFd,     _phantom: PhantomData<&"fd OwnedFd>, }  #[cfg(any(unix, target_os = "wasi"))] #[repr(transparent)] #[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))] // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a // 32-bit c_int. Below is -2, in two"s complement, but that only works out // because c_int is 32 bits. #[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] pub struct OwnedFd {     fd: RawFd, }  #[cfg(any(unix, target_os = "wasi"))] impl BorrowedFd<"_> {     /// Return a `BorrowedFd` holding the given raw file descriptor.     ///     /// # Safety     ///     /// The resource pointed to by `raw` must remain open for the duration of     /// the returned `BorrowedFd`, and it must not have the value `-1`.     #[inline]     pub unsafe fn borrow_raw_fd(fd: RawFd) -> Self {         debug_assert_ne!(fd, -1_i32 as RawFd);         Self {             fd,             _phantom: PhantomData,         }     } }  #[cfg(any(unix, target_os = "wasi"))] impl AsRawFd for BorrowedFd<"_> {     #[inline]     fn as_raw_fd(&self) -> RawFd {         self.fd     } }  #[cfg(any(unix, target_os = "wasi"))] impl AsRawFd for OwnedFd {     #[inline]     fn as_raw_fd(&self) -> RawFd {         self.fd     } }  #[cfg(any(unix, target_os = "wasi"))] impl IntoRawFd for OwnedFd {     #[inline]     fn into_raw_fd(self) -> RawFd {         let fd = self.fd;         forget(self);         fd     } }  #[cfg(any(unix, target_os = "wasi"))] impl Drop for OwnedFd {     #[inline]     fn drop(&mut self) {         #[cfg(feature = "close")]         unsafe {             let _ = libc::close(self.fd as std::os::raw::c_int);         }          // If the `close` feature is disabled, we expect users to avoid letting         // `OwnedFd` instances drop, so that we don"t have to call `close`.         #[cfg(not(feature = "close"))]         {             unreachable!("drop called without the "close" feature in io-lifetimes");         }     } }   复制代码为 std 和其他生态库 支持安全 I/O
  再构建一些跨平台抽象类型之后,为 ffi / async_std/ fs_err/ mio/ os_pipe/ socket2/ tokio / std 来支持 安全I/O 抽象。使用案例// From: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/hello.rs  #[cfg(all(rustc_attrs, unix, feature = "close"))] fn main() -> io::Result<()> {     // write 是 c api,所以用 unsafe     let fd = unsafe {         // Open a file, which returns an `Option`, which we can         // maybe convert into an `OwnedFile`.         // 拥有一个 fd         let fd: OwnedFd = open("/dev/stdout".as_ptr() as *const _, O_WRONLY | O_CLOEXEC)             .ok_or_else(io::Error::last_os_error)?;          // Borrow the fd to write to it.         // 借用这个 fd          let result = write(fd.as_fd(), "hello, world ".as_ptr() as *const _, 13);         match result {             -1 => return Err(io::Error::last_os_error()),             13 => (),             _ => return Err(io::Error::new(io::ErrorKind::Other, "short write")),         }          fd     };      // Convert into a `File`. No `unsafe` here!     // 这里不再需要 Unsafe 了     let mut file = File::from_fd(fd);     writeln!(&mut file, "greetings, y"all")?;      // We can borrow a `BorrowedFd` from a `File`.     unsafe {         // 借用 fd         let result = write(file.as_fd(), "sup? ".as_ptr() as *const _, 5);         match result {             -1 => return Err(io::Error::last_os_error()),             5 => (),             _ => return Err(io::Error::new(io::ErrorKind::Other, "short write")),         }     }      // Now back to `OwnedFd`.     let fd = file.into_fd();      // 不是必须的,会自动析构 fd      unsafe {         // This isn"t needed, since `fd` is owned and would close itself on         // drop automatically, but it makes a nice demo of passing an `OwnedFd`         // into an FFI call.         close(fd);     }      Ok(()) }  复制代码理由与替代方案关于 "unsafe 是为了内存安全" 的说法
  Rust 在历史上划定了一条界线,指出 unsafe 仅仅是用于 内存安全相关。 比较知名的例子是 std::mem::forget, 它增加是 unsafe 的,后来改为了 safe。
  声明 unsafe 只用于内存安全的结论表明,unsafe 不应该用于 其他非内存安全类的 API ,比如 标示某个 API 是应该避免使用的之类。
  内存安全优先级高于其他缺陷,因为它不仅仅是为了避免非预期行为,而是为了避免无法约束一段代码可能做的事情的情况。
  I/O 安全也是属于这类情况,理由有二:I/O安全错误会导致内存安全错误,在mmap周围的安全包装器存在的情况下(在具有操作系统特定API的平台上,允许它们是安全的)。I/O安全错误也意味着一段代码可以读取、写入或删除程序中其他部分使用的数据,而不需要命名它们或给它们一个引用。如果不知道链接到程序中的所有其他crate的实现细节,就不可能约束一个crate可以做的事情的集合。
  原始句柄很像进入独立地址空间的原始指针;它们可以悬空或以虚假的方式进行计算。I/O安全与内存安全类似;两者都是为了防止诡异的远隔作用,而且在两者中,所有权是健壮抽象的主要基础,所以使用类似的安全概念是很自然的。相关github.com/smiller123/…github.com/bytecodeall…RFC #3128 IO Safetynrc 的 RFC 索引列表
  作者:Rust_Magazine
  链接:https://juejin.cn/post/7044050750367858696

女生为什么想不通男友愿意花4000块钱买一张显卡,老程来告诉你导读现在很多人配电脑趋于理性化了,但是还是有很多人为了高性能,发烧配置而花钱,如果你是单身一人或者还是家里给你的消费负责,那么发烧级别肯定是没问题的,有钱,任性这个老程没办法啊,但浅谈品牌机与组装机的差异,品牌机和组装机到底哪些地方不同导读今天下雨,老程作为一个专业的无业游民,很不幸今天睡到了9点,然后起来发现自己的显卡有点脏,于是乎就出现了水洗显卡的事情,但是现在显卡已经在晾晒过程中了,那么剩下的时间我们要干啥如何装一台性价比的电脑主机老程带你了解怎么装电脑导读作为失踪人口,总要在合适的时间更新一篇合适的文章,老程又是好几天没有更新了,说实话老程是想收集素材,给大家带来更好的阅读体验老程有点懒,临沂天太热,电脑刚换显示器,好吧这些都是建个20平方的冷库多少钱?冷库行情冷库是如何计算价格的呢?建个冷库多少钱?相信行业里的人,每每有用户咨询建库的相关事宜,都会涉及到这个话题,冷库是个性化的定制品,价格的多少与实的一些数据至关重要,当用户问价冷库拆卸时的注意事项及处理方法冷库拆装基本常识概述现今社会,随着生活的越来越好,冷库的应用也十分普及,涉及到的冷藏冷冻,保温保鲜等的发展需要,冷库鲜食品已成为人们日常生活的必需品。使用商大到公司集团,小到个体个冷库制冷系统施工及注意事项氟里昂冷库系统的工艺与制作。系统连接铜管两头要封住,不能有其它杂物进入,如发现有进入异物,要及时用高压氮气冲洗干净,直到确定没有异物在内为止,杂物的进入,如不小心进入系统,不仅造成医药冷库的特点及要求医用冷库概述医药冷库顾名思意,就是医药医院医疗医用方面的冷库,称之为医药冷库,医用冷库一般应在医院医药企业医疗单位经销医用相关的公司等。医药冷库的种类医药冷库的类型有,疫苗冷库医药冷库常见的几种故障及维修方法冷库维修分析。制冷设备长时间的工作及运行中,难免会出现一些问题及故障,影响到冷库设备的正常使用,小编为你解板下列几种常用的故障及解决方法。。电源跳闸电路自身电路断路或短路,空开自身冷库安装工程的工艺要求冷库施工要素冷库作为特种设备,业内有三分质量,七分安装之说,由此可见,冷库的施工艺和安装方法相当重要,一不小心就会影响到整体工程和用户体验,造成的后果不可估量。冷库建造前的准备工作中央空调不制热是什么原因?中央空调的功能就是制冷和制热,专门调节空气冷热的设备,一般情况下是夏天制冷,冬天制热,当然不是每台空调都能制热的,只有带制热功能的空调才能制热,按照行业习惯,制热功率要比制冷功率大冷库内部结霜过多怎么办?如何处理?冷库积霜原因冷库在长期使用中,积霜会堆积过厚时,就可能影响到制冷的效果,造成效益低下的情况发生,这种现象是怎样造成的呢,如何处理,下面我们就来讨论讨论。积霜太厚时,冷量以及冷气就会
原创新茶酒论为中国共产党百年华诞献礼作者王彦涛网络文学文学交流专注原创王彦涛原创作品授权天美五星全网首发文章阅读也可在微信读书讯飞有声上检索此文新茶酒论为中国共产党百年华诞献礼作者王彦涛编审天美五星新茶酒论为中国共产党百年华诞天津车展实拍阿尔法S华为HI版,配鸿蒙OS系统百公里加速3。5秒!国庆期间,对于爱车人士来讲并不寂寞,天津车展中亮相许多新车,我自己进行了光顾,并且拍摄到了极狐埃尔法S华为HI版。跟普通版本车型对比,外观上没有太大差异,不过由于华为的加入,在科技哈弗F7试驾2。0T两驱i范,配360全景影像辅助系统丰富!年轻人选车,除了价格需要优惠之外,颜值跟动力都不能太弱。在10万级别SUV市场,哈弗F7从外观上来讲很符合年轻消费群体,目前在售车型指导售价在11。1815。18万之间,除了1。5冷门宝藏车,法系DS7实力解析,多款车型如何选?有一些品牌,在国内完全不会营销。就比如豪华定位的法系DS,4S店本身就不多,在城市周边很少能够看到实车。作为一款紧凑型SUV,DS7算是一款高辨识度车型,对于不喜欢随主流的用户来讲实拍哈弗H6S,轿跑风格定位,2。0T版本百公里加速6。8秒!在国内紧凑型SUV市场,哈弗H6可以说独占鳌头,出色的月销量让很多品牌商眼红,并且相继推出同级别车型征战市场,近些年来涌现出了很多实力对手。对于长城来讲,同样也在求变,基于H6打造全新广汽传祺GS810月12日下线,配丰田第四代THS混合动力系统!由于搭配丰田全新第四代THS混合动力系统,新一代广汽传祺GS8没有正式上市前就获得了很多关注,作为一款中型SUV,在2016年正式进入市场,这几年表现只能说中规中矩,没有什么特殊之邓增永福建白茶光饼,将来会成为自身发展道路上的大坑吗?最近有多位朋友和我聊起,福建白茶最近两三年来压制了不少不带内飞的光饼,并对这种愈演愈烈的现象表示担忧。笔者搜索福建白茶饼,画风是这样的(点击图片,可放大查看)福建白茶饼,搜索到的茶明明安装了游戏运行库,仍提示缺少C库文件,现在修复大法来了当你安装完一款你喜欢的游戏,却在启动时提示系统缺少d3dx939。dlld3dx940。dlld3dx941。dlld3dx942。dlld3dx943。dllxinput13。d一步步教你如何开启小飞机,展示GPD掌机玩3A大作的实力在GPDWIN3上可以60帧畅玩的3A大作太多太多!但如何算畅玩?每个人有不同的标准!去除一些邪教宣称的,比如5W玩大镖客2之类的反智言论。大多数人会认为要保证畅玩,起码得60帧,喜迎WIN3掌机,评测赢全额返现活动开始啦!还送300元优惠券相信很多人拿到了WIN3掌机,现在,WIN3活动开始啦!你只需通过手机或单反,录制WIN3游戏演示评测视频,并发布到官方指定视频平台。就会赢得全额返现的机会!活动专题页链接http关于WIN3背键自定义工具WinControls的设置教程目前发货已经进入尾声,相信大部分玩家都已经收到了WIN3!那么从WIN3开始,GPD加强了软件层面的功能。比如,GPD助手(GPDAssistant)整合了游戏平台和经常用到的辅助