C++의 Double Free 또는 손상 오류

Muhammad Husnain 2023년10월12일
  1. C++의 동적 메모리 할당 및 할당 해제
  2. C++의 이중 해제 또는 손상 오류
  3. C++에서 double free or corrupt 오류를 방지하는 방법
C++의 Double Free 또는 손상 오류

이 자습서에서는 C++의 동적 메모리 할당에서 발생하는 문제에 대해 설명합니다. 힙 메모리를 사용할 때 힙 메모리 관리를 수행하는 동안 해결해야 할 매우 중요한 많은 문제를 접하게 됩니다.

먼저 메모리를 할당하는 방법과 C++에서 제공되는 다양한 방법에 대해 설명합니다. 그런 다음 C++에서 double free or corruption 오류의 원인과 해결 방법으로 이동합니다.

C++의 동적 메모리 할당 및 할당 해제

C++를 사용하면 런타임 중에 변수 또는 배열 메모리를 할당할 수 있습니다. 이것을 동적 메모리 할당이라고 합니다.

Java 및 Python과 같은 다른 프로그래밍 언어에서 컴파일러는 자동으로 가변 메모리를 유지합니다. 그러나 C++에서는 그렇지 않습니다.

C++에서는 더 이상 필요하지 않은 동적으로 할당된 메모리를 수동으로 할당 해제해야 합니다. newdelete 기능을 사용하여 메모리를 동적으로 할당 및 할당 해제할 수 있습니다.

동적 메모리 할당 및 할당 해제를 위해 C++에도 포함된 mallocfree라는 다른 두 가지 기능이 있습니다.

newdelete 기능의 사용을 보여주는 아래 예를 고려하십시오.

#include <iostream>
using namespace std;

int main() {
  int* ptr;
  ptr = new int;
  *ptr = 40;
  cout << *ptr;
  delete ptr;
  return 0;
}

’new’ 연산자를 사용하여 int 변수에 대한 메모리를 동적으로 할당했습니다. 메모리를 동적으로 할당하기 위해 참조 변수 ptr을 사용했다는 점은 주목할 가치가 있습니다.

new 연산자는 메모리 위치의 주소를 반환합니다. 배열의 경우 new 연산자는 배열의 첫 번째 요소 주소를 반환합니다.

더 이상 사용할 필요가 없으면 동적으로 선언된 변수가 사용하는 메모리를 할당 해제할 수 있습니다. 이를 위해 delete 연산자가 사용됩니다.

운영 체제에 메모리를 다시 제공합니다. 이것을 메모리 할당 해제라고 합니다.

다음 예는 mallocfree 기능의 데모를 보여줍니다.

#include <cstdlib>
#include <iostream>
using namespace std;

int main() {
  int* ptr2 = (int*)malloc(sizeof(int));
  *ptr2 = 50;

  cout << *ptr2;
  free(ptr2);
  return 0;
}

C++에서 malloc() 메서드는 초기화되지 않은 메모리 블록에 포인터를 할당합니다. cstdlib 헤더 파일이 이를 정의합니다.

‘malloc’은 할당해야 하는 메모리 크기를 매개변수로 받아 할당된 메모리 블록을 가리키는 void 포인터를 반환합니다. 따라서 우리의 요구 사항에 따라 typecast할 수 있습니다.

반면에 free() 메소드는 malloc 함수를 사용하여 할당된 메모리를 할당 해제합니다. 해당 메모리를 운영 체제로 반환하고 메모리 누수 문제를 방지합니다.

C++의 이중 해제 또는 손상 오류

이중 자유 오류는 free()가 입력과 동일한 메모리 주소로 두 번 이상 사용될 때 발생합니다.

동일한 변수에서 free()를 두 번 호출하면 메모리 누수가 발생할 수 있습니다. 소프트웨어가 동일한 매개변수로 free()를 두 번 실행하면 애플리케이션의 메모리 관리 데이터 구조가 손상되어 악의적인 사용자가 모든 메모리 영역에 값을 쓸 수 있습니다.

이 손상으로 인해 프로그램이 충돌하거나 경우에 따라 실행 흐름이 변경될 수 있습니다. 공격자는 특정 레지스터나 메모리 영역을 덮어써서 프로그램이 선택한 코드를 실행하도록 오도하여 권한이 상승된 대화형 셸을 만들 수 있습니다.

여유 메모리의 조각을 재구성하고 결합하기 위해 버퍼가 free()될 때 여유 버퍼의 연결 목록을 읽습니다(앞으로 더 큰 버퍼를 할당하기 위해). 이러한 청크는 앞뒤 청크에 대한 링크가 있는 이중 연결 목록으로 구성됩니다.

공격자는 사용하지 않는 버퍼의 연결을 해제하고(free() 호출 시 발생), 중요한 레지스터를 효과적으로 덮어쓰고, 버퍼에서 셸코드를 실행하여 메모리에 임의의 값을 쓸 수 있습니다.

아래의 예를 고려하십시오.

#include <cstdlib>
#include <iostream>
using namespace std;

int main() {
  int* ptr2 = (int*)malloc(sizeof(int));
  *ptr2 = 50;

  cout << *ptr2;
  free(ptr2);
  free(ptr2);
  return 0;
}

출력:

free(): double free detected in cache 2
Aborted

위의 코드 스니펫에서 free() 함수를 두 번 사용했습니다. 이는 이미 사용 가능하고 더 이상 할당되지 않은 메모리를 해제하려고 한다는 것을 의미합니다. 이로 인해 메모리 누수 문제가 발생하고 코드 충돌의 근본 원인이 됩니다.

이러한 오류가 발생할 수 있는 다른 상황도 많이 있습니다. 그러나 이중 자유 취약점에는 세 가지 공통적인(그리고 종종 중복되는) 이유가 있습니다.

  1. 오류 조건 및 기타 비정상적인 상황
  2. 메모리 공간이 해제된 후 사용됩니다.
  3. 프로그램의 어느 부분이 메모리 해제를 담당하는지에 대한 혼란

일부 이중 자유 취약점은 앞의 예보다 훨씬 복잡하지 않지만 대부분은 수백 줄의 코드 또는 여러 파일에 분산되어 있습니다. 프로그래머는 특히 전역 변수를 여러 번 해제하는 경향이 있는 것으로 보입니다.

C++에서 double free or corrupt 오류를 방지하는 방법

이러한 유형의 취약점은 포인터가 해제될 때마다 포인터에 NULL을 할당하여 피할 수 있습니다(즉, 이 포인터가 가리키는 메모리가 해제됨). 그 다음에는 대부분의 힙 관리자가 무료 널 포인터를 무시합니다.

삭제된 모든 포인터를 null로 만들고 참조를 해제하기 전에 참조가 null인지 여부를 확인하는 것이 좋습니다. 코드 시작 시 어떤 경우에도 해당 포인터를 사용하기 전에 null 포인터를 초기화해야 합니다.

Muhammad Husnain avatar Muhammad Husnain avatar

Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.

LinkedIn

관련 문장 - C++ Error