Chinaunix

标题: 内存中是否有不可读的区域 [打印本页]

作者: wzabcd1234    时间: 2009-07-27 11:23
标题: 内存中是否有不可读的区域
例子程序如下:
#include <stdio.h>

int main()
{
        char *p = "abcd";
        int i = 0;
        for (i = 0; i < 10000; i++) {
                printf("p[%d] = %d\n", i, p);
        }
}

在AIX5.2的环境下,当i=2700左右的时候,程序会coredump.请问AIX的内存区域中是否有不可读的区域啊?谢谢
作者: baopbird2005    时间: 2009-07-27 11:25
应该可以读取的。修改就不一定了
作者: wzabcd1234    时间: 2009-07-27 11:27
标题: 回复 #2 baopbird2005 的帖子
当i=2700左右的时候,printf语句coredump了,这应该是不可读吧?
作者: kouu    时间: 2009-07-27 13:06
标题: 回复 #3 wzabcd1234 的帖子
LZ,你觉得随着 i 的自增,printf读的地址有变化吗?
作者: wzabcd1234    时间: 2009-07-27 13:12
标题: 回复 #4 kouu 的帖子
你好,地址是有变化的,源代码修改了一下,打印出来的结果如下,内存地址是在递增的:
p[2745] = 0, addr = 110000ff9
p[2746] = 0, addr = 110000ffa
p[2747] = 0, addr = 110000ffb
p[2748] = 0, addr = 110000ffc
p[2749] = 0, addr = 110000ffd
p[2750] = 0, addr = 110000ffe
p[2751] = 0, addr = 110000fff
作者: yecheng_110    时间: 2009-07-27 13:16
看源码咋能递增?
作者: wzabcd1234    时间: 2009-07-27 13:27
标题: 回复 #6 yecheng_110 的帖子
#include <stdio.h>

int main()
{
        int i = 0;
        char *p = "abcd";
        for (i = 0; i < 10000; i++) {
                printf("p[%d] = %d, addr = %p\n", i, p, p + i);
        }
}
作者: progliker    时间: 2009-07-27 13:52
标题: 回复 #7 wzabcd1234 的帖子
产生段错误了阿。比如说给你分配的栈是从b7ef3000-b7efc000,当地址超过b7efc000就会出现段错误的。
你可以看看/proc/$pid/maps, 了解一下各个段的分配情况。
作者: wzabcd1234    时间: 2009-07-27 15:03
标题: 回复 #8 progliker 的帖子
你好,我去/proc/$pid目录下找了,maps是乱码,没法看。

有一些问题还是不太理解,段错误一般是由于写越界引起的吧,读越界为什么也会有段错误?
作者: peimichael    时间: 2009-07-27 15:33
读一样可以引起段错误。
有兴趣可以看看内核代码do_page_fault函数是怎么处理读越界的
作者: aaaaa5aa    时间: 2009-07-27 22:08
不可能每次都出现错误吧,重新运行也会出现?
作者: action08    时间: 2009-07-27 22:24
看不懂,

[ 本帖最后由 action08 于 2009-7-27 22:27 编辑 ]
作者: shuchi91    时间: 2009-07-28 11:07
标题: 回复 #10 peimichael 的帖子
LZ的程序在给我乐趣,你的P什么时候加了?
会core DUMP?
作者: wzabcd1234    时间: 2009-07-28 11:10
标题: 回复 #13 shuchi91 的帖子
你好,你运行一下程序就知道啦。
P一直没有递增的,是i在递增,我打印的p[ i ]的值。i大概在2700左右的时候,程序会coredump
作者: wzabcd1234    时间: 2009-07-28 11:13
标题: 回复 #11 aaaaa5aa 的帖子
我的环境下每次运行都会core
作者: wzabcd1234    时间: 2009-07-28 11:31
标题: 回复 #15 wzabcd1234 的帖子
出现这种情况是否说明不要乱使用memcpy,如果要拷贝字符串,应该使用snprintf或者strncpy?
作者: shuchi91    时间: 2009-07-28 13:21
标题: 回复 #16 wzabcd1234 的帖子
这是越界访问,很容易造成core dump。
我始终认为这样的程序就是错的,因为你偷了别人的菜,虽然很多时候并没有core dump(没被抓到)。

