diff --git a/README.md b/README.md index 222b451..f42249e 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,10 @@ See [migration.md](docs/migration.md) for a list of the API changes in the lates ## VMF Compatibility -As of now, VMF can be run in Docker and on the following distributions of Linux: +As of now, VMF can be run in Docker and on the following distributions of Linux and BSD: - CentOS 8 and 9 +- FreeBSD 15.0 and 15.1 - Kali - Oracle Linux 8 and 9 - RedHat 8 and 9 @@ -116,6 +117,26 @@ You may alternatively open the VMF.sln file that has been generated in the build More information on the build system is available in our [Build System Documentation](docs/build_system.md). +### Building VMF (FreeBSD) + +Install dependencies for the build with `pkg install curl cmake gmake`. + +When building and installing AFL++, support for FreeBSD has been merged into the official repository. The most recent tested commit is e33e7cd4f4b1da9cc76c19b7766deea790a2c5ee. + +Execute the following commands to build and install VMF. Note the use of the `gmake` command instead of `make`, the BSD make is incompatible and the GNU make must be used. + +*Note: The -DCMAKE_INSTALL_PREFIX may be used to optionally specify an install location other than the default (build/vmf_install).* + +```bash +# from /path/to/vmf/ directory: +mkdir build +cd build +cmake .. +#Or optionally use this version instead to specify an install path +#cmake -DCMAKE_INSTALL_PREFIX= .. +gmake install -j8 +``` + ### Running VMF VMF can be run in a standalone mode, with a single fuzzing instance, as well as in a distributed mode where multiple VMF instances work together to fuzz something. diff --git a/vmf/src/framework/CMakeLists.txt b/vmf/src/framework/CMakeLists.txt index 9cab590..17c8e41 100644 --- a/vmf/src/framework/CMakeLists.txt +++ b/vmf/src/framework/CMakeLists.txt @@ -59,7 +59,10 @@ set(Framework_LINKLIST restclient-cpp ) -if(NOT WIN32) +if (BSD) + #explicitly linking stdc++fs not needed on FreeBSD + list(APPEND Framework_LINKLIST) +elseif(NOT WIN32) list(APPEND Framework_LINKLIST stdc++fs) else() list(APPEND Framework_LINKLIST wldap32 ws2_32 Crypt32.lib Wldap32 Normaliz) diff --git a/vmf/src/framework/util/OSAPI.hpp b/vmf/src/framework/util/OSAPI.hpp index 8ccad33..f7787b0 100644 --- a/vmf/src/framework/util/OSAPI.hpp +++ b/vmf/src/framework/util/OSAPI.hpp @@ -130,7 +130,7 @@ class OSAPI * @param handler this is the pointer to a function that will be run when an interrupt signal is received by * VMF. */ - virtual void setSignalHandlers(sighandler_t handler) = 0; + virtual void setSignalHandlers(__sighandler_t handler) = 0; #endif virtual ~OSAPI() {}; diff --git a/vmf/src/framework/util/linux/OSAPIImp.cpp b/vmf/src/framework/util/linux/OSAPIImp.cpp index 5b1b489..49d296e 100644 --- a/vmf/src/framework/util/linux/OSAPIImp.cpp +++ b/vmf/src/framework/util/linux/OSAPIImp.cpp @@ -25,7 +25,12 @@ #include #include // dirname #include // readlink +#ifdef __FreeBSD__ +#include +#include +#else #include // PATH_MAX +#endif using namespace vmf; @@ -100,7 +105,19 @@ std::string OSAPIImp::getExecutablePath() char result[PATH_MAX]; const char *path = nullptr; + #ifdef __FreeBSD__ + //no /proc/self/exe on FreeBSD + size_t count = sizeof(result); + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + sysctl(mib, 4, result, &count, NULL, 0); + #else ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); + + #endif if (count > 0) { result[count] = 0; path = dirname(result); @@ -164,7 +181,7 @@ bool OSAPIImp::commandLineZip(std::string zipFilePath, std::string inputDir) return success; } -void OSAPIImp::setSignalHandlers(sighandler_t handler) +void OSAPIImp::setSignalHandlers(__sighandler_t handler) { signal(SIGINT, handler); signal(SIGTERM, handler); diff --git a/vmf/src/framework/util/linux/OSAPIImp.hpp b/vmf/src/framework/util/linux/OSAPIImp.hpp index d565d9b..ea75806 100644 --- a/vmf/src/framework/util/linux/OSAPIImp.hpp +++ b/vmf/src/framework/util/linux/OSAPIImp.hpp @@ -56,8 +56,8 @@ class OSAPIImp: public OSAPI * @param handler this is the pointer to a function that will be run when an interrupt signal is received by * VMF. */ - virtual void setSignalHandlers(sighandler_t handler); + virtual void setSignalHandlers(__sighandler_t handler); virtual ~OSAPIImp(); }; -} \ No newline at end of file +} diff --git a/vmf/src/modules/linux/executor/AFLForkserverExecutor.cpp b/vmf/src/modules/linux/executor/AFLForkserverExecutor.cpp index cfd62ff..c55b8bc 100644 --- a/vmf/src/modules/linux/executor/AFLForkserverExecutor.cpp +++ b/vmf/src/modules/linux/executor/AFLForkserverExecutor.cpp @@ -102,7 +102,12 @@ void AFLForkserverExecutor::init(ConfigInterface& config) { parseSUTDebugInfo(); validateVersionCompatibility(); + #ifndef __FreeBSD__ bool useCoreDumpCheck = config.getBoolParam(getModuleName(),"enableCoreDumpCheck", true); + #else //FreeBSD + /* /proc/sys/kernel/core_pattern does not exist on FreeBSD, cannot verify core pattern. */ + bool useCoreDumpCheck = config.getBoolParam(getModuleName(),"enableCoreDumpCheck", false); + #endif if(useCoreDumpCheck) { verifyCorePattern(); @@ -138,6 +143,10 @@ bool AFLForkserverExecutor::verifyCorePattern(void) { /* Check that the core dump pattern does not begin with a pipe. This causes crashes to be sent to an external utility, which is very slow and causes VMF to misinterpret them as timeouts. As such, this is a fatal error. */ + ///proc/sys/kernel/core_pattern does not exist on FreeBSD. + #ifdef __FreeBSD__ + throw RuntimeException("/proc/sys/kernel/core_pattern does not exist on FreeBSD, cannot verify core pattern.", RuntimeException::CONFIGURATION_ERROR); + #endif int corepattern_fd = open("/proc/sys/kernel/core_pattern", O_RDONLY); if (corepattern_fd < 0) @@ -150,7 +159,7 @@ bool AFLForkserverExecutor::verifyCorePattern(void) { "to be misinterpreted as timeouts.\nTo fix, please log in as root " "and run the following command: \n" " echo core >/proc/sys/kernel/core_pattern", - RuntimeException::UNEXPECTED_ERROR); + RuntimeException::CONFIGURATION_ERROR); return true; } @@ -1294,10 +1303,18 @@ void AFLForkserverExecutor::setResourceLimits(void) { * values as high as our default constants for pipes */ long unsigned max_fd = std::max({CTRL_PIPE_RD, CTRL_PIPE_WR, STAT_PIPE_RD, STAT_PIPE_WR}); getrlimit(RLIMIT_NOFILE, &r); + #ifdef __FreeBSD__ + /* freebsd r.rlim_cur is signed long, and clang cares about unsigned vs signed comparison */ + if (static_cast(r.rlim_cur) < max_fd + 1) { + r.rlim_cur = max_fd + 2; + setrlimit(RLIMIT_NOFILE, &r); + } + #else if (r.rlim_cur < max_fd + 1) { r.rlim_cur = max_fd + 2; setrlimit(RLIMIT_NOFILE, &r); } + #endif /* Memory Limit */ if (sut_mem_limit > 0) {