1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds #include <linux/mm.h>
41da177e4SLinus Torvalds #include <linux/file.h>
5eb28062fSBryan Wu #include <linux/fdtable.h>
65ad4e53bSAl Viro #include <linux/fs_struct.h>
71da177e4SLinus Torvalds #include <linux/mount.h>
85096add8SKees Cook #include <linux/ptrace.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
101da177e4SLinus Torvalds #include <linux/seq_file.h>
116e84f315SIngo Molnar #include <linux/sched/mm.h>
126e84f315SIngo Molnar
131da177e4SLinus Torvalds #include "internal.h"
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds /*
161da177e4SLinus Torvalds * Logic: we've got two memory sums for each process, "shared", and
17025dfdafSFrederik Schwarzer * "non-shared". Shared memory may get counted more than once, for
181da177e4SLinus Torvalds * each process that owns it. Non-shared memory is counted
191da177e4SLinus Torvalds * accurately.
201da177e4SLinus Torvalds */
task_mem(struct seq_file * m,struct mm_struct * mm)21df5f8314SEric W. Biederman void task_mem(struct seq_file *m, struct mm_struct *mm)
221da177e4SLinus Torvalds {
230c563f14SMatthew Wilcox (Oracle) VMA_ITERATOR(vmi, mm, 0);
248feae131SDavid Howells struct vm_area_struct *vma;
2538f71479SDavid Howells struct vm_region *region;
2638f71479SDavid Howells unsigned long bytes = 0, sbytes = 0, slack = 0, size;
271da177e4SLinus Torvalds
28d8ed45c5SMichel Lespinasse mmap_read_lock(mm);
290c563f14SMatthew Wilcox (Oracle) for_each_vma(vmi, vma) {
308feae131SDavid Howells bytes += kobjsize(vma);
3138f71479SDavid Howells
3238f71479SDavid Howells region = vma->vm_region;
3338f71479SDavid Howells if (region) {
3438f71479SDavid Howells size = kobjsize(region);
3538f71479SDavid Howells size += region->vm_end - region->vm_start;
361da177e4SLinus Torvalds } else {
3738f71479SDavid Howells size = vma->vm_end - vma->vm_start;
3838f71479SDavid Howells }
3938f71479SDavid Howells
4038f71479SDavid Howells if (atomic_read(&mm->mm_count) > 1 ||
41fc4f4be9SDavid Hildenbrand is_nommu_shared_mapping(vma->vm_flags)) {
4238f71479SDavid Howells sbytes += size;
4338f71479SDavid Howells } else {
4438f71479SDavid Howells bytes += size;
4538f71479SDavid Howells if (region)
4638f71479SDavid Howells slack = region->vm_end - vma->vm_end;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds if (atomic_read(&mm->mm_count) > 1)
511da177e4SLinus Torvalds sbytes += kobjsize(mm);
521da177e4SLinus Torvalds else
531da177e4SLinus Torvalds bytes += kobjsize(mm);
541da177e4SLinus Torvalds
55498052bbSAl Viro if (current->fs && current->fs->users > 1)
561da177e4SLinus Torvalds sbytes += kobjsize(current->fs);
571da177e4SLinus Torvalds else
581da177e4SLinus Torvalds bytes += kobjsize(current->fs);
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds if (current->files && atomic_read(¤t->files->count) > 1)
611da177e4SLinus Torvalds sbytes += kobjsize(current->files);
621da177e4SLinus Torvalds else
631da177e4SLinus Torvalds bytes += kobjsize(current->files);
641da177e4SLinus Torvalds
65d036bda7SElena Reshetova if (current->sighand && refcount_read(¤t->sighand->count) > 1)
661da177e4SLinus Torvalds sbytes += kobjsize(current->sighand);
671da177e4SLinus Torvalds else
681da177e4SLinus Torvalds bytes += kobjsize(current->sighand);
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds bytes += kobjsize(current); /* includes kernel stack */
711da177e4SLinus Torvalds
72341d51c8Slipeifeng mmap_read_unlock(mm);
73341d51c8Slipeifeng
74df5f8314SEric W. Biederman seq_printf(m,
751da177e4SLinus Torvalds "Mem:\t%8lu bytes\n"
761da177e4SLinus Torvalds "Slack:\t%8lu bytes\n"
771da177e4SLinus Torvalds "Shared:\t%8lu bytes\n",
781da177e4SLinus Torvalds bytes, slack, sbytes);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds
task_vsize(struct mm_struct * mm)811da177e4SLinus Torvalds unsigned long task_vsize(struct mm_struct *mm)
821da177e4SLinus Torvalds {
830c563f14SMatthew Wilcox (Oracle) VMA_ITERATOR(vmi, mm, 0);
848feae131SDavid Howells struct vm_area_struct *vma;
851da177e4SLinus Torvalds unsigned long vsize = 0;
861da177e4SLinus Torvalds
87d8ed45c5SMichel Lespinasse mmap_read_lock(mm);
880c563f14SMatthew Wilcox (Oracle) for_each_vma(vmi, vma)
8938f71479SDavid Howells vsize += vma->vm_end - vma->vm_start;
90d8ed45c5SMichel Lespinasse mmap_read_unlock(mm);
911da177e4SLinus Torvalds return vsize;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds
task_statm(struct mm_struct * mm,unsigned long * shared,unsigned long * text,unsigned long * data,unsigned long * resident)94a2ade7b6SAlexey Dobriyan unsigned long task_statm(struct mm_struct *mm,
95a2ade7b6SAlexey Dobriyan unsigned long *shared, unsigned long *text,
96a2ade7b6SAlexey Dobriyan unsigned long *data, unsigned long *resident)
971da177e4SLinus Torvalds {
980c563f14SMatthew Wilcox (Oracle) VMA_ITERATOR(vmi, mm, 0);
998feae131SDavid Howells struct vm_area_struct *vma;
10038f71479SDavid Howells struct vm_region *region;
101a2ade7b6SAlexey Dobriyan unsigned long size = kobjsize(mm);
1021da177e4SLinus Torvalds
103d8ed45c5SMichel Lespinasse mmap_read_lock(mm);
1040c563f14SMatthew Wilcox (Oracle) for_each_vma(vmi, vma) {
1058feae131SDavid Howells size += kobjsize(vma);
10638f71479SDavid Howells region = vma->vm_region;
10738f71479SDavid Howells if (region) {
10838f71479SDavid Howells size += kobjsize(region);
10938f71479SDavid Howells size += region->vm_end - region->vm_start;
11038f71479SDavid Howells }
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds
1137e1e0ef2SSteven J. Magnani *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
1147e1e0ef2SSteven J. Magnani >> PAGE_SHIFT;
1157e1e0ef2SSteven J. Magnani *data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK))
1167e1e0ef2SSteven J. Magnani >> PAGE_SHIFT;
117d8ed45c5SMichel Lespinasse mmap_read_unlock(mm);
1187e1e0ef2SSteven J. Magnani size >>= PAGE_SHIFT;
1197e1e0ef2SSteven J. Magnani size += *text + *data;
1201da177e4SLinus Torvalds *resident = size;
1211da177e4SLinus Torvalds return size;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds
1241da177e4SLinus Torvalds /*
1258feae131SDavid Howells * display a single VMA to a sequenced file
1268feae131SDavid Howells */
nommu_vma_show(struct seq_file * m,struct vm_area_struct * vma)127871305bbSVlastimil Babka static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
1288feae131SDavid Howells {
1293c26c9d9SMike Frysinger struct mm_struct *mm = vma->vm_mm;
1308feae131SDavid Howells unsigned long ino = 0;
1318feae131SDavid Howells struct file *file;
1328feae131SDavid Howells dev_t dev = 0;
133652586dfSTetsuo Handa int flags;
1346260a4b0SKAMEZAWA Hiroyuki unsigned long long pgoff = 0;
1358feae131SDavid Howells
1368feae131SDavid Howells flags = vma->vm_flags;
1378feae131SDavid Howells file = vma->vm_file;
1388feae131SDavid Howells
1398feae131SDavid Howells if (file) {
140496ad9aaSAl Viro struct inode *inode = file_inode(vma->vm_file);
1418feae131SDavid Howells dev = inode->i_sb->s_dev;
1428feae131SDavid Howells ino = inode->i_ino;
1434c967291SNobuhiro Iwamatsu pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
1448feae131SDavid Howells }
1458feae131SDavid Howells
146652586dfSTetsuo Handa seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
1478feae131SDavid Howells seq_printf(m,
148652586dfSTetsuo Handa "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
1498feae131SDavid Howells vma->vm_start,
1508feae131SDavid Howells vma->vm_end,
1518feae131SDavid Howells flags & VM_READ ? 'r' : '-',
1528feae131SDavid Howells flags & VM_WRITE ? 'w' : '-',
1538feae131SDavid Howells flags & VM_EXEC ? 'x' : '-',
1548feae131SDavid Howells flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
1556260a4b0SKAMEZAWA Hiroyuki pgoff,
156652586dfSTetsuo Handa MAJOR(dev), MINOR(dev), ino);
1578feae131SDavid Howells
1588feae131SDavid Howells if (file) {
159652586dfSTetsuo Handa seq_pad(m, ' ');
1602726d566SMiklos Szeredi seq_file_path(m, file, "");
16111250fd1SKefeng Wang } else if (mm && vma_is_initial_stack(vma)) {
162652586dfSTetsuo Handa seq_pad(m, ' ');
16308b55775SAlexey Dobriyan seq_puts(m, "[stack]");
1648feae131SDavid Howells }
1658feae131SDavid Howells
1668feae131SDavid Howells seq_putc(m, '\n');
1678feae131SDavid Howells return 0;
1688feae131SDavid Howells }
1698feae131SDavid Howells
1708feae131SDavid Howells /*
171dbf8685cSDavid Howells * display mapping lines for a particular process's /proc/pid/maps
1721da177e4SLinus Torvalds */
show_map(struct seq_file * m,void * _p)173871305bbSVlastimil Babka static int show_map(struct seq_file *m, void *_p)
1741da177e4SLinus Torvalds {
1750c563f14SMatthew Wilcox (Oracle) return nommu_vma_show(m, _p);
1761da177e4SLinus Torvalds }
177dbf8685cSDavid Howells
proc_get_vma(struct proc_maps_private * priv,loff_t * ppos)178*fe441980SBen Wolsieffer static struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv,
179*fe441980SBen Wolsieffer loff_t *ppos)
180*fe441980SBen Wolsieffer {
181*fe441980SBen Wolsieffer struct vm_area_struct *vma = vma_next(&priv->iter);
182*fe441980SBen Wolsieffer
183*fe441980SBen Wolsieffer if (vma) {
184*fe441980SBen Wolsieffer *ppos = vma->vm_start;
185*fe441980SBen Wolsieffer } else {
186*fe441980SBen Wolsieffer *ppos = -1UL;
187*fe441980SBen Wolsieffer }
188*fe441980SBen Wolsieffer
189*fe441980SBen Wolsieffer return vma;
190*fe441980SBen Wolsieffer }
191*fe441980SBen Wolsieffer
m_start(struct seq_file * m,loff_t * ppos)192*fe441980SBen Wolsieffer static void *m_start(struct seq_file *m, loff_t *ppos)
1931da177e4SLinus Torvalds {
194dbf8685cSDavid Howells struct proc_maps_private *priv = m->private;
195*fe441980SBen Wolsieffer unsigned long last_addr = *ppos;
196dbf8685cSDavid Howells struct mm_struct *mm;
1970c563f14SMatthew Wilcox (Oracle)
198*fe441980SBen Wolsieffer /* See proc_get_vma(). Zero at the start or after lseek. */
199*fe441980SBen Wolsieffer if (last_addr == -1UL)
2000c563f14SMatthew Wilcox (Oracle) return NULL;
201dbf8685cSDavid Howells
202dbf8685cSDavid Howells /* pin the task and mm whilst we play with them */
2032c03376dSOleg Nesterov priv->task = get_proc_task(priv->inode);
204dbf8685cSDavid Howells if (!priv->task)
205ec6fd8a4SAl Viro return ERR_PTR(-ESRCH);
206dbf8685cSDavid Howells
20727692cd5SOleg Nesterov mm = priv->mm;
208578d7699SBen Wolsieffer if (!mm || !mmget_not_zero(mm)) {
209578d7699SBen Wolsieffer put_task_struct(priv->task);
210578d7699SBen Wolsieffer priv->task = NULL;
21127692cd5SOleg Nesterov return NULL;
212578d7699SBen Wolsieffer }
213dbf8685cSDavid Howells
214d8ed45c5SMichel Lespinasse if (mmap_read_lock_killable(mm)) {
2158a713e7dSKonstantin Khlebnikov mmput(mm);
216578d7699SBen Wolsieffer put_task_struct(priv->task);
217578d7699SBen Wolsieffer priv->task = NULL;
2188a713e7dSKonstantin Khlebnikov return ERR_PTR(-EINTR);
2198a713e7dSKonstantin Khlebnikov }
2208a713e7dSKonstantin Khlebnikov
221*fe441980SBen Wolsieffer vma_iter_init(&priv->iter, mm, last_addr);
22247fecca1SOleg Nesterov
223*fe441980SBen Wolsieffer return proc_get_vma(priv, ppos);
2241da177e4SLinus Torvalds }
225dbf8685cSDavid Howells
m_stop(struct seq_file * m,void * v)226578d7699SBen Wolsieffer static void m_stop(struct seq_file *m, void *v)
227dbf8685cSDavid Howells {
228dbf8685cSDavid Howells struct proc_maps_private *priv = m->private;
229578d7699SBen Wolsieffer struct mm_struct *mm = priv->mm;
230dbf8685cSDavid Howells
231578d7699SBen Wolsieffer if (!priv->task)
232578d7699SBen Wolsieffer return;
233578d7699SBen Wolsieffer
234578d7699SBen Wolsieffer mmap_read_unlock(mm);
235578d7699SBen Wolsieffer mmput(mm);
236dbf8685cSDavid Howells put_task_struct(priv->task);
23747fecca1SOleg Nesterov priv->task = NULL;
238dbf8685cSDavid Howells }
239dbf8685cSDavid Howells
m_next(struct seq_file * m,void * _p,loff_t * ppos)240*fe441980SBen Wolsieffer static void *m_next(struct seq_file *m, void *_p, loff_t *ppos)
241dbf8685cSDavid Howells {
242*fe441980SBen Wolsieffer return proc_get_vma(m->private, ppos);
243dbf8685cSDavid Howells }
244dbf8685cSDavid Howells
24503a44825SJan Engelhardt static const struct seq_operations proc_pid_maps_ops = {
2461da177e4SLinus Torvalds .start = m_start,
2471da177e4SLinus Torvalds .next = m_next,
2481da177e4SLinus Torvalds .stop = m_stop,
249871305bbSVlastimil Babka .show = show_map
250b7643757SSiddhesh Poyarekar };
251b7643757SSiddhesh Poyarekar
maps_open(struct inode * inode,struct file * file,const struct seq_operations * ops)252b7643757SSiddhesh Poyarekar static int maps_open(struct inode *inode, struct file *file,
253b7643757SSiddhesh Poyarekar const struct seq_operations *ops)
254662795deSEric W. Biederman {
255dbf8685cSDavid Howells struct proc_maps_private *priv;
256dbf8685cSDavid Howells
25727692cd5SOleg Nesterov priv = __seq_open_private(file, ops, sizeof(*priv));
258ce34fddbSOleg Nesterov if (!priv)
259ce34fddbSOleg Nesterov return -ENOMEM;
260ce34fddbSOleg Nesterov
2612c03376dSOleg Nesterov priv->inode = inode;
26227692cd5SOleg Nesterov priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
26327692cd5SOleg Nesterov if (IS_ERR(priv->mm)) {
26427692cd5SOleg Nesterov int err = PTR_ERR(priv->mm);
26527692cd5SOleg Nesterov
26627692cd5SOleg Nesterov seq_release_private(inode, file);
26727692cd5SOleg Nesterov return err;
26827692cd5SOleg Nesterov }
26927692cd5SOleg Nesterov
270ce34fddbSOleg Nesterov return 0;
271662795deSEric W. Biederman }
272662795deSEric W. Biederman
27327692cd5SOleg Nesterov
map_release(struct inode * inode,struct file * file)27427692cd5SOleg Nesterov static int map_release(struct inode *inode, struct file *file)
27527692cd5SOleg Nesterov {
27627692cd5SOleg Nesterov struct seq_file *seq = file->private_data;
27727692cd5SOleg Nesterov struct proc_maps_private *priv = seq->private;
27827692cd5SOleg Nesterov
27927692cd5SOleg Nesterov if (priv->mm)
28027692cd5SOleg Nesterov mmdrop(priv->mm);
28127692cd5SOleg Nesterov
28227692cd5SOleg Nesterov return seq_release_private(inode, file);
28327692cd5SOleg Nesterov }
28427692cd5SOleg Nesterov
pid_maps_open(struct inode * inode,struct file * file)285b7643757SSiddhesh Poyarekar static int pid_maps_open(struct inode *inode, struct file *file)
286b7643757SSiddhesh Poyarekar {
287b7643757SSiddhesh Poyarekar return maps_open(inode, file, &proc_pid_maps_ops);
288b7643757SSiddhesh Poyarekar }
289b7643757SSiddhesh Poyarekar
290b7643757SSiddhesh Poyarekar const struct file_operations proc_pid_maps_operations = {
291b7643757SSiddhesh Poyarekar .open = pid_maps_open,
292b7643757SSiddhesh Poyarekar .read = seq_read,
293b7643757SSiddhesh Poyarekar .llseek = seq_lseek,
29427692cd5SOleg Nesterov .release = map_release,
295b7643757SSiddhesh Poyarekar };
296b7643757SSiddhesh Poyarekar
297