prosource

malloc()로 할당한 메모리보다 더 많은 메모리를 사용할 수 있는데, 그 이유는 무엇입니까?

probook 2023. 6. 17. 09:28
반응형

malloc()로 할당한 메모리보다 더 많은 메모리를 사용할 수 있는데, 그 이유는 무엇입니까?

char *cp = (char *) malloc(1);
strcpy(cp, "123456789");
puts(cp);

C"123456789"이며, 는 빈 메모리가 때 gcc 가능한 메모리)로더할 수 을 의미합니다.malloc()?

왜 ㅠㅠㅠㅠmalloc(0)런타임 오류가 발생하지 않습니까?

감사해요.

당신은 매우 좋은 질문을 했고 이것이 당신의 운영체제에 대한 욕구를 자극할 것입니다.여러분은 이미 이 코드로 일반적으로 예상하지 못했던 것을 달성했다는 것을 알고 있습니다.따라서 휴대용으로 만들고자 하는 코드에서는 이런 작업을 절대 하지 않을 것입니다.

좀 더 구체적으로 말하면, 이것은 전적으로 운영 체제와 CPU 아키텍처에 따라 다릅니다. 운영 체제는 메모리의 "페이지"를 프로그램에 할당합니다. 일반적으로 이는 4킬로바이트의 순서일 수 있습니다.운영 체제는 페이지의 보호자이며 할당되지 않은 페이지에 액세스를 시도하는 모든 프로그램을 즉시 종료합니다.

malloc반면, 는 운영 체제 기능이 아니라 C 라이브러리 호출입니다.그것은 여러 가지 방법으로 구현될 수 있습니다.이 당전는화로 .malloc운영 체제에서 페이지 요청이 발생했습니다.그리고나서malloc페이지 안에 있는 단일 바이트에 대한 포인터를 제공하기로 결정했을 것입니다.주어진 위치에서 메모리에 기록할 때는 운영 체제가 프로그램을 승인했다는 내용을 "페이지"에 기록하는 중이었고, 따라서 운영 체제는 잘못된 작업을 인식하지 못할 것입니다.

물론 진짜 문제는 당신이 전화를 계속할 때 시작될 것입니다.malloc더 많은 메모리를 할당합니다.이것은 결국 방금 쓴 위치에 대한 포인터를 반환합니다.운영 체제의 관점에서 합법적이지만 프로그램의 다른 부분도 사용할 메모리를 덮어쓸 수 있는 메모리 위치에 쓸 때 이를 "버퍼 오버플로"라고 합니다.

이 주제에 대해 계속 배우면 프로그램의 다른 부분에서 실행될 메모리 영역에 어셈블리 언어 명령어를 직접 쓰기 시작할 때까지 이러한 "버퍼 오버플로" 기술을 사용하여 프로그램을 어떻게 활용할 수 있는지 이해하기 시작할 것입니다.

이 단계에 이르면 많은 지혜를 얻게 될 것입니다.하지만 제발 윤리적으로 행동하고 우주를 파괴하는 데 사용하지 마세요!

PS 위에서 "운영 체제"라고 하면 "권한 있는 CPU 액세스와 함께 운영 체제"를 의미합니다.CPU 및 MMU(메모리 관리 장치)는 프로세스가 해당 프로세스에 할당되지 않은 페이지를 사용하려고 할 경우 운영 체제에 특정 인터럽트 또는 콜백을 트리거합니다.그러면 운영 체제가 응용 프로그램을 완전히 종료하고 시스템이 계속 작동할 수 있도록 합니다.예전에는 메모리 관리 장치와 권한 있는 CPU 명령 이전에는 거의 언제든지 메모리의 아무 곳에나 쓸 수 있었습니다. 그러면 시스템이 메모리 쓰기의 결과에 전적으로 좌우될 것입니다.

아니요. 정의되지 않은 행동을 하게 됩니다.즉, 충돌(야간)부터 "작동"(부)까지, 하드 드라이브를 다시 포맷하고 "UB, UB, UB..."(와트)라는 텍스트 파일로 채우는 등 모든 일이 발생할 수 있습니다.

컴파일러, 플랫폼, 환경, 시간, 좋아하는 탄산음료 등에 따라 달라지기 때문에 그 이후에 무슨 일이 일어날지 궁금해할 필요가 없습니다.

