From: jinrui <[email protected]>
The pointer masking selftest contains several bugs that cause it to fail
on real RISC-V hardware:
1. test_pmlen() did not pass PR_TAGGED_ADDR_ENABLE when requesting a
non-zero PMLEN. The kernel requires this flag; without it, PMLEN is
forced to 0 regardless of the requested value (see process.c:352).
2. set_tagged_addr_ctrl() required the PR_GET return value to exactly
match the PR_SET argument. However, the kernel may legitimately:
- Force PMLEN to 0 when the ABI is disabled (tagged_addr_abi=false)
- Round up PMLEN (e.g., request=1 -> PMLEN=7 or PMLEN=16)
3. test_dereference_pmlen() always passed tagged_addr_abi=false, even
for non-zero PMLEN values. This meant PMLEN was never actually
enabled, so the valid tag dereference test always triggered SIGSEGV.
4. test_fork_exec() passed tagged_addr_abi=false, causing the fork
child's tagged pointer dereference to fail because the inherited
PMLEN was 0.
Additionally, add an explicit #include <stdint.h> for uintptr_t, which
is required by the C standard and must be included explicitly on some
compiler/libc combinations.
Fixes: 7470b5afd150 ("riscv: selftests: Add a pointer masking test")
Signed-off-by: jinrui <[email protected]>
---
.../selftests/riscv/abi/pointer_masking.c | 34 +++++++++++++++++--
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/riscv/abi/pointer_masking.c
b/tools/testing/selftests/riscv/abi/pointer_masking.c
index 2d540af7b558..106f12f1ea44 100644
--- a/tools/testing/selftests/riscv/abi/pointer_masking.c
+++ b/tools/testing/selftests/riscv/abi/pointer_masking.c
@@ -5,6 +5,7 @@
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
+#include <stdint.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -17,6 +18,9 @@
#ifndef PR_PMLEN_MASK
#define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT)
#endif
+#ifndef PR_TAGGED_ADDR_ENABLE
+#define PR_TAGGED_ADDR_ENABLE 1
+#endif
static int dev_zero;
@@ -44,7 +48,10 @@ static void test_pmlen(void)
for (int request = 0; request <= 16; request++) {
int pmlen, ret;
- ret = prctl(PR_SET_TAGGED_ADDR_CTRL, request << PR_PMLEN_SHIFT,
0, 0, 0);
+ ret = prctl(PR_SET_TAGGED_ADDR_CTRL,
+ request << PR_PMLEN_SHIFT |
+ (request ? PR_TAGGED_ADDR_ENABLE : 0),
+ 0, 0, 0);
if (ret)
goto pr_set_error;
@@ -85,6 +92,22 @@ static int set_tagged_addr_ctrl(int pmlen, bool
tagged_addr_abi)
ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
if (ret == arg)
return 0;
+
+ /*
+ * When tagged_addr_abi is false, the kernel forces PMLEN
+ * to 0 regardless of what was requested. Accept the result
+ * as long as PR_TAGGED_ADDR_ENABLE is not set.
+ */
+ if (!tagged_addr_abi && !(ret & PR_TAGGED_ADDR_ENABLE))
+ return 0;
+
+ /*
+ * When tagged_addr_abi is true, the kernel may round up
+ * the PMLEN (e.g. request=1 -> PMLEN=7 or PMLEN=16).
+ * Accept the result if the enable bit is set.
+ */
+ if (tagged_addr_abi && (ret & PR_TAGGED_ADDR_ENABLE))
+ return 0;
}
return ret < 0 ? -errno : -ENODATA;
@@ -96,7 +119,12 @@ static void test_dereference_pmlen(int pmlen)
volatile int *p;
int ret;
- ret = set_tagged_addr_ctrl(pmlen, false);
+ /*
+ * When pmlen is 0, the tagged address ABI cannot be enabled (the
+ * kernel rejects PR_TAGGED_ADDR_ENABLE when PMLEN is 0). Disable
+ * pointer masking and test the invalid tag path instead.
+ */
+ ret = set_tagged_addr_ctrl(pmlen, pmlen ? true : false);
if (ret)
return ksft_test_result_error("PMLEN=%d setup (%d)\n", pmlen,
ret);
@@ -157,7 +185,7 @@ static void test_fork_exec(void)
ksft_print_msg("Testing fork/exec behavior\n");
- ret = set_tagged_addr_ctrl(min_pmlen, false);
+ ret = set_tagged_addr_ctrl(min_pmlen, true);
if (ret)
return ksft_test_result_error("setup (%d)\n", ret);
--
2.43.0