[완료]read()로 파일을 읽을 때 정의된 버퍼 크기 이상 읽혀지는 문제

neobug의 이미지

아래는 파일의 내용을 읽어서 출력하는 간단한 코드 입니다.
read() 호출시 사용될 buf 배열의 크기는 512로 정의되어 있구요.
헌데 668 까지는 buf배열에 문제 없이 접근이되고
669 되서야 Segmentaion Fault 오류가 나네요.
왜그러는지 궁굼하네요.

example은 워드프로세서의 파일 문서입니다.

int main()
{
        int fd, nread;
        unsigned short int count;
 
        <span>char* buf[512];</span>
 
        if((fd = open("example", O_RDONLY)) == -1) {
                perror("Cannot open the file");
                exit(-1);
        }
 
	/* 
        if((nread = read(fd, buf, <span>512</span>)) < 512) {
                perror("Not enough header data");
                exit(-1);
        }
 
        for(count=0; count < nread; count++) {
                printf("%08X ", buf[count]);
        }
 
 
	/* 668 까지는 Segmentation Fault가 발생 안합니다 */
        if((nread = read(fd, buf, <span>669</span>)) < 512) {
                perror("Not enough header data");
                exit(-1);
        }
 
        for(count=0; count < nread; count++) {
                printf("%08X ", buf[count]);
        }
 
        printf("\n %d \n", nread);
}
chadr의 이미지

특별한 이유는 없습니다. 컴파일러가 코드를 컴파일 할 때 어떤 목적에 의해서 메모리를 더 할당할 수도 있고
특히 디버깅 모드일 경우에 디버깅 목적으로 할당하라고 하는 이상의 메모리가 할당 될수도 있습니다.

이럴 경우에 메모리를 넘어섰을 경우 문제가 안보이지만 이미 다른 메모리 영역을 침범하고 있는 것입니다.
이때 스택이라는 것은 스레드 공용이므로 함수가 들어가고 나가는 도중 자신이 할당한 스택 영역을 벗어나서
자신을 부른 함수의 데이터를 변경 한다거나 하는 경우가 생기면 잘못된 데이터로 인해 오동작을 하게 되어
세그먼트 폴트로 이어지게 됩니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

neobug의 이미지

정의된 크기 이상 접근이 가능은 하지만 이미 다른 stack 영역에 들어가 있는 것이군요.
이 부분은 좀더 깊게 공부해봐야 할 것 같습니다.
참고로 밑에분의 char buf[BUFSIZE]로 정의 해도 BUFSIZE 이상 접근이 가능하더군요.

제가 char buf[BUFSIZE]로 정의 하지 않고 char* buf[BUFSIZE]로 한 이유는
아래와 같습니다.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main()
{
     int fd1, fd2, nread;
     unsigned short int count;
 
     char* buf1[512];
     unsigned char buf2[512];
 
     <span>/* 파일 디스크립터 fd1 과 fd2 생성 */</span>
     if((fd1 = open("example", O_RDONLY)) == -1) {
          perror("Cannot open the file");
          exit(-1);
     }
     if((fd2 = open("example", O_RDONLY)) == -1) {
          perror("Cannot open the file");
          exit(-1);
     }
 
<span>     /* char* 선언의 buf1과 unsigned char 선언의 buf2로
         데이터 읽어드림. 왜 unsigned인지는 뒤에 설명 */</span>
     if((nread = read(fd1, buf1, 512)) < 512) {
          perror("Not enough header data");
          exit(-1);
     }
     if((nread = read(fd2, buf2, 512)) < 512) {
          perror("Not enough header data");
          exit(-1);
     }
<span>     /* 출력부분: %08X 와 %02X에대해서 뒤에 설명 */</span>
     for(count=0; count < nread; count++) {
          printf("%08X ", buf1[count]);
     }
        puts("\n");
     for(count=0; count < nread; count++) {
          printf("%08X ", buf2[count]);
     }
}

실행 시키면 buf1은 아래와 같이
D0CF11E0 A1B11AE1 00000000 00000000 00000000 00000000 3E000300 FEFF0900 06000000 00000000 00000000 01000000 0C000000 02000000 00001000 0F000000 01000000 FEFFFFFF 00000000 0D000000 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF (512byte 만큼 뒤에 주욱 쓰레기 값이 더 나옵니다)

buf2는 아래와 같이 나옵니다
D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 00 03 00 FE FF 09 00 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 0C 00 00 00 02 00 00 00 00 00 10 00 0F 00 00 00 01 00 00 00 FE FF FF FF 00 00 00 00 0D 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF (끝)

