假设有两个进程:一个是我自己的进程,另一个进程生成随机内存地址并修改其中的内容(我们无法控制它)。
第一个过程(我自己的过程):
// Process 1
int *ptr = (int*)malloc(sizeof(int)); // Suppose the address is 0x138145800
*ptr = 45;
printf("%d", *ptr);
sleep();
printf("%d", *ptr);
第二个过程(我无法控制的外部过程):
// Process 2
int *ptr1;
ptr1 = (int*)0x138145800; // Random memory address is generated
*ptr1 = 25;
printf("%d", *ptr1);
当第一个进程处于休眠状态时,我们没有任何控制权的第二个进程会修改进程1使用的值。
如何通过仅更改第一个进程来防止第二个进程这样做?
一种可能的方法是使用锁。但是,为每个变量进程使用锁1使用效率非常低。有没有有效的方法来解决这个问题?
答案 0 :(得分:2)
操作系统阻止程序通过设计访问另一个程序堆栈中的内存。没有程序需要担心这一点。
为了便于说明,我在CentOS映像上运行了这个场景:
计划1:
int main() {
char *p = "Hello World";
}
当运行时产生:
Dump of assembler code for function main:
0x00000000004004f0 <+0>: push %rbp
0x00000000004004f1 <+1>: mov %rsp,%rbp
=> 0x00000000004004f4 <+4>: movq $0x4005a0,-0x8(%rbp)
0x00000000004004fc <+12>: mov $0x0,%eax
0x0000000000400501 <+17>: pop %rbp
0x0000000000400502 <+18>: retq
End of assembler dump.
我们可以看到注册0x4005a0
持有"Hello World"
:
(gdb) x /s 0x4005a0
0x4005a0: "Hello World"
在不杀死前一个进程的情况下,我们可以运行另一个访问该寄存器的C程序:
int main() {
char * hello_world = (char *) 0x4005a0;
printf("%s", hello_world);
}
运行时,产生:
L??L??D??A??H??H9?u?H?[]A\A]A^A_?ff.?
在一个单独的GDB会话中(与两个程序分开):
(gdb) x /s 0x4005a0
0x4005a0: <Address 0x4005a0 out of bounds>
因此,我们可以在这里观察现代记忆体系结构的一些原理。操作系统正在管理程序的内存,以便您所描述的方案不会发生。
在程序的第二次运行中,它实际上能够打印出一些文本(乱码),因为当程序运行时,实际上是分配了一个虚拟地址空间,根据定义,它是一组范围操作系统可用于进程的虚拟地址。虽然这两个程序使用相同的虚拟地址(0x4005a0
),但在第二个实例中它只是空闲内存。
如果一个程序走出这个边界,操作系统将发送经典SIGSEGV
并终止该程序。操作系统会根据内存访问冲突触发定义的分段错误。
从程序化的角度来看,没有什么可以做,也不需要做,因为这个内存管理由操作系统处理。你的程序在这方面是安全的,假设它在现代操作系统上运行。