더 구체적으로, 할당하지 않은 메모리를 사용하는 것은 정의되지 않은 동작입니다.에서 1바이트를 얻을 수 있습니다.malloc(1),바로 그겁니다.

라고 물었을 때malloc1바이트의 경우 운영 체제에서 1페이지(일반적으로 4KB)를 얻을 수 있습니다.이 페이지는 호출 프로세스에 할당되어 페이지 경계를 벗어나지 않는 한 문제가 없습니다.

그러나 이것은 확실히 정의되지 않은 동작입니다!

사용 시 발생할 수 있는 상황에 대한 다음(가설적인) 예를 고려합니다.malloc:

  1. malloc(1)
  2. 한다면malloc내부적으로 메모리가 부족합니다. 운영 체제에 더 많은 정보를 요청할 것입니다.일반적으로 페이지를 수신합니다.주소가 0x1000으로 시작하는 4KB 크기라고 가정합니다.
  3. 사용할 주소가 0x1000으로 반환됩니다.1바이트를 요청했으므로 0x1000 주소만 사용하면 정의된 동작입니다.
  4. 운영 체제가 주소 0x1000에서 시작하는 프로세스에 4KB의 메모리를 할당했으므로 주소 0x1000-0x1fff에서/로 무언가를 읽거나 써도 문제가 발생하지 않습니다.그래서 당신은 기꺼이 그렇게 할 수 있지만 그것은 정의되지 않은 행동입니다.
  5. 당신이 다른 일을 한다고 가정해 보겠습니다.malloc(1)
  6. 지금이다malloc아직 메모리가 남아 있기 때문에 운영 체제에 더 많은 메모리를 요청할 필요가 없습니다.주소 0x1001을 반환할 것입니다.
  7. 에 입력한 주소를 한 경우에는 1바이트 이상의 .malloc를 사용할 때 가 생깁니다.malloc데이터를 덮어쓰게 되기 때문입니다.

그래서 요점은 당신이 malloc에서 1바이트를 얻는다는 것입니다. 하지만 그것은 아마도.malloc내부적으로 프로세스에 할당된 메모리가 더 많습니다.

아니요. 프로그램이 제대로 작동하지 않는다는 뜻입니다.소유하지 않은 메모리 위치에 씁니다.

정의되지 않은 행동을 하게 됩니다. 어떤 일이든 일어날 수 있습니다.그것을 하지 말고 그것이 효과가 있는지에 대해 추측하지 마세요.기억력이 손상되어 바로 보이지 않을 수도 있습니다.할당된 블록 크기 내의 메모리에만 액세스합니다.

메모리가 프로그램 메모리 또는 보호된 메모리에 액세스하기 위해 응용 프로그램이 충돌할 가능성이 가장 높은 다른 지점에 도달할 때까지 사용할 수 있습니다.

이렇게 많은 응답이 있고 올바른 설명을 해주는 유일한 응답입니다.페이지 크기, 버퍼 오버플로 및 정의되지 않은 동작 스토리는 사실이지만 원래 질문에 정확하게 대답하지는 않습니다.사실 제정신인 사람은malloc 구은적 도정요렬크할의 합니다.int 는또.void *1바이트만 할당하면 다음 메모리 청크가 더 이상 정렬되지 않기 때문입니다.할당된 블록 주위에는 항상 일부 데이터가 보관되어 있으며, 이러한 데이터 구조는 거의 항상 4의 배수로 정렬되어 있습니다.일부 아키텍처는 정렬되지 않은 주소(x86)의 단어에 액세스할 수 있지만, 이로 인해 일부 페널티가 발생하므로 할당자 구현자는 이를 피합니다.슬래브 할당기에서도 작은 크기의 할당은 실제로 드물기 때문에 1바이트 풀을 갖는 것은 의미가 없습니다.따라서 malloc'd 바이트에 4~8바이트의 실제 공간이 있을 가능성이 매우 높습니다(이것은 당신이 그 '기능'을 사용할 수 있다는 것을 의미하는 것은 아닙니다, 틀렸습니다).

편집: 게다가, 대부분malloc 시 보다 큰 합니다.realloc으로 테트로사볼수있다니습해를 해 볼 수.realloc할당 크기가 증가하는 루프에서 반환된 포인터를 비교하면 특정 임계값 이후에만 변경되는 것을 볼 수 있습니다.

