pm3_grabbed_output_get null-terminated the captured output at
g_grabbed_output.size, but size is the allocated capacity, not the number of
bytes written -- ui.c grows it by MAX_PRINT_BUFFER at a time and tracks the
used length separately in idx. Every call therefore wrote one zero byte past
the end of the heap allocation.
The CLI mostly survives it because it calls the function once or twice and
then exits, so the corrupted malloc metadata is never reused. A long-running
libpm3 client that captures output on every command does not: the damage
surfaces later, far from the cause. It was found as a SIGTRAP inside an
unrelated AppKit autorelease pool.
Reproduced against real hardware:
MALLOC_STRICT_SIZE=1 MallocGuardEdges=1 \
DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib ./pm3run <port> "hw version"
which crashes with SIGSEGV inside pm3_grabbed_output_get before this change
and exits cleanly with identical captured output after it.
Terminating at idx is always in bounds: the grabber guarantees at least
MAX_PRINT_BUFFER bytes of headroom before each write, so idx is strictly less
than size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjGgtnsvWJHAMYEksAiYuf
Adds an opt-in `--rgb` flag to the continuous `hf tune` / `lf tune` commands that
mirrors the antenna tuning level on the PM5 antenna RGB LED: blue = low, green =
mid, red = high, tracking the on-screen bar so you can find coupling (e.g. an
implant) by feel without watching the screen.
The colour is computed client-side from the same per-iteration voltage / running
peak the bar uses (so it matches the bar and auto-scales), and pushed to the
device via a new dumb CMD_PM5_RGB_SET {r,g,b}. That command is handled (#ifdef
PM5) by a dedicated AT32 RGB HAL module, common_arm/rgb/{rgb_apis.h,
rgb_hw_at32.c} (RgbLedSet(), I2C controller @ 0x48), wired into the armsrc
Makefile/CMake as SRC_RGB for PM5 only - so no other platform is affected and no
hardware code lands in shared files.
PM5's FPGA (Gowin GW1NR) bitstream is loaded at runtime via `hw fpga config`
and is not compiled into the firmware; only the Xilinx fpga_pm3_hf.bit is pulled
in (to generate g_fpga_version_information[]) which PM5 never runs. So the
`[ FPGA ] fpga_pm3_hf.ncd image 2s30vq100` line described a bitstream unrelated
to the actual device.
- Firmware (appmain.c): omit the [FPGA] section from the version string on PM5
(#ifndef PM5), since there's no meaningful built-in FPGA version to report.
- Client (cmdhw.c): skip the FPGA_TYPE "chip mismatch" check for PM5, which would
otherwise trigger now that the section is gone.
The [Model] section only distinguished RDV4 firmware from a "PM3 GENERIC"
else branch, so PM5 (AT32) fell through and printed "Firmware... PM3 GENERIC"
even though the banner/Target/MCU already report PM5/AT32F437. Add an IfPm5()
branch that prints "Firmware... PM5" and the external-flash status.
Note: the "[ FPGA ] ... 2s30vq100" line in hw version is separate and comes
from the firmware-generated version string (fpga_version_info.c). On PM5 the
build reuses the Xilinx fpga_pm3_hf.bit only to generate that version info
(see Makefile.hal PM5 TODO), while the real Gowin FPGA is loaded via JTAG, so
the reported bitstream name is a placeholder. Fixing that is a firmware/build
change, not addressed here.
`hw version` and `hw status` decoded the reported chip id as an Atmel AT91
CIDR. On PM5 (AT32) the id is an ARM DBGMCU IDCODE, so it showed
"MCU Unknown", a bogus flash size (32 KB / 973% used) and "PM3 GENERIC".
- Client: when IfPm5(), print MCU "AT32F437" and the real flash size, and add
a "PM5" target line, instead of running the AT91 decode.
- Firmware: the flash size can't be derived from the AT32 IDCODE, so the device
now sends GetChipFlashSize(). It is appended AFTER the version string in the
CMD_VERSION reply, so the wire layout is unchanged and it stays compatible in
both directions (old client ignores the trailing bytes; new client length-
guards and treats a missing value as 0). GetChipFlashSize() already exists for
both AT91 and AT32.