about summary refs log tree commit diff
path: root/frida_mode/src/cmplog/cmplog.c
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src/cmplog/cmplog.c')
-rw-r--r--frida_mode/src/cmplog/cmplog.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c
index 7b11c350..3df7d13d 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -1,3 +1,8 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <syscall.h>
+
 #include "frida-gum.h"
 
 #include "debug.h"
@@ -5,10 +10,13 @@
 #include "util.h"
 
 #define DEFAULT_MMAP_MIN_ADDR (32UL << 10)
+#define FD_TMP_MAX_SIZE 65536
 
 extern struct cmp_map *__afl_cmp_map;
 
 static GArray *cmplog_ranges = NULL;
+static int     fd_tmp = -1;
+static ssize_t fd_tmp_size = 0;
 
 static gboolean cmplog_range(const GumRangeDetails *details,
                              gpointer               user_data) {
@@ -27,6 +35,40 @@ static gint cmplog_sort(gconstpointer a, gconstpointer b) {
 
 }
 
+static int cmplog_create_temp(void) {
+
+  const char *tmpdir = g_get_tmp_dir();
+  OKF("CMPLOG Temporary directory: %s", tmpdir);
+  gchar *fname = g_strdup_printf("%s/frida-cmplog-XXXXXX", tmpdir);
+  OKF("CMPLOG Temporary file template: %s", fname);
+  int fd = mkstemp(fname);
+  OKF("CMPLOG Temporary file: %s", fname);
+
+  if (fd < 0) {
+
+    FATAL("Failed to create temp file: %s, errno: %d", fname, errno);
+
+  }
+
+  if (unlink(fname) < 0) {
+
+    FATAL("Failed to unlink temp file: %s (%d), errno: %d", fname, fd, errno);
+
+  }
+
+  if (ftruncate(fd, 0) < 0) {
+
+    FATAL("Failed to ftruncate temp file: %s (%d), errno: %d", fname, fd,
+          errno);
+
+  }
+
+  g_free(fname);
+
+  return fd;
+
+}
+
 void cmplog_init(void) {
 
   if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); }
@@ -44,6 +86,13 @@ void cmplog_init(void) {
 
   }
 
+  /*
+   * We can't use /dev/null or /dev/zero for this since it appears that they
+   * don't validate the input buffer. Persumably as an optimization because they
+   * don't actually write any data. The file will be deleted on close.
+   */
+  fd_tmp = cmplog_create_temp();
+
 }
 
 static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
@@ -67,6 +116,9 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) {
    */
   if (addr < DEFAULT_MMAP_MIN_ADDR) { return false; }
 
+  /* Check our addres/length don't wrap around */
+  if (SIZE_MAX - addr < size) { return false; }
+
   GumAddress inner_base = addr;
   GumAddress inner_limit = inner_base + size;
 
@@ -81,6 +133,38 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) {
 
   }
 
+  /*
+   * Our address map can change (e.g. stack growth), use write as a fallback to
+   * validate our address.
+   */
+  ssize_t written = syscall(__NR_write, fd_tmp, (void *)addr, size);
+
+  /*
+   * If the write succeeds, then the buffer must be valid otherwise it would
+   * return EFAULT
+   */
+  if (written > 0) {
+
+    fd_tmp_size += written;
+    if (fd_tmp_size > FD_TMP_MAX_SIZE) {
+
+      /*
+       * Truncate the file, we don't want our temp file to continue growing!
+       */
+      if (ftruncate(fd_tmp, 0) < 0) {
+
+        FATAL("Failed to truncate fd_tmp (%d), errno: %d", fd_tmp, errno);
+
+      }
+
+      fd_tmp_size = 0;
+
+    }
+
+    if ((size_t)written == size) { return true; }
+
+  }
+
   return false;
 
 }