]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
218faec2d3bc7436dc00bf8a7a074720479b8c15
[libucw.git] / lib / lizard-safe.c
1 /*
2  *      LiZaRd -- Fast compression method based on Lempel-Ziv 77
3  *
4  *      (c) 2004, Robert Spalek <robert@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "lib/lizard.h"
12
13 #include <stdlib.h>
14 #include <sys/mman.h>
15 #include <sys/user.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <setjmp.h>
19 #include <errno.h>
20
21 static void
22 lizard_alloc_internal(struct lizard_buffer *buf, uns max_len)
23 {
24   if (!max_len)
25   {
26     buf->len = 0;
27     buf->start = NULL;
28     return;
29   }
30   buf->len = ALIGN(max_len + 3, PAGE_SIZE);             // +3 due to the unaligned access
31   buf->start = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
32   if (buf->start == MAP_FAILED)
33     die("mmap(anonymous): %m");
34   if (mprotect(buf->start + buf->len, PAGE_SIZE, PROT_NONE) < 0)
35     die("mprotect: %m");
36 }
37
38 struct lizard_buffer *
39 lizard_alloc(uns max_len)
40 {
41   struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
42   lizard_alloc_internal(buf, max_len);
43   buf->old_sigsegv_handler = xmalloc(sizeof(struct sigaction));
44   handle_signal(SIGSEGV, buf->old_sigsegv_handler);
45   return buf;
46 }
47
48 void
49 lizard_free(struct lizard_buffer *buf)
50 {
51   if (buf->start)
52     munmap(buf->start, buf->len + PAGE_SIZE);
53   unhandle_signal(SIGSEGV, buf->old_sigsegv_handler);
54   xfree(buf->old_sigsegv_handler);
55   xfree(buf);
56 }
57
58 void
59 lizard_realloc(struct lizard_buffer *buf, uns max_len)
60 {
61   max_len += 3;
62   if (max_len <= buf->len)
63     return;
64   if (max_len < 2*buf->len)                             // to ensure amortized logarithmic complexity
65     max_len = 2*buf->len;
66   if (buf->start)
67     munmap(buf->start - 3, buf->len + PAGE_SIZE);
68   lizard_alloc_internal(buf, max_len);
69 }
70
71 static jmp_buf safe_decompress_jump;
72 static int
73 sigsegv_handler(int signal UNUSED)
74 {
75   log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
76   longjmp(safe_decompress_jump, 1);
77   return 1;
78 }
79
80 int
81 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
82   /* Decompresses into buf->ptr and returns the length of the uncompressed
83    * file.  If an error has occured, -1 is returned and errno is set.  SIGSEGV
84    * is caught in the case of buffer-overflow.  The function is not re-entrant
85    * because of a static longjmp handler.  */
86 {
87   uns lock_offset = ALIGN(expected_length + 3, PAGE_SIZE);      // +3 due to the unaligned access
88   if (lock_offset > buf->len)
89   {
90     errno = EFBIG;
91     return -1;
92   }
93   volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
94   signal_handler[SIGSEGV] = sigsegv_handler;
95   int len;
96   if (!setjmp(safe_decompress_jump))
97   {
98     buf->ptr = buf->start + buf->len - lock_offset;
99     len = lizard_decompress(in, buf->ptr);
100   }
101   else
102   {
103     buf->ptr = NULL;
104     len = -1;
105     errno = EFAULT;
106   }
107   signal_handler[SIGSEGV] = old_handler;
108   return len;
109 }