[PATCH v14 8/8] kselftest/arm64: Verify that TCO is enabled in load_unaligned_zeropad()

From: Vincenzo Frascino
Date: Mon Mar 08 2021 - 11:16:08 EST


load_unaligned_zeropad() and __get/put_kernel_nofault() functions can
read passed some buffer limits which may include some MTE granule with a
different tag.

When MTE async mode is enable, the load operation crosses the boundaries
and the next granule has a different tag the PE sets the TFSR_EL1.TF1
bit as if an asynchronous tag fault is happened:

==================================================================
BUG: KASAN: invalid-access
Asynchronous mode enabled: no access details available

CPU: 0 PID: 1 Comm: init Not tainted 5.12.0-rc1-ge1045c86620d-dirty #8
Hardware name: FVP Base RevC (DT)
Call trace:
dump_backtrace+0x0/0x1c0
show_stack+0x18/0x24
dump_stack+0xcc/0x14c
kasan_report_async+0x54/0x70
mte_check_tfsr_el1+0x48/0x4c
exit_to_user_mode+0x18/0x38
finish_ret_to_user+0x4/0x15c
==================================================================

Verify that Tag Check Override (TCO) is enabled in these functions before
the load and disable it afterwards to prevent this to happen.

Note: The issue has been observed only with an MTE enabled userspace.

Cc: Catalin Marinas <catalin.marinas@xxxxxxx>
Cc: Will Deacon <will@xxxxxxxxxx>
Reported-by: Branislav Rankov <Branislav.Rankov@xxxxxxx>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@xxxxxxx>
---
.../arm64/mte/check_read_beyond_buffer.c | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 tools/testing/selftests/arm64/mte/check_read_beyond_buffer.c

diff --git a/tools/testing/selftests/arm64/mte/check_read_beyond_buffer.c b/tools/testing/selftests/arm64/mte/check_read_beyond_buffer.c
new file mode 100644
index 000000000000..eb03cd52a58e
--- /dev/null
+++ b/tools/testing/selftests/arm64/mte/check_read_beyond_buffer.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 ARM Limited
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "kselftest.h"
+#include "mte_common_util.h"
+#include "mte_def.h"
+
+#define NUM_DEVICES 8
+
+static char *dev[NUM_DEVICES] = {
+ "/proc/cmdline",
+ "/fstab.fvp",
+ "/dev/null",
+ "/proc/mounts",
+ "/proc/filesystems",
+ "/proc/cmdline",
+ "/proc/device-tre", /* incorrect path */
+ "",
+};
+
+#define FAKE_PERMISSION 0x88000
+#define MAX_DESCRIPTOR 0xffffffff
+
+int mte_read_beyond_buffer_test(void)
+{
+ int fd[NUM_DEVICES];
+ unsigned int _desc, _dev;
+
+ for (_desc = 0; _desc <= MAX_DESCRIPTOR; _desc++) {
+ for (_dev = 0; _dev < NUM_DEVICES; _dev++) {
+#ifdef _TEST_DEBUG
+ printf("[TEST]: openat(0x%x, %s, 0x%x)\n", _desc, dev[_dev], FAKE_PERMISSION);
+#endif
+
+ fd[_dev] = openat(_desc, dev[_dev], FAKE_PERMISSION);
+ }
+
+ for (_dev = 0; _dev <= NUM_DEVICES; _dev++)
+ close(fd[_dev]);
+ }
+
+ return KSFT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+
+ err = mte_default_setup();
+ if (err)
+ return err;
+
+ ksft_set_plan(1);
+
+ evaluate_test(mte_read_beyond_buffer_test(),
+ "Verify that TCO is enabled correctly if a read beyond buffer occurs\n");
+
+ mte_restore_setup();
+ ksft_print_cnts();
+
+ return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
+}
--
2.30.0