Bug 6887 - mmap documentation bugs, perhaps also 2 functional bugs
Summary: mmap documentation bugs, perhaps also 2 functional bugs
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: manual (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-09-15 08:26 UTC by Siward de Groot
Modified: 2016-05-16 17:08 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Siward de Groot 2008-09-15 08:26:14 UTC
Good morning everyone,
i just tested mmap() &co ,
and found a few bugs, mainly in documentation,
  but i think two of them are also functional bugs.

* opening file rw and mmapping it w
  causes segfault at first write to a mapped memory location.
  (functional bug, therefore documentation bug until it is fixed).

* opening file w and mmapping it w causes mmap error 'permission denied',
  which is not mentioned in documentation
  (rather it is said that i may GET more permissions than i asked for).

* trying to read from an empty file generates a 'bus error',
  terminating process,
  which is not mentioned in documentation.

* compiler didn't find O_READ ,
  though fcntl.h was #included,
  and both __USE_GNU and _GNU_SOURCE were #defined.
  (working around by using O_RDONLY is trivial, but it is a documentation bug)

* when mmapping an empty file, mmap() returns a valid address,
  but reading from this address causes a segfault.
  (functional bug, therefore documentation bug until fixed).

* description of mremap says that it returns -1 on error,
  but this is not possible since it's returntype is void* .

* description of MS_ASYNC says
  "This tells `msync' to begin synchronization, but not to wait for it to complete."
   which should be
   "This tells `msync' to begin synchronization, but to not wait for it to
complete."
   because it is confusing.
   (never mind whether it is official english ; it is braindamage.)

* description of MAP_SHARED says
  "This specifies that writes to region will be written back to file."
   but it is valid (or at least it works) to use MAP_SHARED with MAP_ANON,
   in which case there is no file to write back to.
   Perhaps in this case MAP_SHARED controls whether other processes see changed
content ?

For the rest, mmap, mremap, munmap, and msync seem to work perfectly.
Thanks !
Have a nice day :-)

  Siward
Comment 1 Siward de Groot 2008-09-15 08:35:44 UTC
In case it is helpfull, here is code i used for testing :
(macros ex(), esx(), and t() are simple error-reporting thingies)

/* test files :
  /tmp/test  : permissions 0770 : content "#!/bin/bash\necho hello\nexit\n"
  /tmp/empty : permissions 0770 : no content
  /tmp/small : permissions 0770 : initially 10 bytes random content
 */

/* compile with :
 cd /my/test/c
 cc mmap2.c -o mmap2 -I /my/c/include -W -Wall
 */

#include "defines.c"
#include <stdio.h>
#include <sys/mman.h>		 /* mmap()	 */
#include <unistd.h>		 /* sysconf()	 */
#include <fcntl.h>		 /* open()	 */
#include <sys/stat.h>		 /* stat()	 */
#include <errno.h>		 /* ENOENT	 */
#include <string.h>		 /* strcpy()	 */
#include "errordef.c"

char*		 filename	 ;
int		 ck		 ;
struct stat	 stats		 ;
size_t		 filesize	 ; /* unsigned ! */
int		 fd		 ; /* file descriptor */
size_t		 pagesize	 ; /* unsigned ! */
void*		 adres		 ;
size_t		 mappedsize	 ; /* unsigned ! */


