Go version

go version devel go1.22-78b42a5338a Fri Dec 8 03:28:17 2023 +0000 linux/amd64

What operating system and processor architecture are you using (go env)?

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/root/.cache/go-build'
GOENV='/root/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/root/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/root/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/mnt/d/file/gofile/gogit/go1'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/mnt/d/file/gofile/gogit/go1/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='devel go1.22-78b42a5338a Fri Dec 8 03:28:17 2023 +0000'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='clang'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/mnt/d/file/gofile/gogit/go1/src/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build9589947017406823330=/tmp/go-build -gno-record-gcc-switches'
GOROOT/bin/go version: go version devel go1.22-78b42a5338a Fri Dec 8 03:28:17 2023 +0000 linux/amd64
GOROOT/bin/go tool compile -V: compile version devel go1.22-78b42a5338a Fri Dec 8 03:28:17 2023 +0000
uname -sr: Linux 5.15.133.1-microsoft-standard-WSL2
Distributor ID: Kali
Description:    Kali GNU/Linux Rolling
Release:        2023.4
Codename:       kali-rolling
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Debian GLIBC 2.37-12) stable release version 2.37.

What did you do?

In wsl2 export CC=clang clang --version Debian clang version 16.0.6 (16) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin

go test cmd/cgo/internal/testsanitizers

What did you expect to see?

test pass.

What did you see instead?

--- FAIL: TestMSAN (9.40s)
    --- FAIL: TestMSAN/msan8 (3.26s)
        msan_test.go:81: /tmp/TestMSAN1734687677996663673/msan8 exited with exit status 1
            ==29491==WARNING: MemorySanitizer: use-of-uninitialized-value
                #0 0x5062b8 in msanGoLoop (/tmp/TestMSAN1734687677996663673/msan8+0x5062b8) (BuildId: 519103c2e3c209c99d8a9bb8f9d7cf2065339178)
                #1 0x5064c0 in _cgo_a322d3c7e754_Cfunc_msanGoLoop (/tmp/TestMSAN1734687677996663673/msan8+0x5064c0) (BuildId: 519103c2e3c209c99d8a9bb8f9d7cf2065339178)
                #2 0x501143 in runtime.asmcgocall.abi0 /mnt/d/file/gofile/gogit/go1/src/runtime/asm_amd64.s:918

            SUMMARY: MemorySanitizer: use-of-uninitialized-value (/tmp/TestMSAN1734687677996663673/msan8+0x5062b8) (BuildId: 519103c2e3c209c99d8a9bb8f9d7cf2065339178) in msanGoLoop
            Exiting
FAIL
FAIL    cmd/cgo/internal/testsanitizers 32.323s
FAIL

Comment From: mauri870

This appears to be a general clang 16 issue, not only to wsl2.

It is complaining about the use of uninit that is uninitialized here:

https://go.googlesource.com/go/+/78b42a5338aa1fa293acc5bbb7ef9122a7acc2ba/src/cmd/cgo/internal/testsanitizers/testdata/msan8.go#65

Shorter repro:

package main

/*
#include <stdint.h>

#include <sanitizer/msan_interface.h>

__attribute__((noinline))
void funinit(unsigned long a1) {}

static unsigned long uninit;

void f() {
    __msan_poison(&uninit, sizeof uninit);
    funinit(uninit);
}
*/
import "C"

func main() {
    C.f()
}

Strangely enough I can't seem to replicate it with a C version and clang-16.

Comment From: mauri870

Huh, something appear to have changed in clang-16 that makes it report memory as uninitialized in this case. From the test msan8 it reads:

// msanGoLoop loops calling msanGoWait, with the arguments passed
// such that msan thinks that they are undefined. msan permits
// undefined values to be used as long as they are not used to
// for conditionals or for memory access.

I tried clang 14, 15, 16, 17 and this only happens with clang >= 16. Looking at the release notes I can't spot anything that might've caused this change in msan behavior https://releases.llvm.org/16.0.0/docs/ReleaseNotes.html

I'm not sure if this comment above is actually a guarantee from the memory sanitizer, they might have changed it silently in clang-16 without mentioning it.

Here is a short C repro I put together that runs in clang 15 but not on clang 16:

// clang-16 -fsanitize=memory test.c && ./a.out
#include <stdint.h>
#include <sanitizer/msan_interface.h>

__attribute__((noinline))
void funinit(unsigned long a1) {}

static unsigned long uninit ;

void f() {
    __msan_poison(&uninit, sizeof uninit);
    funinit(uninit);
}

int main() {
    f();
    return 0;
}

Comment From: mauri870

Adjusting the labels as this appears to be a clang issue

Comment From: mauri870

cc @dvyukov

Comment From: dvyukov

This looks like -fsanitize-address-param-retval (detects passing uninits as arguments), but it should be disabled by default. Maybe it was accidentally enabled in clang 16 and then disabled. @kda @vitalybuka

Comment From: vitalybuka

It's enabled by default https://reviews.llvm.org/D134669

Comment From: vitalybuka

https://releases.llvm.org/16.0.0/tools/clang/docs/ReleaseNotes.html#sanitizers

Comment From: mauri870

Looks like that is the culprint, I was looking at the llvm release notes and not the tools, that is why I did not spot this change.

Thanks for the valuable insight @vitalybuka @dvyukov.

Comment From: mauri870

@bcmills Do you consider this as a breaking change? This new behavior seems to be beneficial for programs:

-fsanitize-memory-param-retval is turned on by default. With -fsanitize=memory, passing uninitialized variables to functions and returning uninitialized variables from functions is more aggressively reported. -fno-sanitize-memory-param-retval restores the previous behavior.

Should we retain the old behavior?

Comment From: dvyukov

I think end users can control this using CGO_CFLAGS. So the breakage is not that bad. And potentially we can go for the better/default behavior and gave users workaround via CGO_CFLAGS.

Comment From: vitalybuka

I think end users can control this using CGO_CFLAGS. So the breakage is not that bad. And potentially we can go for the better/default behavior and gave users workaround via CGO_CFLAGS.

The state of that flag for linked C/C++ code needs to be consistent with GO code. Mismatch either way will cause false positives.

Comment From: gopherbot

Change https://go.dev/cl/549297 mentions this issue: cmd/cgo/internal/testsanitizers: fix msan test failing with clang >= 16

Comment From: mauri870

So it appears that we cannot use #if with #cgo, so the fix I submitted was reverted. I'm glad to hear suggestions on how to handle this.

Comment From: bcmills

@mauri870, perhaps the test driver itself could first try building with CGO_CFLAGS=-fno-sanitize-memory-param-retval, and try again without that flag if the build fails?

Comment From: gopherbot

Change https://go.dev/cl/601779 mentions this issue: cmd/cgo/internal/testsanitizers: avoid clang error in msan8.go