Skip to content

Commit

Permalink
Merge branch 'akpm' (patches from Andrew)
Browse files Browse the repository at this point in the history
Merge misc fixes from Andrew Morton:
 "8 patches.

  Subsystems affected by this patch series: mm (hugetlb, pagemap, and
  userfaultfd), memfd, selftests, and kconfig"

* emailed patches from Andrew Morton <akpm@linux-foundation.org>:
  configs/debug: set CONFIG_DEBUG_INFO=y properly
  proc: fix documentation and description of pagemap
  kselftest/vm: fix tests build with old libc
  memfd: fix F_SEAL_WRITE after shmem huge page allocated
  mm: fix use-after-free when anon vma name is used after vma is freed
  mm: prevent vm_area_struct::anon_name refcount saturation
  mm: refactor vm_area_struct::anon_vma_name usage code
  selftests/vm: cleanup hugetlb file after mremap test
  • Loading branch information
Linus Torvalds committed Mar 5, 2022
2 parents f9026e1 + d1eff16 commit 0014404
Show file tree
Hide file tree
Showing 18 changed files with 194 additions and 137 deletions.
2 changes: 1 addition & 1 deletion Documentation/admin-guide/mm/pagemap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ There are four components to pagemap:
* Bit 56 page exclusively mapped (since 4.2)
* Bit 57 pte is uffd-wp write-protected (since 5.13) (see
:ref:`Documentation/admin-guide/mm/userfaultfd.rst <userfaultfd>`)
* Bits 57-60 zero
* Bits 58-60 zero
* Bit 61 page is file-page or shared-anon (since 3.5)
* Bit 62 page swapped
* Bit 63 page present
Expand Down
9 changes: 5 additions & 4 deletions fs/proc/task_mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)

name = arch_vma_name(vma);
if (!name) {
const char *anon_name;
struct anon_vma_name *anon_name;

if (!mm) {
name = "[vdso]";
Expand All @@ -327,10 +327,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
goto done;
}

anon_name = vma_anon_name(vma);
anon_name = anon_vma_name(vma);
if (anon_name) {
seq_pad(m, ' ');
seq_printf(m, "[anon:%s]", anon_name);
seq_printf(m, "[anon:%s]", anon_name->name);
}
}

Expand Down Expand Up @@ -1597,7 +1597,8 @@ static const struct mm_walk_ops pagemap_ops = {
* Bits 5-54 swap offset if swapped
* Bit 55 pte is soft-dirty (see Documentation/admin-guide/mm/soft-dirty.rst)
* Bit 56 page exclusively mapped
* Bits 57-60 zero
* Bit 57 pte is uffd-wp write-protected
* Bits 58-60 zero
* Bit 61 page is file-page or shared-anon
* Bit 62 page swapped
* Bit 63 page present
Expand Down
6 changes: 3 additions & 3 deletions fs/userfaultfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
new_flags, vma->anon_vma,
vma->vm_file, vma->vm_pgoff,
vma_policy(vma),
NULL_VM_UFFD_CTX, vma_anon_name(vma));
NULL_VM_UFFD_CTX, anon_vma_name(vma));
if (prev)
vma = prev;
else
Expand Down Expand Up @@ -1438,7 +1438,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
vma->anon_vma, vma->vm_file, vma->vm_pgoff,
vma_policy(vma),
((struct vm_userfaultfd_ctx){ ctx }),
vma_anon_name(vma));
anon_vma_name(vma));
if (prev) {
vma = prev;
goto next;
Expand Down Expand Up @@ -1615,7 +1615,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
prev = vma_merge(mm, prev, start, vma_end, new_flags,
vma->anon_vma, vma->vm_file, vma->vm_pgoff,
vma_policy(vma),
NULL_VM_UFFD_CTX, vma_anon_name(vma));
NULL_VM_UFFD_CTX, anon_vma_name(vma));
if (prev) {
vma = prev;
goto next;
Expand Down
7 changes: 4 additions & 3 deletions include/linux/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2626,7 +2626,7 @@ static inline int vma_adjust(struct vm_area_struct *vma, unsigned long start,
extern struct vm_area_struct *vma_merge(struct mm_struct *,
struct vm_area_struct *prev, unsigned long addr, unsigned long end,
unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
struct mempolicy *, struct vm_userfaultfd_ctx, const char *);
struct mempolicy *, struct vm_userfaultfd_ctx, struct anon_vma_name *);
extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
extern int __split_vma(struct mm_struct *, struct vm_area_struct *,
unsigned long addr, int new_below);
Expand Down Expand Up @@ -3372,11 +3372,12 @@ static inline int seal_check_future_write(int seals, struct vm_area_struct *vma)

#ifdef CONFIG_ANON_VMA_NAME
int madvise_set_anon_name(struct mm_struct *mm, unsigned long start,
unsigned long len_in, const char *name);
unsigned long len_in,
struct anon_vma_name *anon_name);
#else
static inline int
madvise_set_anon_name(struct mm_struct *mm, unsigned long start,
unsigned long len_in, const char *name) {
unsigned long len_in, struct anon_vma_name *anon_name) {
return 0;
}
#endif
Expand Down
97 changes: 69 additions & 28 deletions include/linux/mm_inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,50 +140,91 @@ static __always_inline void del_page_from_lru_list(struct page *page,

#ifdef CONFIG_ANON_VMA_NAME
/*
* mmap_lock should be read-locked when calling vma_anon_name() and while using
* the returned pointer.
* mmap_lock should be read-locked when calling anon_vma_name(). Caller should
* either keep holding the lock while using the returned pointer or it should
* raise anon_vma_name refcount before releasing the lock.
*/
extern const char *vma_anon_name(struct vm_area_struct *vma);
extern struct anon_vma_name *anon_vma_name(struct vm_area_struct *vma);
extern struct anon_vma_name *anon_vma_name_alloc(const char *name);
extern void anon_vma_name_free(struct kref *kref);

/*
* mmap_lock should be read-locked for orig_vma->vm_mm.
* mmap_lock should be write-locked for new_vma->vm_mm or new_vma should be
* isolated.
*/
extern void dup_vma_anon_name(struct vm_area_struct *orig_vma,
struct vm_area_struct *new_vma);
/* mmap_lock should be read-locked */
static inline void anon_vma_name_get(struct anon_vma_name *anon_name)
{
if (anon_name)
kref_get(&anon_name->kref);
}

/*
* mmap_lock should be write-locked or vma should have been isolated under
* write-locked mmap_lock protection.
*/
extern void free_vma_anon_name(struct vm_area_struct *vma);
static inline void anon_vma_name_put(struct anon_vma_name *anon_name)
{
if (anon_name)
kref_put(&anon_name->kref, anon_vma_name_free);
}

/* mmap_lock should be read-locked */
static inline bool is_same_vma_anon_name(struct vm_area_struct *vma,
const char *name)
static inline
struct anon_vma_name *anon_vma_name_reuse(struct anon_vma_name *anon_name)
{
/* Prevent anon_name refcount saturation early on */
if (kref_read(&anon_name->kref) < REFCOUNT_MAX) {
anon_vma_name_get(anon_name);
return anon_name;

}
return anon_vma_name_alloc(anon_name->name);
}

static inline void dup_anon_vma_name(struct vm_area_struct *orig_vma,
struct vm_area_struct *new_vma)
{
struct anon_vma_name *anon_name = anon_vma_name(orig_vma);

if (anon_name)
new_vma->anon_name = anon_vma_name_reuse(anon_name);
}

static inline void free_anon_vma_name(struct vm_area_struct *vma)
{
const char *vma_name = vma_anon_name(vma);
/*
* Not using anon_vma_name because it generates a warning if mmap_lock
* is not held, which might be the case here.
*/
if (!vma->vm_file)
anon_vma_name_put(vma->anon_name);
}

/* either both NULL, or pointers to same string */
if (vma_name == name)
static inline bool anon_vma_name_eq(struct anon_vma_name *anon_name1,
struct anon_vma_name *anon_name2)
{
if (anon_name1 == anon_name2)
return true;

return name && vma_name && !strcmp(name, vma_name);
return anon_name1 && anon_name2 &&
!strcmp(anon_name1->name, anon_name2->name);
}

#else /* CONFIG_ANON_VMA_NAME */
static inline const char *vma_anon_name(struct vm_area_struct *vma)
static inline struct anon_vma_name *anon_vma_name(struct vm_area_struct *vma)
{
return NULL;
}
static inline void dup_vma_anon_name(struct vm_area_struct *orig_vma,
struct vm_area_struct *new_vma) {}
static inline void free_vma_anon_name(struct vm_area_struct *vma) {}
static inline bool is_same_vma_anon_name(struct vm_area_struct *vma,
const char *name)

static inline struct anon_vma_name *anon_vma_name_alloc(const char *name)
{
return NULL;
}

static inline void anon_vma_name_get(struct anon_vma_name *anon_name) {}
static inline void anon_vma_name_put(struct anon_vma_name *anon_name) {}
static inline void dup_anon_vma_name(struct vm_area_struct *orig_vma,
struct vm_area_struct *new_vma) {}
static inline void free_anon_vma_name(struct vm_area_struct *vma) {}

static inline bool anon_vma_name_eq(struct anon_vma_name *anon_name1,
struct anon_vma_name *anon_name2)
{
return true;
}

#endif /* CONFIG_ANON_VMA_NAME */

static inline void init_tlb_flush_pending(struct mm_struct *mm)
Expand Down
5 changes: 4 additions & 1 deletion include/linux/mm_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,10 @@ struct vm_area_struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
/* Serialized by mmap_sem. */
/*
* Serialized by mmap_sem. Never use directly because it is
* valid only when vm_file is NULL. Use anon_vma_name instead.
*/
struct anon_vma_name *anon_name;
};

