[C++] Sanitizer (with GCC)
C++์ ์ฌ๋ฐฉ์ด ์ง๋ขฐ๋ฐญ์ผ๋ก ๊ฐ๋์ฐจ์๋ ์ํํญํ ๊ฐ์ ์ธ์ด๊ณ , ๊ทธ ์์ฒด๋ก๋ ์์ ์ฑ์ด ํ์ํ ํ๋ก๋์ ์ด์์ ์์ด ์ฌ๋ฌ๋ชจ๋ก ๋ถ๋ฆฌํ ์ ์ด ๋ง๋ค.
๊ทผ๋ฐ ๋ฌธ์ ๋ง ์๋ค๋ฉด ๊ทธ๊ฑธ ๊ณ์ ์ผ๊ฒ ๋๊ฐ? ๊ทธ๋๋ ์ด๋ฐ ๋ฌธ์ ๋ค์ ์ข ๋ณด์ํด์ค ๋ณด์กฐ ์๋จ๋ค์ด ์ ๋ฒ ์์ผ๋๊น ์ฐ๋ ๊ฒ์ด๋ค. ๊ทธ ์ค๋ฅ ๊ฐ์ง ๋๊ตฌ๋ค์ Sanitizer๋ผ๊ณ ๋ถ๋ฅธ๋ค.
Sanitizer์ ์ข
๋ฅ๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๊ณ , ์ปดํ์ผ๋ฌ ์ง์๋ถํฐ ์๋ํํฐ ์ง์๊น์ง ์ด๊ฒ์ ๊ฒ ๋ง์ ํธ์ด๋ค.
๊ทธ ์ค์์๋ GCC/Clang์ ํฌํจ๋ ๊ตฌํ์ฒด๋ค์ด ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ Sanitizer๋ค.
์ฌ๊ธฐ์๋ GCC์์ ์ง์๋๋ Sanitizer์ ์ข
๋ฅ์, ๊ทธ ๊ฐ๋ตํ ์ฌ์ฉ๋ฐฉ๋ฒ ์ ๋๋ฅผ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
GCC์ Sanitizer๋ ๋ฐํ์์ ๋ฌธ์ ์ถ์ ์ ์ํ ์ฝ๋๋ฅผ ์ฌ๊ณ , ์ฝ๋ ์คํ ์ค์ ๋ฌธ์ ๋ฅผ ๊ฐ์งํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
๋ฉ๋ชจ๋ฆฌ ๋ฐ ์คํ์ฑ๋ฅ์ ํฌํจํ ์ฑ๋ฅ ์ ํ๊ฐ ๊ฝค ์์ด์ ์ต์ข ํ๋ก๋์ ๋น๋์๋ ์ฌ์ฉ์ด ๊ถ์ฅ๋์ง ์๋๋ค.
์์ฃผ ์ฌ์ฉ๋๋ ์ฃผ์ Sanitizer์ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
-
AddressSanitizer
-
UndefinedBehaviorSanitizer
-
ThreadSanitizer
ํ๋์ฉ ๊ฐ๋จํ ๋ค๋ค๋ณด๊ฒ ๋ค.
1. Address Sanitizer
Address Sanitizer๋ ๋ฉ๋ชจ๋ฆฌ ํ ๋น, ์ฃผ์ ๊ด๋ฆฌ์ ๋ํ ๋ฌธ์ ๋ฅผ ์ถ์ ํ๋ Sanitizer๋ค. ๊ฐ์ฅ ์ค์๋๊ฐ ๋๊ณ ์ฌ์ฉ ๋น๋๋ ๋์ ์ถ์ ์ํ๋ค.
์ด๊ฑด ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํ๋ค.
-
๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฐ์ง
-
ํ ๋นํด์ ๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ๊ฐ์ง (use after free)
-
์ค๋ณต ํ ๋นํด์
-
ํด์ ๋ ์คํ ๋ณ์์ ๋ํ ํฌ์ธํฐ return ๊ฐ์ง (use after return)
-
์ํํ ๋ฐฐ์ด overflow ์ ๊ทผ๋ค
์๋ฅผ ๋ค์ด, ๋ค์๊ณผ ๊ฐ์ด ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฌธ์ ๊ฐ ์๋ ์ฝ๋๊ฐ ์๋ค๊ณ ํด๋ณด์.
#include <iostream>
void memory_leak()
{
int *ptr = new int[100];
// ๋ฒ๊ทธ: delete๋ฅผ ํ์ง ์์
}
int main()
{
for (int i = 0; i < 10; i++)
{
memory_leak();
}
return 0;
}
์ด๊ฑด ๋น์ฐํ ๋์๊ฐ ๋๋ ๋ง๋ ์คํ์ด ์ ๋๋ค. ํ์ง๋ง ์ค์ ์์คํ
์์๋ ์ฉ๋ฉํ ์ ์๋ ์น๋ช
์ ์ธ ๋ฒ๊ทธ์ ์ํ๋ค.
C++์ ๋น์ฐํ ์ธ์ด ์์ค์์ ์ด๋ฐ ๋ฌธ์ ์ ๋ํ ๋ฐฉ์ด์ฑ
์ ์ ๊ณตํ์ง ์์ผ๋ฏ๋ก, ์ฐ๋ฆฌ๊ฐ ์์์ ์ ์ก์์ผ ํ๋ค.
์ ๊ธฐ์ sanitizer๋ฅผ ๋ฃ์ด์ ์คํํด๋ณด๊ฒ ๋ค. ๋ค์๊ณผ ๊ฐ์ด -fsanitize ํ๋๊ทธ๋ฅผ ์ง์ด๋ฃ๊ณ , ์คํ๊น์ง ํด๋ณด๋ฉด ๋๋ค.
g++ -fsanitize=address -g main.cpp -o main.exe && ./main.exe
๊ทธ๋ฌ๋ฉด ๋์๊ฐ ๊ฐ์ง๋์์๋ ์ฆ์ ๋ฌธ์ ๋ณด๊ณ ๋ฅผ ํด์ค ๊ฒ์ด๋ค. ๋์๋ฅผ ์ก์๋ค.
ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ํ ์ ๊ทผ(use after free)์ ๋ํ ๋ฌธ์ ๋ ์ก๊ณ
#include <iostream>
int main()
{
int *ptr = new int(100);
delete ptr;
// ๋ฒ๊ทธ: ์ด๋ฏธ ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผ
*ptr = 200; // โ
std::cout << *ptr << std::endl; // โ
return 0;
}

