Skip to content

lib/vector: Return WKB buffers allocated by G_malloc - #748

Open
echoix wants to merge 4 commits into
mainfrom
vector-wkb-free
Open

echoix wants to merge 4 commits into
mainfrom
vector-wkb-free

Conversation

@echoix

@echoix echoix commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Description

Vect_read_area_to_wkb(), Vect_read_line_to_wkb() and Vect_line_to_wkb() returned the buffer allocated by GEOS. It has to be released with GEOSFree(), but the Python code in pygrass released it with G_free(). That works when all libraries share one C runtime, as on Linux. When GEOS and GRASS use different runtimes, as on Windows, it corrupts the heap. On Windows, test_pygrass_vector_doctests.py stops without a traceback after seven tests, in areas_to_wkb_list(), and has been failing on main.

The WKT functions in the same file already copy the GEOS result into memory from G_store() and free the original, so this makes the WKB functions consistent with them.

  • Copy the buffer into memory from G_malloc() and release the GEOS one with GEOSFree(). G_free() is then correct for the caller. The return value is documented as such. No C code calls these functions.
  • Line.to_wkb() and Area.to_wkb() in pygrass never freed the buffer. Free it after copying the bytes.

Motivation and context

Heap corruption on Windows, and a memory leak on all platforms.

How has this been tested?

New pytest tests in python/grass/pygrass/vector/tests/. Each check runs in a subprocess in a GRASS session, so a crash fails one test:

  • Line.to_wkb() and Area.to_wkb() release the buffer with G_free().
  • The functions returning lists release each buffer with G_free().
  • Calling the functions 20000 times does not leave memory allocated (glibc mallinfo2, skipped elsewhere).
  • All functions return WKB of the expected size when called repeatedly. This is the case which crashes on Windows when a buffer is released with the wrong allocator.

On Linux, without the change the first, second and memory checks fail (no G_free() call, 6.1 MB left allocated after 20000 calls), and with the change all pass. The Windows crash cannot be reproduced on Linux, so the Windows job of this PR is the check for it: the last test and test_pygrass_vector_doctests.py should pass there.

Also on Linux: the pygrass vector doctests and the 7 test files in python/grass/pygrass/vector pass with grass.gunittest.main; MALLOC_CHECK_=3 reports no heap errors. clang-format, ruff check and ruff format pass.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing
    functionality to not work as before)

Checklist

  • PR title provides summary of the changes and starts with one of the
    pre-defined prefixes
  • My code follows the code style
    of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.

The changes were developed with the help of an AI assistant (Claude).

Vect_read_area_to_wkb(), Vect_read_line_to_wkb() and Vect_line_to_wkb()
returned the buffer allocated by GEOS. It has to be released with
GEOSFree(), but the Python code in pygrass released it with G_free().
That works when all libraries share one C runtime, as on Linux. When
GEOS and GRASS use different runtimes, as on Windows, it corrupts the
heap and the process crashes (test_pygrass_vector_doctests.py stopped
at areas_to_wkb_list()). The WKT functions in the same file already
copy the GEOS result into memory from G_store() and free the original.

Copy the buffer into memory from G_malloc() and release the GEOS one with
GEOSFree(), so G_free() is correct for the caller. Document this.

Release the buffer in Line.to_wkb() and Area.to_wkb() in pygrass, which
never freed it. Calling Line.to_wkb() 60000 times grew the memory by 4.7
MB before and by nothing after the change.

The changes were developed with the help of an AI assistant (Claude).
Add pytest tests for the WKB functions of pygrass vector. The checks run
in a subprocess in a GRASS session, as the C libraries can be initialized
only once per process, and a crash then fails one test.

- Line.to_wkb() and Area.to_wkb() release the buffer with G_free().
- The functions returning lists release each buffer with G_free().
- Many calls do not leave memory allocated (glibc only).
- All functions return WKB of the expected size when called repeatedly.
  This is the case which crashes on Windows when a buffer is released
  with the wrong allocator.

Without the change to the vector library and pygrass, the checks for the
buffers of Line.to_wkb() and Area.to_wkb() and for the memory fail.

The tests were developed with the help of an AI assistant (Claude).
The preview rule PLR6201 of ruff, which the Python code quality check
applies, asks for a set literal.

The change was developed with the help of an AI assistant (Claude).
Comment thread python/grass/pygrass/vector/tests/grass_pygrass_vector_wkb_checks.py Outdated
Comment thread python/grass/pygrass/vector/tests/grass_pygrass_vector_wkb_checks.py Outdated
ctypes.CDLL(None) raises a TypeError on Windows, so the check failed
instead of being skipped. Test for Linux first.

The change was developed with the help of an AI assistant (Claude).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants