From 2d603b6d1dd40861df45bb2f24105c064f6a668c Mon Sep 17 00:00:00 2001
From: Christophe Grenier <grenier@cgsecurity.org>
Date: Sat, 8 Dec 2018 08:33:30 +0100
Subject: [PATCH] fuzzerfidentify: use libfuzzer

---
 configure.ac            |   5 +-
 src/Makefile.am         |   4 +-
 src/fuzzerfidentify.cpp | 131 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 137 insertions(+), 3 deletions(-)
 create mode 100644 src/fuzzerfidentify.cpp

diff --git a/configure.ac b/configure.ac
index ed06d177..28d32401 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ AC_INIT([testdisk],[7.1-WIP],[grenier@cgsecurity.org])
 AC_LANG(C)
 sinclude(acx_pthread.m4)
 sinclude(mkdir.m4)
-TESTDISKDATE="September 2018"
+TESTDISKDATE="December 2018"
 AC_SUBST(TESTDISKDATE)
 AC_DEFINE_UNQUOTED([TESTDISKDATE],"$TESTDISKDATE",[Date of release])
 AC_CONFIG_AUX_DIR(config)
@@ -1030,10 +1030,10 @@ then
 fi
 
 photorecf_LDADD=$photorec_LDADD
+fuzzerfidentify_LDADD="-fsanitize=fuzzer $fidentify_LDADD"
 CFLAGS="$CFLAGS $coverage_flags"
 CXXFLAGS="$CXXFLAGS $coverage_flags"
 CPPFLAGS="$CPPFLAGS $coverage_flags"
-
 AC_SUBST(CFLAGS)
 AC_SUBST(CXXFLAGS)
 AC_SUBST(CPPFLAGS)
@@ -1042,6 +1042,7 @@ AC_SUBST(MOC)
 AC_SUBST(LRELEASE)
 AC_SUBST(QT_SELECT)
 AC_SUBST(fidentify_LDADD)
+AC_SUBST(fuzzerfidentify_LDADD)
 AC_SUBST(testdisk_LDADD)
 AC_SUBST(photorec_LDADD)
 AC_SUBST(photorecf_LDADD)
diff --git a/src/Makefile.am b/src/Makefile.am
index 625ef21f..deaaed27 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,7 +12,7 @@ if USEQT
 endif
 
 bin_PROGRAMS		= testdisk photorec fidentify $(QPHOTOREC)
-EXTRA_PROGRAMS		= photorecf
+EXTRA_PROGRAMS		= photorecf fuzzerfidentify
 
 base_C			= autoset.c common.c crc.c ewf.c fnctdsk.c hdaccess.c hdcache.c hdwin32.c hidden.c hpa_dco.c intrf.c iso.c list_sort.c log.c log_part.c misc.c msdos.c parti386.c partgpt.c parthumax.c partmac.c partsun.c partnone.c partxbox.c io_redir.c ntfs_io.c ntfs_utl.c partauto.c sudo.c unicode.c win32.c
 base_H			= alignio.h autoset.h common.h crc.h ewf.h fnctdsk.h hdaccess.h hdwin32.h hidden.h guid_cmp.h guid_cpy.h hdcache.h hpa_dco.h intrf.h iso.h iso9660.h lang.h list.h list_sort.h log.h log_part.h misc.h types.h io_redir.h msdos.h ntfs_utl.h parti386.h partgpt.h parthumax.h partmac.h partsun.h partxbox.h partauto.h sudo.h unicode.h win32.h
@@ -383,6 +383,8 @@ nodist_qphotorec_SOURCES = moc_qphotorec.cpp rcc_qphotorec.cpp rcc_qphotorec_loc
 
 fidentify_SOURCES	= fidentify.c common.c common.h misc.c misc.h phcfg.c phcfg.h setdate.c setdate.h $(file_C) $(file_H) log.c log.h crc.c crc.h ext2_common.c fat_common.c fat_common.h suspend_no.c
 
+fuzzerfidentify_SOURCES		= fuzzerfidentify.cpp common.c common.h misc.c misc.h phcfg.c phcfg.h setdate.c setdate.h $(file_C) $(file_H) log.c log.h crc.c crc.h ext2_common.c fat_common.c fat_common.h suspend_no.c
+
 QT_QM=$(QT_TS:.ts=.qm)
 SECONDARY: $(QT_QM)
 
diff --git a/src/fuzzerfidentify.cpp b/src/fuzzerfidentify.cpp
new file mode 100644
index 00000000..f5be3742
--- /dev/null
+++ b/src/fuzzerfidentify.cpp
@@ -0,0 +1,131 @@
+/*
+
+    File: fuzzerfidentify.cpp
+
+    Copyright (C) 2018 Christophe GRENIER <grenier@cgsecurity.org>
+
+    This software is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write the Free Software Foundation, Inc., 51
+    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <dirent.h>
+#include "types.h"
+#include "common.h"
+#include "filegen.h"
+#include <sys/types.h>
+#include <unistd.h>
+extern file_enable_t list_file_enable[];
+extern file_check_list_t file_check_list;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
+{
+  const size_t blocksize=65536;
+  static unsigned char *buffer_start=NULL;
+  static file_stat_t *file_stats=NULL;
+  static char *filename=NULL;
+  uint8_t *buffer;
+  if(Size == 0)
+    return 0;
+  if(filename==NULL)
+  {
+    pid_t pid=getpid();
+    filename=(char *)MALLOC(64);
+    sprintf(filename, "sample%u", pid);
+  }
+  if(file_stats==NULL)
+  {
+    /* Enable all file formats */
+    file_enable_t *file_enable;
+    for(file_enable=list_file_enable;file_enable->file_hint!=NULL;file_enable++)
+      file_enable->enable=1;
+    file_stats=init_file_stats(list_file_enable);
+  }
+  if(buffer_start==NULL)
+  {
+    buffer_start=(unsigned char *)MALLOC(2*blocksize);
+  }
+  buffer=buffer_start+blocksize;
+  memcpy(buffer, Data, (Size < blocksize ? Size : blocksize));
+  {
+    struct td_list_head *tmpl;
+    file_recovery_t file_recovery_new;
+    file_recovery_t file_recovery;
+    reset_file_recovery(&file_recovery);
+    file_recovery.blocksize=blocksize;
+    file_recovery_new.blocksize=blocksize;
+    file_recovery_new.file_stat=NULL;
+    td_list_for_each(tmpl, &file_check_list.list)
+    {
+      struct td_list_head *tmp;
+      const file_check_list_t *pos=td_list_entry_const(tmpl, const file_check_list_t, list);
+      if(pos->offset <= blocksize)
+      {
+	td_list_for_each(tmp, &pos->file_checks[buffer[pos->offset]].list)
+	{
+	  const file_check_t *file_check=td_list_entry_const(tmp, const file_check_t, list);
+	  if((file_check->length==0 ||
+		(file_check->offset+file_check->length <=blocksize &&
+		 memcmp(buffer + file_check->offset, file_check->value, file_check->length)==0)) &&
+	      file_check->header_check(buffer, blocksize, 0, &file_recovery, &file_recovery_new)!=0)
+	  {
+	    file_recovery_new.file_stat=file_check->file_stat;
+	    break;
+	  }
+	}
+      }
+      if(file_recovery_new.file_stat!=NULL)
+	break;
+    }
+#if 0
+    if( file_recovery_new.file_stat!=NULL && file_recovery_new.file_stat->file_hint!=NULL &&
+	file_recovery_new.file_check!=NULL)
+    {
+      FILE *out;
+      file_recovery_new.file_size=Size;
+      file_recovery_new.calculated_file_size=Size;
+      out=fopen(filename, "wb");
+      fwrite(Data, Size, 1, out);
+      file_recovery_new.handle=out;
+      (file_recovery_new.file_check)(&file_recovery_new);
+      fclose(out);
+//      unlink(filename);
+    }
+#endif
+  }
+  return 0;  // Non-zero return values are reserved for future use.
+}