[PATCH] evm: fix TOCTOU heap OOB in protected xattr reads

From: Jérémy Jean

Date: Sat Nov 07 2026 - 14:49:47 EST


ima_eventinodexattrs_init_common() queries the total size of protected
xattrs, allocates that amount, and calls evm_read_protected_xattrs()
again to fill the buffer. This creates a TOCTOU window: if the
protected xattr changes between the two calls to
evm_read_protected_xattrs(), names and lengths are written without
checking whether the data still fits in the target buffer. This can
yield an OOB heap write that KASAN reports as:

BUG: KASAN: slab-out-of-bounds in evm_read_protected_xattrs+0x15e/0x280
Write of size 20 at addr ffff888002c59bcd by task init/62

Reject fields that no longer fit in the target buffer. For xattr
values, use the length actually returned by the copy.

Fixes: 8314b6732ae4e ("ima: Define new template fields xattrnames, xattrlengths and xattrvalues")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Jérémy Jean <Jeremy.Jean@xxxxxxxxxxxxxxxxx>
---
security/integrity/evm/evm_main.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index ac8328812e6ef..4b01ca0b4753a 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -389,6 +389,10 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
case 'n':
size = strlen(xattr->name) + 1;
if (buffer) {
+ if (total_size > buffer_size ||
+ size > buffer_size - total_size)
+ return -ERANGE;
+
if (total_size)
*(buffer + total_size - 1) = '|';

@@ -398,6 +402,10 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
case 'l':
size = sizeof(u32);
if (buffer) {
+ if (total_size > buffer_size ||
+ size > buffer_size - total_size)
+ return -ERANGE;
+
if (canonical_fmt)
rc = (__force int)cpu_to_le32(rc);

@@ -407,12 +415,17 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
case 'v':
size = rc;
if (buffer) {
+ if (total_size > buffer_size ||
+ size > buffer_size - total_size)
+ return -ERANGE;
+
rc = __vfs_getxattr(dentry,
d_backing_inode(dentry), xattr->name,
buffer + total_size,
buffer_size - total_size);
if (rc < 0)
return rc;
+ size = rc;
}
break;
default:
--
2.47.3