]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
4e07036a67a5a1eed30f3bb27b55a2c1efd62778
[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   buf->len = ALIGN(max_len + 3, PAGE_SIZE);             // +3 due to the unaligned access
25   buf->start = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
26   if (buf->start == MAP_FAILED)
27     die("mmap(anonymous): %m");
28   if (mprotect(buf->start + buf->len, PAGE_SIZE, PROT_NONE) < 0)
29     die("mprotect: %m");
30 }
31
32 struct lizard_buffer *
33 lizard_alloc(uns max_len)
34 {
35   struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
36   lizard_alloc_internal(buf, max_len);
37   buf->old_sigsegv_handler = xmalloc(sizeof(struct sigaction));
38   handle_signal(SIGSEGV, buf->old_sigsegv_handler);
39   return buf;
40 }
41
42 void
43 lizard_free(struct lizard_buffer *buf)
44 {
45   munmap(buf->start, buf->len + PAGE_SIZE);
46   unhandle_signal(SIGSEGV, buf->old_sigsegv_handler);
47   xfree(buf->old_sigsegv_handler);
48   xfree(buf);
49 }
50
51 void
52 lizard_realloc(struct lizard_buffer *buf, uns max_len)
53 {
54   munmap(buf->start, buf->len + PAGE_SIZE);
55   lizard_alloc_internal(buf, max_len);
56 }
57
58 static jmp_buf safe_decompress_jump;
59 static int
60 sigsegv_handler(int signal UNUSED)
61 {
62   log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
63   longjmp(safe_decompress_jump, 1);
64   return 1;
65 }
66
67 int
68 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
69   /* Decompresses into buf->ptr and returns the length of the uncompressed
70    * file.  If an error has occured, -1 is returned and errno is set.  SIGSEGV
71    * is caught in the case of buffer-overflow.  The function is not re-entrant
72    * because of a static longjmp handler.  */
73 {
74   uns lock_offset = ALIGN(expected_length + 3, PAGE_SIZE);      // +3 due to the unaligned access
75   if (lock_offset > buf->len)
76   {
77     errno = EFBIG;
78     return -1;
79   }
80   volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
81   signal_handler[SIGSEGV] = sigsegv_handler;
82   int len;
83   if (!setjmp(safe_decompress_jump))
84   {
85     buf->ptr = buf->start + buf->len - lock_offset;
86     len = lizard_decompress(in, buf->ptr);
87   }
88   else
89   {
90     buf->ptr = NULL;
91     len = -1;
92     errno = EFAULT;
93   }
94   signal_handler[SIGSEGV] = old_handler;
95   return len;
96 }