diff --git a/legacy/eina/ChangeLog b/legacy/eina/ChangeLog index d36ad6eb9e..a049e6bd9c 100644 --- a/legacy/eina/ChangeLog +++ b/legacy/eina/ChangeLog @@ -114,3 +114,7 @@ 2011-06-23 Cedric Bail * Add Eina_LockRW. + +2011-07-04 Carsten Haitzler (The Rasterman) + + * Add eina_mmap safety handling. diff --git a/legacy/eina/src/include/Makefile.am b/legacy/eina/src/include/Makefile.am index 308d139ad7..8fc4763d00 100644 --- a/legacy/eina/src/include/Makefile.am +++ b/legacy/eina/src/include/Makefile.am @@ -57,7 +57,8 @@ eina_quadtree.h \ eina_simple_xml_parser.h \ eina_lock.h \ eina_prefix.h \ -eina_refcount.h +eina_refcount.h \ +eina_mmap.h # Will be back for developper after 1.1. # eina_object.h diff --git a/legacy/eina/src/include/eina_mmap.h b/legacy/eina/src/include/eina_mmap.h new file mode 100644 index 0000000000..618b805d08 --- /dev/null +++ b/legacy/eina/src/include/eina_mmap.h @@ -0,0 +1,59 @@ +#ifndef EINA_MMAP_H_ +#define EINA_MMAP_H_ + +/** + * @addtogroup Eina_Mmap_Group Group + * + * @brief These functions provide helpers for safe mmap handling + * + * @{ + * + * @since 1.1.0 + */ + +/** + * @brief Enable or disable safe mmaped IO handling + * + * @param enabled The enabled state (to enable, pass EINA_TRUE) + * + * This enables (if possible on your platform) a signal handler for + * SIGBUS, that replaces the "bad page" with a pzge of 0's (from /dev/zero) + * if a SIGBUS occurs. This allows for safe mmap() of files that may truncate + * or from files on devices with IO errors. Normally these cases will result + * in a SIGBUS being delivered (and termination of yyour process), but + * when "mmap safety" is enabled, this will not occur. Instead a page of + * bytes of the value 0 will replace the "bad page", allowing the process + * to continue and allow its own parsing error detection to safely abort + * the operation without the process falling apart. + * + * If you disable mmap safety, the SIGBUS handler will be restored to its + * default handler. Note that eina_file_map_all() and eina_file_map_new() + * will automatically enable mmap safety as they provide an mmaped file IO + * layer, and rely on mmap to not fail for any part of the file. + * + * If you set up your own SIGBUS handler, then this will effectively disable + * the safe mmap handling and make you liable to crashes on IO to or from + * such "damaged files" that would take down your process. + * + * @since 1.1.0 + */ +EAPI Eina_Bool +eina_mmap_safety_enabled_set(Eina_Bool enabled); + +/** + * @brief Get the enabled state of mmap safety. + * + * @return The safety state (EINA_TRUE if enabled) + * + * This returns the mmap safety state set by eina_mmap_safety_enabled_set(). + * See eina_mmap_safety_enabled_set() for more information. + * + * @since 1.1.0 + */ +EAPI Eina_Bool +eina_mmap_safety_enabled_get(void); + +/** + * @} + */ +#endif diff --git a/legacy/eina/src/lib/Makefile.am b/legacy/eina/src/lib/Makefile.am index 2e89bc714f..0d1717c191 100644 --- a/legacy/eina/src/lib/Makefile.am +++ b/legacy/eina/src/lib/Makefile.am @@ -31,6 +31,7 @@ eina_magic.c \ eina_main.c \ eina_matrixsparse.c \ eina_mempool.c \ +eina_mmap.c \ eina_module.c \ eina_prefix.c \ eina_quadtree.c \ diff --git a/legacy/eina/src/lib/eina_file.c b/legacy/eina/src/lib/eina_file.c index 373065a4cf..e3796e92f4 100644 --- a/legacy/eina/src/lib/eina_file.c +++ b/legacy/eina/src/lib/eina_file.c @@ -65,6 +65,7 @@ void *alloca (size_t); #include "eina_hash.h" #include "eina_list.h" #include "eina_lock.h" +#include "eina_mmap.h" /*============================================================================* * Local * @@ -867,6 +868,7 @@ eina_file_map_all(Eina_File *file, Eina_File_Populate rule) if (file->length > EINA_HUGE_PAGE) flags |= MAP_HUGETLB; #endif + eina_mmap_safety_enabled_set(EINA_TRUE); eina_lock_take(&file->lock); if (file->global_map == MAP_FAILED) file->global_map = mmap(NULL, file->length, PROT_READ, flags, file->fd, 0); @@ -900,6 +902,7 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule, key[0] = offset; key[1] = length; + eina_mmap_safety_enabled_set(EINA_TRUE); eina_lock_take(&file->lock); map = eina_hash_find(file->map, &key); diff --git a/legacy/eina/src/lib/eina_mmap.c b/legacy/eina/src/lib/eina_mmap.c new file mode 100644 index 0000000000..fe2a12071d --- /dev/null +++ b/legacy/eina/src/lib/eina_mmap.c @@ -0,0 +1,198 @@ +/* EINA - EFL data type library + * Copyright (C) 2011 Carsten Haitzler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# ifndef HAVE_ALLOCA +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_log.h" +#include "eina_mmap.h" + +/*============================================================================* + * Local * + *============================================================================*/ +static Eina_Bool mmap_safe = EINA_FALSE; +#ifndef _WIN32 +static int _eina_mmap_log_dom = -1; +static int _eina_mmap_zero_fd = -1; +static long _eina_mmap_pagesize = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_mmap_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_mmap_log_dom, __VA_ARGS__) + +static void +_eina_mmap_safe_sigbus(int sig __UNUSED__, + siginfo_t *siginfo, + void *ptr __UNUSED__) +{ + unsigned char *addr = (unsigned char *)(siginfo->si_addr); + int perrno; + + /* save previous errno */ + perrno = errno; + /* if problems was an unaligned access - complain accordingly and abort */ + if (siginfo->si_code == BUS_ADRALN) + { + ERR("Unaligned memory access. SIGBUS!!!"); + errno = perrno; + abort(); + } + /* send this to stderr - not eina_log. Specifically want this on stderr */ + fprintf(stderr, + "EINA: Data at address 0x%lx is invalid. Replacing with zero page.\n", + (unsigned long)addr); + /* align address to the lower page boundary */ + addr = (unsigned char *)((long)addr & (~(_eina_mmap_pagesize - 1))); + /* mmap a pzge of zero's from /dev/zero in there */ + if (mmap(addr, _eina_mmap_pagesize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_FIXED, + _eina_mmap_zero_fd, 0) == MAP_FAILED) + { + /* mmap of /dev/zero failed :( */ + perror("mmap"); + ERR("Failed to mmap() /dev/zero in place of page. SIGBUS!!!"); + errno = perrno; + abort(); + } + /* restore previous errno */ + errno = perrno; +} +#endif + +/*============================================================================* + * API * + *============================================================================*/ + +EAPI Eina_Bool +eina_mmap_safety_enabled_set(Eina_Bool enabled) +{ +#ifdef _WIN32 + return EINA_FALSE; +#else + if (_eina_mmap_log_dom < 0) + { + _eina_mmap_log_dom = eina_log_domain_register("eina_mmap", + EINA_LOG_COLOR_DEFAULT); + if (_eina_mmap_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_mmap"); + return EINA_FALSE; + } + } + + enabled = !!enabled; + + if (mmap_safe == enabled) return mmap_safe; + if (enabled) + { + struct sigaction sa; + + /* find out system page size the cleanest way we can */ +#ifdef _SC_PAGESIZE + _eina_mmap_pagesize = sysconf(_SC_PAGESIZE); + if (_eina_mmap_pagesize <= 0) return EINA_FALSE; +#else + _eina_mmap_pagesize = 4096; +#endif + /* no zero page device - open it */ + if (_eina_mmap_zero_fd < 0) + { + _eina_mmap_zero_fd = open("/dev/zero", O_RDWR); + /* if we don;'t have one - fail to set up mmap safety */ + if (_eina_mmap_zero_fd < 0) return EINA_FALSE; + } + /* set up signal handler for SIGBUS */ + sa.sa_sigaction = _eina_mmap_safe_sigbus; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGBUS, &sa, NULL) == 0) return EINA_FALSE; + /* setup of SIGBUS handler failed, lets close zero page dev and fail */ + close(_eina_mmap_zero_fd); + _eina_mmap_zero_fd = -1; + return EINA_FALSE; + } + else + { + /* reset signal handler to default for SIGBUS */ + signal(SIGBUS, SIG_DFL); + } + mmap_safe = enabled; + return mmap_safe; +#endif +} + +EAPI Eina_Bool +eina_mmap_safety_enabled_get(void) +{ + return mmap_safe; +}