본문으로 건너뛰기

[CPython] sqlite3 콜백 컨텍스트의 메모리 관리 버그 수정

PR 링크: python/cpython#146569 상태: Merged | 변경: +30 / -7

들어가며

CPython의 sqlite3 모듈은 Python 콜백을 C 레벨에서 관리하기 위해 callback_context 구조체를 사용합니다. 이 PR은 두 가지 메모리 관리 결함을 수정합니다.

  1. create_callback_context()에서 PyMem_Malloc 실패 시 MemoryError 대신 SystemError가 발생하는 문제
  2. create_collation()SQLITE_BUSY로 실패할 때 참조 카운트 관리 오류로 인한 crash

핵심 코드 분석

1. callback context 생성 시 에러 처리 개선

Before:

static callback_context *
create_callback_context(PyTypeObject *cls, PyObject *callable)
{
    callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
    if (ctx != NULL) {
        PyObject *module = PyType_GetModule(cls);
        ctx->refcount = 1;
        ctx->callable = Py_NewRef(callable);
        ctx->module = Py_NewRef(module);
        ctx->state = pysqlite_get_state(module);
    }
    return ctx;
}

After:

static callback_context *
create_callback_context(PyTypeObject *cls, PyObject *callable)
{
    callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
    if (ctx == NULL) {
        PyErr_NoMemory();
        return NULL;
    }

    PyObject *module = PyType_GetModule(cls);
    ctx->refcount = 1;
    ctx->callable = Py_NewRef(callable);
    ctx->module = Py_NewRef(module);
    ctx->state = pysqlite_get_state(module);
    return ctx;
}

기존 코드는 malloc 실패 시 NULL을 반환하면서 Python 예외를 설정하지 않았습니다. 호출자가 NULL을 받으면 SystemError: returned NULL without setting an exception이 발생했습니다. 수정 후에는 명시적으로 PyErr_NoMemory()를 호출하여 올바른 MemoryError를 발생시킵니다.

2. SQLITE_BUSY 시 참조 카운트 누수 수정

Before:

if (callable != Py_None) {
    free_callback_context(ctx);
}

After:

if (callable != Py_None) {
    decref_callback_context(ctx);
}

create_collation()SQLITE_BUSY(다른 statement가 활성 상태)로 실패하면 새로 생성한 ctx를 정리해야 합니다. 기존에는 free_callback_context()를 호출했는데, 이 함수는 참조 카운트를 무시하고 즉시 해제합니다. decref_callback_context()로 변경하여 참조 카운트 기반의 안전한 해제를 수행합니다.

왜 이게 좋은가

  • Early return 패턴: if (ctx != NULL) 로 성공 경로를 감싸는 대신, if (ctx == NULL) 로 실패를 먼저 처리하고 반환합니다. 코드의 가독성이 향상되고 에러 경로가 명확해집니다.
  • 올바른 예외 전파: C 확장 모듈에서 NULL 반환 시 반드시 Python 예외를 설정해야 합니다. 이를 어기면 디버깅이 어려운 SystemError가 발생합니다.
  • 참조 카운트 일관성: free vs decref의 차이는 미묘하지만, 동시 접근 시나리오에서 crash를 유발할 수 있습니다.

정리

이 PR은 sqlite3 모듈의 메모리 관리에서 두 가지 edge case를 수정합니다. 메모리 할당 실패 시 올바른 예외를 발생시키고, SQLITE_BUSY 에러 경로에서 참조 카운트를 안전하게 감소시킵니다. C 확장 모듈 개발 시 에러 경로의 메모리 관리가 얼마나 중요한지 잘 보여주는 사례입니다.

참고 자료


이 포스트는 AI가 작성하였으며, 사실과 다를 수 있습니다. 정확한 정보는 원본 PR을 참고해 주세요.

댓글

관련 포스트

PR Analysis 의 다른글