1 /*
2 * Stack-less Just-In-Time compiler
3 *
4 * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without modification, are
7 * permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this list of
10 * conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 * of conditions and the following disclaimer in the documentation and/or other materials
14 * provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19 * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /*
28 This file contains a simple W^X executable memory allocator for POSIX
29 like systems and Windows
30
31 In *NIX, MAP_ANON is required (that is considered a feature) so make
32 sure to set the right availability macros for your system or the code
33 will fail to build.
34
35 If your system doesn't support mapping of anonymous pages (ex: IRIX) it
36 is also likely that it doesn't need this allocator and should be using
37 the standard one instead.
38
39 It allocates a separate map for each code block and may waste a lot of
40 memory, because whatever was requested, will be rounded up to the page
41 size (minimum 4KB, but could be even bigger).
42
43 It changes the page permissions (RW <-> RX) as needed and therefore, if you
44 will be updating the code after it has been generated, need to make sure to
45 block any concurrent execution, or could result in a SIGBUS, that could
46 even manifest itself at a different address than the one that was being
47 modified.
48
49 Only use if you are unable to use the regular allocator because of security
50 restrictions and adding exceptions to your application or the system are
51 not possible.
52 */
53
54 #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
55 sljit_update_wx_flags((from), (to), (enable_exec))
56
57 #ifndef _WIN32
58 #include <sys/types.h>
59 #include <sys/mman.h>
60
61 #ifdef __NetBSD__
62 #if defined(PROT_MPROTECT)
63 #define check_se_protected(ptr, size) (0)
64 #define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC)
65 #else /* !PROT_MPROTECT */
66 #ifdef _NETBSD_SOURCE
67 #include <sys/param.h>
68 #else /* !_NETBSD_SOURCE */
69 typedef unsigned int u_int;
70 #define devmajor_t sljit_s32
71 #endif /* _NETBSD_SOURCE */
72 #include <sys/sysctl.h>
73 #include <unistd.h>
74
75 #define check_se_protected(ptr, size) netbsd_se_protected()
76
netbsd_se_protected(void)77 static SLJIT_INLINE int netbsd_se_protected(void)
78 {
79 int mib[3];
80 int paxflags;
81 size_t len = sizeof(paxflags);
82
83 mib[0] = CTL_PROC;
84 mib[1] = getpid();
85 mib[2] = PROC_PID_PAXFLAGS;
86
87 if (SLJIT_UNLIKELY(sysctl(mib, 3, &paxflags, &len, NULL, 0) < 0))
88 return -1;
89
90 return (paxflags & CTL_PROC_PAXFLAGS_MPROTECT) ? -1 : 0;
91 }
92 #endif /* PROT_MPROTECT */
93 #else /* POSIX */
94 #define check_se_protected(ptr, size) generic_se_protected(ptr, size)
95
generic_se_protected(void * ptr,sljit_uw size)96 static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size)
97 {
98 if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
99 return mprotect(ptr, size, PROT_READ | PROT_WRITE);
100
101 return -1;
102 }
103 #endif /* NetBSD */
104
105 #if defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED
106 #define SLJIT_SE_LOCK()
107 #define SLJIT_SE_UNLOCK()
108 #else /* !SLJIT_SINGLE_THREADED */
109 #include <pthread.h>
110 #define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock)
111 #define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock)
112 #endif /* SLJIT_SINGLE_THREADED */
113
114 #ifndef SLJIT_PROT_WX
115 #define SLJIT_PROT_WX 0
116 #endif /* !SLJIT_PROT_WX */
117
sljit_malloc_exec(sljit_uw size)118 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
119 {
120 #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
121 static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
122 #endif
123 static int se_protected = !SLJIT_PROT_WX;
124 int prot = PROT_READ | PROT_WRITE | SLJIT_PROT_WX;
125 sljit_uw* ptr;
126
127 if (SLJIT_UNLIKELY(se_protected < 0))
128 return NULL;
129
130 #ifdef PROT_MAX
131 prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
132 #endif
133
134 size += sizeof(sljit_uw);
135 ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
136
137 if (ptr == MAP_FAILED)
138 return NULL;
139
140 if (SLJIT_UNLIKELY(se_protected > 0)) {
141 SLJIT_SE_LOCK();
142 se_protected = check_se_protected(ptr, size);
143 SLJIT_SE_UNLOCK();
144 if (SLJIT_UNLIKELY(se_protected < 0)) {
145 munmap((void *)ptr, size);
146 return NULL;
147 }
148 }
149
150 *ptr++ = size;
151 return ptr;
152 }
153
154 #undef SLJIT_PROT_WX
155 #undef SLJIT_SE_UNLOCK
156 #undef SLJIT_SE_LOCK
157
sljit_free_exec(void * ptr)158 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
159 {
160 sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
161 munmap((void*)start_ptr, *start_ptr);
162 }
163
sljit_update_wx_flags(void * from,void * to,sljit_s32 enable_exec)164 static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
165 {
166 sljit_uw page_mask = (sljit_uw)get_page_alignment();
167 sljit_uw start = (sljit_uw)from;
168 sljit_uw end = (sljit_uw)to;
169 int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
170
171 SLJIT_ASSERT(start < end);
172
173 start &= ~page_mask;
174 end = (end + page_mask) & ~page_mask;
175
176 mprotect((void*)start, end - start, prot);
177 }
178
179 #else /* windows */
180
sljit_malloc_exec(sljit_uw size)181 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
182 {
183 sljit_uw *ptr;
184
185 size += sizeof(sljit_uw);
186 ptr = (sljit_uw*)VirtualAlloc(NULL, size,
187 MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
188
189 if (!ptr)
190 return NULL;
191
192 *ptr++ = size;
193
194 return ptr;
195 }
196
sljit_free_exec(void * ptr)197 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
198 {
199 sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw);
200 #if defined(SLJIT_DEBUG) && SLJIT_DEBUG
201 sljit_uw page_mask = (sljit_uw)get_page_alignment();
202
203 SLJIT_ASSERT(!(start & page_mask));
204 #endif
205 VirtualFree((void*)start, 0, MEM_RELEASE);
206 }
207
sljit_update_wx_flags(void * from,void * to,sljit_s32 enable_exec)208 static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
209 {
210 DWORD oldprot;
211 sljit_uw page_mask = (sljit_uw)get_page_alignment();
212 sljit_uw start = (sljit_uw)from;
213 sljit_uw end = (sljit_uw)to;
214 DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE;
215
216 SLJIT_ASSERT(start < end);
217
218 start &= ~page_mask;
219 end = (end + page_mask) & ~page_mask;
220
221 VirtualProtect((void*)start, end - start, prot, &oldprot);
222 }
223
224 #endif /* !windows */
225
sljit_free_unused_memory_exec(void)226 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
227 {
228 /* This allocator does not keep unused memory for future allocations. */
229 }
230