C++ 임시 문자열 수명 오류, 최적화 뒤 권한 판정 결과 뒤집혀
Ted Kim의 C++ 예제는 임시 `std::string`의 데이터를 가리키는 `std::string_view`를 원본이 사라진 뒤 비교해 미정의 동작을 일으킵니다. 작성자의 Clang 19·GCC 14 실행에서는 `-O0`의 `false`가 `-O2`에서 `true`로 바뀌며, 글은 이를 실제 서비스 사례가 아닌 재현용 코드로 다룹니다.
본문
Ted Kim이 운영하는 nablag 개인 블로그는 10월 31일 C++의 문자열 수명 문제를 보여 주는 권한 판정 예제를 공개했습니다. Sanitize에 문자열 리터럴을 넘긴 뒤 username과 admin_id를 비교하는 코드가 작성자의 Debian/WSL2 환경에서 Clang 19.1.7과 GCC 14.2.0 모두 -O0일 때는 false, -O2일 때는 true를 출력합니다. 작성자는 실제 서비스의 권한 우회나 악용 사례를 제시하지 않습니다. 글은 C++ 미정의 동작을 설명하는 재현용 예제입니다.
원인은 Sanitize가 입력 문자열을 소유하지 않는 std::string_view로 반환하는 데 있습니다. 함수 매개변수는 const std::string&이므로 호출부의 문자열 리터럴은 임시 std::string으로 바뀌어 전달됩니다. 그 문자열에서 만든 뷰를 반환할 때 원본은 아직 살아 있지만, 임시 객체의 수명은 함수 호출이 들어 있는 문장이 끝날 때 종료됩니다. 따라서 std::string_view username = Sanitize(...) 선언문 다음에 실행하는 비교에서는 username이 이미 수명이 끝난 문자열의 데이터를 가리킵니다. string_view는 가리키는 문자열의 수명을 관리하지 않습니다. 함수 매개변수에 바인딩한 임시 객체는 그 호출을 포함한 전체 표현식이 끝날 때까지 유지됩니다.
그 뒤의 username == admin_id는 수명이 끝난 문자열을 가리키는 뷰를 사용합니다. 이 비교는 정의되지 않은 동작이므로 결과가 보장되지 않습니다. -O0에서 false, -O2에서 true가 나온 것은 최적화 옵션에 따라 이 예제의 관측 결과가 달라진 사례입니다. 해당 true 출력은 이 실행 환경에서 나타난 결과이며, 작성자는 실제 서비스의 취약점이나 권한 우회 사례로 보고하지 않았습니다.
Kim은 재현 조건도 설명합니다. 입력 문자열과 admin_id의 길이가 같고 입력을 함수 인자로 바로 넘기는 형태에서 현상이 나타났으며, 입력 길이를 바꾸거나 먼저 변수에 저장하면 관찰한 결과가 사라졌다고 적었습니다. 작성자는 이런 버그가 실제 코드에서 나타나기 어렵고 악용도 쉽지 않을 것으로 봤습니다. 이 평가는 해당 예제에 대한 작성자의 판단입니다.
글은 admin_id를 전역 변수나 싱글턴으로 옮기면 이 프로그램이 정상적으로 동작한다고 제안합니다. 그러나 전역 변수 변경은 username이 수명이 끝난 입력 문자열을 가리키는 문제를 해결하지 않습니다. 근본 수정은 호출자 소유의 std::string을 비교가 끝날 때까지 유지하거나, 정제 함수가 소유권을 가진 std::string을 반환하는 방식입니다.
자료: nablag 개인 블로그 / 정리·해설: Trawling