Recent Homebrew builds of zbar 0.23.93 crash with SIGSEGV inside
zbar_scan_image() on Apple Silicon Macs (observed on macOS 26 "Tahoe",
arm64, with the current Apple clang). This breaks everything that links
libzbar: zbarimg, Python pyzbar, the Ruby qrscan gem, etc.
This guide shows how to install a patched zbar through Homebrew, from scratch, on a clean machine. It is a temporary workaround until the upstream fix is released — see Upstream below.
Any use of zbar segfaults, even on a trivial image:
$ zbarimg some_qr.png
[1] 47701 segmentation fault zbarimg some_qr.png
$ echo $?
139From Ruby (qrscan gem) you'll see a [BUG] Segmentation fault with a
backtrace ending in CFUNC :scan. Typically it "worked until a few days ago"
— what changed is the compiler/OS, not your code.
The image scan loop in zbar/img_scanner.c advances the row pointer through
the movedelta macro:
#define movedelta(dx, dy) \
do { \
x += (dx); \
y += (dy); \
p += (dx) + ((uintptr_t)(dy)*w); \
} while (0);The reverse passes call movedelta(-1, …), i.e. with a negative dx/dy.
Because the delta is computed in uintptr_t, the negative int becomes a huge
unsigned value and the pointer is advanced relying on two's-complement
wraparound. Pointer arithmetic that overflows the object is undefined
behavior. It worked for years, but recent clang exploits the UB at -O1 and
above: the scan pointer is folded to (void *)-1 and the next *p dereference
crashes.
Proof: the same source built at -O0 does not crash; -O1/-Os/-O2 do.
The fix is to compute the delta in signed ptrdiff_t arithmetic.
Install a patched zbar into its own local tap, built from source. The patch is downloaded straight from the upstream PR, so there is nothing fragile to paste.
# 1. Create a local tap to hold the patched formula
brew tap-new local/patches --no-git
# 2. Write the patched formula
cat > "$(brew --repository)/Library/Taps/local/homebrew-patches/Formula/zbar.rb" <<'RUBY'
class Zbar < Formula
desc "Suite of barcodes-reading tools"
homepage "https://linuxtv.org/downloads/zbar/"
url "https://linuxtv.org/downloads/zbar/zbar-0.23.93.tar.bz2"
sha256 "83be8f85fc7c288fd91f98d52fc55db7eedbddcf10a83d9221d7034636683fa0"
license "LGPL-2.1-only"
revision 4
depends_on "pkgconf" => :build
depends_on "xmlto" => :build
depends_on "imagemagick"
depends_on "jpeg-turbo"
on_macos do
depends_on "fontconfig"
depends_on "freetype"
depends_on "gettext"
depends_on "glib"
depends_on "liblqr"
depends_on "libomp"
depends_on "libtool"
depends_on "little-cms2"
end
on_linux do
depends_on "dbus"
end
# Fix pointer-arithmetic UB in the scan loop that recent clang miscompiles
# at -O1+, causing SIGSEGV in zbar_scan_image() on arm64 macOS.
# Upstream: https://github.com/mchehab/zbar/pull/332
patch do
url "https://github.com/mchehab/zbar/commit/3eb0cb195408212fd54e6a1e1e9b9b3c0ec868e7.patch?full_index=1"
sha256 "1d9c8f8cdcec264df24076119c3f69c5e473cbd76a1d33ff241b39c7b8dd6f5a"
end
def install
ENV["XML_CATALOG_FILES"] = etc/"xml/catalog"
ENV.append "LDFLAGS", "-lintl" if OS.mac?
system "./configure", "--disable-silent-rules",
"--disable-video",
"--without-python",
"--without-qt",
"--without-gtk",
"--without-x",
*std_configure_args
system "make", "install"
end
test do
system bin/"zbarimg", "-h"
end
end
RUBY
# 3. Build & install it from source (the bottle is the broken one, so force source)
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source local/patches/zbarIf zbar is already installed (e.g. pulled in as a dependency), use reinstall
instead of install in step 3:
HOMEBREW_NO_INSTALL_FROM_API=1 brew reinstall --build-from-source local/patches/zbarzbarimg /path/to/any_qr.png # decodes, exits 0 — no segfaultA blank image is enough to prove the crash is gone (the original bug fires before any decoding happens).
If a language binding was compiled against the broken libzbar, rebuild it so it
links the patched one. For the Ruby qrscan gem:
gem pristine qrscan # or: bundle pristine qrscanbrew upgradeor a plainbrew reinstall zbarwill pull the broken core bottle again and reintroduce the crash. Just re-run thereinstallcommand above to restore the patched build.- This pins zbar to 0.23.93 with the patch applied. That is intentional.
- The
revision 4number is arbitrary; it only needs to differ from whatever core ships so Homebrew rebuilds the keg.
brew untap local/patches
brew reinstall zbarThe real fix is a one-liner submitted upstream:
- mchehab/zbar PR #332 — mchehab/zbar#332
Once it is merged and a new zbar release lands in Homebrew, drop this workaround
(brew untap local/patches && brew reinstall zbar).
For reference, the entire fix is:
--- a/zbar/img_scanner.c
+++ b/zbar/img_scanner.c
@@ -858,11 +858,11 @@
}
#endif
-#define movedelta(dx, dy) \
- do { \
- x += (dx); \
- y += (dy); \
- p += (dx) + ((uintptr_t)(dy)*w); \
+#define movedelta(dx, dy) \
+ do { \
+ x += (dx); \
+ y += (dy); \
+ p += (ptrdiff_t)(dx) + (ptrdiff_t)(dy) * (ptrdiff_t)(w); \
} while (0);
static void *_zbar_scan_image(zbar_image_scanner_t *iscn, zbar_image_t *img)