Expand Down
2 changes: 1 addition & 1 deletion kernel/configs/debug.config
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ CONFIG_SYMBOLIC_ERRNAME=y
#
# Compile-time checks and compiler options
#
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_FRAME_WARN=2048
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
Expand Down
4 changes: 2 additions & 2 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,14 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)
*new = data_race(*orig);
INIT_LIST_HEAD(&new->anon_vma_chain);
new->vm_next = new->vm_prev = NULL;
dup_vma_anon_name(orig, new);
dup_anon_vma_name(orig, new);
}
return new;
}

void vm_area_free(struct vm_area_struct *vma)
{
free_vma_anon_name(vma);
free_anon_vma_name(vma);
kmem_cache_free(vm_area_cachep, vma);
}

Expand Down
19 changes: 12 additions & 7 deletions kernel/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <linux/export.h>
#include <linux/mm.h>
#include <linux/mm_inline.h>
#include <linux/utsname.h>
#include <linux/mman.h>
#include <linux/reboot.h>
Expand Down Expand Up @@ -2286,15 +2287,16 @@ static int prctl_set_vma(unsigned long opt, unsigned long addr,
{
struct mm_struct *mm = current->mm;
const char __user *uname;
char *name, *pch;
struct anon_vma_name *anon_name = NULL;
int error;

switch (opt) {
case PR_SET_VMA_ANON_NAME:
uname = (const char __user *)arg;
if (uname) {
name = strndup_user(uname, ANON_VMA_NAME_MAX_LEN);
char *name, *pch;

name = strndup_user(uname, ANON_VMA_NAME_MAX_LEN);
if (IS_ERR(name))
return PTR_ERR(name);

Expand All @@ -2304,15 +2306,18 @@ static int prctl_set_vma(unsigned long opt, unsigned long addr,
return -EINVAL;
}
}
} else {
/* Reset the name */
name = NULL;
/* anon_vma has its own copy */
anon_name = anon_vma_name_alloc(name);
kfree(name);
if (!anon_name)
return -ENOMEM;

}

mmap_write_lock(mm);
error = madvise_set_anon_name(mm, addr, size, name);
error = madvise_set_anon_name(mm, addr, size, anon_name);
mmap_write_unlock(mm);
kfree(name);
anon_vma_name_put(anon_name);
break;
default:
error = -EINVAL;
Expand Down
Loading

0 comments on commit 0014404

Please sign in to comment.