Skip to content
This repository was archived by the owner on Oct 30, 2026. It is now read-only.

Runtime: reference counts from callback reads and __cast_instance__; buffers stay exported for the LVGL call - #35

Merged
bdbarnett merged 1 commit into
mainfrom
ostinato/lvgl-runtime-fixes
Oct 15, 2026
Merged

bdbarnett merged 1 commit into
mainfrom
ostinato/lvgl-runtime-fixes

Conversation

@bdbarnett

Copy link
Copy Markdown
Contributor

Three reference bugs in src/lvpy_runtime.c, on every Python build.

Reading a callback back from a struct (drv.open_cb, font.get_glyph_dsc, any callback field) returned the callback dict's borrowed reference as if it were new. Each read dropped the callback's reference count by one, so a few reads freed a callback that LVGL still calls. mp_lv_funcptr now returns a reference of its own, and None when the dict has no entry (it returned NULL with no exception set).

A buffer passed where LVGL takes a void * (obj.send_event(code, buf), a user_data, a dst) was released before LVGL used the pointer. Python code run during the call, such as an event callback, could then resize a bytearray and leave LVGL reading freed memory. The buffer now stays exported until the outermost LVGL call returns, so resizing it during the call raises BufferError and works again afterwards. A non-contiguous buffer is refused with BufferError rather than passed through.

__cast_instance__ returned self without a reference, and a struct that owned its memory freed it twice after being cast onto another struct's. It now lets go of its own memory and returns a new reference.

tests/test_runtime_references.py checks all three in a subprocess under MALLOC_PERTURB_. On the code before this change it fails three ways: the callback's count drops by 4 over 4 reads, the bytearray resizes mid-call, and __cast_instance__ aborts with a double free. With the fix reverted by hand on this branch (a planted fault, since undone), the same three tests fail; with the fix, the full suite passes (28 tests).

…ffers stay exported for the LVGL call

- Reading a callback back from a struct field (drv.open_cb) returned the
  callback dict's borrowed reference as a new one, so every read dropped
  the callback's reference count by one until it was freed.
- A buffer passed as a void * (obj.send_event(code, buf)) was released
  before LVGL used the pointer, so Python code run during the call could
  resize it under LVGL. The export now lasts until the outermost LVGL call
  returns.
- __cast_instance__ returned self without a reference, and a struct that
  owned its memory freed it twice once cast onto another struct's.
@bdbarnett
bdbarnett merged commit 40179d9 into main Oct 15, 2026
1 check passed
@bdbarnett
bdbarnett deleted the ostinato/lvgl-runtime-fixes branch October 15, 2026 20:33
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant