[CPython] OrderedDict.popitem()의 메모리 누수 수정
PR 링크: python/cpython#146538 (3.13 backport), #146537 (3.14 backport) 상태: Merged | 변경: +3 / -1
들어가며
CPython의 C 확장 코드에서 참조 카운트(reference counting) 관리는 메모리 안전성의 핵심입니다. 이 PR은 OrderedDict.popitem()에서 에러 발생 시 key 객체의 참조를 해제하지 않아 메모리가 누수되던 버그를 수정합니다.
핵심 코드 분석
Before:
// Objects/odictobject.c
node = last ? _odict_LAST(self) : _odict_FIRST(self);
key = Py_NewRef(_odictnode_KEY(node));
value = _odict_popkey_hash((PyObject *)self, key, NULL, _odictnode_HASH(node));
if (value == NULL)
return NULL;
item = PyTuple_Pack(2, key, value);
Py_DECREF(key);
Py_DECREF(value);
After:
node = last ? _odict_LAST(self) : _odict_FIRST(self);
key = Py_NewRef(_odictnode_KEY(node));
value = _odict_popkey_hash((PyObject *)self, key, NULL, _odictnode_HASH(node));
if (value == NULL) {
Py_DECREF(key);
return NULL;
}
item = PyTuple_Pack(2, key, value);
Py_DECREF(key);
Py_DECREF(value);
문제의 핵심은 Py_NewRef로 증가시킨 key의 참조 카운트입니다. 정상 경로에서는 함수 끝의 Py_DECREF(key)로 해제되지만, _odict_popkey_hash가 실패하여 value == NULL인 경우 return NULL로 즉시 반환하면서 Py_DECREF(key)를 건너뛰게 됩니다.
수정은 단순합니다: 에러 경로에서도 Py_DECREF(key)를 호출합니다.
왜 이게 좋은가
- 3줄로 메모리 누수 해결: 중괄호 추가와
Py_DECREF(key)한 줄이 전부입니다. 에러 경로의 리소스 정리는 항상 누락되기 쉬운 부분입니다. - 패턴 인식:
Py_NewRef→ 중간 연산 →Py_DECREF패턴에서 중간 연산이 실패하면DECREF에 도달하지 못합니다. C 확장 개발의 전형적인 함정입니다. - 동일 수정 2개 브랜치: 3.13과 3.14 모두에 backport되어 모든 지원 버전에서 수정됩니다.
정리
CPython의 참조 카운트 기반 메모리 관리에서 에러 경로의 Py_DECREF 누락은 가장 흔한 메모리 누수 패턴입니다. 이 PR은 OrderedDict.popitem()의 에러 경로에서 key 참조를 올바르게 해제하여 누수를 방지합니다.
참고 자료
이 포스트는 AI가 작성하였으며, 사실과 다를 수 있습니다. 정확한 정보는 원본 PR을 참고해 주세요.
관련 포스트
PR Analysis 의 다른글
- 이전글 [triton] GSan AxisInfo 기반 Shadow Update 중복 제거로 2~10배 성능 향상
- 현재글 : [CPython] OrderedDict.popitem()의 메모리 누수 수정
- 다음글 [CPython 3.14] OrderedDict.popitem() 메모리 누수 수정 (backport)
댓글