Summary
OpenBLAS 0.3.34 built for Windows x86-64 with clang-cl/flang, DYNAMIC_ARCH=ON, NO_AVX512=1, pthreads (the conda-forge win-64 package libopenblas 0.3.34 pthreads_h877e47f_1) crashes with an access violation inside dgemm_kernel_ZEN / dgemm_kernel_HASWELL on plain dsyrk/dpotrf calls with small matrices (dsyrk('U','T', n=55, k=165) is enough), on AMD Zen 4 and Zen 5 machines when the CPU exposes AVX-512. The same binary works on AMD machines where AVX-512 is not exposed (e.g. Zen 3, or Zen 4 VMs whose hypervisor masks it) and on Intel Xeons that do expose it. 0.3.33, 0.3.31, 0.3.29 and 0.3.27 built by the same recipe (same build-config hash) run the same reproducer fine on the same machines, so this is a regression between the 0.3.33 and 0.3.34 sources.
Reported first at conda-forge/openblas-feedstock#196; the maintainer there does not think it is a packaging problem and asked for an upstream issue.
Details
- Deterministic on an affected machine; independent of the selected core table (
OPENBLAS_CORETYPE=Zen forced: same crash at dgemm_kernel_ZEN+0x6ea6; on a Zen 5 the default fallback is Haswell and it crashes at dgemm_kernel_HASWELL+0x6ea6) and of threading (OPENBLAS_NUM_THREADS=1: still crashes, earlier in the kernel at +0x16e).
- Under
cdb (with _NO_DEBUG_HEAP=1) the fault is on the kernel's ret: the stack pointer and every callee-saved register hold IEEE doubles, i.e. the kernel's own frame was overwritten with matrix data.
- All runners had the same OS image (GitHub Actions
windows-latest, Windows Server 2025, image 20260819.586); AVX-512 exposure was read from numpy's __cpu_features__['AVX512F'] in the same process.
- The same 0.3.34 sources built by the numpy project (
scipy-openblas32 0.3.34.106.0, AVX-512 kernels included) run the reproducer fine on the affected machines, where they select SkylakeX.
- Between v0.3.33 and v0.3.34 the commits API shows no changes under
kernel/x86_64; the changed areas that look relevant to a corrupted kernel frame are the level-3 locking rework (driver/level3/level3_thread_lock.c, 9363452, 298d53c, a2261f9), driver/others/memory.c, the BLAS_LOCK_DEFINED guard in common_x86_64.h, and the build-system changes (Makefile.system, cmake/arch.cmake, cmake/system.cmake, cmake/prebuild.cmake). I have not bisected.
- Side note:
dynamic.c handles AMD extended families 8, 9 and 10 only, so family 0x1A (Zen 5, e.g. AMD EPYC 9V45) returns NULL and the capability fallback selects Haswell under NO_AVX512 (or Cooperlake/SkylakeX otherwise) instead of Zen.
Machines (standalone reproducer, fresh conda environments, 16 GitHub runners)
| runner CPU |
AVX-512 exposed |
VMs |
core selected |
0.3.34 |
0.3.33 |
0.3.31 |
0.3.29 |
0.3.27 |
| AMD EPYC 9V74 (Zen 4) |
yes |
2 |
Zen |
crash (0xC0000005) 2/2 |
ok |
ok |
ok |
ok |
| AMD EPYC 9V74 (Zen 4) |
no |
8 |
Zen |
ok |
ok |
ok |
ok |
ok |
| AMD EPYC 7763 (Zen 3) |
no |
5 |
Zen |
ok |
ok |
ok |
ok |
ok |
| Intel Xeon Platinum 8573C |
yes |
1 |
Haswell |
ok |
ok |
ok |
ok |
ok |
An AMD EPYC 9V45 (Zen 5) runner crashed the same way with 0.3.34 in an earlier run (only 0.3.34 was tested there; core selected Haswell).
Runs: version sweep, debugger run with the dump, forced core / thread modes.
Build configuration (conda-forge recipe)
clang-cl + flang, CMake with -DDYNAMIC_ARCH=ON -DNO_AVX512=1 -DNOFORTRAN=0 -DBUILD_WITHOUT_LAPACK=no -DNUM_THREADS=128 -DBUILD_SHARED_LIBS=on -DUSE_OPENMP=0 (see the feedstock's recipe/bld.bat). 0.3.33 from the same recipe is pthreads_h877e47f_0, 0.3.34 is pthreads_h877e47f_1.
Reproducer
# dsyrk/dpotrf/dpotrs/dgemv via ctypes on the conda-forge openblas.dll; crashes in the first dsyrk on affected machines
import ctypes, glob, os, sys
import numpy as np
binDir = os.path.join(sys.prefix, "Library", "bin")
os.add_dll_directory(binDir)
ob = ctypes.CDLL(os.path.join(binDir, "openblas.dll"))
ob.openblas_get_config.restype = ctypes.c_char_p
ob.openblas_get_corename.restype = ctypes.c_char_p
try:
from numpy._core._multiarray_umath import __cpu_features__ as feat
except ImportError:
from numpy.core._multiarray_umath import __cpu_features__ as feat
print("CONFIG:", ob.openblas_get_config().decode(), "| CORE:", ob.openblas_get_corename().decode(),
"| threads:", ob.openblas_get_num_threads(), "| AVX512F exposed:", feat.get("AVX512F"), flush=True)
def by(v): return ctypes.byref(ctypes.c_int(v))
D = ctypes.POINTER(ctypes.c_double)
for (m, n) in [(165, 55), (1200, 400)]:
rng = np.random.RandomState(0)
A = np.asfortranarray(rng.randn(m, n)); G = np.zeros((n, n), order="F")
b = rng.randn(n); y = rng.randn(m); info = ctypes.c_int(0)
one, zero = ctypes.c_double(1.0), ctypes.c_double(0.0)
ob.dsyrk_(b"U", b"T", by(n), by(m), ctypes.byref(one), A.ctypes.data_as(D), by(m), ctypes.byref(zero), G.ctypes.data_as(D), by(n))
G[np.diag_indices(n)] += 1.0 + m
ob.dpotrf_(b"U", by(n), G.ctypes.data_as(D), by(n), ctypes.byref(info)); assert info.value == 0, info.value
ob.dpotrs_(b"U", by(n), by(1), G.ctypes.data_as(D), by(n), b.ctypes.data_as(D), by(n), ctypes.byref(info)); assert info.value == 0
ob.dgemv_(b"T", by(m), by(n), ctypes.byref(one), A.ctypes.data_as(D), by(m), y.ctypes.data_as(D), by(1), ctypes.byref(one), b.ctypes.data_as(D), by(1))
ob.dgemv_(b"N", by(m), by(n), ctypes.byref(one), A.ctypes.data_as(D), by(m), b.ctypes.data_as(D), by(1), ctypes.byref(zero), y.ctypes.data_as(D), by(1))
print(f"ok m={m} n={n}", flush=True)
print("REPRO OK")
conda create -p .\ob -c conda-forge "libopenblas=0.3.34=*pthreads*" openblas=0.3.34 python=3.12 numpy
.\ob\python.exe repro.py
Crash dump (AMD EPYC 9V45, default core selection = Haswell, 4 threads)
rax=000001e9d8cdc200 rbx=3ff699ece033200f rcx=000001e9f7520000
rdx=0000000000000000 rsi=bfd0d1ca60b6f99e rdi=bff543da6fd24b01
rip=00007ff80b441166 rsp=000000c06efe9c38 rbp=3fd1f264f736fd5a
r8=000001e9e7727bc0 r9=000001e9d8cdc8c0 r10=00000000000001b8
r11=0000000000000080 r12=bffaea7f2b139a9b r13=bfdb26aecf319480
r14=3fe52314bfed9253 r15=bfb0447583b403d1
openblas_115cad90a43485aceeae12031d62cd82!dgemm_kernel_HASWELL+0x6ea6:
Child-SP RetAddr : Args to Child : Call Site
000000c0`6efe9c38 3fe22753`b6c6084e : bfd8660e`50759a89 3fc44d32`199d116a bfe7c85b`6fbfafdc c0026722`7d37cd7a : openblas_115cad90a43485aceeae12031d62cd82!dgemm_kernel_HASWELL+0x6ea6
Happy to run patches or a bisect build on the affected runner class; the sweep over GitHub's runner pool is automated on our side.
Summary
OpenBLAS 0.3.34 built for Windows x86-64 with clang-cl/flang,
DYNAMIC_ARCH=ON,NO_AVX512=1, pthreads (the conda-forge win-64 packagelibopenblas 0.3.34 pthreads_h877e47f_1) crashes with an access violation insidedgemm_kernel_ZEN/dgemm_kernel_HASWELLon plaindsyrk/dpotrfcalls with small matrices (dsyrk('U','T', n=55, k=165)is enough), on AMD Zen 4 and Zen 5 machines when the CPU exposes AVX-512. The same binary works on AMD machines where AVX-512 is not exposed (e.g. Zen 3, or Zen 4 VMs whose hypervisor masks it) and on Intel Xeons that do expose it. 0.3.33, 0.3.31, 0.3.29 and 0.3.27 built by the same recipe (same build-config hash) run the same reproducer fine on the same machines, so this is a regression between the 0.3.33 and 0.3.34 sources.Reported first at conda-forge/openblas-feedstock#196; the maintainer there does not think it is a packaging problem and asked for an upstream issue.
Details
OPENBLAS_CORETYPE=Zenforced: same crash atdgemm_kernel_ZEN+0x6ea6; on a Zen 5 the default fallback isHaswelland it crashes atdgemm_kernel_HASWELL+0x6ea6) and of threading (OPENBLAS_NUM_THREADS=1: still crashes, earlier in the kernel at+0x16e).cdb(with_NO_DEBUG_HEAP=1) the fault is on the kernel'sret: the stack pointer and every callee-saved register hold IEEE doubles, i.e. the kernel's own frame was overwritten with matrix data.windows-latest, Windows Server 2025, image 20260819.586); AVX-512 exposure was read from numpy's__cpu_features__['AVX512F']in the same process.scipy-openblas320.3.34.106.0, AVX-512 kernels included) run the reproducer fine on the affected machines, where they selectSkylakeX.kernel/x86_64; the changed areas that look relevant to a corrupted kernel frame are the level-3 locking rework (driver/level3/level3_thread_lock.c, 9363452, 298d53c, a2261f9),driver/others/memory.c, theBLAS_LOCK_DEFINEDguard incommon_x86_64.h, and the build-system changes (Makefile.system,cmake/arch.cmake,cmake/system.cmake,cmake/prebuild.cmake). I have not bisected.dynamic.chandles AMD extended families 8, 9 and 10 only, so family 0x1A (Zen 5, e.g.AMD EPYC 9V45) returns NULL and the capability fallback selectsHaswellunderNO_AVX512(orCooperlake/SkylakeXotherwise) instead ofZen.Machines (standalone reproducer, fresh conda environments, 16 GitHub runners)
An
AMD EPYC 9V45(Zen 5) runner crashed the same way with 0.3.34 in an earlier run (only 0.3.34 was tested there; core selectedHaswell).Runs: version sweep, debugger run with the dump, forced core / thread modes.
Build configuration (conda-forge recipe)
clang-cl + flang, CMake with
-DDYNAMIC_ARCH=ON -DNO_AVX512=1 -DNOFORTRAN=0 -DBUILD_WITHOUT_LAPACK=no -DNUM_THREADS=128 -DBUILD_SHARED_LIBS=on -DUSE_OPENMP=0(see the feedstock'srecipe/bld.bat). 0.3.33 from the same recipe ispthreads_h877e47f_0, 0.3.34 ispthreads_h877e47f_1.Reproducer
Crash dump (AMD EPYC 9V45, default core selection = Haswell, 4 threads)
Happy to run patches or a bisect build on the affected runner class; the sweep over GitHub's runner pool is automated on our side.