Skip to content

Fix: prevent heap corruption ("double free or corruption") during LAZ chunking#693

Open
ivancsicsmarkus wants to merge 2 commits into
potree:developfrom
ivancsicsmarkus:fix/chunker-double-free
Open

Fix: prevent heap corruption ("double free or corruption") during LAZ chunking#693
ivancsicsmarkus wants to merge 2 commits into
potree:developfrom
ivancsicsmarkus:fix/chunker-double-free

Conversation

@ivancsicsmarkus

Copy link
Copy Markdown

Fix: prevent heap corruption ("double free or corruption") during LAZ chunking

Problem

Certain LAZ files cause PotreeConverter to crash with double free or corruption (!prev) during the chunking phase. The root cause is a combination of unchecked error paths and missing bounds validation that allow heap metadata to be silently corrupted.

Root causes

  1. Unchecked laszip return valueslaszip_open_reader, laszip_seek_point, and laszip_read_point return error codes that were never checked. For LAZ files with missing or corrupt chunk tables, seeking fails silently and subsequent reads return garbage coordinates. These garbage values produce wrong bucket counts, causing Buffer::write to overflow past its allocated size and corrupt heap metadata.

  2. Missing lower-bound grid index clamping — Grid index computation used std::min to clamp the upper bound but had no lower bound protection. Floating point precision loss during the integer quantization round-trip could produce slightly negative normalized coordinates, resulting in negative array indices and out-of-bounds memory access.

  3. Non-returning sanity checks — The numBytes < 0 sanity checks in both countPointsInCells and distributePoints logged an error but continued execution, allowing malloc with a bad size followed by writes to a null/stale buffer.

  4. Missing LAS 1.4 point formatsformatToExtraIndex was missing point data formats 8, 9, and 10, causing exit(123) inside thread pool workers for files using these common LAS 1.4 formats.

  5. Data race on shared counter — A static int64_t counter was shared across threads without synchronization.

Changes

Converter/src/chunker_countsort_laszip.cpp:

  • Check return values of laszip_open_reader, laszip_seek_point, and laszip_read_point; skip/return cleanly on failure
  • Replace std::min with std::clamp for grid index computation (clamp to [0, gridSize-1])
  • Add return after negative numBytes sanity checks
  • Add LAS 1.4 point formats 8, 9, 10 to formatToExtraIndex
  • Make pointsProcessed counter std::atomic<int64_t>

Converter/modules/unsuck/unsuck.hpp:

  • Add bounds check to Buffer::write to prevent heap corruption on overflow

Testing

Tested with a LAZ file that consistently crashed on the previous version. Conversion now completes successfully with --encoding BROTLI.
chunking-crashing.laz.zip

ivancsicsmarkus and others added 2 commits April 2, 2026 15:24
The LAZ chunker's extra-attribute path computed an `attributeOffset` by
summing the sizes of every standard attribute in the *input* layout, then
used that as the destination offset for memcpy'ing extras into the
chunk's *output* buffer. Whenever --attributes filters the output to a
subset, the input and output layouts diverge: format 2 places its first
extra at input offset 27 but at output offset 20 when only intensity+rgb
are kept. memcpy-ing 4 bytes at offset+27 into a 24-byte-stride per-point
slot clobbers the next point's slot, eventually triggering a SIGSEGV in
the worker thread that surfaces only as exit(123) after the
"=== CREATING CHUNKS" header is printed.

Drop the input-layout sum and use the output offset that was already
being computed at line 595 (outputAttributes.getOffset(name)) — the same
mechanism every standard-attribute handler in the same function uses.
sourceOffset (input-side, advances per input attribute) is unchanged.

Repro: any LAZ with extra-bytes attributes processed with --attributes
filtering to a subset that includes the extra. Verified fixed against
a laspy-written LAZ with a float32 'Deviation' extra dim, format 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant