[1. 개요]
버퍼 오버플로우를 유발 할 수 있는 사례들을 정리한다.
포인터를 전달할 때, 실제 메모리는 해당 포인터 타입에 해당하는 크기만큼의 공간이 할당되어 있어야 한다.
아래와 같은 타입 캐스팅은 가급적 하지 않는 것이 좋다.
- int value = 0x1234;
- long long *ptr = reinterpret_cast<int*>(&value);
왜 그런가, ptr 을 대상으로 발생하는 연산은 ptr 이 가리키는 공간이 8byte 만큼 할당되어 있다고 판단하고
연산을 진행한다.
그러나, 실제 메모리 공간은 4바이트 이므로 할당된 메모리 공간을 넘어서는 문제가 발생할 수 있다.
아래 코드를 보면, func() 에는 변수 a 의 주소만 넘기는데, b 의 값이 변경되어 버린다.
#include <iostream>
void func(unsigned long *ptr)
{
*ptr = 0x1234567887654321;
}
int main()
{
unsigned int a = 0x1111;
unsigned int b = 0x2222;
std::cout << "a: 0x" << std::hex << a << std::endl;
std::cout << "b: 0x" << std::hex << b << std::endl;
func(reinterpret_cast<unsigned long*>(&a));
std::cout << "a: 0x" << std::hex << a << std::endl;
std::cout << "b: 0x" << std::hex << b << std::endl;
std::cout << "addr(a): 0x" << std::hex << &a << std::endl;
std::cout << "addr(b): 0x" << std::hex << &b << std::endl;
return 0;
}
// output
// a: 0x1111
// b: 0x2222
// a: 0x87654321
// b: 0x12345678
// addr(a): 0x0x7fffa9210740
// addr(b): 0x0x7fffa9210744
여기서 변수 b 를 const 선언하면, 그 값이 바뀌지 않을 수 도 있다.
- 반드시 바뀌지 않는다고 보장 할 수 없다.
- 테스트 시에는 바뀌지 않았다.
# 경우에 따라, 변수 b를 스택이 아닌 레지스터에 저장한다거나
# 변수를 재배치하여 주소가 연속되지 않게 한다거나..
# 결국, 작성자가 예측할 수 없으므로 어찌되었든 문제가 된다. - 그러나 이러한 코드자체가 undefined behavior 이므로, 권장하지 않는다.
정리하면, 포인터 형변환은 반드시 신중히해야 한다.
- reinterpret_cast 의 사용을 자제하는 편이 좋아 보임
- 포인터 변수를 선언하는 편이 더 좋아 보임. (컴파일 오류를 유발하도록 하여, 수정 할 수 있도록)
'C++' 카테고리의 다른 글
Segmentation fault 사례5 (0) | 2025.06.24 |
---|---|
Placement new (0) | 2025.05.28 |
wchar_t 사용에 관하여 [2] (0) | 2025.05.13 |
wchar_t 사용과 관련하여. (0) | 2025.05.12 |
std::bind (0) | 2025.04.28 |