162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/mm.h>
462306a36Sopenharmony_ci#include <linux/file.h>
562306a36Sopenharmony_ci#include <linux/fdtable.h>
662306a36Sopenharmony_ci#include <linux/fs_struct.h>
762306a36Sopenharmony_ci#include <linux/mount.h>
862306a36Sopenharmony_ci#include <linux/ptrace.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/seq_file.h>
1162306a36Sopenharmony_ci#include <linux/sched/mm.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "internal.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * Logic: we've got two memory sums for each process, "shared", and
1762306a36Sopenharmony_ci * "non-shared". Shared memory may get counted more than once, for
1862306a36Sopenharmony_ci * each process that owns it. Non-shared memory is counted
1962306a36Sopenharmony_ci * accurately.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_civoid task_mem(struct seq_file *m, struct mm_struct *mm)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	VMA_ITERATOR(vmi, mm, 0);
2462306a36Sopenharmony_ci	struct vm_area_struct *vma;
2562306a36Sopenharmony_ci	struct vm_region *region;
2662306a36Sopenharmony_ci	unsigned long bytes = 0, sbytes = 0, slack = 0, size;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	mmap_read_lock(mm);
2962306a36Sopenharmony_ci	for_each_vma(vmi, vma) {
3062306a36Sopenharmony_ci		bytes += kobjsize(vma);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		region = vma->vm_region;
3362306a36Sopenharmony_ci		if (region) {
3462306a36Sopenharmony_ci			size = kobjsize(region);
3562306a36Sopenharmony_ci			size += region->vm_end - region->vm_start;
3662306a36Sopenharmony_ci		} else {
3762306a36Sopenharmony_ci			size = vma->vm_end - vma->vm_start;
3862306a36Sopenharmony_ci		}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		if (atomic_read(&mm->mm_count) > 1 ||
4162306a36Sopenharmony_ci		    is_nommu_shared_mapping(vma->vm_flags)) {
4262306a36Sopenharmony_ci			sbytes += size;
4362306a36Sopenharmony_ci		} else {
4462306a36Sopenharmony_ci			bytes += size;
4562306a36Sopenharmony_ci			if (region)
4662306a36Sopenharmony_ci				slack = region->vm_end - vma->vm_end;
4762306a36Sopenharmony_ci		}
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (atomic_read(&mm->mm_count) > 1)
5162306a36Sopenharmony_ci		sbytes += kobjsize(mm);
5262306a36Sopenharmony_ci	else
5362306a36Sopenharmony_ci		bytes += kobjsize(mm);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (current->fs && current->fs->users > 1)
5662306a36Sopenharmony_ci		sbytes += kobjsize(current->fs);
5762306a36Sopenharmony_ci	else
5862306a36Sopenharmony_ci		bytes += kobjsize(current->fs);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (current->files && atomic_read(&current->files->count) > 1)
6162306a36Sopenharmony_ci		sbytes += kobjsize(current->files);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		bytes += kobjsize(current->files);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (current->sighand && refcount_read(&current->sighand->count) > 1)
6662306a36Sopenharmony_ci		sbytes += kobjsize(current->sighand);
6762306a36Sopenharmony_ci	else
6862306a36Sopenharmony_ci		bytes += kobjsize(current->sighand);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	bytes += kobjsize(current); /* includes kernel stack */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	mmap_read_unlock(mm);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	seq_printf(m,
7562306a36Sopenharmony_ci		"Mem:\t%8lu bytes\n"
7662306a36Sopenharmony_ci		"Slack:\t%8lu bytes\n"
7762306a36Sopenharmony_ci		"Shared:\t%8lu bytes\n",
7862306a36Sopenharmony_ci		bytes, slack, sbytes);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciunsigned long task_vsize(struct mm_struct *mm)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	VMA_ITERATOR(vmi, mm, 0);
8462306a36Sopenharmony_ci	struct vm_area_struct *vma;
8562306a36Sopenharmony_ci	unsigned long vsize = 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	mmap_read_lock(mm);
8862306a36Sopenharmony_ci	for_each_vma(vmi, vma)
8962306a36Sopenharmony_ci		vsize += vma->vm_end - vma->vm_start;
9062306a36Sopenharmony_ci	mmap_read_unlock(mm);
9162306a36Sopenharmony_ci	return vsize;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciunsigned long task_statm(struct mm_struct *mm,
9562306a36Sopenharmony_ci			 unsigned long *shared, unsigned long *text,
9662306a36Sopenharmony_ci			 unsigned long *data, unsigned long *resident)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	VMA_ITERATOR(vmi, mm, 0);
9962306a36Sopenharmony_ci	struct vm_area_struct *vma;
10062306a36Sopenharmony_ci	struct vm_region *region;
10162306a36Sopenharmony_ci	unsigned long size = kobjsize(mm);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	mmap_read_lock(mm);
10462306a36Sopenharmony_ci	for_each_vma(vmi, vma) {
10562306a36Sopenharmony_ci		size += kobjsize(vma);
10662306a36Sopenharmony_ci		region = vma->vm_region;
10762306a36Sopenharmony_ci		if (region) {
10862306a36Sopenharmony_ci			size += kobjsize(region);
10962306a36Sopenharmony_ci			size += region->vm_end - region->vm_start;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
11462306a36Sopenharmony_ci		>> PAGE_SHIFT;
11562306a36Sopenharmony_ci	*data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK))
11662306a36Sopenharmony_ci		>> PAGE_SHIFT;
11762306a36Sopenharmony_ci	mmap_read_unlock(mm);
11862306a36Sopenharmony_ci	size >>= PAGE_SHIFT;
11962306a36Sopenharmony_ci	size += *text + *data;
12062306a36Sopenharmony_ci	*resident = size;
12162306a36Sopenharmony_ci	return size;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * display a single VMA to a sequenced file
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_cistatic int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
13062306a36Sopenharmony_ci	unsigned long ino = 0;
13162306a36Sopenharmony_ci	struct file *file;
13262306a36Sopenharmony_ci	dev_t dev = 0;
13362306a36Sopenharmony_ci	int flags;
13462306a36Sopenharmony_ci	unsigned long long pgoff = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	flags = vma->vm_flags;
13762306a36Sopenharmony_ci	file = vma->vm_file;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (file) {
14062306a36Sopenharmony_ci		struct inode *inode = file_inode(vma->vm_file);
14162306a36Sopenharmony_ci		dev = inode->i_sb->s_dev;
14262306a36Sopenharmony_ci		ino = inode->i_ino;
14362306a36Sopenharmony_ci		pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
14762306a36Sopenharmony_ci	seq_printf(m,
14862306a36Sopenharmony_ci		   "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
14962306a36Sopenharmony_ci		   vma->vm_start,
15062306a36Sopenharmony_ci		   vma->vm_end,
15162306a36Sopenharmony_ci		   flags & VM_READ ? 'r' : '-',
15262306a36Sopenharmony_ci		   flags & VM_WRITE ? 'w' : '-',
15362306a36Sopenharmony_ci		   flags & VM_EXEC ? 'x' : '-',
15462306a36Sopenharmony_ci		   flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
15562306a36Sopenharmony_ci		   pgoff,
15662306a36Sopenharmony_ci		   MAJOR(dev), MINOR(dev), ino);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (file) {
15962306a36Sopenharmony_ci		seq_pad(m, ' ');
16062306a36Sopenharmony_ci		seq_file_path(m, file, "");
16162306a36Sopenharmony_ci	} else if (mm && vma_is_initial_stack(vma)) {
16262306a36Sopenharmony_ci		seq_pad(m, ' ');
16362306a36Sopenharmony_ci		seq_puts(m, "[stack]");
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	seq_putc(m, '\n');
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * display mapping lines for a particular process's /proc/pid/maps
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistatic int show_map(struct seq_file *m, void *_p)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	return nommu_vma_show(m, _p);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv,
17962306a36Sopenharmony_ci						loff_t *ppos)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct vm_area_struct *vma = vma_next(&priv->iter);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (vma) {
18462306a36Sopenharmony_ci		*ppos = vma->vm_start;
18562306a36Sopenharmony_ci	} else {
18662306a36Sopenharmony_ci		*ppos = -1UL;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return vma;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void *m_start(struct seq_file *m, loff_t *ppos)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct proc_maps_private *priv = m->private;
19562306a36Sopenharmony_ci	unsigned long last_addr = *ppos;
19662306a36Sopenharmony_ci	struct mm_struct *mm;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* See proc_get_vma(). Zero at the start or after lseek. */
19962306a36Sopenharmony_ci	if (last_addr == -1UL)
20062306a36Sopenharmony_ci		return NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* pin the task and mm whilst we play with them */
20362306a36Sopenharmony_ci	priv->task = get_proc_task(priv->inode);
20462306a36Sopenharmony_ci	if (!priv->task)
20562306a36Sopenharmony_ci		return ERR_PTR(-ESRCH);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mm = priv->mm;
20862306a36Sopenharmony_ci	if (!mm || !mmget_not_zero(mm)) {
20962306a36Sopenharmony_ci		put_task_struct(priv->task);
21062306a36Sopenharmony_ci		priv->task = NULL;
21162306a36Sopenharmony_ci		return NULL;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (mmap_read_lock_killable(mm)) {
21562306a36Sopenharmony_ci		mmput(mm);
21662306a36Sopenharmony_ci		put_task_struct(priv->task);
21762306a36Sopenharmony_ci		priv->task = NULL;
21862306a36Sopenharmony_ci		return ERR_PTR(-EINTR);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	vma_iter_init(&priv->iter, mm, last_addr);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return proc_get_vma(priv, ppos);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void m_stop(struct seq_file *m, void *v)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct proc_maps_private *priv = m->private;
22962306a36Sopenharmony_ci	struct mm_struct *mm = priv->mm;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (!priv->task)
23262306a36Sopenharmony_ci		return;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mmap_read_unlock(mm);
23562306a36Sopenharmony_ci	mmput(mm);
23662306a36Sopenharmony_ci	put_task_struct(priv->task);
23762306a36Sopenharmony_ci	priv->task = NULL;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void *m_next(struct seq_file *m, void *_p, loff_t *ppos)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return proc_get_vma(m->private, ppos);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic const struct seq_operations proc_pid_maps_ops = {
24662306a36Sopenharmony_ci	.start	= m_start,
24762306a36Sopenharmony_ci	.next	= m_next,
24862306a36Sopenharmony_ci	.stop	= m_stop,
24962306a36Sopenharmony_ci	.show	= show_map
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int maps_open(struct inode *inode, struct file *file,
25362306a36Sopenharmony_ci		     const struct seq_operations *ops)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct proc_maps_private *priv;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	priv = __seq_open_private(file, ops, sizeof(*priv));
25862306a36Sopenharmony_ci	if (!priv)
25962306a36Sopenharmony_ci		return -ENOMEM;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	priv->inode = inode;
26262306a36Sopenharmony_ci	priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
26362306a36Sopenharmony_ci	if (IS_ERR(priv->mm)) {
26462306a36Sopenharmony_ci		int err = PTR_ERR(priv->mm);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		seq_release_private(inode, file);
26762306a36Sopenharmony_ci		return err;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int map_release(struct inode *inode, struct file *file)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct seq_file *seq = file->private_data;
27762306a36Sopenharmony_ci	struct proc_maps_private *priv = seq->private;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (priv->mm)
28062306a36Sopenharmony_ci		mmdrop(priv->mm);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return seq_release_private(inode, file);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int pid_maps_open(struct inode *inode, struct file *file)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	return maps_open(inode, file, &proc_pid_maps_ops);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ciconst struct file_operations proc_pid_maps_operations = {
29162306a36Sopenharmony_ci	.open		= pid_maps_open,
29262306a36Sopenharmony_ci	.read		= seq_read,
29362306a36Sopenharmony_ci	.llseek		= seq_lseek,
29462306a36Sopenharmony_ci	.release	= map_release,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
297