EFL 1.7 svn doobies
[profile/ivi/eina.git] / src / lib / eina_mmap.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2011 Carsten Haitzler
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef HAVE_SIGINFO_T
24
25 #ifdef STDC_HEADERS
26 # include <stdlib.h>
27 # include <stddef.h>
28 #else
29 # ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 # endif
32 #endif
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <limits.h>
41 #include <signal.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45
46 #if HAVE_SIGINFO_H
47 # include <siginfo.h>
48 #endif
49
50 #endif
51
52 #include "eina_config.h"
53 #include "eina_private.h"
54 #include "eina_log.h"
55 #include "eina_mmap.h"
56
57 /*============================================================================*
58  *                                 Local                                      *
59  *============================================================================*/
60
61 static Eina_Bool mmap_safe = EINA_FALSE;
62 #ifdef HAVE_SIGINFO_T
63
64 static int _eina_mmap_log_dom = -1;
65 static int _eina_mmap_zero_fd = -1;
66 static long _eina_mmap_pagesize = -1;
67
68 #ifdef ERR
69 #undef ERR
70 #endif
71 #define ERR(...) EINA_LOG_DOM_ERR(_eina_mmap_log_dom, __VA_ARGS__)
72
73 #ifdef DBG
74 #undef DBG
75 #endif
76 #define DBG(...) EINA_LOG_DOM_DBG(_eina_mmap_log_dom, __VA_ARGS__)
77
78 static void
79 _eina_mmap_safe_sigbus(int sig __UNUSED__,
80                        siginfo_t *siginfo,
81                        void *ptr __UNUSED__)
82 {
83    unsigned char *addr = (unsigned char *)(siginfo->si_addr);
84    int perrno;
85
86    /* save previous errno */
87    perrno = errno;
88    /* if problems was an unaligned access - complain accordingly and abort */
89    if (siginfo->si_code == BUS_ADRALN)
90      {
91         ERR("Unaligned memory access. SIGBUS!!!");
92         errno = perrno;
93         abort();
94      }
95    /* send this to stderr - not eina_log. Specifically want this on stderr */
96    fprintf(stderr,
97            "EINA: Data at address 0x%lx is invalid. Replacing with zero page.\n",
98            (unsigned long)addr);
99    /* align address to the lower page boundary */
100    addr = (unsigned char *)((long)addr & (~(_eina_mmap_pagesize - 1)));
101    /* mmap a pzge of zero's from /dev/zero in there */
102    if (mmap(addr, _eina_mmap_pagesize,
103             PROT_READ | PROT_WRITE | PROT_EXEC,
104             MAP_PRIVATE | MAP_FIXED,
105             _eina_mmap_zero_fd, 0) == MAP_FAILED)
106      {
107         /* mmap of /dev/zero failed :( */
108         perror("mmap");
109         ERR("Failed to mmap() /dev/zero in place of page. SIGBUS!!!");
110         errno = perrno;
111         abort();
112      }
113    /* Look into mmaped Eina_File if it was one of them, just to remember for later request */
114    eina_file_mmap_faulty(addr, _eina_mmap_pagesize);
115    /* restore previous errno */
116    errno = perrno;
117 }
118 #endif
119
120 /*============================================================================*
121  *                                   API                                      *
122  *============================================================================*/
123
124 EAPI Eina_Bool
125 eina_mmap_safety_enabled_set(Eina_Bool enabled)
126 {
127 #ifndef HAVE_SIGINFO_T
128    (void) enabled;
129    return EINA_FALSE;
130 #else
131    if (_eina_mmap_log_dom < 0)
132      {
133         _eina_mmap_log_dom = eina_log_domain_register("eina_mmap",
134                                                       EINA_LOG_COLOR_DEFAULT);
135         if (_eina_mmap_log_dom < 0)
136           {
137              EINA_LOG_ERR("Could not register log domain: eina_mmap");
138              return EINA_FALSE;
139           }
140      }
141
142    enabled = !!enabled;
143
144    if (mmap_safe == enabled) return mmap_safe;
145    if (enabled)
146      {
147         struct sigaction  sa;
148
149         /* find out system page size the cleanest way we can */
150 #ifdef _SC_PAGESIZE
151         _eina_mmap_pagesize = sysconf(_SC_PAGESIZE);
152         if (_eina_mmap_pagesize <= 0) return EINA_FALSE;
153 #else
154         _eina_mmap_pagesize = 4096;
155 #endif
156         /* no zero page device - open it */
157         if (_eina_mmap_zero_fd < 0)
158           {
159              _eina_mmap_zero_fd = open("/dev/zero", O_RDWR);
160              /* if we don;'t have one - fail to set up mmap safety */
161              if (_eina_mmap_zero_fd < 0) return EINA_FALSE;
162           }
163         /* set up signal handler for SIGBUS */
164         sa.sa_sigaction = _eina_mmap_safe_sigbus;
165         sa.sa_flags = SA_RESTART | SA_SIGINFO;
166         sigemptyset(&sa.sa_mask);
167         /* FIXME: This is rubbish. We return EINA_FALSE whether sigaction
168          * fails or not. And we never set mmap_safe, so we always hit this
169          * code path. */
170         if (sigaction(SIGBUS, &sa, NULL) == 0) return EINA_FALSE;
171         /* setup of SIGBUS handler failed, lets close zero page dev and fail */
172         close(_eina_mmap_zero_fd);
173         _eina_mmap_zero_fd = -1;
174         return EINA_FALSE;
175      }
176    else
177      {
178         /* reset signal handler to default for SIGBUS */
179         signal(SIGBUS, SIG_DFL);
180      }
181    mmap_safe = enabled;
182    return mmap_safe;
183 #endif
184 }
185
186 EAPI Eina_Bool
187 eina_mmap_safety_enabled_get(void)
188 {
189    return mmap_safe;
190 }