From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
With the announcement of ChkTag, it's worth preparing a stable x86
linear address masking (lam) user interface. One important aspect of lam
is the tag width, and aligning it with other industry solutions can
provide a more popular, generalized interface that other technologies
could utilize.
ChkTag will use 4-bit tags and since that's the direction other memory
tagging implementations seem to be taking too (for example Arm's MTE)
it's reasonable to converge lam in linux to the same specification. Even
though x86's LAM supports 6-bit tags it is beneficial to shorten lam to
4 bits as ChkTag will likely be the main user of the interface and such
connection should simplify things in the future.
Shrink the maximum acceptable tag width from 6 to 4.
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
Changelog v4:
- Ditch the default wording in the patch message.
- Add the imperative last line as Dave suggested.
Changelog v3:
- Remove the variability of the lam width after the debugfs part was
removed from the patchset.
arch/x86/kernel/process_64.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 08e72f429870..1a0e96835bbc 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -797,7 +797,7 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
#ifdef CONFIG_ADDRESS_MASKING
-#define LAM_U57_BITS 6
+#define LAM_DEFAULT_BITS 4
static void enable_lam_func(void *__mm)
{
@@ -814,7 +814,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(62, 57);
+ mm->context.untag_mask = ~GENMASK(57 + LAM_DEFAULT_BITS - 1, 57);
/*
* Even though the process must still be single-threaded at this
@@ -850,7 +850,7 @@ static int prctl_enable_tagged_addr(struct mm_struct *mm, unsigned long nr_bits)
return -EBUSY;
}
- if (!nr_bits || nr_bits > LAM_U57_BITS) {
+ if (!nr_bits || nr_bits > LAM_DEFAULT_BITS) {
mmap_write_unlock(mm);
return -EINVAL;
}
@@ -965,7 +965,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_U57_BITS, (unsigned long __user *)arg2);
+ return put_user(LAM_DEFAULT_BITS, (unsigned long __user *)arg2);
#endif
case ARCH_SHSTK_ENABLE:
case ARCH_SHSTK_DISABLE:
--
2.53.0
On Tue, 07 Apr 2026 17:45:20 +0000
Maciej Wieczor-Retman <m.wieczorretman@pm.me> wrote:
> From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>
> With the announcement of ChkTag, it's worth preparing a stable x86
> linear address masking (lam) user interface. One important aspect of lam
> is the tag width, and aligning it with other industry solutions can
> provide a more popular, generalized interface that other technologies
> could utilize.
>
> ChkTag will use 4-bit tags and since that's the direction other memory
> tagging implementations seem to be taking too (for example Arm's MTE)
> it's reasonable to converge lam in linux to the same specification. Even
> though x86's LAM supports 6-bit tags it is beneficial to shorten lam to
> 4 bits as ChkTag will likely be the main user of the interface and such
> connection should simplify things in the future.
>
> Shrink the maximum acceptable tag width from 6 to 4.
>
> Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
> ---
> Changelog v4:
> - Ditch the default wording in the patch message.
> - Add the imperative last line as Dave suggested.
>
> Changelog v3:
> - Remove the variability of the lam width after the debugfs part was
> removed from the patchset.
>
> arch/x86/kernel/process_64.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
> index 08e72f429870..1a0e96835bbc 100644
> --- a/arch/x86/kernel/process_64.c
> +++ b/arch/x86/kernel/process_64.c
> @@ -797,7 +797,7 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
>
> #ifdef CONFIG_ADDRESS_MASKING
>
> -#define LAM_U57_BITS 6
> +#define LAM_DEFAULT_BITS 4
>
> static void enable_lam_func(void *__mm)
> {
> @@ -814,7 +814,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(62, 57);
> + mm->context.untag_mask = ~GENMASK(57 + LAM_DEFAULT_BITS - 1, 57);
I'm not sure that GENMASK() is really the best way to describe that value.
It really is ((1ul << LAM_BITS) - 1) << 57 and even the 57 shouldn't be
a magic constant.
I also wonder how userspace knows which bits to use. The other patches
just seem to handle a count from userspace, but you aren't giving out
the highest available bits.
If this had been done for 48bit vaddr, you would really have wished that
that bits 62-59 had been used not 51-48.
David
On 2026-04-07 at 22:36:53 +0100, David Laight wrote:
>On Tue, 07 Apr 2026 17:45:20 +0000
>Maciej Wieczor-Retman <m.wieczorretman@pm.me> wrote:
>
>> From: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>>
>> With the announcement of ChkTag, it's worth preparing a stable x86
>> linear address masking (lam) user interface. One important aspect of lam
>> is the tag width, and aligning it with other industry solutions can
>> provide a more popular, generalized interface that other technologies
>> could utilize.
>>
>> ChkTag will use 4-bit tags and since that's the direction other memory
>> tagging implementations seem to be taking too (for example Arm's MTE)
>> it's reasonable to converge lam in linux to the same specification. Even
>> though x86's LAM supports 6-bit tags it is beneficial to shorten lam to
>> 4 bits as ChkTag will likely be the main user of the interface and such
>> connection should simplify things in the future.
>>
>> Shrink the maximum acceptable tag width from 6 to 4.
>>
>> Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
>> ---
>> Changelog v4:
>> - Ditch the default wording in the patch message.
>> - Add the imperative last line as Dave suggested.
>>
>> Changelog v3:
>> - Remove the variability of the lam width after the debugfs part was
>> removed from the patchset.
>>
>> arch/x86/kernel/process_64.c | 8 ++++----
>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
>> index 08e72f429870..1a0e96835bbc 100644
>> --- a/arch/x86/kernel/process_64.c
>> +++ b/arch/x86/kernel/process_64.c
>> @@ -797,7 +797,7 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
>>
>> #ifdef CONFIG_ADDRESS_MASKING
>>
>> -#define LAM_U57_BITS 6
>> +#define LAM_DEFAULT_BITS 4
>>
>> static void enable_lam_func(void *__mm)
>> {
>> @@ -814,7 +814,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(62, 57);
>> + mm->context.untag_mask = ~GENMASK(57 + LAM_DEFAULT_BITS - 1, 57);
>
>I'm not sure that GENMASK() is really the best way to describe that value.
>It really is ((1ul << LAM_BITS) - 1) << 57 and even the 57 shouldn't be
>a magic constant.
I recall people were annoyed when I previously open coded something that could
have bee na GENMASK() instead. Is there a downside to using GENMASK() here?
>I also wonder how userspace knows which bits to use. The other patches
>just seem to handle a count from userspace, but you aren't giving out
>the highest available bits.
I'd imagine if someone was writing a userspace program that'd interface with LAM
they'd have to know which bits are okay to use.
But maybe I'm misunderstanding what you meant.
>If this had been done for 48bit vaddr, you would really have wished that
>that bits 62-59 had been used not 51-48.
>
> David
>
--
Kind regards
Maciej Wieczór-Retman
On 4/7/2026 10:45 AM, Maciej Wieczor-Retman wrote:
> diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
> index 08e72f429870..1a0e96835bbc 100644
> --- a/arch/x86/kernel/process_64.c
> +++ b/arch/x86/kernel/process_64.c
> @@ -797,7 +797,7 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
>
> #ifdef CONFIG_ADDRESS_MASKING
>
> -#define LAM_U57_BITS 6
> +#define LAM_DEFAULT_BITS 4
>
As Dave mentioned earlier, the default wording makes it seem like this
can be configured. But that isn't true anymore, right?
How about naming this as LAM_TAG_BITS to match ARCH_GET_MAX_TAG_BITS?
> static void enable_lam_func(void *__mm)
> {
> @@ -814,7 +814,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(62, 57);
> + 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)?
On 2026-04-07 at 12:52:07 -0700, Sohil Mehta wrote:
>On 4/7/2026 10:45 AM, Maciej Wieczor-Retman wrote:
>
>> diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
>> index 08e72f429870..1a0e96835bbc 100644
>> --- a/arch/x86/kernel/process_64.c
>> +++ b/arch/x86/kernel/process_64.c
>> @@ -797,7 +797,7 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
>>
>> #ifdef CONFIG_ADDRESS_MASKING
>>
>> -#define LAM_U57_BITS 6
>> +#define LAM_DEFAULT_BITS 4
>>
>
>As Dave mentioned earlier, the default wording makes it seem like this
>can be configured. But that isn't true anymore, right?
>
>How about naming this as LAM_TAG_BITS to match ARCH_GET_MAX_TAG_BITS?
Right, I'll rename it, thanks!
>> static void enable_lam_func(void *__mm)
>> {
>> @@ -814,7 +814,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(62, 57);
>> + 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.
--
Kind regards
Maciej Wieczór-Retman
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;
On 2026-04-07 at 14:30:32 -0700, Sohil Mehta wrote:
>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
...
Oh, okay, yeah, that does look better. Thanks, if there are no objections I'll
apply that!
--
Kind regards
Maciej Wieczór-Retman
© 2016 - 2026 Red Hat, Inc.