BPF List
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: Andrii Nakryiko <andrii@kernel.org>,
	akpm@linux-foundation.org, linux-mm@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org, bpf@vger.kernel.org,
	surenb@google.com,
	syzbot+4e70c8e0a2017b432f7a@syzkaller.appspotmail.com
Subject: Re: [PATCH mm-stable] procfs: avoid fetching build ID while holding VMA lock
Date: Thu, 29 Jan 2026 07:52:07 -0800	[thread overview]
Message-ID: <a1214f2e-dbf7-41d9-ad8a-703193c84b67@linux.dev> (raw)
In-Reply-To: <20260128183232.2854138-1-andrii@kernel.org>



On 1/28/26 10:32 AM, Andrii Nakryiko wrote:
> Fix PROCMAP_QUERY to fetch optional build ID only after dropping mmap_lock or
> per-VMA lock, whichever was used to lock VMA under question, to avoid deadlock
> reported by syzbot:
>
>   -> #1 (&mm->mmap_lock){++++}-{4:4}:
>          __might_fault+0xed/0x170
>          _copy_to_iter+0x118/0x1720
>          copy_page_to_iter+0x12d/0x1e0
>          filemap_read+0x720/0x10a0
>          blkdev_read_iter+0x2b5/0x4e0
>          vfs_read+0x7f4/0xae0
>          ksys_read+0x12a/0x250
>          do_syscall_64+0xcb/0xf80
>          entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
>   -> #0 (&sb->s_type->i_mutex_key#8){++++}-{4:4}:
>          __lock_acquire+0x1509/0x26d0
>          lock_acquire+0x185/0x340
>          down_read+0x98/0x490
>          blkdev_read_iter+0x2a7/0x4e0
>          __kernel_read+0x39a/0xa90
>          freader_fetch+0x1d5/0xa80
>          __build_id_parse.isra.0+0xea/0x6a0
>          do_procmap_query+0xd75/0x1050
>          procfs_procmap_ioctl+0x7a/0xb0
>          __x64_sys_ioctl+0x18e/0x210
>          do_syscall_64+0xcb/0xf80
>          entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
>   other info that might help us debug this:
>
>    Possible unsafe locking scenario:
>
>          CPU0                    CPU1
>          ----                    ----
>     rlock(&mm->mmap_lock);
>                                  lock(&sb->s_type->i_mutex_key#8);
>                                  lock(&mm->mmap_lock);
>     rlock(&sb->s_type->i_mutex_key#8);
>
>    *** DEADLOCK ***
>
> To make this safe, we need to grab file refcount while VMA is still locked, but
> other than that everything is pretty straightforward. Internal build_id_parse()
> API assumes VMA is passed, but it only needs the underlying file reference, so
> just add another variant build_id_parse_file() that expects file passed
> directly.
>
> Fixes: ed5d583a88a9 ("fs/procfs: implement efficient VMA querying API for /proc/<pid>/maps")
> Reported-by: syzbot+4e70c8e0a2017b432f7a@syzkaller.appspotmail.com
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
>   fs/proc/task_mmu.c      | 42 ++++++++++++++++++++++++++---------------
>   include/linux/buildid.h |  3 +++
>   lib/buildid.c           | 34 +++++++++++++++++++++++++--------
>   3 files changed, 56 insertions(+), 23 deletions(-)
>
> diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> index 480db575553e..dd3b5cf9f0b7 100644
> --- a/fs/proc/task_mmu.c
> +++ b/fs/proc/task_mmu.c
> @@ -656,6 +656,7 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg)
>   	struct proc_maps_locking_ctx lock_ctx = { .mm = mm };
>   	struct procmap_query karg;
>   	struct vm_area_struct *vma;
> +	struct file *vm_file = NULL;
>   	const char *name = NULL;
>   	char build_id_buf[BUILD_ID_SIZE_MAX], *name_buf = NULL;
>   	__u64 usize;
> @@ -727,21 +728,6 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg)
>   		karg.inode = 0;
>   	}
>   
> -	if (karg.build_id_size) {
> -		__u32 build_id_sz;
> -
> -		err = build_id_parse(vma, build_id_buf, &build_id_sz);
> -		if (err) {
> -			karg.build_id_size = 0;
> -		} else {
> -			if (karg.build_id_size < build_id_sz) {
> -				err = -ENAMETOOLONG;
> -				goto out;
> -			}
> -			karg.build_id_size = build_id_sz;
> -		}
> -	}
> -
>   	if (karg.vma_name_size) {
>   		size_t name_buf_sz = min_t(size_t, PATH_MAX, karg.vma_name_size);
>   		const struct path *path;
> @@ -775,10 +761,34 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg)
>   		karg.vma_name_size = name_sz;
>   	}
>   
> +	if (karg.build_id_size && vma->vm_file)
> +		vm_file = get_file(vma->vm_file);
> +
>   	/* unlock vma or mmap_lock, and put mm_struct before copying data to user */
>   	query_vma_teardown(&lock_ctx);
>   	mmput(mm);
>   
> +	if (karg.build_id_size) {
> +		__u32 build_id_sz;
> +
> +		if (vm_file)
> +			err = build_id_parse_file(vm_file, build_id_buf, &build_id_sz);
> +		else
> +			err = -ENOENT;
> +		if (err) {
> +			karg.build_id_size = 0;
> +		} else {
> +			if (karg.build_id_size < build_id_sz) {
> +				err = -ENAMETOOLONG;
> +				goto out;
> +			}
> +			karg.build_id_size = build_id_sz;
> +		}
> +	}
> +
> +	if (vm_file)
> +		fput(vm_file);
> +
>   	if (karg.vma_name_size && copy_to_user(u64_to_user_ptr(karg.vma_name_addr),
>   					       name, karg.vma_name_size)) {
>   		kfree(name_buf);
> @@ -798,6 +808,8 @@ static int do_procmap_query(struct mm_struct *mm, void __user *uarg)
>   out:
>   	query_vma_teardown(&lock_ctx);
>   	mmput(mm);
> +	if (vm_file)
> +		fput(vm_file);
>   	kfree(name_buf);
>   	return err;
>   }
> diff --git a/include/linux/buildid.h b/include/linux/buildid.h
> index 831c1b4b626c..7acc06b22fb7 100644
> --- a/include/linux/buildid.h
> +++ b/include/linux/buildid.h
> @@ -7,7 +7,10 @@
>   #define BUILD_ID_SIZE_MAX 20
>   
>   struct vm_area_struct;
> +struct file;
> +
>   int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size);
> +int build_id_parse_file(struct file *file, unsigned char *build_id, __u32 *size);
>   int build_id_parse_nofault(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size);
>   int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size);
>   
> diff --git a/lib/buildid.c b/lib/buildid.c
> index 818331051afe..dc643a6293c1 100644
> --- a/lib/buildid.c
> +++ b/lib/buildid.c
> @@ -279,7 +279,7 @@ static int get_build_id_64(struct freader *r, unsigned char *build_id, __u32 *si
>   /* enough for Elf64_Ehdr, Elf64_Phdr, and all the smaller requests */
>   #define MAX_FREADER_BUF_SZ 64
>   
> -static int __build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
> +static int __build_id_parse(struct file *file, unsigned char *build_id,
>   			    __u32 *size, bool may_fault)
>   {
>   	const Elf32_Ehdr *ehdr;
> @@ -287,11 +287,7 @@ static int __build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
>   	char buf[MAX_FREADER_BUF_SZ];
>   	int ret;
>   
> -	/* only works for page backed storage  */
> -	if (!vma->vm_file)
> -		return -EINVAL;
> -
> -	freader_init_from_file(&r, buf, sizeof(buf), vma->vm_file, may_fault);
> +	freader_init_from_file(&r, buf, sizeof(buf), file, may_fault);
>   
>   	/* fetch first 18 bytes of ELF header for checks */
>   	ehdr = freader_fetch(&r, 0, offsetofend(Elf32_Ehdr, e_type));
> @@ -332,7 +328,10 @@ static int __build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
>    */
>   int build_id_parse_nofault(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size)
>   {
> -	return __build_id_parse(vma, build_id, size, false /* !may_fault */);
> +	if (!vma->vm_file)
> +		return -EINVAL;
> +
> +	return __build_id_parse(vma->vm_file, build_id, size, false /* !may_fault */);
>   }
>   
>   /*
> @@ -348,7 +347,26 @@ int build_id_parse_nofault(struct vm_area_struct *vma, unsigned char *build_id,
>    */
>   int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size)
>   {
> -	return __build_id_parse(vma, build_id, size, true /* may_fault */);
> +	if (!vma->vm_file)
> +		return -EINVAL;
> +
> +	return __build_id_parse(vma->vm_file, build_id, size, true /* may_fault */);
> +}
> +
> +/*
> + * Parse build ID of ELF file
> + * @vma:      file object

Should this be
      @file:	file object
?

> + * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
> + * @size:     returns actual build id size in case of success
> + *
> + * Assumes faultable context and can cause page faults to bring in file data
> + * into page cache.
> + *
> + * Return: 0 on success; negative error, otherwise
> + */
> +int build_id_parse_file(struct file *file, unsigned char *build_id, __u32 *size)
> +{
> +	return __build_id_parse(file, build_id, size, true /* may_fault */);
>   }
>   
>   /**


  parent reply	other threads:[~2026-01-29 15:52 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-28 18:32 [PATCH mm-stable] procfs: avoid fetching build ID while holding VMA lock Andrii Nakryiko
2026-01-28 18:50 ` Andrew Morton
2026-01-28 21:30   ` Andrii Nakryiko
2026-01-29 23:28     ` Andrew Morton
2026-01-29  5:50 ` Suren Baghdasaryan
2026-01-29 21:43   ` Andrii Nakryiko
2026-01-29 15:52 ` Yonghong Song [this message]
2026-01-29 17:14   ` Mykyta Yatsenko
2026-01-29 21:46     ` Andrii Nakryiko
2026-01-29 21:48   ` Andrii Nakryiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a1214f2e-dbf7-41d9-ad8a-703193c84b67@linux.dev \
    --to=yonghong.song@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mm@vger.kernel.org \
    --cc=surenb@google.com \
    --cc=syzbot+4e70c8e0a2017b432f7a@syzkaller.appspotmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox