当前位置: 首页 > news >正文

Rust: 关于Pin以及move前后分析

一、Pin由来
在Rust中,自引用结构会导致,此变量被move后,其内部自引用的指针指向不改变,从而存在安全隐患 。

注意:Pin是一个struct。UnPin和!UnPin是trait。这个要分清。

二、方案

对原始的自引用结构,如何增加安全性,方案有以下两步:
1、里面加Phantompinned,
2、外面套上Pin,
这样新的结构被move后,可以保证里面的自引用的指针指向的内容正确。

基于以上分析,我们来进行相关验证:

1、验证普通的结构体move前后行为:move的行为会产生什么影响?
2、带自引用结构move前后的行为:是否存在安全隐患?
3、带PhantomPinned自引用结构move前后的行为:是否解决了原先产生的问题?

在行为方面,具体的考察在于指针地址,指针指向内容的变化,是否能揭示相关问题。

三、相关代码

因为不涉及外部库,cargo.toml文件不需要另列。main.rs代码如下:

use std::pin::Pin;
use std::marker::PhantomPinned;// unpin结构,没有自引用
#[derive(Debug,Default)]
struct Free{content: String,
}// unpin结构 =>move后,ptr对应的指针还会指向原来的地址,但不能正确指向content内容
#[derive(Debug)]
struct SelfRef {content: String,_ptr: *const String,//对应content 或者是&String
}
impl SelfRef{fn default() ->Self{SelfRef{content: String::from("hello world!"),_ptr: std::ptr::null(),}}fn set_ref_value(&mut self){self._ptr = & self.content as *const String; }
}
// !unpin结构,通过引入phantompinned,再pin后,SelfRefPinned被move后,_ptr对应指针可以正确指向content
#[derive(Debug)]
struct SelfRefPinned {content: String,_ptr: *const String,//对应content_marker: PhantomPinned,
}impl SelfRefPinned{fn default() ->Self{SelfRefPinned{content: String::from("hello world!"),_ptr: std::ptr::null(),_marker: PhantomPinned,}}fn set_ref_value(pinned_obj: Pin<&mut Self>){let content_ptr = &pinned_obj.content as *const String;let mut_self_ref: &mut SelfRefPinned = unsafe { pinned_obj.get_unchecked_mut() };mut_self_ref._ptr = content_ptr;}
}
fn main() {let _free = Free::default();let mut _self_ref = SelfRef::default();_self_ref.set_ref_value();let mut self_ref_pin = SelfRefPinned::default();let mut _self_ref_pinned = unsafe { Pin::new_unchecked(&mut self_ref_pin) };SelfRefPinned::set_ref_value(_self_ref_pinned.as_mut());// Free before moveprintln!("-----------before move-------------");println!("_free content:{:?}  content 内存指针: {:p}",_free.content,&_free.content);// Free after moveprintln!("-----------after move-------------");let free_ = _free; //第1次move操作println!("free_ content:{:?}  content 内存指针: {:p}",free_.content,&free_.content);// SelfRef before move println!("-----------before move-------------");println!("_self_ref 内存地址:{:p} content内存地址:{:p}",&_self_ref,&_self_ref.content);println!("_self_ref._ptr对应value :{:?}  自引用指针的内存地址: {:?}",unsafe{&*_self_ref._ptr},_self_ref._ptr);// SelfRef after move  println!("-----------after move-------------");let self_ref_ = _self_ref; //第1次move操作println!("self_ref_ 内存地址:{:p} content内存地址:{:p}",&self_ref_,&self_ref_.content);println!("self_ref_._ptr对应value :{:?}  自引用指针的内存地址: {:?}",unsafe{&*self_ref_._ptr},self_ref_._ptr);// SelfRef  move againprintln!("-----------after move again -------------");let _self_ref_ = self_ref_; //第2次 move操作println!("_self_ref_ 内存地址:{:p} content内存地址:{:p}",&_self_ref_,&_self_ref_.content);println!("_self_ref_._ptr对应value :{:?}  自引用指针的内存地址: {:?}",unsafe{&*_self_ref_._ptr},_self_ref_._ptr);// SelfRefPinned before moveprintln!("-----------before move-------------");println!("_self_ref_pinned 内存地址:{:p} content内存地址:{:p}",&_self_ref_pinned,&_self_ref_pinned.content);println!("_self_ref_pinned._ptr对应value :{:?}  自引用指针的内存地址: {:?}",unsafe{&*_self_ref_pinned._ptr},_self_ref_pinned._ptr);// SelfRefPinned after moveprintln!("-----------after move-------------");let self_ref_pinned_ = _self_ref_pinned; //第1次move 操作println!("self_ref_pinned_ 内存地址:{:p} content内存地址:{:p}",&self_ref_pinned_,&self_ref_pinned_.content);println!("self_ref_pinned_._ptr对应value :{:?}  自引用指针的内存地址: {:?}",unsafe{&*self_ref_pinned_._ptr},self_ref_pinned_._ptr);}

四、输出

-----------before move-------------
_free content:""  content 内存指针: 0x7ffd840875f0
-----------after move-------------
free_ content:""  content 内存指针: 0x7ffd840875d0
-----------before move-------------
_self_ref 内存地址:0x7ffd84087570 content内存地址:0x7ffd84087570
_self_ref._ptr对应value :"hello world!"  自引用指针的内存地址: 0x7ffd84087570
-----------after move-------------
self_ref_ 内存地址:0x7ffd84087590 content内存地址:0x7ffd84087590
self_ref_._ptr对应value :"hello world!"  自引用指针的内存地址: 0x7ffd84087570
-----------after move again -------------
_self_ref_ 内存地址:0x7ffd84087540 content内存地址:0x7ffd84087540
_self_ref_._ptr对应value :"hello world!"  自引用指针的内存地址: 0x7ffd84087570
-----------before move-------------
_self_ref_pinned 内存地址:0x7ffd84087530 content内存地址:0x7ffd840875b0
_self_ref_pinned._ptr对应value :"hello world!"  自引用指针的内存地址: 0x7ffd840875b0
-----------after move-------------
self_ref_pinned_ 内存地址:0x7ffd84087568 content内存地址:0x7ffd840875b0
self_ref_pinned_._ptr对应value :"hello world!"  自引用指针的内存地址: 0x7ffd840875b0

五、总结

1、几点观察

(1)从上面Free类型来看,move后,struct对象的内存地址有变动。
(2)自引用结构move后,可以看见:尽管对象的struct内存地址发生变化后,但对象中的content的地址也发生了新的变化,但对象中的自引用对应指针并没有变化(还是指向move前的对象的content地址),并没指向新对象content所对应的内存地址。这样,就存在了不一致的问题。
(3)PhantomPinned和Pin之后,在move后,尽管新struct对象的地址有变动,但新的对象的content的地址也没有变化,新的对象自引用指针也没有变动。因此,对象的自引对应的指针正确指向的content所对应的地址,之前的隐患消除。

2、相关问题

不知大家注意到没有,上面的代码,SelfRef对象被安排了2次move,但是经过2次move的对象,其自引用指针的内存还是指次第1次move前的对象的地址位置。如果多次move后,这些变量被drop后,原始的地址没清空或初始化,那么解引用就会出现"指引悬垂"的问题。

但是,在这些变量的地址没有被清空或初始化前,而且这个地址看似”还能“正确解引用出”正确“的值。一方面,造成了假象;另一方面,也把问题隐藏了起来,在很多情况下,看起来让程序在大部分的情况下运行结果表现正确。但其实已经造成潜在的安全隐患。

正因为此,异步的Future才需要通过Pin来堵住这个安全的漏洞。这个才是Pin的价值所在。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python基础-循环语句
  • 安全防御,防火墙配置NAT转换智能选举综合实验
  • 在家上网IP地址是固定的吗?
  • Is Temperature the Creativity Parameter of Large Language Models?阅读笔记
  • GitHub+Picgo图片上传
  • Web学习day04
  • 未来互联网的新篇章:深度解析Facebook的技术与战略
  • rust way step 1
  • 宏碁F5-572G-59K3笔记本笔记本电脑拆机清灰教程(详解)
  • 中职网络安全B模块渗透测试server2003
  • 解释如单例、工厂、观察者等常见设计模式在Android开发中的应用。
  • kali安装vulhub遇到的问题及解决方法(docker及docker镜像源更换)
  • 两段序列帧动画播放,在ios机型上出现闪屏
  • python的字符串
  • C++从入门到起飞之——缺省参数/函数重载/引用全方位剖析!
  • C++入门教程(10):for 语句
  • JavaScript设计模式之工厂模式
  • MySQL数据库运维之数据恢复
  • Next.js之基础概念(二)
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 技术发展面试
  • 简单易用的leetcode开发测试工具(npm)
  • 如何编写一个可升级的智能合约
  • 手写双向链表LinkedList的几个常用功能
  • 我这样减少了26.5M Java内存!
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • # windows 安装 mysql 显示 no packages found 解决方法
  • (delphi11最新学习资料) Object Pascal 学习笔记---第14章泛型第2节(泛型类的类构造函数)
  • (void) (_x == _y)的作用
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (一)80c52学习之旅-起始篇
  • (转)linux下的时间函数使用
  • (最新)华为 2024 届秋招-硬件技术工程师-单板硬件开发—机试题—(共12套)(每套四十题)
  • .NET C# 操作Neo4j图数据库
  • .net core 外观者设计模式 实现,多种支付选择
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .Net的DataSet直接与SQL2005交互
  • .project文件
  • /dev/sda2 is mounted; will not make a filesystem here!
  • :O)修改linux硬件时间
  • [20181219]script使用小技巧.txt
  • [2024最新教程]地表最强AGI:Claude 3注册账号/登录账号/访问方法,小白教程包教包会
  • [BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)
  • [BZOJ] 1001: [BeiJing2006]狼抓兔子
  • [Django学习]查询过滤器(lookup types)
  • [E单调栈] lc2487. 从链表中移除节点(单调栈+递归+反转链表+多思路)
  • [godot] 采用状态机时,如何处理攻击时移动?如“冲撞”
  • [GXYCTF2019]禁止套娃
  • [Hive]五、Hive 源码编译
  • [hive]中的字段的数据类型有哪些