kselftest/arm64: Verify mte tag inclusion via prctl
authorAmit Daniel Kachhap <amit.kachhap@arm.com>
Fri, 2 Oct 2020 11:56:26 +0000 (17:26 +0530)
committerWill Deacon <will@kernel.org>
Mon, 5 Oct 2020 17:52:17 +0000 (18:52 +0100)
This testcase verifies that the tag generated with "irg" instruction
contains only included tags. This is done via prtcl call.

This test covers 4 scenarios,
* At least one included tag.
* More than one included tags.
* All included.
* None included.

Co-developed-by: Gabor Kertesz <gabor.kertesz@arm.com>
Signed-off-by: Gabor Kertesz <gabor.kertesz@arm.com>
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@arm.com>
Tested-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20201002115630.24683-3-amit.kachhap@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
tools/testing/selftests/arm64/mte/.gitignore
tools/testing/selftests/arm64/mte/check_tags_inclusion.c [new file with mode: 0644]

diff --git a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
new file mode 100644 (file)
index 0000000..94d245a
--- /dev/null
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 ARM Limited
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+#include <sys/wait.h>
+
+#include "kselftest.h"
+#include "mte_common_util.h"
+#include "mte_def.h"
+
+#define BUFFER_SIZE            (5 * MT_GRANULE_SIZE)
+#define RUNS                   (MT_TAG_COUNT * 2)
+#define MTE_LAST_TAG_MASK      (0x7FFF)
+
+static int verify_mte_pointer_validity(char *ptr, int mode)
+{
+       mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE);
+       /* Check the validity of the tagged pointer */
+       memset((void *)ptr, '1', BUFFER_SIZE);
+       mte_wait_after_trig();
+       if (cur_mte_cxt.fault_valid)
+               return KSFT_FAIL;
+       /* Proceed further for nonzero tags */
+       if (!MT_FETCH_TAG((uintptr_t)ptr))
+               return KSFT_PASS;
+       mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE + 1);
+       /* Check the validity outside the range */
+       ptr[BUFFER_SIZE] = '2';
+       mte_wait_after_trig();
+       if (!cur_mte_cxt.fault_valid)
+               return KSFT_FAIL;
+       else
+               return KSFT_PASS;
+}
+
+static int check_single_included_tags(int mem_type, int mode)
+{
+       char *ptr;
+       int tag, run, result = KSFT_PASS;
+
+       ptr = (char *)mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
+       if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
+                                  mem_type, false) != KSFT_PASS)
+               return KSFT_FAIL;
+
+       for (tag = 0; (tag < MT_TAG_COUNT) && (result == KSFT_PASS); tag++) {
+               mte_switch_mode(mode, MT_INCLUDE_VALID_TAG(tag));
+               /* Try to catch a excluded tag by a number of tries. */
+               for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
+                       ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
+                       /* Check tag value */
+                       if (MT_FETCH_TAG((uintptr_t)ptr) == tag) {
+                               ksft_print_msg("FAIL: wrong tag = 0x%x with include mask=0x%x\n",
+                                              MT_FETCH_TAG((uintptr_t)ptr),
+                                              MT_INCLUDE_VALID_TAG(tag));
+                               result = KSFT_FAIL;
+                               break;
+                       }
+                       result = verify_mte_pointer_validity(ptr, mode);
+               }
+       }
+       mte_free_memory_tag_range((void *)ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
+       return result;
+}
+
+static int check_multiple_included_tags(int mem_type, int mode)
+{
+       char *ptr;
+       int tag, run, result = KSFT_PASS;
+       unsigned long excl_mask = 0;
+
+       ptr = (char *)mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
+       if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
+                                  mem_type, false) != KSFT_PASS)
+               return KSFT_FAIL;
+
+       for (tag = 0; (tag < MT_TAG_COUNT - 1) && (result == KSFT_PASS); tag++) {
+               excl_mask |= 1 << tag;
+               mte_switch_mode(mode, MT_INCLUDE_VALID_TAGS(excl_mask));
+               /* Try to catch a excluded tag by a number of tries. */
+               for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
+                       ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
+                       /* Check tag value */
+                       if (MT_FETCH_TAG((uintptr_t)ptr) < tag) {
+                               ksft_print_msg("FAIL: wrong tag = 0x%x with include mask=0x%x\n",
+                                              MT_FETCH_TAG((uintptr_t)ptr),
+                                              MT_INCLUDE_VALID_TAGS(excl_mask));
+                               result = KSFT_FAIL;
+                               break;
+                       }
+                       result = verify_mte_pointer_validity(ptr, mode);
+               }
+       }
+       mte_free_memory_tag_range((void *)ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
+       return result;
+}
+
+static int check_all_included_tags(int mem_type, int mode)
+{
+       char *ptr;
+       int run, result = KSFT_PASS;
+
+       ptr = (char *)mte_allocate_memory(BUFFER_SIZE + MT_GRANULE_SIZE, mem_type, 0, false);
+       if (check_allocated_memory(ptr, BUFFER_SIZE + MT_GRANULE_SIZE,
+                                  mem_type, false) != KSFT_PASS)
+               return KSFT_FAIL;
+
+       mte_switch_mode(mode, MT_INCLUDE_TAG_MASK);
+       /* Try to catch a excluded tag by a number of tries. */
+       for (run = 0; (run < RUNS) && (result == KSFT_PASS); run++) {
+               ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
+               /*
+                * Here tag byte can be between 0x0 to 0xF (full allowed range)
+                * so no need to match so just verify if it is writable.
+                */
+               result = verify_mte_pointer_validity(ptr, mode);
+       }
+       mte_free_memory_tag_range((void *)ptr, BUFFER_SIZE, mem_type, 0, MT_GRANULE_SIZE);
+       return result;
+}
+
+static int check_none_included_tags(int mem_type, int mode)
+{
+       char *ptr;
+       int run;
+
+       ptr = (char *)mte_allocate_memory(BUFFER_SIZE, mem_type, 0, false);
+       if (check_allocated_memory(ptr, BUFFER_SIZE, mem_type, false) != KSFT_PASS)
+               return KSFT_FAIL;
+
+       mte_switch_mode(mode, MT_EXCLUDE_TAG_MASK);
+       /* Try to catch a excluded tag by a number of tries. */
+       for (run = 0; run < RUNS; run++) {
+               ptr = (char *)mte_insert_tags(ptr, BUFFER_SIZE);
+               /* Here all tags exluded so tag value generated should be 0 */
+               if (MT_FETCH_TAG((uintptr_t)ptr)) {
+                       ksft_print_msg("FAIL: included tag value found\n");
+                       mte_free_memory((void *)ptr, BUFFER_SIZE, mem_type, true);
+                       return KSFT_FAIL;
+               }
+               mte_initialize_current_context(mode, (uintptr_t)ptr, BUFFER_SIZE);
+               /* Check the write validity of the untagged pointer */
+               memset((void *)ptr, '1', BUFFER_SIZE);
+               mte_wait_after_trig();
+               if (cur_mte_cxt.fault_valid)
+                       break;
+       }
+       mte_free_memory((void *)ptr, BUFFER_SIZE, mem_type, false);
+       if (cur_mte_cxt.fault_valid)
+               return KSFT_FAIL;
+       else
+               return KSFT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+       int err;
+
+       err = mte_default_setup();
+       if (err)
+               return err;
+
+       /* Register SIGSEGV handler */
+       mte_register_signal(SIGSEGV, mte_default_handler);
+
+       evaluate_test(check_single_included_tags(USE_MMAP, MTE_SYNC_ERR),
+                     "Check an included tag value with sync mode\n");
+       evaluate_test(check_multiple_included_tags(USE_MMAP, MTE_SYNC_ERR),
+                     "Check different included tags value with sync mode\n");
+       evaluate_test(check_none_included_tags(USE_MMAP, MTE_SYNC_ERR),
+                     "Check none included tags value with sync mode\n");
+       evaluate_test(check_all_included_tags(USE_MMAP, MTE_SYNC_ERR),
+                     "Check all included tags value with sync mode\n");
+
+       mte_restore_setup();
+       ksft_print_cnts();
+       return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
+}