出现这种情况是否说明不要乱使用memcpy,如果要拷贝字符串,应该使用snprintf或者strncpy?
==========
什么函数都不能乱用。
作者: 写自己的操作系    时间: 2009-07-28 13:26
提示: 作者被禁止或删除 内容自动屏蔽
作者: wzabcd1234    时间: 2009-07-28 13:54
标题: 回复 #17 shuchi91 的帖子
我这里说的乱用可能是如下的情况:

char a[1000];
void func(char *p) {
    memcpy(a, p, sizeof(a));
}

我看有不少人是这样使用的,现在看来这样使用应该是错误的。
作者: wzabcd1234    时间: 2009-07-28 13:57
标题: 回复 #17 shuchi91 的帖子
我知道越界写操作很容易会coredump,因为修改了栈的内容,一直不大明白为什么读操作也会coredump,能用一些理论来解释一下吗?谢谢!
作者: 混沌时代    时间: 2009-07-28 14:07
好像是printf()函数那边出了点错误吧,把p改为p+i,我试了一下,是可以正常运行的。
#include <stdio.h>

int main()
{
        char *p = "abcd";
        int i = 0;
        for (i = 0; i < 10000; i++) {
                printf("p[%d] = %d\n", i, p+i);
        }
}
作者: ddgfff    时间: 2009-07-28 14:25
p\[i\] = 整数??

[ 本帖最后由 ddgfff 于 2009-7-28 14:26 编辑 ]
作者: wzabcd1234    时间: 2009-07-28 14:27
标题: 回复 #21 混沌时代 的帖子
printf("p[%d] = %d\n", i, p+i);
这里只是打印了p+i的地址吧,这是可以的,但是*(p+i)就会core了。
作者: wzabcd1234    时间: 2009-07-28 14:29
标题: 回复 #22 ddgfff 的帖子
我也不知道p[ i ]是什么类型了,因为已经越界了。。
我用%c, %d, %x都试过了,还是会core的。
作者: wzabcd1234    时间: 2009-07-28 14:32
感觉不太像是printf的问题,我的原始程序是这样:
#include <memory.h>

int main(){
   char a[10000];
   char *p = "132434";

   memcpy(a, p, sizeof(a));
   printf("%s\n",a);
}

这里memcpy会coredump。
作者: foolishx    时间: 2009-07-28 14:56
使用coreadm,打开coredump开关,当进程coredump之后,会在指定目录下生成coredump文件,使用gdb或者mdb可以查看coredump文件中的栈顶函数即可断定是coredump在哪个函数里了。
作者: wzabcd1234    时间: 2009-07-28 15:27
标题: 回复 #26 foolishx 的帖子
恩,是core在memcpy上了。
Segmentation fault in . at 0xf454
0x000000000000f454 warning: Unable to access address 0xf454 from core
(dbx) where
.() at 0xf454
main(), line 7 in "a.c"
作者: peimichael    时间: 2009-07-28 15:35
AIX没接触过,但是linux是不能读越界的,否则会段错误
从你的信息来看AIX明显也是不允许读越界,这是很正常的保护措施啊,有什么好奇怪的呢?
作者: wzabcd1234    时间: 2009-07-28 15:38
标题: 回复 #28 peimichael 的帖子
你好,我是想不大明白,为什么读越界也会core。读越界有什么危害嘛?
作者: peimichael    时间: 2009-07-28 15:44
比如内核里面有一些重要隐私数据(比如其他用户的密码),如果可以读越界,
因为内核空间是公用的,那么就可以获取内核空间的这些数据,当然有危害。
内核甚至在给用户空间分配页的时候都会将它赋成0,防止以前残留的数据被用户获取,
当然更要防止用户直接读到正在使用的数据了
作者: maxxfire    时间: 2009-07-28 15:48
似乎进入内核地址空间了。。?
作者: peimichael    时间: 2009-07-28 15:50
仅仅读取用户空间的话感觉也没什么大危害,
但是内核为了让你读用户空间不产生段错误,它需要建立所有的页表,
这不仅是时间上的浪费,也是空间上的浪费,因为这些页表也要占用空间的
并且,这还不利于程序员debug,因为更加不容易发现读越界。
完全是费力不讨好的事情,为什么要这样做呢?

以上针对x86下的Linux来说
其实大部分体系结构下的类unix系统都是这样
作者: wzabcd1234    时间: 2009-07-28 15:50
标题: 回复 #30 peimichael 的帖子
恩,明白了,谢谢了。
作者: jhjx704    时间: 2009-07-30 11:01

