Safety Configuration
mcjtag is designed for use alongside safety-critical firmware — avionics, medical devices, automotive. It ships with conservative defaults that prevent accidental damage to your target. Every safety mechanism is configurable, because the right level of protection depends on what you are doing.
Layer 1: Safe write ranges
Section titled “Layer 1: Safe write ranges”By default, write_memory() only allows writes to SRAM (0x20000000-0x20100000).
Writes to any other address are rejected with an error before reaching the target.
This prevents:
- Accidental flash corruption — writing to 0x08000000+ would brick the device
- Unexpected peripheral behavior — writing to 0x40000000+ could reconfigure hardware (toggle GPIOs, change clock trees, disable interrupts)
- System register writes — writing to 0xE0000000+ could lock out the debugger or alter the MPU/NVIC configuration
Configure with the MCJTAG_SAFE_WRITE_RANGES environment variable:
# Default: SRAM onlyMCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20100000"
# Add CCM RAM (STM32F4 has core-coupled memory at 0x10000000)MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x10000000-0x10010000"
# Allow peripheral writes too (for register configuration)MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x40000000-0x50000000"
# Unrestricted — you know what you are doingMCJTAG_SAFE_WRITE_RANGES="none"The format is start-end pairs in hex, comma-separated. Each range is inclusive of
start and exclusive of end. The write must fall entirely within at least one range.
Setting the value to none (case-insensitive) disables the check entirely.
Layer 2: Raw command deny-list
Section titled “Layer 2: Raw command deny-list”raw_command() provides direct access to OpenOCD’s TCL interface, but blocks
commands that have dedicated, safer tools:
| Blocked pattern | Reason | Use instead |
|---|---|---|
flash write_image, flash erase, flash protect, flash fill | Flash modification | flash_program() — validates files before writing |
mww, mwh, mwb, write_memory | Memory writes | write_memory() — checks safe write ranges |
reset | Target reset | target_control("reset_halt") or target_control("reset_run") |
shutdown, exit | Terminates OpenOCD | disconnect() — cleanly closes the session |
reg | Register writes | write_register() |
eval, subst, uplevel | TCL metaprogramming | Blocks patterns that could bypass the deny-list |
Override with:
MCJTAG_ALLOW_RAW=trueThis disables the entire deny-list. All OpenOCD TCL commands are passed through without filtering.
Important caveat: The deny-list is best-effort pattern matching against the command string. It is not a sandbox. TCL metaprogramming (string concatenation, variable expansion) could theoretically construct a blocked command at runtime. The deny-list is a safety net for accidental invocations, not a security boundary.
Layer 3: Flash programming validation
Section titled “Layer 3: Flash programming validation”flash_program() validates the firmware image before writing:
| Check | Behavior |
|---|---|
| File existence | Rejects if the path does not exist |
| File extension | Accepts .bin, .hex, .elf, .ihex, .srec, .s19 only |
| File size | Must be non-zero and under 16 MB |
| Empty file | Rejects zero-byte files |
Progress is reported through three phases — erase, write, and verify — so the LLM (and you) can track what is happening.
There is no environment variable to disable these checks. If you need to program
a file that does not meet these criteria, use raw_command() with
MCJTAG_ALLOW_RAW=true.
Layer 4: Input validation
Section titled “Layer 4: Input validation”All tool inputs are validated before reaching OpenOCD:
| Input | Validation |
|---|---|
| Hex addresses | Must parse as valid hex. Negative values rejected. Accepts with or without 0x prefix. |
| Alignment | Warns on unaligned reads (may fault on Cortex-M0). Rejects unaligned writes. |
| Value ranges | Values must fit in the specified width (8-bit max 0xFF, 16-bit max 0xFFFF, 32-bit max 0xFFFFFFFF). |
| Read count | Maximum 4096 elements per read_memory() call. |
| Search range | Maximum 1 MB per search_memory() call (configurable via MCJTAG_MAX_SEARCH_RANGE). |
| SVD files | Must have .svd extension and be under 50 MB. |
| Resume/step addresses | Must be 2-byte aligned (ARM Thumb requirement). |
Recommended configurations
Section titled “Recommended configurations”| Use case | Write ranges | Raw command | Notes |
|---|---|---|---|
| Learning / exploring | Default (SRAM) | Deny-list on | Safest. Cannot accidentally damage the target. |
| Firmware development | SRAM + your chip’s RAM | Deny-list on | Normal daily use. Expand the SRAM range to match your chip. |
| Peripheral bring-up | SRAM + peripheral region | Deny-list on | Allows writing to GPIO, UART, SPI registers. Be careful with clock and power config. |
| Automated testing | Chip-specific ranges | Deny-list on | Tailor ranges to exactly what your test suite needs. |
| Power user / automation | none | MCJTAG_ALLOW_RAW=true | Full access. No guardrails. You own the consequences. |
Environment variable reference
Section titled “Environment variable reference”| Variable | Default | Description |
|---|---|---|
MCJTAG_SAFE_WRITE_RANGES | 0x20000000-0x20100000 | Comma-separated hex ranges for write_memory(). Set to none for unrestricted. |
MCJTAG_ALLOW_RAW | false | Set to true, 1, or yes to disable the raw command deny-list. |
MCJTAG_MAX_SEARCH_RANGE | 1048576 (1 MB) | Maximum byte range for search_memory(). Integer. |
OPENOCD_CONFIG | (none) | OpenOCD config file path. If set, mcjtag auto-spawns OpenOCD on startup. |
OPENOCD_HOST | localhost | OpenOCD TCL RPC hostname (when not auto-spawning). |
OPENOCD_PORT | 6666 | OpenOCD TCL RPC port. |
OPENOCD_SVD | (none) | SVD file path. Auto-loaded on startup if a connection is established. |