]> mj.ucw.cz Git - libucw.git/blobdiff - lib/lizard.c
print a fancy message when SIGSEGV is caught at the decompression
[libucw.git] / lib / lizard.c
index 8d53234b3d5b3846b34e58c5760420f329ed7e3e..ad1a7897eca9ec0b58e725efa657d1d0fe2421a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     LiZzaRd -- Fast compression method based on Lempel-Ziv 77
+ *     LiZaRd -- Fast compression method based on Lempel-Ziv 77
  *
  *     (c) 2004, Robert Spalek <robert@ucw.cz>
  *
@@ -8,9 +8,14 @@
  */
 
 #include "lib/lib.h"
-#include "lib/lizzard.h"
+#include "lib/lizard.h"
 
 #include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <signal.h>
 
 typedef u16 hash_ptr_t;
 struct hash_record {
@@ -149,9 +154,9 @@ flush_copy_command(uns bof, byte *out, byte *start, uns len)
 }
 
 int
-lizzard_compress(byte *in, uns in_len, byte *out)
-  /* Requires out being allocated for at least in_len * LIZZARD_MAX_MULTIPLY +
-   * LIZZARD_MAX_ADD.  There must be at least LIZZARD_NEEDS_CHARS characters
+lizard_compress(byte *in, uns in_len, byte *out)
+  /* Requires out being allocated for at least in_len * LIZARD_MAX_MULTIPLY +
+   * LIZARD_MAX_ADD.  There must be at least LIZARD_NEEDS_CHARS characters
    * allocated after in.  Returns the actual compressed length. */
 {
   hash_ptr_t hash_tab[HASH_SIZE];
@@ -280,7 +285,7 @@ read_unary_value(byte *in, uns *val)
 }
 
 int
-lizzard_decompress(byte *in, byte *out)
+lizard_decompress(byte *in, byte *out)
   /* Requires out being allocated for the decompressed length must be known
    * beforehand.  It is desirable to lock the following memory page for
    * read-only access to prevent buffer overflow.  Returns the actual
@@ -394,6 +399,50 @@ perform_copy_command:
   return out - out_start;
 }
 
+struct lizard_buffer *
+lizard_alloc(uns max_len)
+{
+  int fd = open("/dev/zero", O_RDWR);
+  if (fd < 0)
+    die("open(/dev/zero): %m");
+  struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
+  buf->len = (max_len + 2*PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+  buf->ptr = mmap(NULL, buf->len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (buf->ptr == MAP_FAILED)
+    die("mmap(/dev/zero): %m");
+  return buf;
+}
+
+void
+lizard_free(struct lizard_buffer *buf)
+{
+  munmap(buf->ptr, buf->len);
+  xfree(buf);
+}
+
+static void
+sigsegv_handler(int UNUSED whatsit)
+{
+  die("SIGSEGV caught when decompressing.");
+}
+
+int
+lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
+  /* Decompresses into buf->ptr and returns the length of the uncompressed
+   * file.  Negative return values signalise errors.  If something goes wrong
+   * and buffer would overflow, SIGSEGV is raised.  */
+{
+  uns lock_offset = (expected_length + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+  if (lock_offset + PAGE_SIZE > buf->len)
+    return -1;
+  mprotect(buf->ptr, lock_offset, PROT_READ | PROT_WRITE);
+  mprotect(buf->ptr + lock_offset, PAGE_SIZE, PROT_READ);
+  sighandler_t old_handler = signal(SIGSEGV, sigsegv_handler);
+  int len = lizard_decompress(in, buf->ptr);
+  signal(SIGSEGV, old_handler);
+  return len;
+}
+
 /*
 
 Description of the LZO1X format :