pcloudcc-lneely/tests/smoke-tests/smoke-test-large-read.sh

108 lines
3.8 KiB
Bash
Executable File

#!/bin/bash
# Smoke test: large file read through FUSE mount (pcl-26j)
#
# Regression test for bad-free heap corruption in psync_pagecache_free_request.
# A large read forces multiple psync_request_range_t allocations — one per
# non-contiguous page range — which are then freed via free_request_range().
# The old code called bare free() on pmem_malloc-allocated nodes; this build
# uses AddressSanitizer to catch any such mismatched free.
#
# Usage:
# PCLOUD_USER=you@example.com bash tests/smoke-tests/smoke-test-large-read.sh
#
# Requirements:
# - PCLOUD_USER env var set (credentials prompted at daemon start)
# - A file >= 50 MB present in the pCloud root (REMOTE_FILE below)
# - FUSE available on the host
set -euo pipefail
MOUNT="${HOME}/pCloudDrive"
ASAN_LOG="/tmp/pcloudcc_asan_large_read_$$.log"
REMOTE_FILE="${SMOKE_REMOTE_FILE:-}" # override via env if needed
MIN_SIZE_MB=50
# ---- helpers ---------------------------------------------------------------
die() { echo "ERROR: $*" >&2; exit 1; }
cleanup() {
echo "--- stopping daemon ---"
echo "finalize" | ./pcloudcc -k 2>/dev/null || true
sleep 1
# Kill any stray daemon
pkill -f "pcloudcc -d" 2>/dev/null || true
echo "--- ASAN log ($ASAN_LOG) ---"
cat "$ASAN_LOG" 2>/dev/null || echo "(empty)"
}
trap cleanup EXIT
# ---- sanity checks ---------------------------------------------------------
[[ -n "${PCLOUD_USER:-}" ]] || die "PCLOUD_USER not set"
[[ -x ./pcloudcc ]] || die "pcloudcc binary not found — run make first"
# Verify the binary was built with ASAN
if ! readelf -d ./pcloudcc 2>/dev/null | grep -q "libasan\|asan"; then
echo "WARNING: pcloudcc does not appear to be an ASAN build."
echo " For a meaningful test, rebuild with:"
echo " make clean && CFLAGS='-fsanitize=address -g -O1' \\"
echo " CXXFLAGS='-fsanitize=address -g -O1' \\"
echo " LDFLAGS='-fsanitize=address' make -j\$(nproc)"
echo " Continuing anyway — crash-level corruption may still surface."
fi
# ---- build env -------------------------------------------------------------
export ASAN_OPTIONS="log_path=${ASAN_LOG}:abort_on_error=0:detect_leaks=0"
# ---- start daemon ----------------------------------------------------------
echo "--- starting pcloudcc daemon ---"
./pcloudcc -u "$PCLOUD_USER" -d
sleep 3 # wait for mount and initial sync
[[ -d "$MOUNT" ]] || die "mount point $MOUNT does not exist after daemon start"
# ---- find a large remote file ----------------------------------------------
if [[ -z "$REMOTE_FILE" ]]; then
echo "--- searching for a file >= ${MIN_SIZE_MB} MB in ${MOUNT} ---"
REMOTE_FILE=$(find "$MOUNT" -maxdepth 3 -type f \
-size "+${MIN_SIZE_MB}M" -print -quit 2>/dev/null || true)
fi
if [[ -z "$REMOTE_FILE" ]]; then
echo "SKIP: no file >= ${MIN_SIZE_MB} MB found in ${MOUNT}."
echo " Upload a large file first, or set SMOKE_REMOTE_FILE=/path/to/file."
exit 0
fi
echo "--- reading: $REMOTE_FILE ---"
SIZE=$(stat -c%s "$REMOTE_FILE" 2>/dev/null || echo 0)
echo " size: $((SIZE / 1024 / 1024)) MB"
# ---- read the file, exercising the page-cache request path -----------------
echo "--- streaming file to /dev/null ---"
dd if="$REMOTE_FILE" of=/dev/null bs=1M 2>&1 | tail -1
echo "--- read complete ---"
# Give ASAN a moment to flush any deferred reports
sleep 1
# ---- evaluate ASAN output --------------------------------------------------
ASAN_ERRORS=$(grep -c "ERROR: AddressSanitizer" "$ASAN_LOG" 2>/dev/null || true)
BAD_FREE_ERRORS=$(grep -c "attempting free on address" "$ASAN_LOG" 2>/dev/null || true)
echo ""
if [[ "$ASAN_ERRORS" -eq 0 ]]; then
echo "PASS: no AddressSanitizer errors detected during large file read"
exit 0
else
echo "FAIL: $ASAN_ERRORS ASAN error(s) detected ($BAD_FREE_ERRORS bad-free)"
echo " See $ASAN_LOG for details"
exit 1
fi