여기서 unsigned char로 선언하지 않고 char로 선언해서 코드를 실행 시키면 아래와 같이 나옵니다.
FFFFFFD0 FFFFFFCF 11 FFFFFFE0 FFFFFFA1 FFFFFFB1 1A FFFFFFE1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 00 03 00 FFFFFFFE FFFFFFFF 09 00 06 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 0C 00 00 00 02 00 00 00 00 00 10 00 0F 00 00 00 01 00 00 00 FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 00 00 00 00 0D 00 00 00 FFFFFFFF
(이 뒤로는 512 byte만큼 전부 FFFFFFFF)

출력을 %02X 가 아니라 %08X로 하면 바로 위 결과의 00 부분이 00000000로 출력 되구요.

여기서 한가지 질문 더 던집니다
buf1과 buf2의 차이는 왜 그런가? 입니다

char* buf1[512] 선언은 데이터를 D0CF11E0 A1B11AE1 이렇게 읽어 들이고

unsigned char buf2[512] 아래와 같이 데이터를 읽어드렸습니다.
000000D0 000000CF 00000011 000000E0 000000A1 000000B1 0000001A 000000E1

char buf[512]로 선언 하면 아래와 같은 결과가 나오지요
FFFFFFD0 FFFF FFCF 00000011 FFFFFFE0 FFFFFFA1 FFFFFFB1 0000001A FFFFFFE1

panda005의 이미지

* 32비트 OS 기준으로 말씀드리겠습니다.

char *buf1[512] 라고 선언하면,
실제로 buf1에는 sizeof (char *) * 512 만큼의 메모리가 할당됩니다.
따라서 2k의 메모리가 할당되는 것이지요.

nread = read(fd1, buf1, 668) 해도 메모리 크기상으로는 아무 문제 없습니다.
단지 669로 하면 memory alignment가 안 맞아서 bus error가 날 것 같네요.

그리고, buf1과 buf2가 출력 양상을 다르게 보이는 것은,
buf1은 unsigned int 타입으로 동작한다고 보시면 됩니다.
따라서,
buf1[0] = D0CF11E0 가 됩니다.
하지만 buf2는 unsigned char 타입으로 동작하기 때문에
buf2[0] = D0
buf2[1] = CF
buf2[2] = 11
buf2[3] = E0
이렇게 저장되지요.

이걸 %x로 하여 printf를 하게 되면,
buf1은 그냥 그 값대로 나올 것입니다.
하지만 buf2는 각각의 char를 integer로 변환해서 출력하게 되지요.

그래서 signed로 선언되어 있으면, char의 최상위 비트가 1로 세팅되어 있는 경우 상위 24비트(3바이트)를 모두 '1'로 채우게 되서
FFFFFFD0 FFFFFFCF 00000011 FFFFFFE0 ...
로 출력되게 되고,
unsigned로 선언되어 있으면 상위 24비트(3바이트)를 모두 '0'으로 채우게 되서
000000D0 000000CF 00000011 000000E0 ...
이렇게 출력하게 되는 것입니다.

익명 사용자의 이미지

char* buf[512];

라고 선언하면 char 포인터 배열을 정의한 겁니다.
원래 용도가 저 용도 인것 같지는 않은데 말입니다.
panda005의 이미지

char buf[512];
가 본래 의도이실 것 같네요

headbang의 이미지

제가 알고 있기로는 페이징 단위와 관련되어 있습니다.

위에 어떤 분이 컴파일러가 코드를 컴파일 할 때 어떤 목적에 의해서 메모리를 더 할당할 수도 있기 때문이라고 했는데, 그렇게 구현된 시스템은 들어보지 못했습니다. 제 지식이 얇아서 일수도 있으니 혹시 출처가 있으면 알려주십시오.

프로세스가 메모리를 접근할 때 적합한 접근인지를 바이트 단위로 검사한다면 상식적으로 오버헤드가 상당할 겁니다. 대부분의 운영체제는 단순히 페이지테이블에 Read Write 접근권한 설정을 하고 실행시에는 MMU 하드웨어가 페이지테이블 참조해서 페이지폴트 exception을 낼지 결정합니다. 즉, 사용자 프로세스는 한 페이지의 첫 바이트만 할당받고도 그 페이지 전체를 아무런 제약없이 접근할 수 있습니다.

따라서 이런식의 장점으로는 오버헤드가 거의 없다. 단점으로는 접근권한 위반을 세밀하게 잡아내지 못한다는 거겠죠.

위에 668이상에서 세그멘트가 생긴다는 이야기는 아래 그림에서 buf1의 끝과 local variables의 영역등을 지나 할당되지 않은 곳을 접근했기 때문이겠죠.

highM start of stack lowM
-------------------------------------------------------------
not allocated || local_variables end_of_buf1 start_of_buf1
-------------------------------------------------------------

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.