[CPython] sqlite3 콜백 컨텍스트의 메모리 관리 버그 수정
PR 링크: python/cpython#146569 상태: Merged | 변경: +30 / -7
들어가며
CPython의 sqlite3 모듈은 Python 콜백을 C 레벨에서 관리하기 위해 callback_context 구조체를 사용합니다. 이 PR은 두 가지 메모리 관리 결함을 수정합니다.
create_callback_context()에서PyMem_Malloc실패 시MemoryError대신SystemError가 발생하는 문제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가 발생합니다. - 참조 카운트 일관성:
freevsdecref의 차이는 미묘하지만, 동시 접근 시나리오에서 crash를 유발할 수 있습니다.
정리
이 PR은 sqlite3 모듈의 메모리 관리에서 두 가지 edge case를 수정합니다. 메모리 할당 실패 시 올바른 예외를 발생시키고, SQLITE_BUSY 에러 경로에서 참조 카운트를 안전하게 감소시킵니다. C 확장 모듈 개발 시 에러 경로의 메모리 관리가 얼마나 중요한지 잘 보여주는 사례입니다.
참고 자료
이 포스트는 AI가 작성하였으며, 사실과 다를 수 있습니다. 정확한 정보는 원본 PR을 참고해 주세요.
관련 포스트
PR Analysis 의 다른글
- 이전글 [SGLang] GDN의 kkt + solve_tril을 하나의 Triton 커널로 퓨전
- 현재글 : [CPython] sqlite3 콜백 컨텍스트의 메모리 관리 버그 수정
- 다음글 [sglang] GB300 Nightly 벤치마크 테스트 스위트 추가
댓글