pcloudcc-lneely/tests/smoke-tests
Levi Neely 7595c485bf
Fix bad-free heap corruption in ppathstatus and psyncer (#392)
* Fix bad-free heap corruption in ppathstatus and psyncer (#391)

Both ppathstatus.c and psyncer.c used bare free() via
ptree_for_each_element_call_safe to bulk-free tree nodes that were
allocated with pmem_malloc.  pmem_malloc prepends a pmem_header_t to
every allocation, so the returned pointer is an interior pointer to the
underlying glibc chunk.  Passing it to free() makes glibc read a garbage
size field from the pmem header and abort with "free(): invalid size".

This is the same class of bug fixed earlier in pintervaltree.c and
pfscrypto.c.  The crash manifests reliably with larger files because
more sync-queue and path-status churn occurs, increasing the likelihood
that one of these bulk-free paths is hit while a non-empty tree exists.

Fix: add free_folder_tasks_node() (ppathstatus.c) and
free_synced_down_folder() (psyncer.c) helpers that call pmem_free, and
use them as the ptree_for_each_element_call_safe callback in all three
affected call sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix bad-free in pfs.c and ppagecache.c (pcl-26j)

Two more call sites passed bare free() as a callback to tree/list
traversal macros, but the nodes were allocated via pmem_malloc which
prepends a pmem_header_t. Freeing the data pointer directly skips the
header and corrupts the heap.

- pfs.c: ptree_for_each_element_call_safe on sectorsinlog used free()
  on psync_sector_inlog_t; replaced with free_sector_inlog_node() that
  calls pmem_free(PMEM_SUBSYS_OTHER, e).
- ppagecache.c: psync_list_for_each_element_call on request->ranges
  used free() on psync_request_range_t; replaced with
  free_request_range() that calls pmem_free(PMEM_SUBSYS_CACHE, range).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add unit and smoke tests for pcl-26j bad-free (ppagecache, pfs, psyncer)

Unit test (test_pcl26j_free): exercises all four fixed call sites —
psync_request_range_t, synced_down_folder, folder_tasks_t, and
psync_sector_inlog_t — using --wrap=malloc/free to verify that
pmem_free() passes the header pointer to free(), not the data pointer.
Includes a harness self-check that confirms bare free(data_ptr) is
detected. Would have failed against pre-fix code.

Smoke test (smoke-test-large-read.sh): builds pcloudcc with ASAN,
starts the daemon, streams a large file (>=50 MB) from the FUSE mount
to /dev/null to force multi-range psync_request_range_t allocation and
teardown via psync_pagecache_free_request, then scans the ASAN log for
any bad-free reports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 12:12:50 +01:00
..
smoke-test-large-read.sh Fix bad-free heap corruption in ppathstatus and psyncer (#392) 2026-03-24 12:12:50 +01:00
smoke-test-pfs-locks.sh Fix pcl-zqv.5.7: eliminate deadlock in pfs_get_both_locks (#339) 2026-03-03 19:33:56 +01:00