[C++] Sanitizer (with GCC)

C++์€ ์‚ฌ๋ฐฉ์ด ์ง€๋ขฐ๋ฐญ์œผ๋กœ ๊ฐ€๋“์ฐจ์žˆ๋Š” ์‹œํ•œํญํƒ„ ๊ฐ™์€ ์–ธ์–ด๊ณ , ๊ทธ ์ž์ฒด๋กœ๋Š” ์•ˆ์ •์„ฑ์ด ํ•„์š”ํ•œ ํ”„๋กœ๋•์…˜ ์šด์˜์— ์žˆ์–ด ์—ฌ๋Ÿฌ๋ชจ๋กœ ๋ถˆ๋ฆฌํ•œ ์ ์ด ๋งŽ๋‹ค.

๊ทผ๋ฐ ๋ฌธ์ œ๋งŒ ์žˆ๋‹ค๋ฉด ๊ทธ๊ฑธ ๊ณ„์† ์ผ๊ฒ ๋Š”๊ฐ€? ๊ทธ๋ž˜๋„ ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ ์ข€ ๋ณด์™„ํ•ด์ค„ ๋ณด์กฐ ์ˆ˜๋‹จ๋“ค์ด ์ œ๋ฒ• ์žˆ์œผ๋‹ˆ๊นŒ ์“ฐ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ์˜ค๋ฅ˜ ๊ฐ์ง€ ๋„๊ตฌ๋“ค์„ Sanitizer๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

Sanitizer์˜ ์ข…๋ฅ˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๊ณ , ์ปดํŒŒ์ผ๋Ÿฌ ์ง€์›๋ถ€ํ„ฐ ์„œ๋“œํŒŒํ‹ฐ ์ง€์›๊นŒ์ง€ ์ด๊ฒƒ์ €๊ฒƒ ๋งŽ์€ ํŽธ์ด๋‹ค.
๊ทธ ์ค‘์—์„œ๋„ GCC/Clang์— ํฌํ•จ๋œ ๊ตฌํ˜„์ฒด๋“ค์ด ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” Sanitizer๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” GCC์—์„œ ์ง€์›๋˜๋Š” Sanitizer์˜ ์ข…๋ฅ˜์™€, ๊ทธ ๊ฐ„๋žตํ•œ ์‚ฌ์šฉ๋ฐฉ๋ฒ• ์ •๋„๋ฅผ ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค.
GCC์˜ Sanitizer๋Š” ๋Ÿฐํƒ€์ž„์— ๋ฌธ์ œ ์ถ”์ ์„ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์‹ฌ๊ณ , ์ฝ”๋“œ ์‹คํ–‰ ์ค‘์— ๋ฌธ์ œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ฐ ์‹คํ–‰์„ฑ๋Šฅ์„ ํฌํ•จํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๊ฝค ์žˆ์–ด์„œ ์ตœ์ข… ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ์—๋Š” ์‚ฌ์šฉ์ด ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ฃผ์š” Sanitizer์˜ ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.



ํ•˜๋‚˜์”ฉ ๊ฐ„๋‹จํžˆ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.




1. Address Sanitizer

Address Sanitizer๋Š” ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น, ์ฃผ์†Œ ๊ด€๋ฆฌ์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๋Š” Sanitizer๋‹ค. ๊ฐ€์žฅ ์ค‘์š”๋„๊ฐ€ ๋†’๊ณ  ์‚ฌ์šฉ ๋นˆ๋„๋„ ๋†’์€ ์ถ•์— ์†ํ•œ๋‹ค.

์ด๊ฑด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

  1. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ์ง€

  2. ํ• ๋‹นํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ๊ฐ์ง€ (use after free)

  3. ์ค‘๋ณต ํ• ๋‹นํ•ด์ œ

  4. ํ•ด์ œ๋œ ์Šคํƒ ๋ณ€์ˆ˜์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ return ๊ฐ์ง€ (use after return)

  5. ์œ„ํ—˜ํ•œ ๋ฐฐ์—ด 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์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ๋‹ค. ๊ต‰์žฅํžˆ ๋งŽ์•„์„œ ์ผ๋ถ€๋งŒ ์ ์—ˆ๋‹ค.

  1. ์ •์ˆ˜ ์˜ค๋ฒ„ํ”Œ๋กœ

  2. 0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ

  3. ๋„ํฌ์ธํ„ฐ ์ฐธ์กฐ

  4. ์Œ์ˆ˜ Shift ์—ฐ์‚ฐ

  5. ์ž˜๋ชป๋œ ํƒ€์ž… ์บ์ŠคํŒ… ๋“ฑ๋“ฑ...



์‚ฌ์šฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์ปดํŒŒ์ผํ•  ๋•Œ -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