On 4/7/2026 1:31 PM, Maciej Wieczor-Retman wrote:
> On 2026-04-07 at 12:52:07 -0700, Sohil Mehta wrote:
>>> + mm->context.untag_mask = ~GENMASK(57 + LAM_DEFAULT_BITS - 1, 57);
>>>
>>
>> Also, would it be useful to calculate the LAM mask as a #define because
>> it might need to be reused later or copied over to the selftest (as in
>> patch 3)?
>
> I think as it's only used during this initialization here it's not very useful
> to give it a separate #define. And if it's only for selftest purposes it's
> probably not worth it to export it to userspace.
>
Oh, I didn't mean export to userspace. I just meant having these two
defined together in arch/x86/kernel/process_64.c mainly for readability.
It makes copying the define over to the selftest easier. The names also
match ARCH_GET_MAX_TAG_BITS and ARCH_GET_UNTAG_MASK.
#define LAM_TAG_BITS 4
#define LAM_UNTAG_MASK (~GENMASK(57 + LAM_TAG_BITS - 1, 57))
To me, it makes the resulting code significantly more readable. I am
suggesting it because you are already touching these lines in patch 1
and 3. I'll leave it up to your/maintainer's preference, but personally
I like the below result:
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 1a0e96835bbc..745e16bde227 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -797,7 +797,8 @@ static long prctl_map_vdso(const struct vdso_image
*image, unsigned long addr)
#ifdef CONFIG_ADDRESS_MASKING
-#define LAM_DEFAULT_BITS 4
+#define LAM_TAG_BITS 4
+#define LAM_UNTAG_MASK (~GENMASK(57 + LAM_TAG_BITS - 1, 57))
static void enable_lam_func(void *__mm)
{
@@ -814,7 +815,7 @@ static void enable_lam_func(void *__mm)
static void mm_enable_lam(struct mm_struct *mm)
{
mm->context.lam_cr3_mask = X86_CR3_LAM_U57;
- mm->context.untag_mask = ~GENMASK(57 + LAM_DEFAULT_BITS - 1, 57);
+ mm->context.untag_mask = LAM_UNTAG_MASK;
/*
* Even though the process must still be single-threaded at this
@@ -850,7 +851,7 @@ static int prctl_enable_tagged_addr(struct mm_struct
*mm, unsigned long nr_bits)
return -EBUSY;
}
- if (!nr_bits || nr_bits > LAM_DEFAULT_BITS) {
+ if (!nr_bits || nr_bits > LAM_TAG_BITS) {
mmap_write_unlock(mm);
return -EINVAL;
}
@@ -965,7 +966,7 @@ long do_arch_prctl_64(struct task_struct *task, int
option, unsigned long arg2)
if (!cpu_feature_enabled(X86_FEATURE_LAM))
return put_user(0, (unsigned long __user *)arg2);
else
- return put_user(LAM_DEFAULT_BITS, (unsigned long __user
*)arg2);
+ return put_user(LAM_TAG_BITS, (unsigned long __user
*)arg2);
#endif
case ARCH_SHSTK_ENABLE:
case ARCH_SHSTK_DISABLE:
diff --git a/tools/testing/selftests/x86/lam.c
b/tools/testing/selftests/x86/lam.c
index d27f947ea694..4e514cae27f2 100644
--- a/tools/testing/selftests/x86/lam.c
+++ b/tools/testing/selftests/x86/lam.c
@@ -17,6 +17,7 @@
#include <sched.h>
#include <sys/uio.h>
+#include <linux/bits.h>
#include <linux/io_uring.h>
#include "kselftest.h"
@@ -26,9 +27,9 @@
/* LAM modes, these definitions were copied from kernel code */
#define LAM_NONE 0
-#define LAM_BITS 4
+#define LAM_TAG_BITS 4
+#define LAM_UNTAG_MASK (~GENMASK(57 + LAM_TAG_BITS - 1, 57))
-#define LAM_MASK (0xfULL << 57)
/* arch prctl for LAM */
#define ARCH_GET_UNTAG_MASK 0x4001
#define ARCH_ENABLE_TAGGED_ADDR 0x4002
@@ -175,7 +176,7 @@ static int set_lam(unsigned long lam)
int ret = 0;
uint64_t ptr = 0;
- if (lam != LAM_BITS && lam != LAM_NONE)
+ if (lam != LAM_TAG_BITS && lam != LAM_NONE)
return -1;
/* Skip check return */
@@ -185,8 +186,8 @@ static int set_lam(unsigned long lam)
syscall(SYS_arch_prctl, ARCH_GET_UNTAG_MASK, &ptr);
/* Check mask returned is expected */
- if (lam == LAM_BITS)
- ret = (ptr != ~(LAM_MASK));
+ if (lam == LAM_TAG_BITS)
+ ret = (ptr != LAM_UNTAG_MASK);
else if (lam == LAM_NONE)
ret = (ptr != -1ULL);
@@ -204,8 +205,8 @@ static unsigned long get_default_tag_bits(void)
perror("Fork failed.");
} else if (pid == 0) {
/* Set LAM mode in child process */
- if (set_lam(LAM_BITS) == 0)
- lam = LAM_BITS;
+ if (set_lam(LAM_TAG_BITS) == 0)
+ lam = LAM_TAG_BITS;
else
lam = LAM_NONE;
exit(lam);
@@ -230,8 +231,8 @@ static int get_lam(void)
return -1;
/* Check mask returned is expected */
- if (ptr == ~(LAM_MASK))
- ret = LAM_BITS;
+ if (ptr == LAM_UNTAG_MASK)
+ ret = LAM_TAG_BITS;
else if (ptr == -1ULL)
ret = LAM_NONE;
@@ -247,10 +248,10 @@ static uint64_t set_metadata(uint64_t src,
unsigned long lam)
srand(time(NULL));
switch (lam) {
- case LAM_BITS: /* Set metadata in bits 62:57 */
+ case LAM_TAG_BITS: /* Set metadata in bits 60:57 */
/* Get a random non-zero value as metadata */
- metadata = (rand() % ((1UL << LAM_BITS) - 1) + 1) << 57;
- metadata |= (src & ~(LAM_MASK));
+ metadata = (rand() % ((1UL << LAM_TAG_BITS) - 1) + 1) << 57;
+ metadata |= (src & LAM_UNTAG_MASK);
break;
default:
metadata = src;
@@ -291,7 +292,7 @@ int handle_max_bits(struct testcases *test)
unsigned long bits = 0;
if (exp_bits != LAM_NONE)
- exp_bits = LAM_BITS;
+ exp_bits = LAM_TAG_BITS;
/* Get LAM max tag bits */
if (syscall(SYS_arch_prctl, ARCH_GET_MAX_TAG_BITS, &bits) == -1)
@@ -719,8 +720,8 @@ int do_uring(unsigned long lam)
uint64_t addr = ((uint64_t)fi->iovecs[i].iov_base);
switch (lam) {
- case LAM_BITS: /* Clear bits 60:57 */
- addr = (addr & ~(LAM_MASK));
+ case LAM_TAG_BITS: /* Clear bits 60:57 */
+ addr = (addr & LAM_UNTAG_MASK);
break;
}
free((void *)addr);
@@ -937,14 +938,14 @@ static void run_test(struct testcases *test, int
count)
static struct testcases uring_cases[] = {
{
.later = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_uring,
.msg = "URING: LAM. Dereferencing pointer with metadata\n",
},
{
.later = 1,
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_uring,
.msg = "URING:[Negative] Disable LAM. Dereferencing pointer with
metadata.\n",
},
@@ -953,14 +954,14 @@ static struct testcases uring_cases[] = {
static struct testcases malloc_cases[] = {
{
.later = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_malloc,
.msg = "MALLOC: LAM. Dereferencing pointer with metadata\n",
},
{
.later = 1,
.expected = 2,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_malloc,
.msg = "MALLOC:[Negative] Disable LAM. Dereferencing pointer
with
metadata.\n",
},
@@ -976,41 +977,41 @@ static struct testcases bits_cases[] = {
static struct testcases syscall_cases[] = {
{
.later = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_syscall,
.msg = "SYSCALL: LAM. syscall with metadata\n",
},
{
.later = 1,
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_syscall,
.msg = "SYSCALL:[Negative] Disable LAM. Dereferencing pointer
with
metadata.\n",
},
{
.later = GET_USER_USER,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER: get_user() and pass a properly tagged user
pointer.\n",
},
{
.later = GET_USER_KERNEL_TOP,
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() with a kernel pointer
and the
top bit cleared.\n",
},
{
.later = GET_USER_KERNEL_BOT,
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() with a kernel pointer
and the
bottom sign-extension bit cleared.\n",
},
{
.later = GET_USER_KERNEL,
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = get_user_syscall,
.msg = "GET_USER:[Negative] get_user() and pass a kernel
pointer.\n",
},
@@ -1020,7 +1021,7 @@ static struct testcases mmap_cases[] = {
{
.later = 1,
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.addr = HIGH_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First mmap high address, then set LAM.\n",
@@ -1028,7 +1029,7 @@ static struct testcases mmap_cases[] = {
{
.later = 0,
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.addr = HIGH_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First LAM, then High address.\n",
@@ -1036,7 +1037,7 @@ static struct testcases mmap_cases[] = {
{
.later = 0,
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.addr = LOW_ADDR,
.test_func = handle_mmap,
.msg = "MMAP: First LAM, then Low address.\n",
@@ -1046,32 +1047,32 @@ static struct testcases mmap_cases[] = {
static struct testcases inheritance_cases[] = {
{
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_inheritance,
.msg = "FORK: LAM, child process should get LAM mode same as
parent\n",
},
{
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_thread,
.msg = "THREAD: LAM, child thread should get LAM mode same as
parent\n",
},
{
.expected = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_thread_enable,
.msg = "THREAD: [NEGATIVE] Enable LAM in child.\n",
},
{
.expected = 1,
.later = 1,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_thread,
.msg = "THREAD: [NEGATIVE] Enable LAM in parent after thread
created.\n",
},
{
.expected = 0,
- .lam = LAM_BITS,
+ .lam = LAM_TAG_BITS,
.test_func = handle_execve,
.msg = "EXECVE: LAM, child process should get disabled LAM
mode\n",
},
@@ -1224,7 +1225,7 @@ int handle_pasid(struct testcases *test)
if (tmp & 0x1) {
/* run set lam mode*/
if ((runed & 0x1) == 0) {
- err = set_lam(LAM_BITS);
+ err = set_lam(LAM_TAG_BITS);
runed = runed | 0x1;
} else
err = 1;