作者: thy295    时间: 2009-07-30 12:43
不是整个用户空间都可以访问的,可以访问的空间内核都用vm_area_struct管理起来了,而且除了用户栈空间核心是自动扩展外其他的都是固定的或是需要通过系统调用修改的,如brk. 当访问的地址相应的页表没有建立起来便触发异常,在do_page_fault处理中会再判断这个触发异常的地址是否落在某个vm_area_struct管理的范围中,然后再作进一步判断。楼主这应该属于访问范围越界所致
作者: kouu    时间: 2009-07-30 13:07
稀奇了…… 难道大家觉得 楼主贴在 1楼 或 7楼 的程序有可能出现读越界的问题?
作者: thy295    时间: 2009-07-30 14:48
系统在进行一些基本的初始化之后便进入了保护模式,所有送出的地址都是虚地址,都需要经过MMU来根据页表的映射规则来转化成物理地址,所以不管是读还是写操作都绕不过这个硬件机制(DMA除外).那么什么时候MMU给CPU报报告一个异常呢?两种情况,一种是对应虚拟地址的页表项已经建立,但是内容不在内存中(可能被交换到硬盘上),这种情况时合法的,OS再处理此种情况时会申请内存并将相应数据从硬盘是读回,另一种是页表项根本就不存在,那么又可以细分成多种情况,我们只拿那虚拟地址属于用户空间来讲,如果该地址落在某个用户空间段中(内核中用vm_area_struct管理),且访问权限一致,则OS会建立该页表,并分配内存,用户的堆就是这种情况的典型(事实上通过brk扩展堆空间只是将堆的vm_area_struct中范围修改了下,既不建立页表也不分配内存,哪什么时候分配?真正用的时候由触发异常后OS来自动补上)。另一个情况就是该地址不属于任何一个段,那么OS视其为一个非法地址访问,便产生segment fault~~~
    楼主的 char *p="abcd", "abcd"在程序经过编译连接后会放在.rodata,程序被加载运行时会被放在一个只读段中(是否与.text放在一个区段这个我不确定),而这个只读段是有范围的,具体可以看/proc下的maps,当你的地址超出了该段范围,不就因为缺页异常而被OS抓住了吗?
作者: shuchi91    时间: 2009-07-30 14:56
原帖由 kouu 于 2009-7-30 13:07 发表
稀奇了…… 难道大家觉得 楼主贴在 1楼 或 7楼 的程序有可能出现读越界的问题?


我一直就很纳闷
作者: windaoo    时间: 2009-07-30 15:06
原帖由 thy295 于 2009-7-30 14:48 发表
系统在进行一些基本的初始化之后便进入了保护模式,所有送出的地址都是虚地址,都需要经过MMU来根据页表的映射规则来转化成物理地址,所以不管是读还是写操作都绕不过这个硬件机制(DMA除外).那么什么时候MMU给C ...


那个字符串的确是放在 .text 段中的。
作者: chenxiaopang    时间: 2009-07-30 19:55
原帖由 thy295 于 2009-7-30 14:48 发表
系统在进行一些基本的初始化之后便进入了保护模式,所有送出的地址都是虚地址,都需要经过MMU来根据页表的映射规则来转化成物理地址,所以不管是读还是写操作都绕不过这个硬件机制(DMA除外).那么什么时候MMU给C ...

正解。简单地说,出错的地址(虚地址)肯定是未分配内存的地址,也就是还没有在页表中建立到物理内存地址映射的,内核就不知道到哪儿去读了,当然会出错的。
作者: albeta    时间: 2009-07-30 22:33
系统还没分配内存段出来,当然会core啦
作者: lucasman    时间: 2009-07-31 10:27
标题: 回复 #1 & #7 wzabcd1234 的帖子
the reason is  you did NOT allocate the memory location you are referring.

in fact, the system does NOT kill you program immediately  ,
because the Pointer P points to the data section of you program. what your are printing is the program's data section ie,you have access right to this memory location.

when you exceed the section(maybe need to exceed some other section following the data section  ) , the system kill you
作者: fxpbupt    时间: 2009-07-31 22:19
标题: 回复 #25 wzabcd1234 的帖子
这个怎么为什么产生coredump阿?
越爬楼越迷茫,乱七八糟!
作者: 独孤求败unix    时间: 2009-07-31 22:41
楼上各位,楼主的代码应该没错,可能是编译环境的问题,我在cygwin下可以完好运行楼主的程序!没有出现楼主所说的现象!!
作者: bsdc    时间: 2009-07-31 22:42
原帖由 fxpbupt 于 2009-7-31 22:19 发表
这个怎么为什么产生coredump阿?
越爬楼越迷茫,乱七八糟!

