>From 36ba2c05f02a7120be024b93e7b71af4972703a5 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 26 Sep 2017 19:48:39 +0200 Subject: [PATCH 2/2] vma-iter: Improvements for Linux and BSD platforms. - Add support for DragonFly BSD. - Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD. * lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close): Read the entire file into memory in a single system call. (vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD. * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD. --- ChangeLog | 10 +++ lib/vma-iter.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++---------- lib/vma-iter.h | 2 +- 3 files changed, 173 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9643700..af67c6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2017-09-26 Bruno Haible + vma-iter: Improvements for Linux and BSD platforms. + - Add support for DragonFly BSD. + - Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD. + * lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close): + Read the entire file into memory in a single system call. + (vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD. + * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD. + +2017-09-26 Bruno Haible + vma-iter: Provide the protection flags on FreeBSD. * lib/vma-iter.c (vma_iterate) [FreeBSD]: When reading from /proc, skip three fields between the addresses and the protection flags. diff --git a/lib/vma-iter.c b/lib/vma-iter.c index ab2eb3f..961e5a2 100644 --- a/lib/vma-iter.c +++ b/lib/vma-iter.c @@ -23,7 +23,12 @@ #include /* errno */ #include /* size_t */ #include /* open, O_RDONLY */ -#include /* getpagesize, read, close, getpid */ +#include /* getpagesize, lseek, read, close, getpid */ + +#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */ +# include +# include /* mmap, munmap */ +#endif #if defined __FreeBSD__ || defined __FreeBSD_kernel__ /* FreeBSD, GNU/kFreeBSD */ # include @@ -81,33 +86,131 @@ /* Support for reading text files in the /proc file system. */ -#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */ +#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */ /* Buffered read-only streams. We cannot use here, because fopen() calls malloc(), and a malloc() - call may call mmap() and thus pre-allocate available memory. */ + call may call mmap() and thus pre-allocate available memory. + Also, we cannot use multiple read() calls, because if the buffer size is + smaller than the file's contents: + - On NetBSD, the second read() call would return 0, thus making the file + appear truncated. + - On DragonFly BSD, the first read() call would fail with errno = EFBIG. + - On all platforms, if some other thread is doing memory allocations or + deallocations between two read() calls, there is a high risk that the + result of these two read() calls don't fit together, and as a + consequence we will parse gargage and either omit some VMAs or return + VMAs with nonsensical addresses. + So use mmap(), and ignore the resulting VMA. */ + +# ifdef TEST +/* During testing, we want to run into the hairy cases. */ +# define STACK_ALLOCATED_BUFFER_SIZE 32 +# else +# define STACK_ALLOCATED_BUFFER_SIZE 1024 +# endif struct rofile { - int fd; size_t position; size_t filled; int eof_seen; - char buffer[1024]; + /* These fields deal with allocation of the buffer. */ + char *buffer; + char *auxmap; + size_t auxmap_length; + unsigned long auxmap_start; + unsigned long auxmap_end; + char stack_allocated_buffer[STACK_ALLOCATED_BUFFER_SIZE]; }; /* Open a read-only file stream. */ static int rof_open (struct rofile *rof, const char *filename) { - int fd = open (filename, O_RDONLY); + int fd; + unsigned long pagesize; + size_t size; + + fd = open (filename, O_RDONLY); if (fd < 0) return -1; - rof->fd = fd; rof->position = 0; - rof->filled = 0; rof->eof_seen = 0; - return 0; + /* Try the static buffer first. */ + pagesize = 0; + rof->buffer = rof->stack_allocated_buffer; + size = sizeof (rof->stack_allocated_buffer); + rof->auxmap = NULL; + rof->auxmap_start = 0; + rof->auxmap_end = 0; + for (;;) + { + /* Attempt to read the contents in a single system call. */ + { + int n = read (fd, rof->buffer, size); +# ifdef EINTR + if (n < 0 && errno == EINTR) + goto retry; +# endif +# if defined __DragonFly__ + if (!(n < 0 && errno == EFBIG)) +# endif + { + if (n <= 0) + /* Empty file. */ + goto fail1; + if (n < size) + { + /* The buffer was sufficiently large. */ + rof->filled = n; + close (fd); + return 0; + } + } + } + /* Allocate a larger buffer. */ + if (pagesize == 0) + { + pagesize = getpagesize (); + size = pagesize; + } + else + { + size = 2 * size; + if (size == 0) + /* Wraparound. */ + goto fail1; + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); + } + rof->auxmap = (void *) mmap ((void *) 0, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (rof->auxmap == (void *) -1) + { + close (fd); + return -1; + } + rof->auxmap_length = size; + rof->auxmap_start = (unsigned long) rof->auxmap; + rof->auxmap_end = rof->auxmap_start + size; + rof->buffer = (char *) rof->auxmap; + retry: + /* Restart. */ + if (lseek (fd, 0, SEEK_SET) < 0) + { + close (fd); + fd = open (filename, O_RDONLY); + if (fd < 0) + goto fail2; + } + } + fail1: + close (fd); + fail2: + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); + return -1; } /* Return the next byte from a read-only file stream without consuming it, @@ -117,25 +220,8 @@ rof_peekchar (struct rofile *rof) { if (rof->position == rof->filled) { - if (rof->eof_seen) - return -1; - else - for (;;) - { - int n = read (rof->fd, rof->buffer, sizeof (rof->buffer)); -# ifdef EINTR - if (n < 0 && errno == EINTR) - continue; -# endif - if (n <= 0) - { - rof->eof_seen = 1; - return -1; - } - rof->filled = n; - rof->position = 0; - break; - } + rof->eof_seen = 1; + return -1; } return (unsigned char) rof->buffer[rof->position]; } @@ -180,7 +266,8 @@ rof_scanf_lx (struct rofile *rof, unsigned long *valuep) static void rof_close (struct rofile *rof) { - close (rof->fd); + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); } #endif @@ -470,6 +557,9 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) /* Open the current process' maps file. It describes one VMA per line. */ if (rof_open (&rof, "/proc/self/maps") >= 0) { + unsigned long auxmap_start = rof.auxmap_start; + unsigned long auxmap_end = rof.auxmap_end; + for (;;) { unsigned long start, end; @@ -497,8 +587,22 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) while (c = rof_getchar (&rof), c != -1 && c != '\n') ; - if (callback (data, start, end, flags)) - break; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (data, start, auxmap_start, flags)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (data, auxmap_end, end, flags)) + break; + } + else + { + if (callback (data, start, end, flags)) + break; + } } rof_close (&rof); return 0; @@ -507,13 +611,16 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) /* Fallback if /proc is not accessible: Use sysctl(). */ return vma_iterate_bsd (callback, data); -#elif defined __FreeBSD__ || defined __NetBSD__ +#elif defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ struct rofile rof; /* Open the current process' maps file. It describes one VMA per line. */ if (rof_open (&rof, "/proc/curproc/map") >= 0) { + unsigned long auxmap_start = rof.auxmap_start; + unsigned long auxmap_end = rof.auxmap_end; + for (;;) { unsigned long start, end; @@ -532,7 +639,7 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) && rof_getchar (&rof) == 'x' && rof_scanf_lx (&rof, &end) >= 0)) break; -# if defined __FreeBSD__ +# if defined __FreeBSD__ || defined __DragonFly__ /* Then the resident pages count. */ do c = rof_getchar (&rof); @@ -571,8 +678,22 @@ vma_iterate (vma_iterate_callback_fn callback, void *data) while (c = rof_getchar (&rof), c != -1 && c != '\n') ; - if (callback (data, start, end, flags)) - break; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (data, start, auxmap_start, flags)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (data, auxmap_end, end, flags)) + break; + } + else + { + if (callback (data, start, end, flags)) + break; + } } rof_close (&rof); return 0; @@ -1279,4 +1400,10 @@ main () return 0; } +/* + * Local Variables: + * compile-command: "gcc -ggdb -DTEST -Wall -I.. vma-iter.c" + * End: + */ + #endif /* TEST */ diff --git a/lib/vma-iter.h b/lib/vma-iter.h index c9e7165..2346972 100644 --- a/lib/vma-iter.h +++ b/lib/vma-iter.h @@ -52,7 +52,7 @@ extern int vma_iterate (vma_iterate_callback_fn callback, void *data); this platform. Note that even when this macro is defined, vma_iterate() may still fail to find any virtual memory area, for example if /proc is not mounted. */ -#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __sun && HAVE_SYS_PROCFS_H) || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY +#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __sun && HAVE_SYS_PROCFS_H) || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY # define VMA_ITERATE_SUPPORTED 1 #endif -- 2.7.4