On Tue, Nov 16, 2021 at 07:46PM +0530, Kaiwan N Billimoria wrote:
> On Tue, 2021-11-16 at 12:52 +0100, Marco Elver wrote:
> >
> > KASAN globals support used to be limited in Clang. This was fixed in
> > Clang 11. I'm not sure about GCC.
> ...
> > > Which compiler versions are you using? This is probably the most
> > important piece to the puzzle.
> >
> Right! This is the primary issue i think, thanks!
> am currently using gcc 9.3.0.
>
> So, my Ubuntu system had clang-10; I installed clang-11 on top of it...
> (this causes some issues?). Updated the Makefile to use clang-11, and it did build.
Only the test or the whole kernel? You need to build the whole kernel
and your module with the same compiler, otherwise all bets are off wrt
things like KASAN.
> But when running these tests, *only* UBSAN was triggered, KASAN unseen.
> So: I then rebuilt the 5.10.60 kernel removing UBSAN config and retried (same module rebuilt w/ clang 11).
> This time UBSAN didn't pop up but nor did KASAN ! (For the same rd/wr underflow testcases)...
> My script + dmesg:
> ...
> (Type in the testcase number to run):
> 4.4
> Running testcase "4.4" via test module now...
> [ 371.368096] testcase to run: 4.4
> $
>
> This implies it escaped unnoticed..
>
> To show the difference, here's my testcase #4.1- Read (right) overflow on global memory - output:
>
> Running testcase "4.1" via test module now...
> [ 1372.401484] testcase to run: 4.1
> [ 1372.401515] ==================================================================
> [ 1372.402284] BUG: KASAN: global-out-of-bounds in static_mem_oob_right+0xaf/0x160 [test_kmembugs]
> [ 1372.402851] Read of size 1 at addr ffffffffc088dfcc by task run_tests/1656
>
> [ 1372.403428] CPU: 2 PID: 1656 Comm: run_tests Tainted: G B O 5.10.60-dbg02 #14
> [ 1372.403442] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
> [ 1372.403454] Call Trace:
> [ 1372.403486] dump_stack+0xbd/0xfa
>
> [... lots more, as expected ...]
>
> So, am puzzled... why isn't KASAN catching the underflow...
Please take a look at the paragraph at:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/lib/test_kasan.c#n706
I think your test is giving the compiler opportunities to miscompile
your code, because, well it has undefined behaviour (negative index)
that it very clearly can see. I think you need to put more effort into
hiding the UB from the optimizer like we do in test_kasan.c.
If you want to know in detail what's happening I recommend you
disassemble your compiled code and check if the negative dereferences
are still there.
> A couple of caveats:
> 1) I had to manually setup a soft link to llvm-objdump (it was installed as llvm-objdump-11)
> 2) the module build initially failed with
> /bin/sh: 1: ld.lld: not found
> So I installed the 'lld' package; then the build worked..
>
> Any thoughts?
Is this "make LLVM=1". Yeah, if there's a version suffix it's known to
be problematic.
You can just build the kernel with "make CC=clang" and it'll use
binutils ld, which works as well.
> > FWIW, the kernel has its own KASAN test suite in lib/test_kasan.c.
> > There are a few things to not make the compiler optimize away
> > explicitly buggy code, so I'd also suggest you embed your test in
> > test_kasan and see if it changes anything (unlikely but worth a shot).
> I have studied it, and essentially copied it's techniques where required... Interestingly, the kernel's test_kasan module does _not_ have a test case for this: underflow on global memory! :-)
I just added such a test (below) and it passes just fine with clang 11
(I'll probably send it as a real patch later). Notice that the address
itself ("array") is a volatile, so that the compiler cannot make any
assumptions about it.
Thanks,
-- Marco
------ >8 ------
diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index 67ed689a0b1b..e56c9eb3f16e 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -700,7 +700,7 @@ static void kmem_cache_bulk(struct kunit *test)
static char global_array[10];
-static void kasan_global_oob(struct kunit *test)
+static void kasan_global_oob_right(struct kunit *test)
{
/*
* Deliberate out-of-bounds access. To prevent CONFIG_UBSAN_LOCAL_BOUNDS
@@ -723,6 +723,15 @@ static void kasan_global_oob(struct kunit *test)
KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p);
}
+static void kasan_global_oob_left(struct kunit *test)
+{
+ char *volatile array = global_array;
+ char *p = array - 3;
+
+ KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC);
+ KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p);
+}
+
/* Check that ksize() makes the whole object accessible. */
static void ksize_unpoisons_memory(struct kunit *test)
{
@@ -1160,7 +1169,8 @@ static struct kunit_case kasan_kunit_test_cases[] = {
KUNIT_CASE(kmem_cache_oob),
KUNIT_CASE(kmem_cache_accounted),
KUNIT_CASE(kmem_cache_bulk),
- KUNIT_CASE(kasan_global_oob),
+ KUNIT_CASE(kasan_global_oob_right),
+ KUNIT_CASE(kasan_global_oob_left),
KUNIT_CASE(kasan_stack_oob),
KUNIT_CASE(kasan_alloca_oob_left),
KUNIT_CASE(kasan_alloca_oob_right),