행글링 포인터
Dangling pointer컴퓨터 프로그래밍에서 행글링 포인터와 와일드 포인터는 적절한 유형의 유효한 객체를 가리키지 않는 포인터입니다.이것들은 메모리 안전 위반의 특수한 경우입니다.보다 일반적으로, 행잉 레퍼런스 및 와일드 레퍼런스는, 유효한 수신처로 해결되지 않는 레퍼런스입니다.
덩글링 포인터는 오브젝트 파괴 중에 포인터 값을 변경하지 않고 착신 참조를 가진 오브젝트가 삭제 또는 할당 해제될 때 발생합니다.이 경우 포인터는 할당 해제된 메모리의 메모리 위치를 계속 가리킵니다.시스템은 이전에 빈 메모리를 재할당할 수 있으며, 프로그램이 (지금) 행잉 포인터를 비활성화하면 메모리에 전혀 다른 데이터가 포함되어 있을 수 있기 때문에 예측할 수 없는 동작이 발생할 수 있습니다.프로그램이 달랑거리는 포인터로 참조되는 메모리에 쓸 경우 관련 없는 데이터가 소리 없이 손상되어 발견이 매우 어려울 수 있는 미묘한 버그가 발생할 수 있습니다.메모리가 다른 프로세스에 재할당된 경우, 덩글링 포인터를 참조 해제하려고 하면 세그먼트화 장애(UNIX, Linux) 또는 일반 보호 장애(Windows)가 발생할 수 있습니다.프로그램이 커널의 메모리 할당자에 의해 사용되는 장부 데이터를 덮어쓸 수 있는 충분한 권한을 가지고 있는 경우, 파손으로 인해 시스템이 불안정해질 수 있습니다.가비지 컬렉션이 있는 오브젝트 지향 언어에서는 도달할 수 없는 오브젝트만 파기함으로써 행잉 참조가 방지됩니다.즉, 수신 포인터는 없습니다.이는 트레이스 또는 참조 카운트를 통해 보증됩니다.그러나 최종자는 오브젝트에 대한 새로운 참조를 작성할 수 있으며, 오브젝트 부활이 요구되어 참조가 매달리는 것을 방지할 수 있습니다.
와일드 포인터는 일부 프로그래밍 언어에서 가능한 알려진 상태로 초기화하기 전에 포인터를 사용할 때 발생합니다.이들은 행글링 포인터와 같은 불규칙한 동작을 보이지만 많은 컴파일러가 초기화 [1]전에 선언된 변수에 액세스하면 컴파일 시 경고를 발생시키기 때문에 검출되지 않을 가능성이 낮습니다.
포인터가 매달리는 원인
많은 언어(예를 들어 C 프로그래밍 언어)에서 오브젝트를 메모리에서 명시적으로 삭제하거나 반환 시 스택프레임을 파괴해도 연관된 포인터는 변경되지 않습니다.포인터는, 다른 용도로 사용할 수 있게 되어도, 메모리내의 같은 장소를 가리키고 있습니다.
간단한 예를 다음에 나타냅니다.
{ 차 *에서 = 특수한 순서; /* ... */ { 차 c; 에서 = &c; } /* c가 범위를 벗어납니다*/ /* dp는 매달림 포인터가 되었습니다*/ }
운영체제가 늘 포인터에 대한 런타임 참조를 검출할 수 있는 경우 위의 해결책은 내부 블록이 종료되기 직전에0(늘)을 dp에 할당하는 것입니다.또 다른 해결책은 추가 초기화 없이 dp를 다시 사용하지 않도록 하는 것입니다.
또 다른 포인터의 빈번한 소스는 혼재된 조합입니다.malloc()
그리고.free()
라이브러리 콜: 포인터가 가리키는 메모리 블록이 해방되면 포인터가 행업합니다.위의 예와 마찬가지로 이 문제를 회피하는 한 가지 방법은 다음과 같이 참조를 해제한 후 포인터를 null로 리셋하는 것입니다.
#실패하다 <stdlib.h> 무효 기능하다() { 차 *dp = 마로크(A_CONST); /* ... */ 공짜(dp); /* dp가 매달리는 포인터가 되었습니다*/ dp = 특수한 순서; /* dp가 행업하지 않게 되었습니다*/ /* ... */ }
스택에 할당되어 있는 로컬 변수의 주소를 반환하는 것은 매우 일반적인 실수입니다.호출된 함수가 반환되면 이들 변수의 공간은 할당 해제되어 기술적으로 "쓰레기 값"이 됩니다.
인트 *기능하다(무효) { 인트 숫자 = 1234; /* ... */ 돌아가다 &숫자; }
포인터에서 읽기를 시도해도 호출 후 한동안 올바른 값(1234)이 반환될 수 있습니다.func
단, 그 이후에 호출된 함수는 에 할당된 스택스토리지를 덮어쓸 수 있습니다.num
다른 값을 지정하면 포인터가 올바르게 작동하지 않게 됩니다.에 대한 포인터가 있는 경우num
반환해야 합니다.num
이 함수의 범위를 넘어야 합니다.이것은 다음과 같이 선언될 가능성이 있습니다.static
.
매달린 참조가 없는 수동 할당 해제
Antoni Kreczmar [ 1945-1996)는 매달린 참조 [2]현상이 없는 완전한 객체 관리 시스템을 만들었습니다.비슷한 접근방식은 Locks-and-keys라는 이름으로 Fisher와 LeBlanc에 의해 제안되었다.
와일드 포인터의 원인
와일드 포인터는 처음 사용하기 전에 필요한 초기화를 생략함으로써 생성됩니다.따라서 엄밀히 말하면 초기화를 강제하지 않는 프로그래밍 언어의 모든 포인터는 와일드 포인터로 시작합니다.
이 문제는 대부분 초기화를 생략하지 않고 건너뛰기 때문에 발생합니다.대부분의 컴파일러는 이에 대해 경고할 수 있습니다.
인트 f(인트 i) { 차 *에서; /* dp는 와일드 포인터 */ 정적인 차 *scp; /* scp는 와일드 포인터가 아닙니다. * 정적 변수는 0으로 초기화됩니다. * 시작 시점의 가치를 유지하고 * 그 후 마지막 통화. * 이 기능을 사용하면 좋지 않은 것으로 간주될 수 있습니다. * 코멘트가 없는 경우 스타일 */ }
포인터가 매달려 있는 보안 구멍
버퍼 오버플로우 버그와 마찬가지로 행글링/와일드포인터 버그가 보안의 구멍이 되는 경우가 많습니다.예를 들어 포인터가 가상 함수 호출에 사용되는 경우 vtable 포인터가 덮어쓰기되어 다른 주소(아마도 취약성 코드를 가리키고 있음)가 호출될 수 있습니다.또는 포인터가 메모리에 쓰기에 사용되는 경우 다른 데이터 구조가 손상될 수 있습니다.포인터가 행잉된 후에만 메모리를 읽어도 정보 유출(해당된 다음 구조에 대상 데이터가 배치되는 경우) 또는 권한 상승(현재 유효하지 않은 메모리가 보안 검사에 사용되는 경우)이 발생할 수 있습니다.새로운 메모리 청크를 할당하지 않고 해방된 후 행글링 포인터를 사용하는 경우 이를 "사용 후 빈"[4] 취약성이라고 합니다.예를 들어 CVE-2014-1776은 Microsoft Internet Explorer 6 ~ 11의[5] 사용 후 취약성으로, 고급 영구 [6]위협의 제로 데이 공격에 의해 사용됩니다.
행잉 포인터 오류 방지
C에서 가장 간단한 기술은 다른 버전의 C를 구현하는 것입니다.free()
포인터의 리셋을 보증하는 (또는 유사) 기능.그러나 이 기술은 포인터의 복사본을 포함할 수 있는 다른 포인터 변수를 지우지 않습니다.
#실패하다 <고객명>님.h> #실패하다 <stdlib.h> /* 'free()' 대체 버전 */ 정적인 무효 세이프프리(무효 **pp) { /* 디버깅모드에서 pp가 NULL이면 중단*/ 주장하다(pp); /* free(NULL)는 정상적으로 동작하므로 디버깅모드에서의 아사트 이외에는 체크가 필요 없습니다*/ 공짜(*pp); /* 청크의 할당을 해제합니다.무료(NULL)는 유효합니다*/ *pp = 특수한 순서; /* 원래 포인터를 리셋 */ } 인트 f(인트 i) { 차 *p = 특수한 순서, *p2; p = 마로크(1000); /* 청크를 가져옵니다*/ p2 = p; /* 포인터를 복사합니다*/ /* 여기에 청크를 사용 */ 세이프프리((무효 **)&p); /* 안전 해제, p2 변수에는 영향을 주지 않음 */ 세이프프리((무효 **)&p); /* 이 두 번째 콜은 p가 NULL로 리셋되므로 실패하지 않습니다*/ 차 c = *p2; /* p2는 여전히 덩글링 포인터이므로 이는 정의되지 않은 동작입니다.*/ 돌아가다 i + c; }
대체 버전은 호출하기 전에 빈 포인터의 유효성을 보장하기 위해 사용될 수 있습니다.malloc()
:
세이프프리(&p); /* 청크가 출시되었는지 잘 모르겠습니다 */ p = 마로크(1000); /* 지금 당장 할당 */
이러한 용도를 통해 마스킹할 수 있습니다.#define
유용한 매크로를 구축하기 위한 지침(일반적인 것)#define XFREE(ptr) safefree((void **)&(ptr))
)를 작성하거나 도구 라이브러리에 별도로 삽입할 수 있습니다.모든 경우에 이 기술을 사용하는 프로그래머는 안전한 버전을 사용해야 합니다.free()
사용할 수 있습니다.그렇게 하지 않으면 다시 문제가 발생합니다.또한 이 솔루션은 단일 프로그램 또는 프로젝트의 범위로 한정되므로 적절하게 문서화해야 합니다.
보다 구조화된 솔루션 중, C++에서 포인터가 매달리는 것을 피하기 위한 인기 있는 기술은 스마트 포인터를 사용하는 것이다.스마트 포인터는 일반적으로 참조 카운트를 사용하여 개체를 회수합니다.다른 기술로는 묘석법과 자물쇠 [3]달기법이 있다.
또 다른 방법은 Bohm 가비지 컬렉터를 사용하는 것입니다.이는 C 및 C++의 표준 메모리 할당 기능을 가비지 컬렉터로 대체하는 보수적인 가비지 컬렉터입니다.이 방법에서는 빈 공간을 사용하지 않도록 설정하고 가비지 수집을 통해 개체를 회수하여 매달림 포인터 오류를 완전히 제거합니다.
Java와 같은 언어에서는 메모리 할당을 명시적으로 해제하는 메커니즘이 없기 때문에 덩글링 포인터가 발생할 수 없습니다.대신 가비지 컬렉터는 메모리 할당을 해제할 수 있지만 참조에서 개체에 더 이상 연결할 수 없는 경우에만 해당됩니다.
Rust라는 언어에서 유형 시스템은 변수 수명 및 리소스 획득을 포함하도록 확장되었습니다.언어의 기능을 무효로 하지 않는 한, 행글링 포인터는 컴파일시에 검출되어 프로그래밍 에러로서 보고됩니다.
매달림 포인터 검출
덩글링 포인터 오류를 노출하기 위해 일반적인 프로그래밍 기법 중 하나는 포인터가 가리키는 스토리지가 해제되면 늘 포인터 또는 비활성 주소로 포인터를 설정하는 것입니다.null 포인터가 참조되지 않은 경우(대부분의 언어에서) 프로그램은 즉시 종료됩니다.데이터 손상이나 예측할 수 없는 동작이 발생할 가능성은 없습니다.이를 통해 기본 프로그래밍 오류를 더 쉽게 찾고 해결할 수 있습니다.이 기술은 포인터의 복사본이 여러 개 있는 경우에는 도움이 되지 않습니다.
일부 디버거는 해방된 데이터를 다음과 같은 특정 패턴으로 자동으로 덮어쓰고 파기합니다.0xDEADBEEF
(예를 들어 Microsoft의 Visual C/C++ 디버거는0xCC
,0xCD
또는0xDD
해제된[7] 내용에 따라 달라집니다).이것은 보통 데이터를 쓸모없게 하고 매우 두드러지게 함으로써 데이터가 재사용되는 것을 막습니다(패턴이 프로그래머에게 메모리가 이미 해방되었음을 보여주는 역할을 합니다).
Polyspace, TotalView, Valgrind, Mudflap,[8] AddressSanitizer 등의 툴이나 LLVM 기반의[9] 툴을 사용하여 덩글링 포인터의 사용을 검출할 수도 있습니다.
다른 도구(SoftBound, Insure++ 및 CheckPointer)는 소스 코드를 계측하여 포인터의 정당한 값("메타데이터")을 수집 및 추적하고 각 포인터 액세스를 메타데이터와 비교하여 확인합니다.
또 다른 전략은 작은 클래스 세트를 의심하는 경우 모든 멤버 함수를 일시적으로 가상화하는 것입니다.클래스 인스턴스가 파기/자유된 후 Virtual Method Table로의 포인터는 다음과 같이 설정됩니다.NULL
멤버 함수에 대한 호출은 프로그램을 크래시하고 디버거에 의심스러운 코드를 표시합니다.
「 」를 참조해 주세요.
레퍼런스
- ^ "Warning Options - Using the GNU Compiler Collection (GCC)".
- ^ Gianna Cioni, Antoni Kreczmar, 달링 참조 없이 프로그래밍된 할당 해제, 정보처리 서신, v. 18, 1984, 페이지 179-185
- ^ a b C.N. Fisher, R.J. Leblanc, Pascal, IEEE Trans에서의 런타임 진단 구현.소프트, 영어, 6(4):313-319, 1980
- ^ Dalci, Eric; anonymous author; CWE Content Team (May 11, 2012). "CWE-416: Use After Free". Common Weakness Enumeration. Mitre Corporation. Retrieved April 28, 2014.
{{cite web}}
:author2=
범용명(도움말)이 있습니다. - ^ "CVE-2014-1776". Common Vulnerabilities and Exposures (CVE). 2014-01-29. Archived from the original on 2017-04-30. Retrieved 2017-05-16.
- ^ Chen, Xiaobo; Caselden, Dan; Scott, Mike (April 26, 2014). "New Zero-Day Exploit targeting Internet Explorer Versions 9 through 11 Identified in Targeted Attacks". FireEye Blog. FireEye. Retrieved April 28, 2014.
- ^ Visual C++ 6.0 메모리 충전 패턴
- ^ Mudflap 포인터의 디버깅
- ^ Durjati, D. 및 Adve, V. 프로덕션 서버에서 사용되는 모든 당글링 포인터를 효율적으로 검출