거기서 운이 좋았어요.소유하지 않은 위치에 쓰는 경우 정의되지 않은 동작이 발생합니다.

대부분의 플랫폼에서는 1바이트만 할당할 수 없습니다.종종 할당된 메모리의 양을 기억하기 위해 malloc에 의해 수행되는 약간의 하우스키핑도 있습니다.이는 일반적으로 메모리를 다음 4바이트 또는 8바이트로 반올림하여 "할당"한다는 사실을 의미합니다.하지만 이것은 정의된 행동이 아닙니다.

몇 바이트를 더 사용하면 액세스 위반이 발생할 가능성이 높습니다.

두 번째 질문에 답하기 위해, 표준은 구체적으로 다음을 요구합니다.malloc(0)따라 , 반환은 구현에 따라 다를 수 .NULL또는 일반 메모리 주소입니다.두 경우 모두 합법적으로 전화할 수 있습니다.free완료되면 반환값에 따라 변경됩니다.그렇지 않은 경우에도NULL해당 주소의 데이터에 액세스하면 안 됩니다.

malloc은 힙에서 요청하는 메모리 양을 할당한 다음 원하는 모든 것에 캐스트할 수 있는 void(void*)로 포인터를 반환합니다.

할당된 메모리만 사용하는 것은 프로그래머의 책임입니다.사용자가 예상하지 못한 곳에서 쓰기(및 보호된 환경에서 읽기)하면 실행모든 종류의 무작위 문제가 발생할 수 있습니다.운이 좋으면 예외가 있는 프로그램이 즉시 중단되고 버그를 쉽게 찾아 수정할 수 있습니다.운이 좋지 않으면 무작위로 충돌하거나 예상치 못한 행동을 일으킬 수 있습니다.

머피의 법칙대해서는 "잘못될 수 있는 모든 것은 잘못될 것"이며, 그 결과 "적절한 시기에 잘못되어 가장 큰 피해를 초래할 것"입니다.그것은 슬프게도 사실입니다.그것을 예방하는 유일한 방법은 언어로 실제로 그런 일을 할 수 있는 것을 피하는 것입니다.

현대 언어들은 프로그래머가 그/그녀가 생각하지 않는 (적어도 표준 프로그래밍을 하는) 메모리에서 쓰는 것을 허용하지 않습니다.이것이 자바가 많은 매력을 얻게 된 방법입니다.저는 C보다 C++을 더 좋아합니다.포인터를 사용하여 손상을 입힐 수는 있지만 그럴 가능성은 적습니다.그것이 스마트 포인터가 인기 있는 이유입니다.

