[완료]malloc(1)로 1바이트 할당 했는데 여러 메모리가 참조되는 이유가..
글쓴이: coathanger / 작성시간: 토, 2007/03/24 - 8:19오후
아래와 같이 malloc()로 1Byte 메모리만 생성하였으나.. 제가 의도적으로 1바이트 이상인 30바이트까지
데이터를 넣어 보았더니 값이 기억되고 출력 또한 됩니다.
제가 알기로는.. pSlSqc = (Byte*)malloc(1)하게 되면 1Byte만 생성 되는 것으로 알고 있습니다.
즉 30바이트 공간을 받으려면 malloc(30)이라고 해야 하지 않나요?
typedef unsigned char Byte; int main(void) { int i; Byte* pSlSqc = NULL; pSlSqc = (Byte*)malloc(1); if(pSlSqc == NULL) printf("memory error!!\n"); for(i = 0 ; i < 30 ; i++) pSlSqc[i] = i; for(i = 0 ; i < 30 ; i++) printf("%d, ", pSlSqc[i]); return 0; }
Forums:
받지 않아도 쓸 수는 있습니다.
int a[3] = {1, 2, 3};
a[5] = 6;
위 코드에서 무슨 일이 일어나는지 아신다면 저것도 쉽게 이해하실 수 있으리라고 믿습니다만...
윗분이 말씀하신대로
윗분이 말씀하신대로 할당하지 않아도 임의의 메모리 주소를 그냥 쓰거나 읽을수 있습니다.
단 그렇게 하면 해당 메모리를 쓰는 다른 부분의 메모리가 오염되고 프로그램이 죽을수 있습니다.
메모리를 할당 한다는 의미는 해당 메모리를 내가 사용할테니 다른데서 사용하지 않도록 마크를 해두라는 의미로 생각하시면 됩니다.
-------------------------------------------------------------------------------
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.
아...그렇다면..
저렇게 할당하지 않은 곳에 데이터를 넣어도 그 당시 읽고 쓸 수는 있지만 다른 프로그램에서
그 메모리(윗분 발씀처럼 마크하지 않은 영역)에 다른 정보가 들어가게 되면 예기치 않은
에러가 난다는 말씀이시군요.
잘 알겠습니다~ 상세한 답변 감사합니다.....
프로그램이 작동하는
프로그램이 작동하는 OS가 궁금하네요.
임베디드 같은 아주 작은 시스템의 OS이거나 DOS이거나 NT 계열 윈도우즈가 아닌 윈도우즈
95, 98, ME 는 서로 다른 프로그램 간의 메모리를 접근 할 수 있습니다만
윈도우즈 2000, XP, Vista같은 NT계열 OS나 유닉스계열 OS라면 서로 다른 프로그램 간의 메모리는 절대 침범 할 수 없습니다.
이건 물리적인 메모리 주소를 직접사용하지 않고, pageing 라는 기법을 사용한 논리적인 메모리 주소를 사용하기 때문인데요.
pageing 기법을 사용하게 된 이유는 하드디스크의 조각화와 같은 현상인 메모리 조각화 때문인데요.
이 조각화를 해결하기 위한 수단으로 등장하게 됐으며, 그게 부수적으로 얻을 수 있는 기능이 바로
프로그램간의 메모리를 제한 할 수가 있어서 그 메모리를 침법하게 되면, 그 즉시 OS가 그 사실을
알아차리고 뭔가 액션을 취할 수 있습니다. 그리고 그 액션이 대부분 해당 프로그램에게
segment fault 라는 signal 을 날리는게 일반적이죠.
좀 더 정확히 말하자면, 다른 프로그램의 메모리를 접근했을 때가 아니고 내 프로그램이 할당 받은
page 가 아닌곳을 접근 했을 때입니다.
malloc으로 요청한 영역 이상을 접근했는데도 "왜 에러가 발생하지 않는가" 하는 이유를 생각해 봐야 하는데요.
seg. fault는 위에서 말씀 드렸듯이 자기가 할당 받은 page가 아닌 곳을 접근했을 때 인데
보통 page는 4Kbyte나 8Kbyte 단위로 이루어져 있습니다.
즉, 내가 1바이트를 할당 받더라도 실제는 4K나 8K 정도가 할당 받게 되는거요.
보통 x86 계열은 4Kbyte단위로 할당 받는 걸로 알고 있습니다.
그래서 1바이트 할당 받고 30바이트 더 접근하지 말고 4K 정도를 접근하면 seg. fault를 경험 할 수 있을 겁니다.
메모리를 overflow 했는데 seg. fault가 발생 안했으니 우연이지만 운이 좋구나! 라고 생각하면 큰일입니다.
이로 인해 다른 코드가 사용해야 하는 메모리가 오염되버리면 디버깅시에 정말 힘들거든요.
보통 개발자들은 1byte라도 overflow되면, 즉시 seg. fault를 받고 싶어지게 되는데요.
그래서 등장하는 많은 디버깅 라이브러리가 있는데 그 중에 하나가 efence(electric fence)입니다.
efence를 사용하면 malloc을 1바이트 한 후 2바이트 부분을 접근하면, 그 즉시 seg. fault가 발생합니다.
efence 같은 류의 프로그램을 처음 들으셨다면, 지금 즉시 사용하실 것을 강력히 권해 드립니다.
글에 틀린점이 많아 보여서 답글남깁니다.
글에 틀린점이 많네요.
DOS 는 DPMI 라고 protected mode 로 전환하는 인터페이스만 정의할뿐, 어떠한 가상메모리도 지원하지 않습니다.
윈도우 95,98,ME 도 가상메모리를 지원합니다. 따라서 개념적으로 다른 프로세스의 메모리에 접근할 수 없습니다. 단, 공유메모리를 통해 물리 메모리를 공유할 수는 있습니다.
질문글에 대한 설명을 위해 페이징을 언급하셨는데, 페이징은 가상메모리를 구현하기 위한 수단으로서 질문글에 대한 답하고 전혀 관련이 없습니다. 예로 가상메모리를 지원하지 않는 시스템 , 예를 들면 MS-DOS 에서도 위의 코드는 똑같이 동작합니다. 이것은 언어 적인 특성입니다.
malloc(1) 은 대부분 효율성을 위해 시스템 워드(메모리연산의 가장 효율적인크기) 크기만큼 할당하지만 그것은 구현적인 문제이고 함수의 정의는 1바이트를 할당하고 반환합니다. malloc (1) 로 할당된 메모리를 가리키는 pSlSqc 로 1바이트 이상 접근 할수 있는 이유는 언어적인 문제입니다. C 의 포인터 연산은 아무런 바운더리 체킹을 하지 않습니다. 즉 할당받지 않은 어떠한 메모리로도 접근가능합니다. 이것은 언어의 특성입니다. 이것은 때에 따라서 유용한 특성일 수도 있고, 어떤 프로그램에서는 매우 위험한 특성일 수도 있습니다.따라서 개발자 역량껏 잘 이해하고 사용해야 합니다.
malloc
먼저 ssehonny님의 말씀 중 잘못된 내용이 있습니다. Windows 95/98/ME도 당연히 다른 프로세스의 메모리는 보지 못합니다. 가상메모리 구조를 쓰는데 어떻게 다른 프로세스의 메모리를 봅니까 ^^;
malloc의 원리를 대충 설명드리면 이러합니다. 윈도우던 리눅스던 동일합니다. 일단 큰 메모리 덩어리를 할당 받습니다. 보통 단위가 큰 녀석들이죠. 윈도우 같으면 VirtualAlloc, 리눅스라면 sbrk로 메모리를 일단 크게 하나 받습니다.
그리고 이제 malloc은 여기서 작은 메모리 공간을 잘라서 사용자에게 줍니다. 그리고 이 메모리 할당 내역을 잘 기록을 합니다. free를 하면 이 할당된 기록을 지우는 역할을 하죠. malloc(1)을 했을 경우 어떻게 돌아가냐면 일단 1바이트는 워낙 거시기해서 -_- 8바이트 정도로 얼라인먼트 하는 경우가 대부분입니다. 그래서 8바이트 정도가 할당이 되구요. 그리고 말씀드렸듯이 malloc을 한 기록을 저장해야하므로 이 정보를 기록하기 위한 별도의 4 또는 8바이트가 필요합니다 (이건 운영체제마다 다릅니다). 그리고 또 malloc(8)을 불렀다고 합시다. 그러면 대충...
| malloc(1) | meta-data of malloc(1) | malloc(8) | meta-data of malloc(8) | ....
meta-data가 바로 내가 몇바이트나 할당했느냐, 이 메모리 공간이 사용중이냐를 기억하는 곳입니다.
그래서... 보시다시피 malloc(1)로 할당받은 공간부터 열심히 데이터를 쭉 쓰면 저 뒤에 있는 meta-data 뿐만 아니라 다른 메모리 영역까지 다 더럽히게 됩니다 ^^; 바로 이것이 버퍼 오버플로우이고 이걸 악용하면 외부의 악성 코드를 실행까지 할 수 있습니다. 윈도우나 리눅스의 각종 보안 패치는 대부분 이러한 문제때문에 발생하는 것이죠.
그렇다면 malloc한 만큼만 딱 쓰고 그 외에는 접근하면 익셉션을 나게 하면 왜 안되냐고 반문하실 수 있는데요. 그건 너무 오버헤드가 큽니다. 적어도 현재의 운영체제와 CPU 구조로는 어쩔 수가 없습니다. 지금은 접근 권한의 단위가 페이지(4KB) 단위라서 너무 크죠. 그래서 malloc 같이 매우 성근 데이터의 보호는 불가능한 것이 현실입니다.
물론 소프트웨어로 무식하게 할 수 있습니다만 오버헤드가 장난이 아닙니다. 디버깅에만 겨우 가능하고 실제 프로그램 배치 후에는 사용이 불가능합니다.
그래서... CPU에 새로운 기능을 추가해서 4바이트 정도마다 각각 다른 보안 설정을 할 수 있는 방법이 연구되고 있습니다. 이미 방법은 나왔는데 아직 CPU가 구현을 못했을 뿐이죠. 그러면 malloc 한 데이터 이상을 접근하면 바로 익셉션이 뜰 겁니다~ 그러면 많은 보안패치도 필요없어지고 좀 더 안전한 컴퓨팅 환경이 만들어지겠죠.
efence는 정확히 잘 모르겠지만 일반적으로 malloc을 하면 할당한 바이트 바로 뒤에 일종의 canary value라는 것을 둡니다. 그래서 나중에 free가 될 때 이 값이 바뀌어있다면 적절히 assert 오류 등을 내는 방법이 있습니다.
내부적으로
내부적으로 복잡하게 돌아가는군요..
이번 계기로 좋은 정보를 알게 되었습니다.
답변 주신 모든 분들 진심으로 감사드립니다. ;-O
M$의 95, 98, ME 는
M$의 95, 98, ME 는 가상메모리 주소를 사용하지 않습니다. 그렇기 때문에 타 프로세스의 접근이 가능합니다.
그래서 위의 OS가 유행할 당시에는 game hack 과 같은 타 프로그램의 메모리 값을 직접 수정해서 게임 데이터를 수정하는
게임의 메모리를 직접 조작해서 데이터 값을 수정하는 프로그램이 유행 했었습니다.
M$의 경우는 윈도우즈 NT, 2000, XP 등 NT계열 OS에서 부터 가상 메모리를 사용합니다. 그래서 2000 이전엔 자주 보이던 블루스크린이 2000 이후에는 거의 볼 수 없게 된것이지요.
malloc 의 내부 구현은 OS마다 다른것이 아니고, 사용하는 라이브러리에 따라 다릅니다.
(커널은 brk, sbrk 만을 제공하고, malloc은 제공하지 않습니다.)
그래서 dmalloc 같은 디버깅용 메모리 할당 라이브러리가 별도로 존재하는 게 가능한것이지요.
메모리 얼라이먼트의 cpu가 접근하는 데이터 단위가 (32비트 cpu는) 4바이트이기 때문에 4바이트 혹은 8바이트가 일반적입니다.
cpu는 항상 4의 배수 메모리만 접근이 가능하기 때문에,
4*m + n (1 <= n <= 3)
인 주소, 예를 들면 3,4,5,6 에 기록된 32비트 정수를 읽으려 들면 sparc cpu의 경우는 sig bus 에러가 발생하고, 인텔 같은 경우는 1,2,3,4 를 한번 읽고 5,6,7,8 을 읽어서 3,4,5,6 을 조합해 내도록 구현이 되어 있습니다.(인텔의 경우 메모리 정렬이 맞지않게 데이터를 접근해도 프로그램은 잘 작동하지만, 성능 저하가 발생합니다.)
canary value를 둬서 오퍼플로우를 찾게 되면 free 시점에서 알기 때문에 어디서 문제가 발생했는지 알 수 없습니다.
문제가 어디에서 발생하는 것인지 그것을 알고자 efence 를 사용하는 것인데요.
efence 는 mprotect()(control allowable accesses to a region of memory)를 이용해서 구현되어 있고,
efence의 man 페이지에 구현 원리가 잘 설명되어 있습니다.
아닙니다 -_-
윈도우 95 계열도 당연히 가상 메모리 사용합니다 -_-; 다른 프로세스의 메모리는 *원칙당연히 보지 못합니다 -_-; CPU 자체가 가상메모리 주소를 항상 피지컬 주소로 바꾸고 있는데 어떻게 운영체제가 바꾸지 않습니까.
Under Windows 95, each running 32-bit process is mapped to the 4MB-2GB range of linear address space. Although 32-bit applications share the same linear address space, Windows loads each application to a different physical memory location (if enough memory is available). Whenever a task switch occurs, Windows modifies its page tables to reflect the new linear to physical mapping scheme and swaps least recently used pages of memory to disk.
가상 메모리는 -_- 멀티태스킹을 하려면 반드시 구현을 할 수 밖에 없는 이야기입니다. 단!!!! 윈도우 95/98에서는 공유하는 메모리 영역에 누구나 쓸 수 있다는 문제가 있었습니다. 그러나 그것이 가상 메모리를 쓰지 않는다는 소리는 절대 아닙니다 -_-; 윈도 95에서 프로그램 시작하면 대부분 0x00040000 정도의 메모리에서 동일하게 시작하죠. 윈도우 위에서 프로세스가 보는 모든 주소는 가상 주소입니다. 그러니까 보호되지 않는 영역이 있다고해도 (왜냐면 도스 프로그램은 리얼모드에서 짜여진게 많으니깐요. 그리고 초창기 윈도우는 도스 위에 올라가는 일종의 어플리케이션이었으니깐요) VA를 쓰지 않는 것은 아닙니다. 윈도우 3.1은 모르겠습니다. 그러나 32비트 윈도우 95/98은 반드시 가상 메모리를 사용하며 *일반적인* 경우에는 타 프로세스 메모리를 보지 못합니다. 아 물론 완벽한 보안은 아니었습니다만.
http://www.everything2.com/index.pl?node_id=668981&lastnode_id=0 여기를 참고
카나리 밸류는 가장 심플한 체크 방법이고 오버헤드가 거의 없다는 뜻에서 말씀드린 겁니다. mprotect의 오버헤드는 장난이 아니죠. 물론 디버그 타임에는 감당할만하지만..
네 그렇군요. 95
네 그렇군요. 95 계열이 가상메모리를 사용하지 않는다는 것은 제가 잘 못 알고 있었던 것 같네요.
95 계열도 swap 파일을 지원하는 걸 알면서도 미쳐 생각을 하지 못했네요.
..
8바이트 정도로 얼라인먼트 하는 경우가 대부분입니다.
어사인먼트(assignment) 같은데...
ㅎㅎㅎㅎ
Alignment맞습니다
디버거들은 다른
디버거들은 다른 프로세스의 메모리 영역을 접근합니다.
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
디버거 뿐만
디버거 뿐만 아니라...
MS 9x,nt 계열에선 다른 프로세스 메모리를 원하는대로 에디트하는 조그만 툴도 있습니다.
hex editor 가 필요해서 가져다 설치해놓고 보니 파일 뿐만 아니라 다른 프로세스가 사용하는 메모리까지 에디트가 가능하더군요 -.-;
(혹시나 싶어 뒤져봤는데... 리눅스에선 도저히 못 찾겠습니다.
마지막으로, malloc()
마지막으로, malloc() 등 memory allocation 의 구현 중 page size 보다 작은 크기에 대응하기 위한 부분은...
"메모리를 절약" 하자는게 주목적이 아닙니다.
1 byte 건 10 bytes 건 무조건 page size align 으로 "할당"할 때 발생하는 가장 큰 문제는 성능저하입니다.
댓글 달기