| #!/bin/bash | |
| # Test that perf report handles truncated perf.data gracefully (no crash, no segfault — clean error exit). | |
| # SPDX-License-Identifier: GPL-2.0 | |
| # | |
| # Exercises the bounds checking and minimum-size validation added | |
| # by the perf-data-validation hardening series. | |
| err=0 | |
| cleanup() { | |
| [ -n "${perfdata}" ] && rm -f "${perfdata}" "${perfdata}.old" | |
| rm -f "${truncated}" "${stderrfile}" | |
| trap - EXIT TERM INT | |
| } | |
| trap 'cleanup; exit 1' TERM INT | |
| trap cleanup EXIT | |
| perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 | |
| truncated=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 | |
| stderrfile=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 | |
| # Record a simple workload | |
| if ! perf record -o "${perfdata}" -- perf test -w noploop 2>/dev/null; then | |
| echo "Skip: perf record failed" | |
| cleanup | |
| exit 2 | |
| fi | |
| file_size=$(wc -c < "${perfdata}") | |
| if [ "${file_size}" -lt 512 ]; then | |
| echo "Skip: perf.data too small (${file_size} bytes)" | |
| cleanup | |
| exit 2 | |
| fi | |
| # Test truncation at various offsets that exercise different | |
| # parsing stages: | |
| # 8 — file header magic only, no attrs or data | |
| # 64 — partial file header (attr section incomplete) | |
| # 256 — into the first events (partial event headers) | |
| # 75% — mid-stream truncation (partial event data) | |
| for cut_at in 8 64 256 $((file_size * 3 / 4)); do | |
| if [ "${cut_at}" -ge "${file_size}" ]; then | |
| continue | |
| fi | |
| dd if="${perfdata}" of="${truncated}" bs="${cut_at}" count=1 2>/dev/null | |
| # perf report should exit with an error, not crash. | |
| # Capture stderr to detect sanitizer violations. | |
| perf report -i "${truncated}" --stdio > /dev/null 2> "${stderrfile}" | |
| exit_code=$? | |
| # A truncated file should never parse successfully | |
| if [ ${exit_code} -eq 0 ]; then | |
| echo "FAIL: perf report exited 0 (success) on ${cut_at}-byte truncated file — expected an error" | |
| err=1 | |
| continue | |
| fi | |
| # Detect sanitizer violations — ASAN/MSAN/TSAN/UBSAN exit | |
| # with code 1 by default, which would otherwise look like a | |
| # clean error exit. Check stderr for their markers. | |
| if grep -qE "^(==[0-9]+==ERROR:|SUMMARY: [A-Za-z]*Sanitizer)" "${stderrfile}" 2>/dev/null; then | |
| sanitizer=$(grep -oE "(Address|Memory|Thread|UndefinedBehavior)Sanitizer" "${stderrfile}" | head -1) | |
| echo "FAIL: perf report triggered ${sanitizer:-sanitizer} on ${cut_at}-byte truncated file" | |
| err=1 | |
| continue | |
| fi | |
| # Detect crash signals portably — signal numbers differ | |
| # across architectures (e.g. SIGBUS is 7 on x86/ARM but | |
| # 10 on MIPS/SPARC). Use kill -l to map the number to a | |
| # name on the running system. | |
| if [ ${exit_code} -gt 128 ] && [ ${exit_code} -lt 200 ]; then | |
| sig_name=$(kill -l $((exit_code - 128)) 2>/dev/null) | |
| case ${sig_name} in | |
| KILL|ILL|ABRT|BUS|FPE|SEGV|TRAP|SYS) | |
| echo "FAIL: perf report crashed (SIG${sig_name}) on ${cut_at}-byte truncated file" | |
| err=1 | |
| ;; | |
| esac | |
| fi | |
| done | |
| cleanup | |
| exit ${err} |