void TestMmapFile( void ){
  char* pc ;

  filename = "/tmp/test" ;
  ck = stat( filename, &stats );
  if( ck ){ esx("stat nok"); }
  filesize = stats.st_size ;
  t("filesize = %d", filesize );

  fd = open( filename, O_RDWR );
  /* bug: O_READ not found, though fcntl.h is included */
  if( fd < 0 ){ esx("open nok"); }
  t("open ok");

  pagesize = sysconf( _SC_PAGESIZE ) ;
  if( pagesize <= 0 ){ esx("sysconf nok"); }
  t("pagesize = %d", pagesize );
  if( pagesize < filesize ){ ex("pagesize is smaller than filesize"); }

  adres = mmap( NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
  /* what if mmap permissions don't match permissions with which file was opened
? */
  /*   open rw , mmap rw , action w -> ok      		 */
  /*   open rw , mmap r  , action w -> segfault		 */ /* pebcak	 */
  /*   open rw , mmap w  , action w -> segfault		 */ /* BUG !	 */
  /*   open r  , mmap r  , action w -> segfault		 */ /* pebcak	 */
  /*   open r  , mmap w             -> permission denied */
  /*   open w  , mmap r             -> permission denied */
  /*   open w  , mmap w             -> permission denied */ /* doc bug	 */

  if( adres == MAP_FAILED ){ esx("mmap nok"); }
  t("mmap ok");
  mappedsize = pagesize ;

  pc = (char*) adres ; pc[21]++ ;	 /* changes 'echo hello' to 'echo hellp' */
  t("change content ok");

  ck = munmap( adres, mappedsize );
  /* what if i munmap filesize ? -> ok */
  if( ck < 0 ){ esx("munmap nok"); }
  t("munmap ok");

  ck = close( fd );
  if( ck < 0 ){ esx("close nok"); }
  t("close ok");
  }

void TestMmapEmptyfile( void ){
  char	 c	 ;

  filename = "/tmp/empty" ;
  ck = stat( filename, &stats );
  if( ck ){ esx("stat nok"); }
  filesize = stats.st_size ;
  t("filesize = %d", filesize );

  fd = open( filename, O_RDWR );
  /* bug: O_READ not found, though fcntl.h is included */
  if( fd < 0 ){ esx("open nok"); }
  t("open ok");

  pagesize = sysconf( _SC_PAGESIZE ) ;
  if( pagesize <= 0 ){ esx("sysconf nok"); }
  t("pagesize = %d", pagesize );
  if( pagesize < filesize ){ ex("pagesize is smaller than filesize"); }

  adres = mmap( NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
  if( adres == MAP_FAILED ){ esx("mmap nok"); }
  t("mmap ok ; adres = %p", adres );
  mappedsize = 0 ;

  c = *((char*) adres) ;
  t("read content ok");
  }

void TestMmapResize( void ){
  size_t newsize ;
  int	 i	 ;
  int	 imax	 ;
  char*	 pc	 ;

  filename = "/tmp/small" ;
  ck = stat( filename, &stats );
  if( ck ){ esx("stat nok"); }
  filesize = stats.st_size ;
  t("filesize = %d", filesize );

  fd = open( filename, O_RDWR );
  if( fd < 0 ){ esx("open nok"); }
  t("open ok");

  pagesize = sysconf( _SC_PAGESIZE ) ;
  if( pagesize <= 0 ){ esx("sysconf nok"); }
  t("pagesize = %d", pagesize );

  adres = mmap( NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
  if( adres == MAP_FAILED ){ esx("mmap nok"); }
  t("mmap ok ; adres = %p", adres );
  mappedsize = pagesize ;

  newsize = 2 * pagesize ;
  ck = ftruncate( fd, newsize );
  if( ck < 0 ){ esx("ftruncate nok"); }
  t("ftruncate ok");

  adres = mremap( adres, mappedsize, newsize, MREMAP_MAYMOVE );
  t("mremap ok ; new adres = %p", adres );
  mappedsize = newsize ;

  imax = pagesize + pagesize / 2 ;
  pc = (char*) adres ;
  for( i=0 ; i<imax ; i++ ){ pc[i] = 'a' ; }
  t("write ok ; imax = %d", imax );

  ck = ftruncate( fd, imax );
  if( ck < 0 ){ esx("ftruncate nok"); }
  t("ftruncate ok");

  ck = msync( adres, imax, 0 );
  /* tested with all 3 possible values of flags ; all work ok */
  if( ck < 0 ){ esx("msync nok"); }
  t("msync ok");

  ck = munmap( adres, mappedsize );
  if( ck < 0 ){ esx("munmap nok"); }
  t("munmap ok");

  ck = close( fd );
  if( ck < 0 ){ esx("close nok"); }
  t("close ok");
  }

void TestMmapAnon( void ){
  int i ;
  int* pi ;

  pagesize = sysconf( _SC_PAGESIZE ) ;
  if( pagesize <= 0 ){ esx("sysconf nok"); }
  t("pagesize = %d", pagesize );

  adres = mmap( NULL, pagesize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0 );
  /* with flags MAP_PRIVATE | MAP_ANON : ok  */
  /* with flags MAP_SHARED  | MAP_ANON : ok  */
  /* with flags MAP_ANON               : nok */

  if( adres == MAP_FAILED ){ esx("mmap nok"); }
  t("mmap ok ; adres = %p", adres );
  mappedsize = pagesize ;
  pi = (int*) adres ;
  for( i=0 ; i < 100 ; i++ ){ pi[i] = i ; }
  t("assign ok");
  for( i=0 ; i < 100 ; i++ ){ if( pi[i] != i ){ ex("pi[%d] = %d",i,pi[i] ); } }
  t("readback ok");
  }

int main( void ){
  TestMmapAnon();
  return(0);
  }
Comment 2 Siward de Groot 2008-09-15 08:52:16 UTC
Oops,
3rd and 5th items are both about same thing,
and neither of them is completely correct.
Please replace these two with :

* when mmapping an empty file, mmap() returns a valid address,
  but reading from this address causes a 'bus error'.
  Bus error is not mentioned in documentation, and
  A valid address is returned despite that there is no valid memorylocation.