Fix SIGSEGV in open wrapper on multibyte arguments
Problem
Inside a cmux terminal (CMUX_SOCKET_PATH set), open runs cmux's wrapper script
(Resources/bin/open) instead of /usr/bin/open. Passing an argument containing
multibyte characters — e.g. a Japanese filename like 日本語.pdf — crashes the
bash process executing the wrapper itself with SIGSEGV, rather than the script
simply failing.
Repro conditions (confirmed on real hardware)
- macOS 15.1 (24B83)
GNU bash, version 5.3.9(1)-release (aarch64-apple-darwin24.6.0)from MacPorts
(/opt/local/bin/bash; the script is#!/usr/bin/env bash, so PATH resolution
picks this build over Apple's/bin/bash3.2)- Locale:
LANG=ja_JP.UTF-8,LC_CTYPE=ja_JP.UTF-8,LC_ALLunset
open 日本語.pdf
reproducibly crashes with Segmentation fault: 11 (exit 139), 10/10 runs.
Crash report excerpt
~/Library/Logs/DiagnosticReports/bash-*.ips:
exception: EXC_BAD_ACCESS (SIGSEGV), KERN_INVALID_ADDRESS at 0x0
termination: SIGNAL / Segmentation fault: 11
remove_pattern
parameter_brace_remove_pattern (nested — called again below)
getpattern
expand_string_for_pat
parameter_brace_remove_pattern
parameter_brace_expand
param_expand
expand_word_internal
...
This nested parameter_brace_remove_pattern call (pattern-removal expanding its
own pattern operand) matches trim()'s implementation:
trim() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
to_lower_ascii() already guards against a related issue by forcing
LC_ALL=C before doing ASCII-only work:
to_lower_ascii() {
# Bash 3.2-compatible lowercase conversion.
LC_ALL=C printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
}
trim() had no such guard, and is called from several places
(normalize_boolean, is_html_extension, normalize_host,
normalize_whitelist_pattern, ...) with arguments that can contain arbitrary
multibyte content (file paths, URLs).
Root cause — broader than trim()
Isolating this on the affected bash build showed the bug is not confined to
trim()'s bracket-class pattern removal. Under the same locale, this bash build's
multibyte-aware glob/pattern matcher (remove_pattern for ${var%pattern} /
${var#pattern}, and xstrmatch for case statements) crashes on any attempted
match against a non-ASCII string — including:
- The wrapper's very first
case "$arg" in -a|-b|...|-*) ... *) ... esacarg-scanning
loop, independent oftrim(), even with plain literal (non-wildcard) alternation
patterns. normalize_host()/normalize_whitelist_pattern()'s${value#*://}-style glob
prefix stripping, when given a raw (non-punycode) Unicode hostname.
Bash's POSIX ERE engine ([[ $x =~ pattern ]], used by has_uri_scheme) does not
crash — this is specific to the glob/pattern-matching code path.
Fix
Rather than scattering local LC_ALL=C through every affected function, force
C-locale (byte-wise) matching once for the whole script, right before it starts
inspecting arguments:
export LC_ALL=C
Every pattern in this script only ever matches ASCII structure (URL schemes, flags,
extensions, whitespace, :// separators), so this never changes what any pattern
matches — it only avoids invoking the buggy multibyte matcher. This is the same
reasoning to_lower_ascii() already applies locally, generalized to the functions
that need it (trim, the arg-scanning case, normalize_host,
normalize_whitelist_pattern) without touching unrelated code.
Testing
- Added a regression test to
tests/test_open_wrapper.pythat passes a Japanese
filename (日本語.pdf) through the wrapper underja_JP.UTF-8.- Runs against
/bin/bashas a baseline (does not reproduce there — Apple's
bundled bash 3.2 doesn't hit this bug). - Runs against any alternate bash build found on the machine (e.g.
/opt/local/bin/bash); on this development machine (MacPorts bash 5.3.9,
matching the reported build) it reproduces the crash before the fix and passes
after. If no such build is present, the test documents this and does not fail —
the crash is bash-build/version-dependent, so CI machines without an affected
bash won't exercise this path.
- Runs against
- Verified locally: before the fix,
open 日本語.pdfcrashes 10/10 runs
(Segmentation fault: 11) on/opt/local/bin/bash5.3.9; after the fix, 10/10
runs succeed and correctly pass the filename through to systemopen. - Existing
tests/test_open_wrapper.pysuite (whitelist matching, HTML routing,
IDN/punycode host matching, etc.) still passes unchanged.
Two commits: the first adds the regression test only (red on an affected bash
build), the second adds the fix (green).
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Fixes a SIGSEGV crash in cmux's open wrapper when passed multibyte arguments (e.g. open 日本語.pdf) under a UTF-8 locale on some bash builds (observed with MacPorts bash 5.3.9 on macOS 15).
- Forces
export LC_ALL=Cbefore argument processing to sidestep the buggy multibyte glob matcher; all patterns match ASCII structure, so matches are unchanged. - Captures the caller's original
LC_ALL(distinguishing unset from empty) before forcing C, and restores it (or unsets it) before dispatching to systemopenand the cmux CLI, so the forced locale doesn't leak into child processes. - Adds regression tests passing a Japanese filename through the wrapper under
ja_JP.UTF-8, run against/bin/bashand any alternate bash build found; skipped when none exists. - Adds a static guard asserting
export LC_ALL=Cprecedes any top-levelcaseor${var%pattern}/${var#pattern}construct, covering named and positional parameters,;/&&/||-chained statements, and comments. - Verifies via fake child processes that system
openand the cmux CLI observe the caller's original locale.
Summary by CodeRabbit
-
Bug Fixes
- Improved consistent matching of URLs, flags, file extensions, and paths by using a standard locale during argument processing.
- Prevented crashes when opening filenames containing multibyte characters under supported Bash environments.
- Improved behavior across different locale settings.
-
Tests
- Added regression coverage for non-ASCII filenames with default and alternate Bash installations.
- Expanded test configuration for custom shell binaries and environment overrides.
- Added checks verifying locale handling before argument processing.