Skip to content

04327_png_terminal_mode fails under amd_msan on CPUs without AVX512VBMI #2265

Description

@CarlosFelipeOR

Describe the situation

Under amd_msan, clickhouse-client aborts while base64-encoding image output. The test's own checks all pass — it is marked FAIL only because the abort reaches stderr.

04327_png_terminal_mode: [ FAIL ]   Reason: having stderror:
stdout:  iterm: OK / kitty: OK / sixel: OK / auto: OK / BAD_ARGUMENTS

Two aborts, two call sites, both on a stack buffer encoded passed to libbase64:

Uninitialized bytes in write at offset 29 inside [.., 143)
  created by an allocation of 'encoded'  #0 DB::Base64WriteBuffer::nextImpl()  src/IO/Base64WriteBuffer.cpp:34

Uninitialized bytes in write at offset 17 inside [.., 132)
  created by an allocation of 'encoded'  #0 DB::writeImageKitty(...)  src/Formats/PNGTerminalOutput.cpp:157

Evidence

1. The result follows the CPU. Flags from Available CPU instruction sets in each job's clickhouse-server.log; runner from the job log header.

result AVX512VBMI runner date PR job
FAIL no hetzner 08-21 branch 96684279998
FAIL no hetzner 08-21 #2252 96936257916
FAIL no hetzner 08-22 #2248 96962036405
FAIL no standby 08-21 #2197 96638931386
FAIL no standby 08-23 #2248 97214112650
FAIL no standby 08-24 branch 97295230100
FAIL no standby 08-25 #2159 97705515972
OK yes hetzner 08-20 #2235 96469522425
OK yes hetzner 08-20 #2235 96552758123
OK yes hetzner 08-21 #2159 96914867849
OK yes hetzner 08-21 #2196 96938079223
OK yes hetzner 08-21 #2235 96831248135
OK yes hetzner 08-24 branch 97560923193
OK yes hetzner 08-24 #2196 97616995404
OK yes hetzner 08-24 #2248 97515578372
OK yes hetzner 08-24 #2255 97599783434
OK yes hetzner 08-25 branch 97908469357

All 7 failures were on CPUs without AVX512VBMI; all 10 passes were on CPUs with it. Jobs whose S3 artefact was overwritten by a later attempt are excluded, since the CPU flags would belong to a different attempt.

The runner pool is a proxy, not the cause: the four standby runners sampled all lack VBMI; the ephemeral pool draws mixed hardware and its result follows the CPU.

2. Same binary, different machine. Reproduced locally on a VBMI-less CPU (Intel Core 7 240H) with a binary from a failing CI run and with one from a passing run — both abort.

3. The output is byte-for-byte correct. Same machine, same query, comparing the raw PNG against the decoded base64:

raw PNG (terminal_mode=auto) : 84 bytes  sha256 672225eaa704acf7
base64 decoded (mode=iterm)  : 84 bytes  sha256 672225eaa704acf7   -> identical

Nothing is missing. The reported offsets are also exactly where the base64 output begins (iTerm prefix 29 bytes, kitty prefix 17) — no byte written by ClickHouse itself is flagged.


AI-generated analysis

Everything above is measured. What follows is AI-generated analysis and a set of options; it has not been validated by applying any of them.

libbase64 picks its codec at runtime and takes the AVX-512 path only when the CPU has both VL and VBMI — lib/codec_choose.c:

if ((ebx & bit_AVX512vl) && (ecx & bit_AVX512vbmi)) {
    codec->enc = base64_stream_encode_avx512;

Without VBMI it falls back to the AVX2/SSSE3 codec, whose hand-written SIMD MSan cannot follow, so it believes the output buffer was never written.

Our build configuration matches upstream 26.6 (cmake/sanitize.cmake, the MSan cmake flags, the contrib/base64 submodule commit, both source files, the Dockerfiles). Upstream does not see this because their runners have VBMI.

Options

  1. __msan_unpoison(encoded, encoded_size) after the libbase64 call, at both sites. The project's existing pattern — already used in 107 places, e.g. __msan_unpoison(ciphertext_and_tag, out_len); /// OpenSSL uses assembly which evades msan's analysis. base/base/MemorySanitizer.h makes it a no-op on non-MSan builds. Fixes it everywhere, including upstream.
  2. Pin the msan jobs to VBMI-capable runners. Not a config change: the job uses FUNC_TESTER_AMD = ["self-hosted", "altinity-on-demand", "altinity-func-tester"], and no existing label describes the CPU, so a new label would have to be added to the machines first. Also shrinks the pool.
  3. broken_tests.yaml entry restricted to msan — stops the noise, hides the cause.
  4. no-msan tag on the test — stronger than 3, but drops coverage on the half of the fleet where the test works.

Note Base64WriteBuffer is generic, so any future caller that base64-encodes output hits the same false positive on VBMI-less hardware.

Metadata

Metadata

Assignees

No one assigned

    Labels

    antalyabugSomething isn't workingcicdImprovements and fixes to the CICD process

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions