diff mbox

[v2] kasan: don't emit builtin calls when sanitization is off

Message ID 8ffecfffe04088c52c42b92739c2bd8a0bcb3f5e.1516384594.git.andreyknvl@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrey Konovalov Jan. 19, 2018, 5:58 p.m. UTC
With KASAN enabled the kernel has two different memset() functions, one
with KASAN checks (memset) and one without (__memset). KASAN uses some
macro tricks to use the proper version where required. For example memset()
calls in mm/slub.c are without KASAN checks, since they operate on poisoned
slab object metadata.

The issue is that clang emits memset() calls even when there is no memset()
in the source code. They get linked with improper memset() implementation
and the kernel fails to boot due to a huge amount of KASAN reports during
early boot stages.

The solution is to add -fno-builtin flag for files with KASAN_SANITIZE := n
marker.

Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
---

Changed in v2:
- dropped cc-option for -fno-builtin

 Makefile               | 3 ++-
 scripts/Makefile.kasan | 3 +++
 scripts/Makefile.lib   | 2 +-
 3 files changed, 6 insertions(+), 2 deletions(-)

Comments

Nick Desaulniers Jan. 19, 2018, 7:06 p.m. UTC | #1
Hmm... I had mentioned this patch to some coworkers who have much more
knowledge about LLVM than me.  They had concern that LLVM needs memset
to be defined, and that there were discussions on the llvm mailing
list about this.

I'm digging through trying to find anything relevant, maybe this one
about memcpy: https://lists.llvm.org/pipermail/llvm-dev/2012-May/050108.html

I wonder if -ffreestanding is more appropriate?
--
To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nick Desaulniers Jan. 19, 2018, 7:08 p.m. UTC | #2
sorry, for new people I just cc'ed: https://patchwork.kernel.org/patch/10175831/

On Fri, Jan 19, 2018 at 2:06 PM, Nick Desaulniers
<ndesaulniers@google.com> wrote:
> Hmm... I had mentioned this patch to some coworkers who have much more
> knowledge about LLVM than me.  They had concern that LLVM needs memset
> to be defined, and that there were discussions on the llvm mailing
> list about this.
>
> I'm digging through trying to find anything relevant, maybe this one
> about memcpy: https://lists.llvm.org/pipermail/llvm-dev/2012-May/050108.html
>
> I wonder if -ffreestanding is more appropriate?
Andrey Konovalov Jan. 22, 2018, 5:22 p.m. UTC | #3
On Fri, Jan 19, 2018 at 8:06 PM, Nick Desaulniers
<ndesaulniers@google.com> wrote:
> Hmm... I had mentioned this patch to some coworkers who have much more
> knowledge about LLVM than me.  They had concern that LLVM needs memset
> to be defined, and that there were discussions on the llvm mailing
> list about this.
>
> I'm digging through trying to find anything relevant, maybe this one
> about memcpy: https://lists.llvm.org/pipermail/llvm-dev/2012-May/050108.html
>
> I wonder if -ffreestanding is more appropriate?

I don't mind using either of those, they both fix the issue.

I'm struggling to understand the difference though. GCC documentation
doesn't really explain it [1] and Clang documentation [2] is
completely useless in this case.

[1] https://gcc.gnu.org/onlinedocs/gcc/Standards.html
[2] https://clang.llvm.org/docs/CommandGuide/clang.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nick Desaulniers Jan. 22, 2018, 6:12 p.m. UTC | #4
On Mon, Jan 22, 2018 at 12:22 PM, Andrey Konovalov
<andreyknvl@google.com> wrote:
> On Fri, Jan 19, 2018 at 8:06 PM, Nick Desaulniers
> <ndesaulniers@google.com> wrote:
>> Hmm... I had mentioned this patch to some coworkers who have much more
>> knowledge about LLVM than me.  They had concern that LLVM needs memset
>> to be defined, and that there were discussions on the llvm mailing
>> list about this.
>>
>> I'm digging through trying to find anything relevant, maybe this one
>> about memcpy: https://lists.llvm.org/pipermail/llvm-dev/2012-May/050108.html
>>
>> I wonder if -ffreestanding is more appropriate?
>
> I don't mind using either of those, they both fix the issue.
>
> I'm struggling to understand the difference though. GCC documentation
> doesn't really explain it [1] and Clang documentation [2] is
> completely useless in this case.
>
> [1] https://gcc.gnu.org/onlinedocs/gcc/Standards.html
> [2] https://clang.llvm.org/docs/CommandGuide/clang.html

There's always the source, which is the ultimate source of truth. In LLVM,

lib/Transforms/Utils/SimplifyLibCalls.cpp
lib/Transforms/Scalar/MemCpyOptimizer.cpp
include/llvm/Analysis/TargetLibraryInfo.h

are the only things with mentions of `no-builtin` or `freestanding`.

The driver is what parses these options:

