요 며칠, 개인적으로 작업하던 프로젝트에 아주 난감한 문제가 발생했다. 바로 특정 상황에서 스레드 정리할 때 쓰레드의 EIP가 말도 안 되는 곳으로 가서 죽는 경우.
이런 경우는 처음 보는 경우라서 살짝 뇌정지가 왔다. 이걸 어떻게 해결한다...
보통 디버깅 모드에서 스택 스매싱이 일어나면, 스택을 빠져나가거나 하는 그 즉시 값이 오염되었는지를 확인하여 ASSERT를 띄운다. 그런데 이번 경우는 어째서인지 그런 증상도 전혀 일어나지 않아서 ... 스택 스매싱이 아닌가? 의심도 해 보았다. 1
문제 원인을 파악하는 것 부터가 난관이었는데, 어찌어찌 보다 보니 굉장히 이상한 특징을 찾았다.
쓰레드 정리 직전에 콜스택이 굉장히 이상하게 찍히는데, 원래 일반적인 Win32에서의 std::thread는 아래처럼 thread_start 함수에서부터 시작된다. 2
그런데 이렇게 멀쩡하던 콜스택이 쓰레드 종료 직전 상황에 오니까 ...
콜스택이 증발했다. 저기서 메인 쓰레드 한두줄만 더 넘기면 바로 해괴한 EIP로 가 버린다. 아마 thread_start 부근에서 스택 스매싱이 일어나면 탐지가 안 되는 모양이다.
감이 오는 부분이 있어서 프로그램 실행 영역을 좁혀 보니, 원인을 금방 찾을 수 있었다. double pointer으로 인자를 넘겼는데 이걸 일반 pointer으로 받아 버려서 문제가 발생했던 것이었다.
여하튼, 이번 삽질은 스택 스매싱은 디버그 모드에서 잡기 쉽다는 내 신념을 깨뜨려 주었고, 믿을 만한 건 오로지 내 자신 뿐이라는 걸 다시금 깨닫는 좋은 계기가 되었다 ...
이런 별거 아닌 멀티스레딩 프로그램 하나 짜는 데도 이렇게 삽질하는 거 보면 아직 멀었구만.
추가 - 스택 스매싱 탐지는 디버깅 컴파일 시 메모리 가드 영역이 있어서, 스택을 빠져나갈 때 거기에 있는 값이 손상될 경우 런타임 오류를 띄우는 방식이다. 그런데 손상된 부분이 외부 코드 영역이라면, 메모리 가드 영역이 없는 부분일테니 탐지가 안 될 수 있을 것 같다.
'개발 > Developing' 카테고리의 다른 글
Boost.build와 jam build system (0) | 2022.02.19 |
---|---|
[C++17] std::execution - 알고리즘의 병렬화 (0) | 2022.02.19 |
[C++] template에서 rvalue reference로 파라미터 받기? (0) | 2018.09.14 |
객체지향 프로그래밍 (0) | 2017.01.04 |
안드로이드 롤리팝에서 새로 제공되는 Storage Access Framework를 통한 SD 카드 I/O 접근에 대하여 (1) | 2015.06.27 |