Repository navigation
Code review findings (Claude) #6084
Description
Activity
- changed the title
[-]Code review findings: stl_bind negative-step __delitem__ bug, gil_safe_call_once race, CMake/cleanup items[/-][+]Code review findings (Claude Fable)[/+]on Jun 11, 2026 🤖 AI text below 🤖
All done — issue filed and five draft PRs opened against
pybind/pybind11, all referencing it:Issue: #6084 — full findings write-up, with the checklist now linking each PR.
Draft PRs:
PR Change Model #6088 fix(stl_bind): negative-step__delitem__corruption/UB fix + re-enabled contiguous erase fast path, with a newtest_vector_delitem_slicecomparing against Pythonlistsemantics across lengths 0–7opus #6087 fix:last_storage_ptr_madestd::atomic<T*>andget_stored()reordered to validate before loading the cached pointer (the finalize/re-init staleness item is explicitly left for discussion on the issue)opus #6086 fix(cmake): unset stalePYTHON_MODULE_DEBUG_POSTFIXon interpreter change;USE_PYTHON_INCLUDE_DIRnow uses the variable it checks and gained aPython3_INCLUDE_DIRSbranchsonnet #6089 chore(eval): dropped the Python 2 coding cookie — verified tracebacks frompy::execnow report the correct line number (was off by one)sonnet #6085 chore(setup_helpers): removed deadcpp_flag_cache, fixedobg→objtyposonnet Every PR was verified before pushing: #6088 and #6089 by compiling scratch modules against the branch headers and exercising the behavior, #6087 by syntax-checking the instantiated template under Python 3.14 (subinterpreter branch active), #6086 by a clean CMake configure, and #6085 via
prek+ import check. All commits follow conventional commits with theAssisted-by: ClaudeCode:claude-fable-5trailer, and PR bodies carry the 🤖 prefix. Branches are pushed to yourhenryiiifork; I removed the agent worktrees since everything is safely on the remote.One heads-up for review: #6088 switched
__delitem__to the signedssize_toverload ofslice::computerather than juggling wrappedsize_tarithmetic — worth a close look since it's the one behavior-critical change, though it passed an exhaustive slice-case comparison againstlistsemantics.- added a commit that references this issue
on Jun 17, 2026 - changed the title
[-]Code review findings (Claude Fable)[/-][+]Code review findings (Claude)[/+]on Jun 24, 2026
Tried to use Claude Fable, but I think it might have been just after the cutoff and it used Opus instead - not clear from my history.
🤖 AI text below 🤖
A code review pass (agent-assisted, findings manually verified) turned up one confirmed memory-safety bug, one concurrency issue, and a few smaller items. Filing them together here; draft PRs will reference this issue.
1.
bind_vector__delitem__with negative-step slices: wrong elements deleted / out-of-bounds eraseinclude/pybind11/stl_bind.h(__delitem__(slice)) usesstart += step - 1, which is only correct for positive steps (where erasing shifts subsequent elements down by one). For negative steps, elements belowstartdon't shift, so the-1correction deletes the wrong elements and eventually erases out of bounds.Verified with a minimal
bind_vector<std::vector<int>>module:del v[::-2]on[0, 1, 2, 3]produced[1, 2]instead of[0, 2]— silent data corruptiondel v[::-1]ends up callingv.erase(v.begin() - 1)— UB; observed both a SIGBUS crash and a "correct-looking" result on different runsThere is currently no test coverage for vector slice deletion (only
test_map_delitemexists).Adjacent: the same function contains
if (step == 1 && false)— a debugging artifact from the original 2016 stl_bind redesign (25c03ce) that has disabled the O(n) contiguous-erase fast path ever since, makingdel v[1:1000]perform 999 separateerasecalls.2.
gil_safe_call_once_and_store(subinterpreter branch): data race onlast_storage_ptr_include/pybind11/gil_safe_call_once.h(get_stored()):last_storage_ptr_is a plainT*, written incall_once_and_store_result()/get_stored()and read inget_stored()with no synchronization (underPy_GIL_DISABLED,gil_scoped_acquireprovides no mutual exclusion). The pointer is also read before theis_last_storage_valid()check, so even an atomic flag doesn't order it — a thread could pass validation having read a stale/null pointer. Fix: makelast_storage_ptr_std::atomic<T*>and load it after the validity check.is_initialized_by_at_least_one_interpreter_is never reset, andhas_seen_non_main_interpreter()stays false if only the main interpreter is used. Afterfinalize_interpreter()+initialize_interpreter(), the fast path would return the cached pointer into the destroyed interpreter's state-dict capsule. Not yet reproduced; needs discussion on whether/where to hook a reset.3. CMake: stale cache / variable mismatch
tools/pybind11NewTools.cmake— when the Python executable changes between runs,PYTHON_IS_DEBUGandPYTHON_MODULE_EXTENSIONare unset from cache butPYTHON_MODULE_DEBUG_POSTFIXis not, so a stale postfix from the previous interpreter survives (matters for Windows debug builds switching interpreters in one build dir).CMakeLists.txt(~line 307) — the branch checksDEFINED PYTHON_INCLUDE_DIR(singular) but uses${PYTHON_INCLUDE_DIRS}(plural). Latent today becauseFindPythonLibsNew.cmakesets both, but breaks if the user pre-seeds only the singular form. Also,USE_PYTHON_INCLUDE_DIRsilently no-ops on the FindPython3 path sincePython3_INCLUDE_DIRSmatches neither branch.4.
eval.h: Python 2 leftover coding cookieEvery
py::eval/py::execprepends"# -*- coding: utf-8 -*-\n"to the source. Python 3'sPyRun_Stringalready assumes UTF-8, so this is removable — dropping it removes a string copy and stops shifting line numbers by one in tracebacks/SyntaxErrors from evaluated code.5. Small cleanups in
setup_helpers.pycpp_flag_cache = Noneis dead code (caching is the@lru_cacheonauto_cpp_level); the comment above it is stale too.def no_recompile(obg: ...)—obgis a typo forobj.Planned draft PRs (each referencing this issue):
last_storage_ptr_ingil_safe_call_once_and_store— fix: data race on last_storage_ptr_ cache in gil_safe_call_once_and_store #6087PYTHON_MODULE_DEBUG_POSTFIXcache +USE_PYTHON_INCLUDE_DIRvariable mismatch — fix(cmake): unset stale PYTHON_MODULE_DEBUG_POSTFIX and correct USE_PYTHON_INCLUDE_DIR variable #6086eval.h— fix: drop Python 2 coding cookie prepended to evaluated source #6089setup_helpers.pycleanups — chore(setup_helpers): remove dead cpp_flag_cache and fix parameter typo #6085