build/tools/clang/include/clang/Driver/Options.inc:
 774 OPTION(prefix_1, "ffreestanding", ffreestanding, Flag, f_Group,
INVALID, nullptr, CC1Option, 0,
 775        "Assert that the compilation takes place in a freestanding
environment", nullptr, nullptr)
...
1004 OPTION(prefix_1, "fno-builtin-", fno_builtin_, Joined, f_Group,
INVALID, nullptr, CC1Option, 0,
1005        "Disable implicit builtin knowledge of a specific
function", nullptr, nullptr)
1006 OPTION(prefix_1, "fno-builtin", fno_builtin, Flag, f_Group,
INVALID, nullptr, CC1Option, 0,
1007        "Disable implicit builtin knowledge of functions", nullptr, nullptr)

It looks like the TargetLibraryInfo#disableAllFunctions() method is
called in a few different drivers.  The options table generates all
kinds of code, but places that parse command line flags look for
`OPT_` + 3rd arg to OPTION.

In Clang, looking for OPT_no_fbuiltin and OPT_ffreestanding:

lib/Driver/ToolChains/Clang.cpp
lib/Frontend/CompilerInvocation.cpp

the second seems to set:

 559   Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) ||
 560                             Args.hasArg(OPT_ffreestanding));
...
2201   Opts.Freestanding = Args.hasArg(OPT_ffreestanding);

which are then used back in LLVM.

Opts.Freestanding seems to be used from lots of LTO handling code in LLVM.

If ffreestanding implies fno-builtin, but does additional things with
LTO, then we might regress LTO (there's 2 patch sets in the works that
I know of) with KASAN by using ffreestanding rather than fno-builtin.
But lets cross that bridge when we get there (if we decide for some
reason that we need LTO+KASAN; I like both and if we start shipping
LTO kernels then you can bet we'd want to test them with KASAN).

So I don't think it hurts to keep v2 as is (sorry for the noise).

Acked-by: Nick Desaulniers <ndesaulniers@google.com>
Andrey Ryabinin Jan. 23, 2018, 9:24 a.m. UTC | #5
On 01/19/2018 08:58 PM, Andrey Konovalov wrote:
> With KASAN enabled the kernel has two different memset() functions, one
> with KASAN checks (memset) and one without (__memset). KASAN uses some
> macro tricks to use the proper version where required. For example memset()
> calls in mm/slub.c are without KASAN checks, since they operate on poisoned
> slab object metadata.
> 
> The issue is that clang emits memset() calls even when there is no memset()
> in the source code. They get linked with improper memset() implementation
> and the kernel fails to boot due to a huge amount of KASAN reports during
> early boot stages.
> 

So how did you observe this? Why am I not seeing this problem?

> The solution is to add -fno-builtin flag for files with KASAN_SANITIZE := n
> marker.
> 

I'm not sure I understand how is this solves the problem. And what clang does
instead of memset()? Does it inlines memset()? But according to GCC's man (clang's man is very vague about this)
-fno-builtin should do exactly the opposite  - prevent inlining builtin functions
and always generate a function call.
--
To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrey Konovalov Jan. 23, 2018, 4:34 p.m. UTC | #6
On Tue, Jan 23, 2018 at 10:24 AM, Andrey Ryabinin
<aryabinin@virtuozzo.com> wrote:
>
>
> On 01/19/2018 08:58 PM, Andrey Konovalov wrote:
>> With KASAN enabled the kernel has two different memset() functions, one
>> with KASAN checks (memset) and one without (__memset). KASAN uses some
>> macro tricks to use the proper version where required. For example memset()
>> calls in mm/slub.c are without KASAN checks, since they operate on poisoned
>> slab object metadata.
>>
>> The issue is that clang emits memset() calls even when there is no memset()
>> in the source code. They get linked with improper memset() implementation
>> and the kernel fails to boot due to a huge amount of KASAN reports during
>> early boot stages.
>>
>
> So how did you observe this? Why am I not seeing this problem?

I only observed this when I tried to cross-compile the kernel for
arm64 with clang. I suspect this can happen on x86 as well.

>
>> The solution is to add -fno-builtin flag for files with KASAN_SANITIZE := n
>> marker.
>>
>
> I'm not sure I understand how is this solves the problem. And what clang does
> instead of memset()? Does it inlines memset()? But according to GCC's man (clang's man is very vague about this)
> -fno-builtin should do exactly the opposite  - prevent inlining builtin functions
> and always generate a function call.

The issue is that the compiler is allowed to replace plain assignment
with memset() calls. And -fno-builtin suppresses that behavior, but
there are no mentions of this in the documentation that I could find.
I also failed to find the exact place in the source code that leads to
this, perhaps Nick can help with that.
--
To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Makefile b/Makefile
index bf5b8cbb9469..d45e31b293d0 100644
--- a/Makefile
+++ b/Makefile
@@ -432,7 +432,8 @@  export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
 
 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
-export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_KASAN CFLAGS_UBSAN
+export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
+export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN
 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index dbbd4382f15a..3fb382c69ff6 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -39,4 +39,7 @@  else
 endif
 
 CFLAGS_KASAN += $(call cc-option, -fsanitize-address-use-after-scope)
+
+CFLAGS_KASAN_NOSANITIZE := -fno-builtin
+
 endif
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 1ca4dcd2d500..015aa9dbad86 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -121,7 +121,7 @@  endif
 ifeq ($(CONFIG_KASAN),y)
 _c_flags += $(if $(patsubst n%,, \
 		$(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)y), \
-		$(CFLAGS_KASAN))
+		$(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
 endif
 
 ifeq ($(CONFIG_UBSAN),y)