free๋ฅผ 2๋ฒ ๋๋ฆฌ๋ ๊ฒ๋ ์ก๋๋ค.
#include <iostream>
int main()
{
int *ptr = new int(42);
delete ptr;
// ๋ฒ๊ทธ: ๊ฐ์ ํฌ์ธํฐ๋ฅผ ๋ ๋ฒ ํด์
delete ptr; // โ
return 0;
}

ํ ๋นํด์ ๋๋ ์คํ ๋ณ์๋ฅผ ํฌ์ธํฐ๋ก ๋ฐํํ๋ ๊ฒ๋ ์ก๋๋ค. lifetime ๋ฌธ์ ๋ค.
#include <iostream>
int *dangerous_function()
{
int local_var = 42;
// ๋ฒ๊ทธ: ์ง์ญ ๋ณ์์ ์ฃผ์ ๋ฐํ
return &local_var; // ํจ์ ์ข
๋ฃ ํ ๋ฌดํจํ๋จ
}
int main()
{
int *ptr = dangerous_function();
std::cout << *ptr << std::endl; // โ
return 0;
}
์ ์ก์์คฌ๋ค.
๋ค์์ ๋ฐฐ์ด์ ๋ํ overflow ์ ๊ทผ ์์ ๋ค.
#include <iostream>
int main()
{
int *arr = new int[10];
// ๋ฒ๊ทธ: ๋ฐฐ์ด ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ ๊ทผ
arr[10] = 42; // โ index 0-9๋ง ์ ํจ
std::cout << arr[10] << std::endl;
delete[] arr;
return 0;
}
๋ง์ฐฌ๊ฐ์ง๋ก ์ ๊ทผ ์ง์ ์ ์ค๋ฅ๋ก ์ ์ก์์ค๋ค.
2. UndefinedBehaviorSanitizer
๋ง ๊ทธ๋๋ก UB๋ฅผ ์ก์์ฃผ๋ Sanitizer๋ค.
C/C++์ ์ธ์ด ๋ช
์ธ์์ ์ ์๋์ง ์์, ์๋ฆฌ ์๋ ๋ฒ๊ทธ๋ค์ ์ ๋ฐํ๋ ๋ถ๋ถ๋ค์ด ๋ง๋ค. ์ปดํ์ผ๋ฌ๋ ํ๋ซํผ๋ง๋ค ๋์์ด ๋ค๋ฅธ ๊ทธ๋ฐ ๋ฌธ์ ๋ค ๋ง์ด๋ค.
์ฌ๊ธฐ์ ์ก์์ฃผ๋ UB์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒ๋ค์ด ์๋ค. ๊ต์ฅํ ๋ง์์ ์ผ๋ถ๋ง ์ ์๋ค.
-
์ ์ ์ค๋ฒํ๋ก
-
0์ผ๋ก ๋๋๊ธฐ
-
๋ํฌ์ธํฐ ์ฐธ์กฐ
-
์์ Shift ์ฐ์ฐ
-
์๋ชป๋ ํ์ ์บ์คํ ๋ฑ๋ฑ...
์ฌ์ฉ๋ฒ์ ๊ฐ๋จํ๋ค. ์ปดํ์ผํ ๋ -fsanitize=undefined ์ต์ ์ ๋ฃ๊ณ ์คํํ๋ฉด ๋๋ค.
g++ -fsanitize=undefined -g main.cpp -o main.exe && ./main.exe
๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ํํ ์ฝ๋๊ฐ ์๋ค๋ฉด
#include <iostream>
struct Data
{
int value;
void print()
{
std::cout << value << std::endl;
}
};
int main()
{
Data *ptr = nullptr;
// ๋ฒ๊ทธ: null ํฌ์ธํฐ ์ญ์ฐธ์กฐ
ptr->print(); // โ
return 0;
}

