[PATCH] lsm: don't call d_path() when current->fs is NULL

From: Drif Abdelmalek Mohamed Said

Date: Mon Oct 19 2026 - 18:29:16 EST


When the last reference to a file is put from exit_task_work(), the task
has already run exit_fs(), so current->fs is NULL.

syzbot reported:

Unable to handle kernel paging request at virtual address dfff800000000002
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]

Call trace:
kasan_mem_to_shadow include/linux/kasan.h:65 [inline] (P)
kasan_byte_accessible+0xc/0x1c mm/kasan/generic.c:210 (P)
kasan_check_byte include/linux/kasan.h:402 [inline]
lock_acquire+0xa4/0x320 kernel/locking/lockdep.c:5916
seqcount_lockdep_reader_access include/linux/seqlock.h:73 [inline]
read_seqbegin+0x6c/0x144 include/linux/seqlock.h:838
get_fs_root_rcu fs/d_path.c:244 [inline]
d_path+0x224/0x4dc fs/d_path.c:287
audit_log_d_path+0xdc/0x19c kernel/audit.c:2221
audit_log_lsm_data+0xe0/0x12d0 security/lsm_audit.c:199
dump_common_audit_data security/lsm_audit.c:421 [inline]
common_lsm_audit+0x1d0/0x290 security/lsm_audit.c:451
smack_log+0x1f4/0x264 security/smack/smack_access.c:400
smk_tskacc+0x210/0x2ac security/smack/smack_access.c:255
smack_file_open+0x1d0/0x230 security/smack/smack_lsm.c:2078
security_file_open+0xec/0x258 security/security.c:2739
do_dentry_open+0x2a8/0xfbc fs/open.c:973
vfs_open+0x44/0x2dc fs/open.c:1101
dentry_open+0x74/0xc0 fs/open.c:1124
ima_calc_file_hash+0x120/0x5cc security/integrity/ima/ima_crypto.c:269
ima_collect_measurement+0x420/0x874 security/integrity/ima/ima_api.c:300
ima_update_xattr+0x160/0x230 security/integrity/ima/ima_appraise.c:617
ima_check_last_writer security/integrity/ima/ima_main.c:227 [inline]
ima_file_free+0x40c/0x528 security/integrity/ima/ima_main.c:251
security_file_release+0x84/0x148 security/security.c:2424
__fput+0x2c4/0x74c fs/file_table.c:506
____fput+0x20/0x30 fs/file_table.c:541
task_work_run+0x1cc/0x25c kernel/task_work.c:233
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0x58c/0x1a74 kernel/exit.c:986
do_group_exit+0x198/0x238 kernel/exit.c:1129
__do_sys_exit_group kernel/exit.c:1140 [inline]
__se_sys_exit_group kernel/exit.c:1138 [inline]
pid_child_should_wake+0x0/0x104 kernel/exit.c:1138
__invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
invoke_syscall+0x98/0x244 arch/arm64/kernel/syscall.c:49
el0_svc_common+0x19c/0x23c arch/arm64/kernel/syscall.c:121
do_el0_svc+0x4c/0x5c arch/arm64/kernel/syscall.c:140
el0_svc+0x60/0x1d8 arch/arm64/kernel/entry-common.c:796
el0t_64_sync_handler+0x44/0x104 arch/arm64/kernel/entry-common.c:815
el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:606

d_path() renders a path relative to current->fs->root. After exit_fs()
there is no such root, and no substitute root is meaningful: the
record's pid/comm describe a task that can no longer name the file, and
the file may have been opened by a different task entirely. Falling
back to another root would produce a well-formed but misleading path,
so report the field as unknown instead, as exe=(null) and name=(null)
already do.

Handle this in audit_log_lsm_data(), where the meaning of the field is
known, for LSM_AUDIT_DATA_PATH (Smack file_open), LSM_AUDIT_DATA_FILE
(SELinux file_open), LSM_AUDIT_DATA_IOCTL_OP and the AF_UNIX path in
LSM_AUDIT_DATA_NET.

Alternative: omit " path=" as it is done for inode in audit_log_lsm_data.

Fixes: 4a9d4b024a31 ("switch fput to task_work_add")
Reported-by: syzbot+975ace299d6ba2eb1cea@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=975ace299d6ba2eb1cea
Signed-off-by: Drif Abdelmalek Mohamed Said <drifabdelmalekmohamedsaid@xxxxxxxxx>
---
security/lsm_audit.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 955b2e7b2c8e..5ed505d781bb 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -169,6 +169,15 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}

+static inline void audit_log_lsm_path(struct audit_buffer *ab,
+ const struct path *path)
+{
+ if (current->fs)
+ audit_log_d_path(ab, " path=", path);
+ else
+ audit_log_format(ab, " path=(null)");
+}
+
/**
* audit_log_lsm_data - helper to log common LSM audit data
* @ab : the audit buffer
@@ -196,7 +205,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_PATH: {
struct inode *inode;

- audit_log_d_path(ab, " path=", &a->u.path);
+ audit_log_lsm_path(ab, &a->u.path);

inode = d_backing_inode(a->u.path.dentry);
if (inode) {
@@ -209,7 +218,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_FILE: {
struct inode *inode;

- audit_log_d_path(ab, " path=", &a->u.file->f_path);
+ audit_log_lsm_path(ab, &a->u.file->f_path);

inode = file_inode(a->u.file);
if (inode) {
@@ -222,7 +231,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_IOCTL_OP: {
struct inode *inode;

- audit_log_d_path(ab, " path=", &a->u.op->path);
+ audit_log_lsm_path(ab, &a->u.op->path);

inode = a->u.op->path.dentry->d_inode;
if (inode) {
@@ -321,7 +330,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
if (!addr)
break;
if (u->path.dentry) {
- audit_log_d_path(ab, " path=", &u->path);
+ audit_log_lsm_path(ab, &u->path);
break;
}
len = addr->len-sizeof(short);
--
2.43.0