Index: exceptions.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/exceptions.cc,v retrieving revision 1.296 diff -u -p -u -p -r1.296 exceptions.cc --- exceptions.cc 13 Jul 2006 08:33:34 -0000 1.296 +++ exceptions.cc 17 Jul 2006 22:31:11 -0000 @@ -526,12 +526,12 @@ _cygtls::handle_exceptions (EXCEPTION_RE break; case STATUS_ACCESS_VIOLATION: - switch (mmap_is_attached_or_noreserve_page (e->ExceptionInformation[1])) + switch (mmap_is_attached_or_noreserve ((void *)e->ExceptionInformation[1], + 1)) { - case 2: /* MAP_NORESERVE page, now commited. */ + case MMAP_NORESERVE_COMMITED: return 0; - case 1: /* MAP_NORESERVE page, commit failed, or - access to mmap page beyond EOF. */ + case MMAP_RAISE_SIGBUS: /* commit failed for noreserve or beyond EOF. */ si.si_signo = SIGBUS; si.si_code = BUS_OBJERR; break; Index: fhandler.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/fhandler.cc,v retrieving revision 1.256 diff -u -p -u -p -r1.256 fhandler.cc --- fhandler.cc 13 Jul 2006 20:56:24 -0000 1.256 +++ fhandler.cc 17 Jul 2006 22:31:11 -0000 @@ -223,8 +223,10 @@ fhandler_base::raw_read (void *ptr, size HANDLE h = NULL; /* grumble */ int prio = 0; /* ditto */ + int try_noreserve = 1; DWORD len = ulen; +retry: ulen = (size_t) -1; if (read_state) { @@ -259,6 +261,19 @@ fhandler_base::raw_read (void *ptr, size bytes_read = 0; break; } + if (try_noreserve) + { + try_noreserve = 0; + switch (mmap_is_attached_or_noreserve (ptr, len)) + { + case MMAP_NORESERVE_COMMITED: + goto retry; + case MMAP_RAISE_SIGBUS: + raise(SIGBUS); + case MMAP_NONE: + break; + } + } case ERROR_INVALID_FUNCTION: case ERROR_INVALID_PARAMETER: case ERROR_INVALID_HANDLE: Index: mmap.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/mmap.cc,v retrieving revision 1.130 diff -u -p -u -p -r1.130 mmap.cc --- mmap.cc 13 Jul 2006 10:29:21 -0000 1.130 +++ mmap.cc 17 Jul 2006 22:31:12 -0000 @@ -900,44 +900,59 @@ map::del_list (unsigned i) } /* This function is called from exception_handler when a segmentation - violation has happened. We have two cases to check here. + violation has occurred. It should also be called from all Cygwin + functions that want to support passing noreserve mmap page addresses + to Windows system calls. In that case, it should be called only after + a system call indicates that the application buffer passed had an + invalid virtual address to avoid any performance impact in non-noreserve + cases. - First, is it an address within "attached" mmap pages (indicated by - the __PROT_ATTACH protection, see there)? In this case the function - returns 1 and the exception_handler raises SIGBUS, as demanded by the - memory protection extension described in SUSv3 (see the mmap man - page). - - Second, check if the address is within "noreserve" mmap pages - (indicated by MAP_NORESERVE flag). If so, the function calls - VirtualAlloc to commit the page and returns 2. The exception handler - then just returns with 0 and the affected application retries the - failing memory access. If VirtualAlloc fails, the function returns - 1, so that the exception handler raises a SIGBUS, as described in the - MAP_NORESERVE man pages for Linux and Solaris. - - In any other case 0 is returned and a normal SIGSEGV is raised. */ -int -mmap_is_attached_or_noreserve_page (ULONG_PTR addr) + Check if the address range is all within noreserve mmap regions. If so, + call VirtualAlloc to commit the pages and return MMAP_NORESERVE_COMMITED + on success. If the page has __PROT_ATTACH (SUSv3 memory protection + extension), or if VirutalAlloc fails, return MMAP_RAISE_SIGBUS. + Otherwise, return MMAP_NONE if the address range is not covered by an + attached or noreserve map. + + On MAP_NORESERVE_COMMITED, the exeception handler should return 0 to + allow the application to retry the memory access, or the calling Cygwin + function should retry the Windows system call. */ +mmap_region_status +mmap_is_attached_or_noreserve (void *addr, size_t len) { - list *map_list; - long record_idx; - caddr_t u_addr; - DWORD u_len; - DWORD pagesize = getsystempagesize (); + list *map_list = mmapped_areas.get_list_by_fd (-1); - addr = rounddown (addr, pagesize); - if (!(map_list = mmapped_areas.get_list_by_fd (-1))) - return 0; - if ((record_idx = map_list->search_record ((caddr_t)addr, pagesize, - u_addr, u_len, -1)) < 0) - return 0; - if (map_list->get_record (record_idx)->attached ()) - return 1; - if (!map_list->get_record (record_idx)->noreserve ()) - return 0; - DWORD new_prot = map_list->get_record (record_idx)->gen_protect (); - return VirtualAlloc ((void *)addr, pagesize, MEM_COMMIT, new_prot) ? 2 : 1; + if (map_list == NULL) + return MMAP_NONE; + + while (len > 0) + { + caddr_t u_addr; + DWORD u_len; + long record_idx = map_list->search_record ((caddr_t)addr, 1, + u_addr, u_len, -1); + if (record_idx < 0) + return MMAP_NONE; + + mmap_record *rec = map_list->get_record (record_idx); + if (rec->attached ()) + return MMAP_RAISE_SIGBUS; + if (!rec->noreserve ()) + return MMAP_NONE; + + size_t commit_len = u_len - ((char *)addr - u_addr); + if (commit_len > len) + commit_len = len; + + if (VirtualAlloc (addr, commit_len, MEM_COMMIT, rec->gen_protect ()) + == NULL) + return MMAP_RAISE_SIGBUS; + + addr = (char *)addr + commit_len; + len -= commit_len; + } + + return MMAP_NORESERVE_COMMITED; } static caddr_t Index: winsup.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/winsup.h,v retrieving revision 1.189 diff -u -p -u -p -r1.189 winsup.h --- winsup.h 13 Jul 2006 08:33:34 -0000 1.189 +++ winsup.h 17 Jul 2006 22:31:12 -0000 @@ -299,7 +299,13 @@ size_t getsystempagesize (); /* mmap functions. */ void mmap_init (); -int mmap_is_attached_or_noreserve_page (ULONG_PTR addr); +enum mmap_region_status + { + MMAP_NONE, + MMAP_RAISE_SIGBUS, + MMAP_NORESERVE_COMMITED + }; +mmap_region_status mmap_is_attached_or_noreserve (void *addr, size_t len); int winprio_to_nice (DWORD) __attribute__ ((regparm (1))); DWORD nice_to_winprio (int &) __attribute__ ((regparm (1)));