diff --git a/Makefile.inc1 b/Makefile.inc1 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -740,6 +740,7 @@ MK_CLANG_FULL=no \ MK_HTML=no \ MK_MAN=no \ + MK_MSAN=no \ MK_PROFILE=no \ MK_RETPOLINE=no \ MK_SSP=no \ @@ -771,6 +772,7 @@ MK_CLANG_FORMAT=no \ MK_CLANG_FULL=no \ MK_LLDB=no \ + MK_MSAN=no \ MK_RETPOLINE=no \ MK_SSP=no \ MK_TESTS=no \ @@ -3110,6 +3112,10 @@ _prereq_libs+= lib/libclang_rt/asan_cxx _prereq_libs+= lib/libclang_rt/asan_static .endif +.if ${MK_MSAN} != "no" +_prereq_libs+= lib/libclang_rt/msan +_prereq_libs+= lib/libclang_rt/msan_cxx +.endif .if ${MK_UBSAN} != "no" _prereq_libs+= lib/libclang_rt/ubsan_minimal _prereq_libs+= lib/libclang_rt/ubsan_standalone diff --git a/Makefile.libcompat b/Makefile.libcompat --- a/Makefile.libcompat +++ b/Makefile.libcompat @@ -69,13 +69,18 @@ -DNO_CPU_CFLAGS \ MK_ASAN=no \ MK_CTF=no \ + MK_MSAN=no \ MK_RETPOLINE=no \ MK_SSP=no \ MK_UBSAN=no \ MK_WERROR=no \ _lc_build-tools ${_+_}cd ${.CURDIR}; \ - ${LIB${_LIBCOMPAT}WMAKE} -f Makefile.inc1 -DNO_FSCHG libraries + ${LIB${_LIBCOMPAT}WMAKE} -f Makefile.inc1 \ + -DNO_FSCHG \ + MK_ASAN=no \ + MK_MSAN=no \ + libraries distribute${libcompat} install${libcompat}: .PHONY ${_+_}cd ${.CURDIR}; \ diff --git a/lib/clang/Makefile.inc b/lib/clang/Makefile.inc --- a/lib/clang/Makefile.inc +++ b/lib/clang/Makefile.inc @@ -1,8 +1,14 @@ - .include PACKAGE= clang + +.if ${MK_MSAN} == "yes" +# Build only PIE static libraries; bsd.lib.mk does not support this directly. +CFLAGS+= ${PIEFLAG} ${SHARED_CFLAGS} +CXXFLAGS+= ${PIEFLAG} ${SHARED_CXXFLAGS} +.else MK_PIE:= no # Explicit libXXX.a references +.endif .if ${MK_LLVM_FULL_DEBUGINFO} == "no" .if ${COMPILER_TYPE} == "clang" diff --git a/lib/csu/Makefile.inc b/lib/csu/Makefile.inc --- a/lib/csu/Makefile.inc +++ b/lib/csu/Makefile.inc @@ -3,6 +3,7 @@ NO_WMISSING_VARIABLE_DECLARATIONS= # Can't instrument these files since that breaks non-sanitized programs. MK_ASAN:= no +MK_MSAN:= no MK_UBSAN:= no .include diff --git a/lib/csu/tests/Makefile b/lib/csu/tests/Makefile --- a/lib/csu/tests/Makefile +++ b/lib/csu/tests/Makefile @@ -1,9 +1,15 @@ +.include SUBDIR= dso + +.if ${MK_MSAN} != "yes" TESTS_SUBDIRS= dynamic +.endif TESTS_SUBDIRS+= dynamiclib TESTS_SUBDIRS+= dynamicpie +.if ${MK_MSAN} != "yes" TESTS_SUBDIRS+= static +.endif SUBDIR_DEPEND_dynamiclib=dso diff --git a/lib/csu/tests/Makefile.tests b/lib/csu/tests/Makefile.tests --- a/lib/csu/tests/Makefile.tests +++ b/lib/csu/tests/Makefile.tests @@ -1,4 +1,3 @@ - ATF_TESTS_C+= init_test ATF_TESTS_C+= fini_test ATF_TESTS_CXX+= cxx_constructors diff --git a/lib/libc/amd64/string/Makefile.inc b/lib/libc/amd64/string/Makefile.inc --- a/lib/libc/amd64/string/Makefile.inc +++ b/lib/libc/amd64/string/Makefile.inc @@ -34,3 +34,8 @@ # sanitizer runtime can initialize itself. CFLAGS.amd64_archlevel.c+= -fno-sanitize=address .endif +.if ${MK_MSAN} != "no" +# Disable MSAN for amd64_archlevel.c since its code is executed before the +# sanitizer runtime can initialize itself. +CFLAGS.amd64_archlevel.c+= -fno-sanitize=memory +.endif diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -18,6 +18,9 @@ CFLAGS.qsort.c+= -Wsign-compare +# The MSan runtime calls cxa_atexit() before it has fully initialized itself. +CFLAGS.atexit.c+= -fno-sanitize=memory + # Work around an issue on case-insensitive file systems. # libc has both _Exit.c and _exit.s and they both yield # _exit.o (case insensitively speaking). diff --git a/lib/libclang_rt/Makefile.inc b/lib/libclang_rt/Makefile.inc --- a/lib/libclang_rt/Makefile.inc +++ b/lib/libclang_rt/Makefile.inc @@ -13,6 +13,7 @@ NO_PIC= MK_PROFILE= no MK_ASAN:= no +MK_MSAN:= no MK_SSP= no MK_UBSAN:= no diff --git a/lib/libgcc_s/Makefile b/lib/libgcc_s/Makefile --- a/lib/libgcc_s/Makefile +++ b/lib/libgcc_s/Makefile @@ -8,6 +8,8 @@ .include +# The MSan runtime depends on this library; don't instrument it. +MK_MSAN= no MK_SSP= no WARNS?= 2 diff --git a/lib/libsys/x86/Makefile.sys b/lib/libsys/x86/Makefile.sys --- a/lib/libsys/x86/Makefile.sys +++ b/lib/libsys/x86/Makefile.sys @@ -19,6 +19,9 @@ .if ${MK_ASAN} != "no" CFLAGS.__vdso_gettc.c+=-fno-sanitize=address .endif +.if ${MK_MSAN} != "no" +CFLAGS.__vdso_gettc.c+=-fno-sanitize=memory +.endif .if ${MK_UBSAN} != "no" CFLAGS.__vdso_gettc.c+=-fno-sanitize=undefined .endif diff --git a/lib/libthr/Makefile b/lib/libthr/Makefile --- a/lib/libthr/Makefile +++ b/lib/libthr/Makefile @@ -15,6 +15,10 @@ .endif .include + +# _libpthread_init() cannot be instrumented yet. For instance, rtprio_thread() +# is not intercepted, so _thr_getscheduler() triggers MSan warnings. +MK_MSAN= no MK_SSP= no LIB=thr diff --git a/lib/libutil/tests/Makefile b/lib/libutil/tests/Makefile --- a/lib/libutil/tests/Makefile +++ b/lib/libutil/tests/Makefile @@ -1,10 +1,16 @@ +.include TAP_TESTS_C+= flopen_test TAP_TESTS_C+= grp_test TAP_TESTS_C+= humanize_number_test TAP_TESTS_C+= pidfile_test +.if ${MK_MSAN} == "no" +# These tests define a gethostname() symbol, which collides with the one +# provided by the sanitizer runtime and thus results in a link error. +# We cannot simply disable MSAN because libatf.so etc. may be instrumented. TAP_TESTS_C+= trimdomain_test TAP_TESTS_C+= trimdomain-nodomain_test +.endif ATF_TESTS_C+= cpuset_test ATF_TESTS_C+= expand_number_test ATF_TESTS_C+= forkpty_test diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -10,6 +10,7 @@ MK_PIE= no # Always position independent using local rules # Not compatible with sanitizer instrumentation or SSP. MK_ASAN= no +MK_MSAN= no MK_SSP= no MK_UBSAN= no diff --git a/share/mk/bsd.opts.mk b/share/mk/bsd.opts.mk --- a/share/mk/bsd.opts.mk +++ b/share/mk/bsd.opts.mk @@ -77,6 +77,7 @@ CCACHE_BUILD \ CTF \ INSTALL_AS_USER \ + MSAN \ PROFILE \ RETPOLINE \ STALE_STAGED \ diff --git a/share/mk/bsd.progs.mk b/share/mk/bsd.progs.mk --- a/share/mk/bsd.progs.mk +++ b/share/mk/bsd.progs.mk @@ -22,7 +22,8 @@ .if defined(PROG) # just one of many PROG_OVERRIDE_VARS += BINDIR BINGRP BINOWN BINMODE CSTD CXXSTD DPSRCS MAN \ - NO_SHARED MK_WERROR PROGNAME STRIP WARNS MK_ASAN MK_UBSAN + NO_SHARED MK_WERROR PROGNAME STRIP WARNS \ + MK_ASAN MK_MSAN MK_UBSAN PROG_VARS += SRCS CFLAGS CXXFLAGS DEBUG_FLAGS DPADD INTERNALPROG LDADD \ LIBADD LINKS LDFLAGS MLINKS ${PROG_OVERRIDE_VARS} .for v in ${PROG_VARS:O:u} diff --git a/share/mk/bsd.sanitizer.mk b/share/mk/bsd.sanitizer.mk --- a/share/mk/bsd.sanitizer.mk +++ b/share/mk/bsd.sanitizer.mk @@ -11,6 +11,17 @@ _use_sanitizers= yes .endif # ${MK_ASAN} == "yes" +.if ${MK_MSAN} == "yes" && ${NO_SHARED:Uno:tl} == "no" +.if ${MK_PIE} == "yes" +SANITIZER_CFLAGS+= -fsanitize=memory -fsanitize-memory-track-origins -fPIC +# -fsanitize-memory-param-retval can raise false positives. +SANITIZER_CFLAGS+= -fno-sanitize-memory-param-retval +_use_sanitizers= yes +.else +.warning MSan is only supported for PIE executables +.endif +.endif + .if ${MK_UBSAN} == "yes" && ${NO_SHARED:Uno:tl} == "no" # Unlike the other sanitizers, UBSan could also work for static libraries. # However, this currently results in linker errors (even with the diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -480,8 +480,8 @@ MK_LLVM_COV:= no .endif -.if ${MK_ASAN} == "yes" -# In order to get sensible backtraces from ASAN we have to install +.if ${MK_ASAN} == "yes" || ${MK_MSAN} == "yes" +# In order to get sensible backtraces from (A|M)SAN we have to install # llvm-symbolizer as /usr/bin/addr2line instead of the elftoolchain version. MK_LLVM_BINUTILS:= yes .endif diff --git a/tests/sys/audit/Makefile b/tests/sys/audit/Makefile --- a/tests/sys/audit/Makefile +++ b/tests/sys/audit/Makefile @@ -1,3 +1,8 @@ +.include + +# XXX-MJ these tests cannot be built with MSAN enabled so long as +# libprivateauditd.a is not position-independent. +.if ${MK_MSAN} != "yes" TESTSDIR= ${TESTSBASE}/sys/audit @@ -64,4 +69,6 @@ CFLAGS.process-control.c+= -I${SRCTOP}/tests +.endif # ${MK_MSAN} != "yes" + .include diff --git a/tests/sys/capsicum/Makefile b/tests/sys/capsicum/Makefile --- a/tests/sys/capsicum/Makefile +++ b/tests/sys/capsicum/Makefile @@ -43,11 +43,14 @@ NO_SHARED.$p= SRCS.$p= mini-me.c .endfor -.if ${MK_ASAN} != "no" || ${MK_UBSAN} != "no" +.if ${MK_ASAN} != "no" || ${MK_MSAN} != "no" || ${MK_UBSAN} != "no" # mini-me.o is linked into a static binary so we can't use sanitizers. # Note: We have to set CFLAGS here since it will be built as part of -# _PROGS_COMMON_OBJS and therefore NO_SHARED.$p does not disable ASAN/UBSAN. -CFLAGS.mini-me.c+= -fno-sanitize=address -fno-sanitize=undefined +# _PROGS_COMMON_OBJS and therefore NO_SHARED.$p does not disable +# ASAN/MSAN/UBSAN. +CFLAGS.mini-me.c+= -fno-sanitize=address \ + -fno-sanitize=memory \ + -fno-sanitize=undefined .endif BINDIR= ${TESTSDIR} diff --git a/tools/build/options/WITH_MSAN b/tools/build/options/WITH_MSAN new file mode 100644 --- /dev/null +++ b/tools/build/options/WITH_MSAN @@ -0,0 +1,4 @@ +Build the base system with Memory Sanitizer (MSan) to detect +uses of uninitialized variables in C and C++ code. +Requires that Clang be used as the base system compiler +and that the runtime support library is available. diff --git a/usr.bin/clang/Makefile.inc b/usr.bin/clang/Makefile.inc --- a/usr.bin/clang/Makefile.inc +++ b/usr.bin/clang/Makefile.inc @@ -1,7 +1,8 @@ - .include +.if ${MK_MSAN} != "yes" MK_PIE:= no # Explicit libXXX.a references +.endif .if ${MK_LLVM_FULL_DEBUGINFO} == "no" .if ${COMPILER_TYPE} == "clang" diff --git a/usr.sbin/rpcbind/tests/Makefile b/usr.sbin/rpcbind/tests/Makefile --- a/usr.sbin/rpcbind/tests/Makefile +++ b/usr.sbin/rpcbind/tests/Makefile @@ -1,6 +1,10 @@ - .include +# We cannot build this test at all when MSAN is enabled, due to the getifaddrs() +# symbol collision and since libatf might have been built with MSAN +# instrumentation. +.if ${MK_MSAN} != "yes" + .PATH: ${.CURDIR}/.. ATF_TESTS_C= addrmerge_test @@ -21,4 +25,6 @@ LDFLAGS+=-shared-libasan .endif +.endif # MK_MSAN != "yes" + .include