怎么回事,楼主贴的代码看起来没什么访问越界啊什么的,始终在打印p,是不是要说明这样的问题啊:


  1. #include <stdio.h>

  2. int main()
  3. {
  4.     int i = 0;
  5.     char *p = "abcd";
  6.    
  7.     printf("p[50000] = %d, addr = %p\n", p[50000], p + 50000);

  8. }
复制代码

怎么一楼一楼的都在说着另外一件事情,莫非夜深了,我眼神不好了?

不过37楼的兄弟说得很好,学习了。
作者: hsienmu    时间: 2009-08-01 01:22
内存不可读的原因太多了。不过看楼主的代码,出错只有两种情况:
1、编译器坏了。
2、机器坏了。

作者: justkain    时间: 2009-08-01 08:51
这个问题就是:
别人告诉你,你到A地的B区去找X,结果你过去A地时,根本不存在B区这个地方。
至于A地的B区,那是以后有人提出这个名,然后管地名的机构让它投入使用,然后A地的B区才存在。
作者: wzabcd1234    时间: 2009-08-03 08:37
这两天有点事情,没顾上看贴,不好意思。

应该不是机器后者编译器的原因,我在不同的机器上(操作系统分别是AIX5.2和AIX6.1),用不同的编译器都试过了,还是会coredump的,因为手上没有其他UNIX的环境,不知道是否只有AIX上会出现这个问题。原因应该是37楼的兄弟说的。
作者: tianxueer    时间: 2009-08-03 16:12
原帖由 wzabcd1234 于 2009-7-27 13:27 发表
#include

int main()
{
        int i = 0;
        char *p = "abcd";
        for (i = 0; i < 10000; i++) {
                printf("p[%d] = %d, addr = %p\n", i, p, p + i);
        }
}


莫非LZ在开国际玩笑??

你那个红色的p根本就没变过,程序应该一直打印第一个字符a才对,哪里会出错?即使出错了也不会和内存这个有关系啊?
太搞笑了!

更可笑的是这么多人居然。。。。。

[ 本帖最后由 tianxueer 于 2009-8-3 16:16 编辑 ]
作者: attiseve    时间: 2009-08-03 17:23
就是打印一万次p的地址吧?
在Linux上没有问题。
作者: leohart    时间: 2009-08-03 22:05
标题: 回复 #1 wzabcd1234 的帖子
#include <stdio.h>

int main()
{
        char *p = "abcd";
        int i = 0;
        for (i = 0; i < 10000; i++) {
                printf("p[%d] = %d\n", i, (p+i));
        }
}

debian下没问题
但是要加上个*就段错误了

我感觉不加*就是把指针递增了但是没读取内容,所以不会报错
作者: ezhimeng    时间: 2009-08-04 08:42
标题: Linux 下没问题~
..............
p[9995] = 134513908, addr = 0x804abff
p[9996] = 134513908, addr = 0x804ac00
p[9997] = 134513908, addr = 0x804ac01
p[9998] = 134513908, addr = 0x804ac02
p[9999] = 134513908, addr = 0x804ac03
bash-3.2$



gcc (GCC) 4.3.2 20081105 (Red Hat 4.3.2-7)
作者: wzabcd1234    时间: 2009-08-05 13:18
标题: 回复 #49 tianxueer 的帖子
我的测试程序是要打印*(p + i)的值,不知道程序贴上来之后怎么变成打印p的值的。没有注意看,不好意思。

但是问题的关键并不是这个,而是37楼兄弟讲的,读越界也会出问题的。
作者: snyh    时间: 2009-08-07 18:29
你用gdb 调式 进去之后p getpid()
看pid 然后 pmap 得到的pid
看内存映射的情况。。。。如果就你这么大的程序。。。肯定在4k以内(不是文件大小)
那就只分配2个页面 ( 因为页面有权限  一个程序至少有2类 权限。   .text只能是r--x--  而.data要 rw--所以至少要2个页面)

一般是0x0804 8000第一个页面  然后第二个页面就是0x804 900开始了。。。
所以这之后的地址根本就不在程序的进程空间里面  你都加到10000多之后了 访问了没有任何权限的页面 (应该说根本就不存在的) 自然出问题

[ 本帖最后由 snyh 于 2009-8-7 18:31 编辑 ]




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2