이러한 문제를 해결하기 위해 malloc 라이브러리의 디버그 버전이 유용할 수 있습니다.메모리가 손상되었는지 여부를 감지하려면 정기적으로 점검 기능을 호출해야 합니다.제가 직장에서 C/C++에 집중적으로 작업했을 때, 우리는 실제로 표준 malloc(C++의 새로운 것)와 무료(C++의 삭제)를 대체하는 Rational Purify를 사용했고, 프로그램이 의도하지 않은 것을 어디서 했는지에 대한 꽤 정확한 보고서를 반환할 수 있습니다.그러나 코드에 오류가 없다고 100% 확신할 수는 없습니다.극히 드물게 발생하는 상태인 경우 프로그램을 실행할 때 해당 상태에서 발생하지 않을 수 있습니다.이는 결국 가장 민감한 데이터에서 가장 바쁜 날에 생산에서 발생할 것입니다(Murphy's Law에 따라 ;-).

디버그 모드일 수도 있습니다. 여기서 malloc에 대한 호출은 실제로 _malloc_dbg를 호출합니다.디버그 버전은 버퍼 오버플로를 처리하기 위해 요청한 것보다 더 많은 공간을 할당합니다.릴리스 모드에서 실행하면 (바라건대) 충돌이 발생할 수 있습니다.

c++에서 새 연산자와 삭제 연산자를 사용해야 합니다.그리고 작업이 할당된 어레이의 한계에 도달하지 않도록 제어할 수 있는 안전한 포인터...

C 런타임은 없습니다.C는 미화된 조립자입니다.그것은 당신이 즐겁게 주소 공간을 돌아다니며 당신이 원하는 것을 할 수 있게 해줄 것이고, 그것이 OS 커널을 작성하기 위한 선택 언어인 이유입니다.프로그램은 일반적인 보안 취약성인 힙 손상 버그의 예입니다.만약 당신이 그 주소에 충분히 긴 문자열을 썼다면, 당신은 결국 힙의 끝을 초과하게 되고 분할 오류가 발생하게 될 것입니다. 하지만 다른 중요한 것들을 먼저 덮어쓰기 전까지는요.

malloc()의 예약 풀에 할당을 충족하기에 충분한 여유 메모리가 없으면 커널에서 페이지를 4kb 이상의 청크로 가져옵니다. 따라서 처음에 할당 범위를 초과할 때 예약되었지만 malloc()가 아닌 공간에 쓰기 때문에 테스트 케이스가 항상 작동합니다.실제로 할당 주소와 크기를 지정하는 것은 완전히 자발적이기 때문에 malloc()를 전혀 호출하지 않고 포인터에 임의 주소를 할당하고 문자열로 작업을 시작할 수 있습니다. 그리고 그 임의 주소가 힙이나 스택과 같은 쓰기 가능한 메모리 세그먼트에 있는 한, 모든 것이 작동하는 것처럼 보일 것입니다.적어도 그렇게 함으로써 손상된 메모리를 사용하려고 할 때까지는.

strcpy()는 쓰고 있는 메모리가 할당되었는지 확인하지 않습니다.대상 주소를 가져와서 '\0'에 도달할 때까지 문자별로 소스 문자를 씁니다.따라서 할당된 대상 메모리가 소스보다 작으면 메모리를 덮어쓰게 됩니다.이것은 추적하기가 매우 어렵기 때문에 위험한 벌레입니다.

puts()는 '\0'에 도달할 때까지 문자열을 씁니다.

내 생각에 malloc(0)는 NULL만 반환하고 런타임 오류는 발생하지 않습니다.

대답은 왜 printf는 고장을 내지 않거나 쓰레기를 만들지 않습니까?

부터

Denis Ritchie & Kernighan의 C 프로그래밍 언어

 typedef long Align;    /* for alignment to long boundary */
   union header {         /* block header */
       struct {
           union header *ptr; /* next block if on free list */
           unsigned size;     /* size of this block */
       } s;
       Align x;           /* force alignment of blocks */
   };
   typedef union header Header;

Align필드는 사용되지 않습니다. 각 헤더가 최악의 경우 경계에 정렬되도록 강제합니다.malloc, 요청된 문자 크기는 적절한 헤더 크기 단위로 반올림됩니다. 할당될 블록은 다음을 위해 하나의 단위를 더 포함합니다.header 자체,은 그자체, 그고이기값에 입니다.size헤더의 필드입니다.malloc에서 반환한 포인터는 헤더 자체가 아닌 사용 가능한 공간에 있습니다.

사용자는 요청된 공간으로 원하는 작업을 수행할 수 있지만 할당된 공간 밖에 기록된 내용이 있으면 목록이 스크램블될 수 있습니다.

   -----------------------------------------
   |        |     SIZE     |               |
   -----------------------------------------
     |        |
  points to   |-----address returned touser
   next free
   block
        -> a block returned by malloc 

진술서에

char* test = malloc(1);

할 수 "malloc()"를 의 힙 합니다.address하기와 같이

 --------------------------------------------------------------
| free memory  | memory in size allocated for user |           |
----------------------------------------------------------------
                                                              0x100(assume address returned by malloc)
                                                              test

그래서 언제malloc(1)실행 그것은 단지 할당되지 않을 것입니다.1바이트, 그것은 일부를 할당했습니다.extra위의 구조/구조 테이블을 유지할 바이트 수 있습니다.요청한 경우에만 실제 메모리가 얼마나 할당되었는지 확인할 수 있습니다.1바트단위인쇄로를 인쇄하여 합니다.test[-1]왜냐하면 그 블록 바로 앞에 크기가 포함되어 있기 때문입니다.

char* test = malloc(1);
printf("memory allocated in bytes = %d\n",test[-1]);

전달된 크기가 0이고 ptr이 NULL이 아닌 경우 호출은 무료입니다.

언급URL : https://stackoverflow.com/questions/3509714/i-can-use-more-memory-than-how-much-ive-allocated-with-malloc-why

반응형