summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-06 13:07:47 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-06 13:07:47 -0800
commit3dc58c9ce1c5802fec680cb8e95962f1430d5771 (patch)
treeabf2ef84eae8e08b2e9828fa7f39319ab13a3256
parentbab849a908496a593af61a9832eea26f1ec3e279 (diff)
parentae9fd76c111bb4a5293c2831a1ad373605251dd6 (diff)
Merge tag 'mm-hotfixes-stable-2026-02-06-12-37' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull hotfixes from Andrew Morton: "A couple of late-breaking MM fixes. One against a new-in-this-cycle patch and the other addresses a locking issue which has been there for over a year" * tag 'mm-hotfixes-stable-2026-02-06-12-37' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: mm/memory-failure: reject unsupported non-folio compound page procfs: avoid fetching build ID while holding VMA lock
-rw-r--r--fs/proc/task_mmu.c42
-rw-r--r--include/linux/buildid.h3
-rw-r--r--lib/buildid.c42
-rw-r--r--mm/memory-failure.c42
4 files changed, 80 insertions, 49 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 81dfc26bfae8..26188a4ad1ab 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..c4b737640621 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));
@@ -319,8 +315,8 @@ out:
return ret;
}
-/*
- * Parse build ID of ELF file mapped to vma
+/**
+ * build_id_parse_nofault() - Parse build ID of ELF file mapped to vma
* @vma: vma object
* @build_id: buffer to store build id, at least BUILD_ID_SIZE long
* @size: returns actual build id size in case of success
@@ -332,11 +328,14 @@ out:
*/
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 */);
}
-/*
- * Parse build ID of ELF file mapped to VMA
+/**
+ * build_id_parse() - Parse build ID of ELF file mapped to VMA
* @vma: vma object
* @build_id: buffer to store build id, at least BUILD_ID_SIZE long
* @size: returns actual build id size in case of success
@@ -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 */);
+}
+
+/**
+ * build_id_parse_file() - Parse build ID of ELF file
+ * @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 */);
}
/**
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index cf0d526e6d41..9fd8355176eb 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -2411,31 +2411,29 @@ try_again:
* In fact it's dangerous to directly bump up page count from 0,
* that may make page_ref_freeze()/page_ref_unfreeze() mismatch.
*/
- if (!(flags & MF_COUNT_INCREASED)) {
- res = get_hwpoison_page(p, flags);
- if (!res) {
- if (is_free_buddy_page(p)) {
- if (take_page_off_buddy(p)) {
- page_ref_inc(p);
- res = MF_RECOVERED;
- } else {
- /* We lost the race, try again */
- if (retry) {
- ClearPageHWPoison(p);
- retry = false;
- goto try_again;
- }
- res = MF_FAILED;
- }
- res = action_result(pfn, MF_MSG_BUDDY, res);
+ res = get_hwpoison_page(p, flags);
+ if (!res) {
+ if (is_free_buddy_page(p)) {
+ if (take_page_off_buddy(p)) {
+ page_ref_inc(p);
+ res = MF_RECOVERED;
} else {
- res = action_result(pfn, MF_MSG_KERNEL_HIGH_ORDER, MF_IGNORED);
+ /* We lost the race, try again */
+ if (retry) {
+ ClearPageHWPoison(p);
+ retry = false;
+ goto try_again;
+ }
+ res = MF_FAILED;
}
- goto unlock_mutex;
- } else if (res < 0) {
- res = action_result(pfn, MF_MSG_GET_HWPOISON, MF_IGNORED);
- goto unlock_mutex;
+ res = action_result(pfn, MF_MSG_BUDDY, res);
+ } else {
+ res = action_result(pfn, MF_MSG_KERNEL_HIGH_ORDER, MF_IGNORED);
}
+ goto unlock_mutex;
+ } else if (res < 0) {
+ res = action_result(pfn, MF_MSG_GET_HWPOISON, MF_IGNORED);
+ goto unlock_mutex;
}
folio = page_folio(p);