From: Aidan Hobson Sayers aidanhs@cantab.net
The read and pread syscalls are permitted to return fewer bytes than requested (unlike the fread libc call which will only perform a short read on error or EOF). This is most likely to occur when binaries are located on slower filesystems (e.g. NFS or 9pfs)
ffab9d9 is a relatively recent regression here, but a lot of the code appears to be much older (e.g. the read call in 341b7dc) --- dlls/ntdll/unix/file.c | 2 +- dlls/ntdll/unix/process.c | 4 ++-- dlls/ntdll/unix/unix_private.h | 18 ++++++++++++++++++ dlls/ntdll/unix/virtual.c | 11 ++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 3a24d4ebbf2..e440045df2e 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -6520,7 +6520,7 @@ static inline BOOL is_device_placeholder( int fd ) static const char wine_placeholder[] = "Wine device placeholder"; char buffer[sizeof(wine_placeholder)-1];
- if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1) + if (pread_all( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1) return FALSE; return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 ); } diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 30bd6f083bd..8224679aba7 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -190,7 +190,7 @@ static BOOL get_so_file_info( int fd, pe_image_info_t *info )
off_t pos;
- if (pread( fd, &header, sizeof(header), 0 ) != sizeof(header)) return FALSE; + if (pread_all( fd, &header, sizeof(header), 0 ) != sizeof(header)) return FALSE;
if (!memcmp( header.elf.magic, "\177ELF", 4 )) { @@ -223,7 +223,7 @@ static BOOL get_so_file_info( int fd, pe_image_info_t *info ) } while (phnum--) { - if (pread( fd, &type, sizeof(type), pos ) != sizeof(type)) return FALSE; + if (pread_all( fd, &type, sizeof(type), pos ) != sizeof(type)) return FALSE; if (type == 3 /* PT_INTERP */) return FALSE; pos += (header.elf.class == 2) ? 56 : 32; } diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 6862d74b863..4cc00130cd0 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -23,6 +23,8 @@
#include <pthread.h> #include <signal.h> +#include <unistd.h> +#include <errno.h> #include "unixlib.h" #include "wine/unixlib.h" #include "wine/server.h" @@ -520,4 +522,20 @@ static inline NTSTATUS map_section( HANDLE mapping, void **ptr, SIZE_T *size, UL 0, NULL, size, ViewShare, 0, protect ); }
+static inline SSIZE_T pread_all( int fd, void *buf, size_t count, off_t offset ) +{ + ssize_t total = 0; + while (count - total != 0) { + ssize_t ret = pread( fd, (char *)buf + total, count - total, offset + total ); + if (ret == -1) { + if (errno == EINTR) { continue; } + return -1; + } else if (ret == 0) { + return total; + } + total += ret; + } + return total; +} + #endif /* __NTDLL_UNIX_PRIVATE_H */ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 62fc1c9dd1f..aecfe661ccb 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2106,7 +2106,9 @@ static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start ptr = anon_mmap_fixed( (char *)view->base + start, size, PROT_READ | PROT_WRITE, 0 ); if (ptr == MAP_FAILED) return STATUS_NO_MEMORY; /* Now read in the file */ - pread( fd, ptr, size, offset ); + if (pread_all( fd, ptr, size, offset ) != size) { + return STATUS_INVALID_PARAMETER; /* file was too short or error reading it */ + } if (prot != (PROT_READ|PROT_WRITE)) mprotect( ptr, size, prot ); /* Set the right protection */ done: set_page_vprot( (char *)view->base + start, size, vprot ); @@ -2411,7 +2413,10 @@ static NTSTATUS map_pe_header( void *ptr, size_t size, int fd, BOOL *removable ) } *removable = TRUE; } - pread( fd, ptr, size, 0 ); + pread_all( fd, ptr, size, 0 ); + if (pread_all( fd, ptr, size, 0 ) != size) { + return STATUS_INVALID_PARAMETER; /* file was too short or error reading it */ + } return STATUS_SUCCESS; /* page protections will be updated later */ }
@@ -4925,7 +4930,7 @@ static NTSTATUS get_working_set_ex( HANDLE process, LPCVOID addr, (vprot & VPROT_COMMITTED)) { if (pagemap_fd == -1 || - pread( pagemap_fd, &pagemap, sizeof(pagemap), ((UINT_PTR)p->VirtualAddress >> page_shift) * sizeof(pagemap) ) != sizeof(pagemap)) + pread_all( pagemap_fd, &pagemap, sizeof(pagemap), ((UINT_PTR)p->VirtualAddress >> page_shift) * sizeof(pagemap) ) != sizeof(pagemap)) { /* If we don't have pagemap information, default to invalid. */ pagemap = 0;