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 #define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC)
63 #define check_se_protected(ptr, size) (0)
64 #else /* POSIX */
65 #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
66 #include <pthread.h>
67 #define SLJIT_SE_LOCK()		pthread_mutex_lock(&se_lock)
68 #define SLJIT_SE_UNLOCK()	pthread_mutex_unlock(&se_lock)
69 #endif /* !SLJIT_SINGLE_THREADED */
70 
71 #define check_se_protected(ptr, size) generic_se_protected(ptr, size)
72 
generic_se_protected(void * ptr,sljit_uw size)73 static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size)
74 {
75 	if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
76 		return mprotect(ptr, size, PROT_READ | PROT_WRITE);
77 
78 	return -1;
79 }
80 #endif /* NetBSD */
81 
82 #ifndef SLJIT_SE_LOCK
83 #define SLJIT_SE_LOCK()
84 #endif
85 #ifndef SLJIT_SE_UNLOCK
86 #define SLJIT_SE_UNLOCK()
87 #endif
88 #ifndef SLJIT_PROT_WX
89 #define SLJIT_PROT_WX 0
90 #endif
91 
sljit_malloc_exec(sljit_uw size)92 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
93 {
94 #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) \
95 	&& !defined(__NetBSD__)
96 	static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
97 #endif
98 	static int se_protected = !SLJIT_PROT_WX;
99 	int prot = PROT_READ | PROT_WRITE | SLJIT_PROT_WX;
100 	sljit_uw* ptr;
101 
102 	if (SLJIT_UNLIKELY(se_protected < 0))
103 		return NULL;
104 
105 #ifdef PROT_MAX
106 	prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
107 #endif
108 
109 	size += sizeof(sljit_uw);
110 	ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
111 
112 	if (ptr == MAP_FAILED)
113 		return NULL;
114 
115 	if (SLJIT_UNLIKELY(se_protected > 0)) {
116 		SLJIT_SE_LOCK();
117 		se_protected = check_se_protected(ptr, size);
118 		SLJIT_SE_UNLOCK();
119 		if (SLJIT_UNLIKELY(se_protected < 0)) {
120 			munmap((void *)ptr, size);
121 			return NULL;
122 		}
123 	}
124 
125 	*ptr++ = size;
126 	return ptr;
127 }
128 
129 #undef SLJIT_PROT_WX
130 #undef SLJIT_SE_UNLOCK
131 #undef SLJIT_SE_LOCK
132 
sljit_free_exec(void * ptr)133 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
134 {
135 	sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
136 	munmap((void*)start_ptr, *start_ptr);
137 }
138 
sljit_update_wx_flags(void * from,void * to,sljit_s32 enable_exec)139 static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
140 {
141 	sljit_uw page_mask = (sljit_uw)get_page_alignment();
142 	sljit_uw start = (sljit_uw)from;
143 	sljit_uw end = (sljit_uw)to;
144 	int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
145 
146 	SLJIT_ASSERT(start < end);
147 
148 	start &= ~page_mask;
149 	end = (end + page_mask) & ~page_mask;
150 
151 	mprotect((void*)start, end - start, prot);
152 }
153 
154 #else /* windows */
155 
sljit_malloc_exec(sljit_uw size)156 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
157 {
158 	sljit_uw *ptr;
159 
160 	size += sizeof(sljit_uw);
161 	ptr = (sljit_uw*)VirtualAlloc(NULL, size,
162 				MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
163 
164 	if (!ptr)
165 		return NULL;
166 
167 	*ptr++ = size;
168 
169 	return ptr;
170 }
171 
sljit_free_exec(void * ptr)172 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
173 {
174 	sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw);
175 #if defined(SLJIT_DEBUG) && SLJIT_DEBUG
176 	sljit_uw page_mask = (sljit_uw)get_page_alignment();
177 
178 	SLJIT_ASSERT(!(start & page_mask));
179 #endif
180 	VirtualFree((void*)start, 0, MEM_RELEASE);
181 }
182 
sljit_update_wx_flags(void * from,void * to,sljit_s32 enable_exec)183 static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
184 {
185 	DWORD oldprot;
186 	sljit_uw page_mask = (sljit_uw)get_page_alignment();
187 	sljit_uw start = (sljit_uw)from;
188 	sljit_uw end = (sljit_uw)to;
189 	DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE;
190 
191 	SLJIT_ASSERT(start < end);
192 
193 	start &= ~page_mask;
194 	end = (end + page_mask) & ~page_mask;
195 
196 	VirtualProtect((void*)start, end - start, prot, &oldprot);
197 }
198 
199 #endif /* !windows */
200 
sljit_free_unused_memory_exec(void)201 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
202 {
203 	/* This allocator does not keep unused memory for future allocations. */
204 }
205