forked from EpicGamesExt/WineResources
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwine-overcommit-prevention-support.patch
More file actions
381 lines (359 loc) · 15.3 KB
/
wine-overcommit-prevention-support.patch
File metadata and controls
381 lines (359 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index d3f2a0e55231e7e13251bc26059d6a73491895f7..7ffbb7c3b691435e628627b140a44c903c654d1a 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -4,7 +4,7 @@ UNIXLIB = ntdll.so
IMPORTLIB = ntdll
IMPORTS = $(MUSL_PE_LIBS) winecrt0
UNIX_CFLAGS = $(UNWIND_CFLAGS)
-UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS)
+UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS) -lmemory-patches
EXTRADLLFLAGS = -nodefaultlibs
i386_EXTRADLLFLAGS = -Wl,--image-base,0x7bc00000
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 53fcc61ccf373dd89a4ccdd2d427150b626284cc..0a46f2d55e67e0c7efcc0cca566a75ab871cf7c6 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -1148,6 +1148,32 @@ done:
return ret;
}
+int server_get_fd_for_address(const void *base, int *fd)
+{
+ int ret = -1;
+ obj_handle_t fd_handle;
+
+ *fd = -1;
+
+ SERVER_START_REQ( get_fd_for_address )
+ {
+ req->process = wine_server_obj_handle( GetCurrentProcess() );
+ req->addr = wine_server_client_ptr( base );
+ req->handle = 0;
+
+ if (!(ret = wine_server_call( req )))
+ {
+ if ((*fd = receive_fd( &fd_handle )) == -1)
+ {
+ ret = STATUS_TOO_MANY_OPENED_FILES;
+ }
+ }
+ }
+ SERVER_END_REQ;
+
+ return ret;
+}
+
/***********************************************************************
* wine_server_fd_to_handle
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c
index 879a5893758e437a2eebc48f00fbc3b4fa2f3f16..f771e02d02b0693572b93a2aa8efab838d69be1d 100644
--- a/dlls/ntdll/unix/system.c
+++ b/dlls/ntdll/unix/system.c
@@ -36,6 +36,9 @@
#include <sys/time.h>
#include <time.h>
#include <dirent.h>
+
+# include <libmemory-patches.h>
+
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
@@ -1952,33 +1955,11 @@ static void get_performance_info( SYSTEM_PERFORMANCE_INFORMATION *info )
#ifdef linux
{
- FILE *fp;
-
- if ((fp = fopen("/proc/meminfo", "r")))
- {
- unsigned long long value, mem_available = 0;
- char line[64];
-
- while (fgets(line, sizeof(line), fp))
- {
- if(sscanf(line, "MemTotal: %llu kB", &value) == 1)
- totalram += value * 1024;
- else if(sscanf(line, "MemFree: %llu kB", &value) == 1)
- freeram += value * 1024;
- else if(sscanf(line, "SwapTotal: %llu kB", &value) == 1)
- totalswap += value * 1024;
- else if(sscanf(line, "SwapFree: %llu kB", &value) == 1)
- freeswap += value * 1024;
- else if (sscanf(line, "Buffers: %llu", &value))
- freeram += value * 1024;
- else if (sscanf(line, "Cached: %llu", &value))
- freeram += value * 1024;
- else if (sscanf(line, "MemAvailable: %llu", &value))
- mem_available = value * 1024;
- }
- fclose(fp);
- if (mem_available) freeram = mem_available;
- }
+ struct current_memory_info current_mem_values = get_current_memory_info();
+ totalram = current_mem_values.totalram;
+ freeram = current_mem_values.freeram;
+ totalswap = current_mem_values.totalswap;
+ freeswap = current_mem_values.freeswap;
}
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || \
defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 07f2724eac706a9441184282e3941fba481b3e46..0f0a41faf90cc4e2b67c6e09118ab293ef38bd99 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -203,6 +203,7 @@ extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *
apc_result_t *result );
extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd,
int *needs_close, enum server_fd_type *type, unsigned int *options );
+extern int server_get_fd_for_address(const void *base, int *fd);
extern void wine_server_send_fd( int fd );
extern void process_exit_wrapper( int status ) DECLSPEC_NORETURN;
extern size_t server_init_process(void);
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index abe1b4dc4ec424c0864d18e726537e680d2ab883..e55d7e68d1b8d3967316f1101cb0ce9b0704231b 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -75,6 +75,8 @@
#include "unix_private.h"
#include "wine/debug.h"
+#include <libmemory-patches.h>
+
WINE_DEFAULT_DEBUG_CHANNEL(virtual);
WINE_DECLARE_DEBUG_CHANNEL(module);
WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges);
@@ -228,12 +230,26 @@ static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *l
/* mmap() anonymous memory at a fixed address */
void *anon_mmap_fixed( void *start, size_t size, int prot, int flags )
{
+ BOOL write_requested = has_write_flags(prot);
+ if (overcommit_prevention_enabled() && write_requested && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented memory mapping due to insufficient memory.\n");
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
return mmap( start, size, prot, MAP_PRIVATE | MAP_ANON | MAP_FIXED | flags, -1, 0 );
}
/* allocate anonymous mmap() memory at any address */
void *anon_mmap_alloc( size_t size, int prot )
{
+ BOOL write_requested = has_write_flags(prot);
+ if (overcommit_prevention_enabled() && write_requested && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented memory mapping due to insufficient memory.\n");
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
return mmap( NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0 );
}
@@ -390,6 +406,14 @@ static size_t unmap_area_above_user_limit( void *addr, size_t size )
static void *anon_mmap_tryfixed( void *start, size_t size, int prot, int flags )
{
void *ptr;
+ BOOL write_requested = has_write_flags(prot);
+
+ if (overcommit_prevention_enabled() && write_requested && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented memory mapping due to insufficient memory.\n");
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
#ifdef MAP_FIXED_NOREPLACE
ptr = mmap( start, size, prot, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANON | flags, -1, 0 );
@@ -1723,14 +1747,41 @@ static void mprotect_range( void *base, size_t size, BYTE set, BYTE clear )
static BOOL set_vprot( struct file_view *view, void *base, size_t size, BYTE vprot )
{
int unix_prot = get_unix_prot(vprot);
+ int fd = -1;
+ int ret = -1;
if (view->protect & VPROT_WRITEWATCH)
{
+ /*
+ * TODO: Figure out how to handle this case before attempting
+ * to upstream the copy-on-write patch.
+ */
+
/* each page may need different protections depending on write watch flag */
set_page_vprot_bits( base, size, vprot & ~VPROT_WRITEWATCH, ~vprot & ~VPROT_WRITEWATCH );
mprotect_range( base, size, 0, 0 );
return TRUE;
}
+
+ /* Check if write copy was added to the mapping */
+ if ((vprot & VPROT_WRITECOPY) && !(view->protect & VPROT_WRITECOPY))
+ {
+ /* Get the associated file descriptor for the mapping */
+ ret = server_get_fd_for_address(view->base, &fd);
+
+ if (fd != -1 && ret == STATUS_SUCCESS)
+ {
+ /* Remap as copy-on-write using mmap() */
+ void *remapped = mmap(base, size, unix_prot, MAP_PRIVATE | MAP_FIXED, fd, 0);
+ close(fd);
+
+ if(remapped == base) return TRUE;
+ ERR("mmap() failed. Error: %s\n", strerror(errno));
+ }
+
+ ERR("Could not remap file as copy-on-write. Falling back to mprotect() instead\n");
+ }
+
if (mprotect_exec( base, size, unix_prot )) return FALSE;
set_page_vprot( base, size, vprot );
return TRUE;
@@ -3133,6 +3184,7 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P
HANDLE shared_file;
LARGE_INTEGER offset;
sigset_t sigset;
+ BOOL write_consumes_memory = FALSE;
switch(protect)
{
@@ -3198,6 +3250,16 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P
if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL ))) return res;
+ /* determine whether writing to the view will consume physical memory */
+ write_consumes_memory = (protect == PAGE_WRITECOPY || protect == PAGE_EXECUTE_WRITECOPY);
+
+ /* if overcommit prevention is enabled then verify that we can satisfy any page commit requests */
+ if (write_consumes_memory && overcommit_prevention_enabled() && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented page overcommit due to insufficient memory.\n");
+ return STATUS_NO_MEMORY;
+ }
+
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
res = map_view( &view, base, size, alloc_type, vprot, limit_low, limit_high, 0 );
@@ -3225,6 +3287,11 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P
*addr_ptr = view->base;
*size_ptr = size;
VIRTUAL_DEBUG_DUMP_VIEW( view );
+
+ /* if overcommit prevention is enabled and we performed a commit, then touch the commited pages */
+ if (write_consumes_memory && (overcommit_prevention_enabled() || overcommit_prevention_exempt())) {
+ touch_committed_pages(view->base, size, protect);
+ }
}
else delete_view( view );
@@ -4499,6 +4566,13 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
if (type & MEM_RESERVE_PLACEHOLDER && (protect != PAGE_NOACCESS)) return STATUS_INVALID_PARAMETER;
if (!arm64ec_view && (attributes & MEM_EXTENDED_PARAMETER_EC_CODE)) return STATUS_INVALID_PARAMETER;
+ /* if overcommit prevention is enabled then verify that we can satisfy any page commit requests */
+ if ((type & MEM_COMMIT) && overcommit_prevention_enabled() && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented page overcommit due to insufficient memory.\n");
+ return STATUS_NO_MEMORY;
+ }
+
/* Reserve the memory */
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
@@ -4557,6 +4631,11 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
{
*ret = base;
*size_ptr = size;
+
+ /* if overcommit prevention is enabled and we performed a commit, then touch the commited pages */
+ if ((type & MEM_COMMIT) && (overcommit_prevention_enabled() || overcommit_prevention_exempt())) {
+ touch_committed_pages(base, size, protect);
+ }
}
else if (status == STATUS_NO_MEMORY)
ERR( "out of memory for allocation, base %p size %08lx\n", base, size );
@@ -4911,8 +4990,27 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T
/* Make sure all the pages are committed */
if (get_committed_size( view, base, &vprot, VPROT_COMMITTED ) >= size && (vprot & VPROT_COMMITTED))
{
+ BOOL write_or_copy_added = FALSE;
old = get_win32_prot( vprot, view->protect );
+ write_or_copy_added =
+ (!has_write_flags(old) && has_write_flags(new_prot)) ||
+ (!has_writecopy_flags(old) && has_writecopy_flags(new_prot));
+
+ /* if overcommit prevention is enabled and write access is about to be granted then verify *
+ * that we have sufficient memory to write to the pages, since doing so may trigger the *
+ * underlying operating system's copy-on-write mechanisms and increase physical memory use */
+ if (overcommit_prevention_enabled() && write_or_copy_added && memory_available_for_commit(size) == FALSE)
+ {
+ WARN("Prevented protection change due to insufficient memory.\n");
+ return STATUS_NO_MEMORY;
+ }
+
status = set_protection( view, base, size, new_prot );
+
+ /* if overcommit prevention is enabled and write access was granted then touch the pages */
+ if ((overcommit_prevention_enabled() || overcommit_prevention_exempt()) && write_or_copy_added) {
+ touch_committed_pages(base, size, new_prot);
+ }
}
else status = STATUS_NOT_COMMITTED;
}
diff --git a/server/Makefile.in b/server/Makefile.in
index 7b46b924c46aec81d01acdee17bad5c49091967f..a0e8eb7969e0f6ed6f5b74c234415a61b0276f7c 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -47,6 +47,6 @@ SOURCES = \
wineserver.man.in \
winstation.c
-UNIX_LIBS = $(LDEXECFLAGS) $(RT_LIBS) $(INOTIFY_LIBS) $(PROCSTAT_LIBS)
+UNIX_LIBS = $(LDEXECFLAGS) $(RT_LIBS) $(INOTIFY_LIBS) $(PROCSTAT_LIBS) -lmemory-patches
unicode_EXTRADEFS = -DNLSDIR="\"${nlsdir}\"" -DBIN_TO_NLSDIR=\"`${MAKEDEP} -R ${bindir} ${nlsdir}`\"
diff --git a/server/mapping.c b/server/mapping.c
index f754078acf7dfe10d25facba703693879805a788..a90edbf79ebda8807c546e1f65b4db8e280dae59 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -43,6 +43,8 @@
#include "request.h"
#include "security.h"
+#include <libmemory-patches.h>
+
/* list of memory ranges, used to store committed info */
struct ranges
{
@@ -342,7 +344,17 @@ static int create_temp_file( file_pos_t size )
fd = make_temp_file( tmpfn );
if (fd != -1)
{
- if (!grow_file( fd, size ))
+ BOOL failed = FALSE;
+ if (overcommit_prevention_enabled() && is_memory_backed_file(fd) && memory_available_for_commit(size) == FALSE)
+ {
+ set_error(STATUS_NO_MEMORY);
+ failed = TRUE;
+ }
+ else {
+ failed = (grow_file( fd, size ) == 0);
+ }
+
+ if (failed)
{
close( fd );
fd = -1;
@@ -619,6 +631,10 @@ static int build_shared_mapping( struct mapping *mapping, int fd,
if ((shared_fd = create_temp_file( total_size )) == -1) return 0;
if (!(file = create_file_for_fd( shared_fd, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0 ))) return 0;
+ if (overcommit_prevention_enabled() && memory_available_for_commit(max_size) == FALSE)
+ {
+ goto error;
+ }
if (!(buffer = malloc( max_size ))) goto error;
/* copy the shared sections data into the temp file */
@@ -1521,3 +1537,22 @@ DECL_HANDLER(get_mapping_filename)
release_object( process );
}
+
+DECL_HANDLER(get_fd_for_address)
+{
+ struct process *process;
+ struct memory_view *view;
+
+ if ((process = get_process_from_handle( req->process, PROCESS_QUERY_INFORMATION )))
+ {
+ if ((view = find_mapped_addr( process, req->addr )))
+ {
+ int unix_fd = get_unix_fd( view->fd );
+ send_client_fd(current->process, unix_fd, req->handle);
+ }
+ else set_error( STATUS_INVALID_ADDRESS );
+
+ release_object( process );
+ }
+ else set_error( STATUS_INVALID_HANDLE );
+}