ode*_*nde 15 c language-lawyer
我编写了两个进程,通过在一段共享内存中相互发送消息进行通信.尽管不会以原子方式访问消息,但通过使用存储释放和加载获取访问的共享原子对象保护消息来实现同步.
我的问题是安全问题.这些过程彼此不信任.收到消息后,进程不会假设消息格式正确; 它首先将消息从共享内存复制到私有内存,然后对此私有副本执行一些验证,如果有效,则继续处理此相同的私有副本.制作此私有副本至关重要,因为它可以防止TOC/TOU攻击,其他进程将在验证和使用之间修改消息.
我的问题如下:标准是否保证聪明的C编译器永远不会决定它可以读取原始而不是副本?想象一下以下场景,其中消息是一个简单的整数:
int private = *pshared; // pshared points to the message in shared memory
...
if (is_valid(private)) {
...
handle(private);
}
Run Code Online (Sandbox Code Playgroud)
如果编译器用完寄存器的暂时需求溢出private
,它可以决定,而不是将其溢出到堆栈,它可以简单地丢弃它的价值和重新加载*pshared
后,只要一个别名分析,确保这个线程并没有改变*pshared
?
我的猜测是这样的编译器优化不会保留源程序的语义,因此是非法的:pshared
不指向只能从该线程可以访问的对象(例如在堆栈上分配的对象,其地址具有因此,编译器不能排除某些其他线程可能同时修改*pshared
.相比之下,编译器可以消除冗余负载,因为其中一种可能的行为是冗余负载之间没有其他线程运行,因此当前线程必须准备好处理这种特定行为.
任何人都可以确认或证实这种猜测并可能提供标准相关部分的参考吗?
(顺便说一下:我假设消息类型没有陷阱表示,因此总是定义负载.)
UPDATE
几张海报评论了同步的必要性,我不打算进入,因为我相信我已经涵盖了这一点.但是,由于人们指出了这一点,我提供更多细节是公平的.
我正在两个不相互信任的实体之间实现一个低级异步通信系统.我使用进程运行测试,但最终会在虚拟机管理程序之上移动到虚拟机.我有两个基本要素:共享内存和通知机制(通常,将IRQ注入其他虚拟机).
我已经实现了一个通用的循环缓冲结构,通信实体可以使用该结构生成消息,然后发送上述通知,让对方知道什么时候需要消费.每个实体都维护自己的私有状态,跟踪它产生/消耗的内容,共享内存中有一个共享状态,由消息槽和原子整数组成,跟踪持有待处理消息的区域的边界.该协议明确地识别哪个实体在任何时间独占访问哪些消息时隙.当需要生成消息时,实体将消息(非原子地)写入适当的槽,然后执行原子存储释放到适当的原子整数以将槽的所有权转移到另一个实体,然后等待直到内存写入已完成,然后发送通知以唤醒其他实体.在接收到通知时,期望另一个实体对适当的原子整数执行原子加载 - 获取,确定有多少未决消息,然后使用它们.
*pshared
我的代码片段中的负载只是消耗一个简单(int
)消息的示例.在现实的设置中,消息将是一个结构.使用消息不需要任何特定的原子性或同步,因为,如协议所指定的,它仅在消费实体与另一个同步并且知道它拥有消息时隙时才发生.只要两个人都遵循协议,一切都会完美无缺.
现在,我不希望这些参与者必须相互信任.它们的实现必须能够抵御恶意实体,该恶意实体会忽略协议并随时在整个共享内存段上进行写入.如果发生这种情况,恶意实体唯一能够实现的就是破坏通信.想象一下典型的服务器,它必须准备好处理恶意客户端的不良请求,而不会让这种不当行为导致缓冲区溢出或越界访问.
因此,虽然协议依赖于正常操作的同步,但实体必须为共享内存的内容做好准备,以便随时更改.我只需要一种方法来确保在实体制作消息的私有副本之后,它验证并使用相同的副本,并且永远不再访问原始副本.
我有一个使用易失性读取来复制消息的实现,从而使编译器清楚地知道共享内存没有普通的内存语义.我相信这已经足够了; 我想知道是否有必要.
很难回答您发布的问题。请注意,您必须使用同步对象来防止并发访问,除非您仅读取平台上的原子单元。
我假设您打算询问(伪代码):
lock_shared_area();
int private = *pshared;
unlock_shared_area();
if (is_valid(private))
Run Code Online (Sandbox Code Playgroud)
并且其他进程也使用相同的锁。(如果没有,最好更新您的问题,使您的同步更加具体)。
*pshared
这段代码保证最多读取一次。使用名称private
意味着读取变量private
,而不是对象*pshared
。编译器“知道”解锁该区域的调用充当内存栅栏,并且不会对超出栅栏的操作进行重新排序。