2005-04-17 02:20:36 +04:00
/* nommu.c: mmu-less memory info files
*
* Copyright ( C ) 2004 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/time.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/mman.h>
# include <linux/proc_fs.h>
# include <linux/mm.h>
# include <linux/mmzone.h>
# include <linux/pagemap.h>
# include <linux/swap.h>
# include <linux/smp.h>
# include <linux/seq_file.h>
# include <linux/hugetlb.h>
# include <linux/vmalloc.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/tlb.h>
# include <asm/div64.h>
# include "internal.h"
/*
2009-01-08 15:04:47 +03:00
* display a single region to a sequenced file
2005-04-17 02:20:36 +04:00
*/
2009-01-08 15:04:47 +03:00
static int nommu_region_show ( struct seq_file * m , struct vm_region * region )
2005-04-17 02:20:36 +04:00
{
unsigned long ino = 0 ;
struct file * file ;
dev_t dev = 0 ;
int flags , len ;
2009-01-08 15:04:47 +03:00
flags = region - > vm_flags ;
file = region - > vm_file ;
2005-04-17 02:20:36 +04:00
if ( file ) {
2009-01-08 15:04:47 +03:00
struct inode * inode = region - > vm_file - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
dev = inode - > i_sb - > s_dev ;
ino = inode - > i_ino ;
}
seq_printf ( m ,
/proc/self/maps doesn't display the real file offset
This addresses
http://bugzilla.kernel.org/show_bug.cgi?id=11318
In function show_map (file: fs/proc/task_mmu.c), if vma->vm_pgoff > 2^20
than (vma->vm_pgoff << PAGE_SIZE) is greater than 2^32 (with PAGE_SIZE
equal to 4096 (i.e. 2^12). The next seq_printf use an unsigned long for
the conversion of (vma->vm_pgoff << PAGE_SIZE), as a result the offset
value displayed in /proc/self/maps is truncated if the page offset is
greater than 2^20.
A test that shows this issue:
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define PAGE_SIZE (getpagesize())
#if __i386__
# define U64_STR "%llx"
#elif __x86_64
# define U64_STR "%lx"
#else
# error "Architecture Unsupported"
#endif
int main(int argc, char *argv[])
{
int fd;
char *addr;
off64_t offset = 0x10000000;
char *filename = "/dev/zero";
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
offset *= 0x10;
printf("offset = " U64_STR "\n", offset);
addr = (char*)mmap64(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd,
offset);
if ((void*)addr == MAP_FAILED) {
perror("mmap64");
return 1;
}
{
FILE *fmaps;
char *line = NULL;
size_t len = 0;
ssize_t read;
size_t filename_len = strlen(filename);
fmaps = fopen("/proc/self/maps", "r");
if (!fmaps) {
perror("fopen");
return 1;
}
while ((read = getline(&line, &len, fmaps)) != -1) {
if ((read > filename_len + 1)
&& (strncmp(&line[read - filename_len - 1], filename, filename_len) == 0))
printf("%s", line);
}
if (line)
free(line);
fclose(fmaps);
}
close(fd);
return 0;
}
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Clement Calmels <cboulte@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-08-21 01:09:00 +04:00
" %08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n " ,
2009-01-08 15:04:47 +03:00
region - > vm_start ,
region - > vm_end ,
2005-04-17 02:20:36 +04:00
flags & VM_READ ? ' r ' : ' - ' ,
flags & VM_WRITE ? ' w ' : ' - ' ,
flags & VM_EXEC ? ' x ' : ' - ' ,
flags & VM_MAYSHARE ? flags & VM_SHARED ? ' S ' : ' s ' : ' p ' ,
2009-01-08 15:04:47 +03:00
( ( loff_t ) region - > vm_pgoff ) < < PAGE_SHIFT ,
2005-04-17 02:20:36 +04:00
MAJOR ( dev ) , MINOR ( dev ) , ino , & len ) ;
if ( file ) {
len = 25 + sizeof ( void * ) * 6 - len ;
if ( len < 1 )
len = 1 ;
seq_printf ( m , " %*c " , len , ' ' ) ;
2008-02-15 06:38:43 +03:00
seq_path ( m , & file - > f_path , " " ) ;
2005-04-17 02:20:36 +04:00
}
seq_putc ( m , ' \n ' ) ;
return 0 ;
}
2006-09-27 12:50:19 +04:00
/*
2009-01-08 15:04:47 +03:00
* display a list of all the REGIONs the kernel knows about
2009-01-13 01:35:47 +03:00
* - nommu kernels have a single flat list
2006-09-27 12:50:19 +04:00
*/
2009-01-08 15:04:47 +03:00
static int nommu_region_list_show ( struct seq_file * m , void * _p )
2006-09-27 12:50:19 +04:00
{
2009-01-08 15:04:47 +03:00
struct rb_node * p = _p ;
2006-09-27 12:50:19 +04:00
2009-01-08 15:04:47 +03:00
return nommu_region_show ( m , rb_entry ( p , struct vm_region , vm_rb ) ) ;
2006-09-27 12:50:19 +04:00
}
2009-01-08 15:04:47 +03:00
static void * nommu_region_list_start ( struct seq_file * m , loff_t * _pos )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
struct rb_node * p ;
2005-04-17 02:20:36 +04:00
loff_t pos = * _pos ;
2009-01-08 15:04:47 +03:00
down_read ( & nommu_region_sem ) ;
2005-04-17 02:20:36 +04:00
2009-01-08 15:04:47 +03:00
for ( p = rb_first ( & nommu_region_tree ) ; p ; p = rb_next ( p ) )
if ( pos - - = = 0 )
return p ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
2009-01-08 15:04:47 +03:00
static void nommu_region_list_stop ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
up_read ( & nommu_region_sem ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-08 15:04:47 +03:00
static void * nommu_region_list_next ( struct seq_file * m , void * v , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
( * pos ) + + ;
return rb_next ( ( struct rb_node * ) v ) ;
}
2009-09-23 03:43:43 +04:00
static const struct seq_operations proc_nommu_region_list_seqop = {
2009-01-08 15:04:47 +03:00
. start = nommu_region_list_start ,
. next = nommu_region_list_next ,
. stop = nommu_region_list_stop ,
. show = nommu_region_list_show
2005-04-17 02:20:36 +04:00
} ;
2009-01-08 15:04:47 +03:00
static int proc_nommu_region_list_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2009-01-08 15:04:47 +03:00
return seq_open ( file , & proc_nommu_region_list_seqop ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-08 15:04:47 +03:00
static const struct file_operations proc_nommu_region_list_operations = {
. open = proc_nommu_region_list_open ,
2005-04-17 02:20:36 +04:00
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static int __init proc_nommu_init ( void )
{
2009-01-08 15:04:47 +03:00
proc_create ( " maps " , S_IRUGO , NULL , & proc_nommu_region_list_operations ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
module_init ( proc_nommu_init ) ;