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
29 
30    In *NIX, MAP_ANON is required (that is considered a feature) so make
31    sure to set the right availability macros for your system or the code
32    will fail to build.
33 
34    If your system doesn't support mapping of anonymous pages (ex: IRIX) it
35    is also likely that it doesn't need this allocator and should be using
36    the standard one instead.
37 
38    It allocates a separate map for each code block and may waste a lot of
39    memory, because whatever was requested, will be rounded up to the page
40    size (minimum 4KB, but could be even bigger).
41 
42    It changes the page permissions (RW <-> RX) as needed and therefore, if you
43    will be updating the code after it has been generated, need to make sure to
44    block any concurrent execution, or could result in a SIGBUS, that could
45    even manifest itself at a different address than the one that was being
46    modified.
47 
48    Only use if you are unable to use the regular allocator because of security
49    restrictions and adding exceptions to your application or the system are
50    not possible.
51 */
52 
53 #include <sys/types.h>
54 #include <sys/mman.h>
55 
56 #define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
57 	sljit_update_wx_flags((from), (to), (enable_exec))
58 
59 #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
60 #include <pthread.h>
61 #define SLJIT_SE_LOCK()		pthread_mutex_lock(&se_lock)
62 #define SLJIT_SE_UNLOCK()	pthread_mutex_unlock(&se_lock)
63 #else
64 #define SLJIT_SE_LOCK()
65 #define SLJIT_SE_UNLOCK()
66 #endif /* !SLJIT_SINGLE_THREADED */
67 
68 #define SLJIT_WX_IS_BLOCK(ptr, size) generic_check_is_wx_block(ptr, size)
69 
generic_check_is_wx_block(void * ptr,sljit_uw size)70 static SLJIT_INLINE int generic_check_is_wx_block(void *ptr, sljit_uw size)
71 {
72 	if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
73 		return !!mprotect(ptr, size, PROT_READ | PROT_WRITE);
74 
75 	return 1;
76 }
77 
sljit_malloc_exec(sljit_uw size)78 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
79 {
80 #if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
81 	static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
82 #endif
83 	static int wx_block = -1;
84 	int prot = PROT_READ | PROT_WRITE;
85 	sljit_uw* ptr;
86 
87 	if (SLJIT_UNLIKELY(wx_block > 0))
88 		return NULL;
89 
90 #ifdef PROT_MAX
91 	prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
92 #endif
93 
94 	size += sizeof(sljit_uw);
95 	ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
96 
97 	if (ptr == MAP_FAILED)
98 		return NULL;
99 
100 	if (SLJIT_UNLIKELY(wx_block < 0)) {
101 		SLJIT_SE_LOCK();
102 		wx_block = SLJIT_WX_IS_BLOCK(ptr, size);
103 		SLJIT_SE_UNLOCK();
104 		if (SLJIT_UNLIKELY(wx_block)) {
105 			munmap((void *)ptr, size);
106 			return NULL;
107 		}
108 	}
109 
110 	*ptr++ = size;
111 	return ptr;
112 }
113 
114 #undef SLJIT_SE_UNLOCK
115 #undef SLJIT_SE_LOCK
116 
sljit_free_exec(void * ptr)117 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
118 {
119 	sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
120 	munmap((void*)start_ptr, *start_ptr);
121 }
122 
sljit_update_wx_flags(void * from,void * to,int enable_exec)123 static void sljit_update_wx_flags(void *from, void *to, int enable_exec)
124 {
125 	sljit_uw page_mask = (sljit_uw)get_page_alignment();
126 	sljit_uw start = (sljit_uw)from;
127 	sljit_uw end = (sljit_uw)to;
128 	int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
129 
130 	SLJIT_ASSERT(start < end);
131 
132 	start &= ~page_mask;
133 	end = (end + page_mask) & ~page_mask;
134 
135 	mprotect((void*)start, end - start, prot);
136 }
137 
sljit_free_unused_memory_exec(void)138 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
139 {
140 	/* This allocator does not keep unused memory for future allocations. */
141 }
142