๋ง์ฐฌ๊ฐ์ง๋ก 0์ผ๋ก ๋๋๋ ๊ฒ๋ UB์ ์ํ๋ค.
#include <iostream>
int divide(int a, int b)
{
// ๋ฒ๊ทธ: 0์ผ๋ก ๋๋๊ธฐ
return a / b;
}
int main()
{
int result = divide(10, 0);
std::cout << result << std::endl;
return 0;
}

์ด๋ ๊ฒ ๋ฐ๋ก ์ก์์ค๋ค.
3. ThreadSanitizer
ThreadSanitizer๋ ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๋ฐ์ํ ์ ์๋ ๋์์ฑ ๋ฒ๊ทธ๋ค์ ๊ฐ์งํ๋ ๋๊ตฌ๋ค.
Data race, Deadlock ๋ฑ์ ์ฃผ์ ๋์์ฑ ๋ฒ๊ทธ๋ค์ด ๊ฐ์ง๋๋ฉด ์ค๋ฅ๋ฅผ ๋์ง๋ค.
์๋ฅผ ๋ค์ด ๋ค์ ์ฝ๋๋ data race๊ฐ ๋ฐ์ํด์ ์๋ํ ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง๋ ์์ง๋ง, ์ค๋ฅ๊ฐ ๋จ์ง๋ ์๋ ์ฝ๋๋ค.
#include <iostream>
#include <thread>
int shared_counter = 0; // ๊ณต์ ๋ณ์
void increment()
{
for (int i = 0; i < 100000; i++)
{
// ๋ฒ๊ทธ: ๋๊ธฐํ ์์ด ๊ณต์ ๋ณ์ ์ ๊ทผ
shared_counter++; // โ data race
}
}
int main()
{
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << shared_counter << std::endl;
std::cout << "Expected: 200000" << std::endl;
return 0;
}
thread ํ๋๊ทธ๋ฅผ ๋ฃ์ด์ ์คํํ๋ฉด thread sanitizer๊ฐ ๋ค์ด๊ฐ์ ์ด๋ฐ ๋์์ฑ ๋ฒ๊ทธ ํจํด์ ์ก์์ค๋ค.
g++ -fsanitize=thread -g main.cpp -o main.exe && ./main.exe

๋ฐ๋๋ฝ๋ ๊ฐ๋จํ ๊ฒฝ์ฐ์๋ ์ก์์ฃผ๊ธฐ๋ ํ๋๋ฐ, ์์ ํ์ง ์๋ค.
์ฐธ์กฐ
https://github.com/google/sanitizers/wiki
https://github.com/google/sanitizers/wiki/AddressSanitizer
https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags