Be more strict and verbose while testing
[wine] / dlls / kernel / virtual.c
1 /*
2  * Win32 virtual memory functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "ntstatus.h"
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winternl.h"
40 #include "winerror.h"
41 #include "wine/exception.h"
42 #include "excpt.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(virtual);
46 WINE_DECLARE_DEBUG_CHANNEL(seh);
47
48 static unsigned int page_size;
49
50 /* filter for page-fault exceptions */
51 static WINE_EXCEPTION_FILTER(page_fault)
52 {
53     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
54         return EXCEPTION_EXECUTE_HANDLER;
55     return EXCEPTION_CONTINUE_SEARCH;
56 }
57
58
59 /***********************************************************************
60  *             VirtualAlloc   (KERNEL32.@)
61  * Reserves or commits a region of pages in virtual address space
62  *
63  * RETURNS
64  *      Base address of allocated region of pages
65  *      NULL: Failure
66  */
67 LPVOID WINAPI VirtualAlloc(
68               LPVOID addr,  /* [in] Address of region to reserve or commit */
69               SIZE_T size,  /* [in] Size of region */
70               DWORD type,   /* [in] Type of allocation */
71               DWORD protect)/* [in] Type of access protection */
72 {
73     return VirtualAllocEx( GetCurrentProcess(), addr, size, type, protect );
74 }
75
76
77 /***********************************************************************
78  *             VirtualAllocEx   (KERNEL32.@)
79  *
80  * Seems to be just as VirtualAlloc, but with process handle.
81  *
82  * RETURNS
83  *      Base address of allocated region of pages
84  *      NULL: Failure
85  */
86 LPVOID WINAPI VirtualAllocEx(
87               HANDLE hProcess, /* [in] Handle of process to do mem operation */
88               LPVOID addr,     /* [in] Address of region to reserve or commit */
89               SIZE_T size,     /* [in] Size of region */
90               DWORD type,      /* [in] Type of allocation */
91               DWORD protect )  /* [in] Type of access protection */
92 {
93     LPVOID ret = addr;
94     NTSTATUS status;
95
96     if ((status = NtAllocateVirtualMemory( hProcess, &ret, 0, &size, type, protect )))
97     {
98         SetLastError( RtlNtStatusToDosError(status) );
99         ret = NULL;
100     }
101     return ret;
102 }
103
104
105 /***********************************************************************
106  *             VirtualFree   (KERNEL32.@)
107  * Release or decommits a region of pages in virtual address space.
108  *
109  * RETURNS
110  *      TRUE: Success
111  *      FALSE: Failure
112  */
113 BOOL WINAPI VirtualFree(
114               LPVOID addr, /* [in] Address of region of committed pages */
115               SIZE_T size, /* [in] Size of region */
116               DWORD type   /* [in] Type of operation */
117 ) {
118     return VirtualFreeEx( GetCurrentProcess(), addr, size, type );
119 }
120
121
122 /***********************************************************************
123  *             VirtualFreeEx   (KERNEL32.@)
124  * Release or decommits a region of pages in virtual address space.
125  *
126  * RETURNS
127  *      TRUE: Success
128  *      FALSE: Failure
129  */
130 BOOL WINAPI VirtualFreeEx( HANDLE process, LPVOID addr, SIZE_T size, DWORD type )
131 {
132     NTSTATUS status = NtFreeVirtualMemory( process, &addr, &size, type );
133     if (status) SetLastError( RtlNtStatusToDosError(status) );
134     return !status;
135 }
136
137
138 /***********************************************************************
139  *             VirtualLock   (KERNEL32.@)
140  * Locks the specified region of virtual address space
141  *
142  * NOTE
143  *      Always returns TRUE
144  *
145  * RETURNS
146  *      TRUE: Success
147  *      FALSE: Failure
148  */
149 BOOL WINAPI VirtualLock( LPVOID addr, /* [in] Address of first byte of range to lock */
150                          SIZE_T size ) /* [in] Number of bytes in range to lock */
151 {
152     NTSTATUS status = NtLockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 );
153     if (status) SetLastError( RtlNtStatusToDosError(status) );
154     return !status;
155 }
156
157
158 /***********************************************************************
159  *             VirtualUnlock   (KERNEL32.@)
160  * Unlocks a range of pages in the virtual address space
161  *
162  * NOTE
163  *      Always returns TRUE
164  *
165  * RETURNS
166  *      TRUE: Success
167  *      FALSE: Failure
168  */
169 BOOL WINAPI VirtualUnlock( LPVOID addr, /* [in] Address of first byte of range */
170                            SIZE_T size ) /* [in] Number of bytes in range */
171 {
172     NTSTATUS status = NtUnlockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 );
173     if (status) SetLastError( RtlNtStatusToDosError(status) );
174     return !status;
175 }
176
177
178 /***********************************************************************
179  *             VirtualProtect   (KERNEL32.@)
180  * Changes the access protection on a region of committed pages
181  *
182  * RETURNS
183  *      TRUE: Success
184  *      FALSE: Failure
185  */
186 BOOL WINAPI VirtualProtect(
187               LPVOID addr,     /* [in] Address of region of committed pages */
188               SIZE_T size,     /* [in] Size of region */
189               DWORD new_prot,  /* [in] Desired access protection */
190               LPDWORD old_prot /* [out] Address of variable to get old protection */
191 ) {
192     return VirtualProtectEx( GetCurrentProcess(), addr, size, new_prot, old_prot );
193 }
194
195
196 /***********************************************************************
197  *             VirtualProtectEx   (KERNEL32.@)
198  * Changes the access protection on a region of committed pages in the
199  * virtual address space of a specified process
200  *
201  * RETURNS
202  *      TRUE: Success
203  *      FALSE: Failure
204  */
205 BOOL WINAPI VirtualProtectEx(
206               HANDLE process,  /* [in]  Handle of process */
207               LPVOID addr,     /* [in]  Address of region of committed pages */
208               SIZE_T size,     /* [in]  Size of region */
209               DWORD new_prot,  /* [in]  Desired access protection */
210               LPDWORD old_prot /* [out] Address of variable to get old protection */ )
211 {
212     NTSTATUS status = NtProtectVirtualMemory( process, &addr, &size, new_prot, old_prot );
213     if (status) SetLastError( RtlNtStatusToDosError(status) );
214     return !status;
215 }
216
217
218 /***********************************************************************
219  *             VirtualQuery   (KERNEL32.@)
220  * Provides info about a range of pages in virtual address space
221  *
222  * RETURNS
223  *      Number of bytes returned in information buffer
224  *      or 0 if addr is >= 0xc0000000 (kernel space).
225  */
226 SIZE_T WINAPI VirtualQuery(
227              LPCVOID addr,                    /* [in]  Address of region */
228              PMEMORY_BASIC_INFORMATION info,  /* [out] Address of info buffer */
229              SIZE_T len                       /* [in]  Size of buffer */
230 ) {
231     return VirtualQueryEx( GetCurrentProcess(), addr, info, len );
232 }
233
234
235 /***********************************************************************
236  *             VirtualQueryEx   (KERNEL32.@)
237  * Provides info about a range of pages in virtual address space of a
238  * specified process
239  *
240  * RETURNS
241  *      Number of bytes returned in information buffer
242  */
243 SIZE_T WINAPI VirtualQueryEx(
244              HANDLE process,                  /* [in] Handle of process */
245              LPCVOID addr,                    /* [in] Address of region */
246              PMEMORY_BASIC_INFORMATION info,  /* [out] Address of info buffer */
247              SIZE_T len                       /* [in] Size of buffer */ )
248 {
249     SIZE_T ret;
250     NTSTATUS status;
251
252     if ((status = NtQueryVirtualMemory( process, addr, MemoryBasicInformation, info, len, &ret )))
253     {
254         SetLastError( RtlNtStatusToDosError(status) );
255         ret = 0;
256     }
257     return ret;
258 }
259
260
261 /***********************************************************************
262  *             CreateFileMappingA   (KERNEL32.@)
263  * Creates a named or unnamed file-mapping object for the specified file
264  *
265  * RETURNS
266  *      Handle: Success
267  *      0: Mapping object does not exist
268  *      NULL: Failure
269  */
270 HANDLE WINAPI CreateFileMappingA(
271                 HANDLE hFile,   /* [in] Handle of file to map */
272                 SECURITY_ATTRIBUTES *sa, /* [in] Optional security attributes*/
273                 DWORD protect,   /* [in] Protection for mapping object */
274                 DWORD size_high, /* [in] High-order 32 bits of object size */
275                 DWORD size_low,  /* [in] Low-order 32 bits of object size */
276                 LPCSTR name      /* [in] Name of file-mapping object */ )
277 {
278     WCHAR buffer[MAX_PATH];
279
280     if (!name) return CreateFileMappingW( hFile, sa, protect, size_high, size_low, NULL );
281
282     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
283     {
284         SetLastError( ERROR_FILENAME_EXCED_RANGE );
285         return 0;
286     }
287     return CreateFileMappingW( hFile, sa, protect, size_high, size_low, buffer );
288 }
289
290
291 /***********************************************************************
292  *             CreateFileMappingW   (KERNEL32.@)
293  * See CreateFileMappingA.
294  */
295 HANDLE WINAPI CreateFileMappingW( HANDLE hFile, LPSECURITY_ATTRIBUTES sa,
296                                   DWORD protect, DWORD size_high,
297                                   DWORD size_low, LPCWSTR name )
298 {
299     static const int sec_flags = SEC_FILE | SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_NOCACHE;
300
301     HANDLE ret;
302     OBJECT_ATTRIBUTES attr;
303     UNICODE_STRING nameW;
304     NTSTATUS status;
305     DWORD access, sec_type;
306     LARGE_INTEGER size;
307
308     attr.Length                   = sizeof(attr);
309     attr.RootDirectory            = 0;
310     attr.ObjectName               = NULL;
311     attr.Attributes               = OBJ_CASE_INSENSITIVE | OBJ_OPENIF |
312                                     ((sa && sa->bInheritHandle) ? OBJ_INHERIT : 0);
313     attr.SecurityDescriptor       = sa ? sa->lpSecurityDescriptor : NULL;
314     attr.SecurityQualityOfService = NULL;
315
316     if (name)
317     {
318         RtlInitUnicodeString( &nameW, name );
319         attr.ObjectName = &nameW;
320     }
321
322     sec_type = protect & sec_flags;
323     protect &= ~sec_flags;
324     if (!sec_type) sec_type = SEC_COMMIT;
325
326     switch(protect)
327     {
328     case 0:
329         protect = PAGE_READONLY;  /* Win9x compatibility */
330         /* fall through */
331     case PAGE_READONLY:
332     case PAGE_WRITECOPY:
333         access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ;
334         break;
335     case PAGE_READWRITE:
336         access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE;
337         break;
338     default:
339         SetLastError( ERROR_INVALID_PARAMETER );
340         return 0;
341     }
342
343     if (hFile == INVALID_HANDLE_VALUE)
344     {
345         hFile = 0;
346         if (!size_low && !size_high)
347         {
348             SetLastError( ERROR_INVALID_PARAMETER );
349             return 0;
350         }
351     }
352
353     size.u.LowPart  = size_low;
354     size.u.HighPart = size_high;
355
356     status = NtCreateSection( &ret, access, &attr, &size, protect, sec_type, hFile );
357     if (status == STATUS_OBJECT_NAME_EXISTS)
358         SetLastError( ERROR_ALREADY_EXISTS );
359     else
360         SetLastError( RtlNtStatusToDosError(status) );
361     return ret;
362 }
363
364
365 /***********************************************************************
366  *             OpenFileMappingA   (KERNEL32.@)
367  * Opens a named file-mapping object.
368  *
369  * RETURNS
370  *      Handle: Success
371  *      NULL: Failure
372  */
373 HANDLE WINAPI OpenFileMappingA(
374                 DWORD access,   /* [in] Access mode */
375                 BOOL inherit, /* [in] Inherit flag */
376                 LPCSTR name )   /* [in] Name of file-mapping object */
377 {
378     WCHAR buffer[MAX_PATH];
379
380     if (!name) return OpenFileMappingW( access, inherit, NULL );
381
382     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
383     {
384         SetLastError( ERROR_FILENAME_EXCED_RANGE );
385         return 0;
386     }
387     return OpenFileMappingW( access, inherit, buffer );
388 }
389
390
391 /***********************************************************************
392  *             OpenFileMappingW   (KERNEL32.@)
393  * See OpenFileMappingA.
394  */
395 HANDLE WINAPI OpenFileMappingW( DWORD access, BOOL inherit, LPCWSTR name)
396 {
397     OBJECT_ATTRIBUTES attr;
398     UNICODE_STRING nameW;
399     HANDLE ret;
400     NTSTATUS status;
401
402     if (!name)
403     {
404         SetLastError( ERROR_INVALID_PARAMETER );
405         return 0;
406     }
407     attr.Length = sizeof(attr);
408     attr.RootDirectory = 0;
409     attr.ObjectName = &nameW;
410     attr.Attributes = OBJ_CASE_INSENSITIVE | (inherit ? OBJ_INHERIT : 0);
411     attr.SecurityDescriptor = NULL;
412     attr.SecurityQualityOfService = NULL;
413     RtlInitUnicodeString( &nameW, name );
414
415     if (access == FILE_MAP_COPY) access = FILE_MAP_READ;
416
417     if ((status = NtOpenSection( &ret, access, &attr )))
418     {
419         SetLastError( RtlNtStatusToDosError(status) );
420         ret = 0;
421     }
422     return ret;
423 }
424
425
426 /***********************************************************************
427  *             MapViewOfFile   (KERNEL32.@)
428  * Maps a view of a file into the address space
429  *
430  * RETURNS
431  *      Starting address of mapped view
432  *      NULL: Failure
433  */
434 LPVOID WINAPI MapViewOfFile(
435               HANDLE mapping,  /* [in] File-mapping object to map */
436               DWORD access,      /* [in] Access mode */
437               DWORD offset_high, /* [in] High-order 32 bits of file offset */
438               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
439               SIZE_T count       /* [in] Number of bytes to map */
440 ) {
441     return MapViewOfFileEx( mapping, access, offset_high,
442                             offset_low, count, NULL );
443 }
444
445
446 /***********************************************************************
447  *             MapViewOfFileEx   (KERNEL32.@)
448  * Maps a view of a file into the address space
449  *
450  * RETURNS
451  *      Starting address of mapped view
452  *      NULL: Failure
453  */
454 LPVOID WINAPI MapViewOfFileEx(
455               HANDLE handle,   /* [in] File-mapping object to map */
456               DWORD access,      /* [in] Access mode */
457               DWORD offset_high, /* [in] High-order 32 bits of file offset */
458               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
459               SIZE_T count,      /* [in] Number of bytes to map */
460               LPVOID addr        /* [in] Suggested starting address for mapped view */
461 ) {
462     NTSTATUS status;
463     LARGE_INTEGER offset;
464     ULONG protect;
465
466     offset.u.LowPart  = offset_low;
467     offset.u.HighPart = offset_high;
468
469     if (access & FILE_MAP_WRITE) protect = PAGE_READWRITE;
470     else if (access & FILE_MAP_READ) protect = PAGE_READONLY;
471     else if (access & FILE_MAP_COPY) protect = PAGE_WRITECOPY;
472     else protect = PAGE_NOACCESS;
473
474     if ((status = NtMapViewOfSection( handle, GetCurrentProcess(), &addr, 0, 0, &offset,
475                                       &count, ViewShare, 0, protect )))
476     {
477         SetLastError( RtlNtStatusToDosError(status) );
478         addr = NULL;
479     }
480     return addr;
481 }
482
483
484 /***********************************************************************
485  *             UnmapViewOfFile   (KERNEL32.@)
486  * Unmaps a mapped view of a file.
487  *
488  * NOTES
489  *      Should addr be an LPCVOID?
490  *
491  * RETURNS
492  *      TRUE: Success
493  *      FALSE: Failure
494  */
495 BOOL WINAPI UnmapViewOfFile( LPVOID addr ) /* [in] Address where mapped view begins */
496 {
497     NTSTATUS status = NtUnmapViewOfSection( GetCurrentProcess(), addr );
498     if (status) SetLastError( RtlNtStatusToDosError(status) );
499     return !status;
500 }
501
502
503 /***********************************************************************
504  *             FlushViewOfFile   (KERNEL32.@)
505  * Writes to the disk a byte range within a mapped view of a file
506  *
507  * RETURNS
508  *      TRUE: Success
509  *      FALSE: Failure
510  */
511 BOOL WINAPI FlushViewOfFile( LPCVOID base,  /* [in] Start address of byte range to flush */
512                              SIZE_T size )   /* [in] Number of bytes in range */
513 {
514     NTSTATUS status = NtFlushVirtualMemory( GetCurrentProcess(), &base, &size, 0 );
515     if (status)
516     {
517         if (status == STATUS_NOT_MAPPED_DATA) status = STATUS_SUCCESS;
518         else SetLastError( RtlNtStatusToDosError(status) );
519     }
520     return !status;
521 }
522
523
524 /***********************************************************************
525  *             IsBadReadPtr   (KERNEL32.@)
526  *
527  * Check for read access on a memory block.
528  *
529  * RETURNS
530  *      FALSE: Process has read access to entire block
531  *      TRUE: Otherwise
532  */
533 BOOL WINAPI IsBadReadPtr(
534               LPCVOID ptr, /* [in] Address of memory block */
535               UINT size )  /* [in] Size of block */
536 {
537     if (!size) return FALSE;  /* handle 0 size case w/o reference */
538     if (!ptr) return TRUE;
539     
540     if (!page_size) page_size = getpagesize();
541     __TRY
542     {
543         volatile const char *p = ptr;
544         char dummy;
545         UINT count = size;
546
547         while (count > page_size)
548         {
549             dummy = *p;
550             p += page_size;
551             count -= page_size;
552         }
553         dummy = p[0];
554         dummy = p[count - 1];
555     }
556     __EXCEPT(page_fault)
557     {
558         TRACE_(seh)("%p caused page fault during read\n", ptr);
559         return TRUE;
560     }
561     __ENDTRY
562     return FALSE;
563 }
564
565
566 /***********************************************************************
567  *             IsBadWritePtr   (KERNEL32.@)
568  *
569  * Check for write access on a memory block.
570  *
571  * RETURNS
572  *      FALSE: Process has write access to entire block
573  *      TRUE: Otherwise
574  */
575 BOOL WINAPI IsBadWritePtr(
576               LPVOID ptr, /* [in] Address of memory block */
577               UINT size ) /* [in] Size of block in bytes */
578 {
579     if (!size) return FALSE;  /* handle 0 size case w/o reference */
580     if (!ptr) return TRUE;
581     
582     if (!page_size) page_size = getpagesize();
583     __TRY
584     {
585         volatile char *p = ptr;
586         UINT count = size;
587
588         while (count > page_size)
589         {
590             *p |= 0;
591             p += page_size;
592             count -= page_size;
593         }
594         p[0] |= 0;
595         p[count - 1] |= 0;
596     }
597     __EXCEPT(page_fault)
598     {
599         TRACE_(seh)("%p caused page fault during write\n", ptr);
600         return TRUE;
601     }
602     __ENDTRY
603     return FALSE;
604 }
605
606
607 /***********************************************************************
608  *             IsBadHugeReadPtr   (KERNEL32.@)
609  *
610  * Check for read access on a memory block.
611  *
612  * RETURNS
613  *      FALSE: Process has read access to entire block
614  *      TRUE: Otherwise
615  */
616 BOOL WINAPI IsBadHugeReadPtr(
617               LPCVOID ptr, /* [in] Address of memory block */
618               UINT size  /* [in] Size of block */
619 ) {
620     return IsBadReadPtr( ptr, size );
621 }
622
623
624 /***********************************************************************
625  *             IsBadHugeWritePtr   (KERNEL32.@)
626  *
627  * Check for write access on a memory block.
628  *
629  * RETURNS
630  *      FALSE: Process has write access to entire block
631  *      TRUE: Otherwise
632  */
633 BOOL WINAPI IsBadHugeWritePtr(
634               LPVOID ptr, /* [in] Address of memory block */
635               UINT size /* [in] Size of block */
636 ) {
637     return IsBadWritePtr( ptr, size );
638 }
639
640
641 /***********************************************************************
642  *             IsBadCodePtr   (KERNEL32.@)
643  *
644  * Check for read access on a memory address.
645  *
646  * RETURNS
647  *      FALSE: Process has read access to specified memory
648  *      TRUE: Otherwise
649  */
650 BOOL WINAPI IsBadCodePtr( FARPROC ptr ) /* [in] Address of function */
651 {
652     return IsBadReadPtr( ptr, 1 );
653 }
654
655
656 /***********************************************************************
657  *             IsBadStringPtrA   (KERNEL32.@)
658  *
659  * Check for read access on a range of memory pointed to by a string pointer.
660  *
661  * RETURNS
662  *      FALSE: Read access to all bytes in string
663  *      TRUE: Else
664  */
665 BOOL WINAPI IsBadStringPtrA(
666               LPCSTR str, /* [in] Address of string */
667               UINT max )  /* [in] Maximum size of string */
668 {
669     if (!str) return TRUE;
670     
671     __TRY
672     {
673         volatile const char *p = str;
674         while (p != str + max) if (!*p++) break;
675     }
676     __EXCEPT(page_fault)
677     {
678         TRACE_(seh)("%p caused page fault during read\n", str);
679         return TRUE;
680     }
681     __ENDTRY
682     return FALSE;
683 }
684
685
686 /***********************************************************************
687  *             IsBadStringPtrW   (KERNEL32.@)
688  * See IsBadStringPtrA.
689  */
690 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT max )
691 {
692     if (!str) return TRUE;
693     
694     __TRY
695     {
696         volatile const WCHAR *p = str;
697         while (p != str + max) if (!*p++) break;
698     }
699     __EXCEPT(page_fault)
700     {
701         TRACE_(seh)("%p caused page fault during read\n", str);
702         return TRUE;
703     }
704     __ENDTRY
705     return FALSE;
706 }