summaryrefslogtreecommitdiff
path: root/libhfcommon/files.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhfcommon/files.c')
-rw-r--r--libhfcommon/files.c316
1 files changed, 167 insertions, 149 deletions
diff --git a/libhfcommon/files.c b/libhfcommon/files.c
index cc4e54f4..5a7763ec 100644
--- a/libhfcommon/files.c
+++ b/libhfcommon/files.c
@@ -29,8 +29,12 @@
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
+#if defined(_HF_ARCH_LINUX)
+#include <linux/memfd.h>
+#endif /* defined(_HF_ARCH_LINUX) */
#include <netinet/in.h>
#include <netinet/ip.h>
+#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,9 +42,11 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/un.h>
#if defined(_HF_ARCH_LINUX)
#include <sys/syscall.h>
#endif /* defined(_HF_ARCH_LINUX) */
+#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
@@ -48,42 +54,46 @@
#include "libhfcommon/log.h"
#include "libhfcommon/util.h"
-ssize_t files_readFileToBufMax(const char* fileName, uint8_t* buf, size_t fileMaxSz) {
- int fd = TEMP_FAILURE_RETRY(open(fileName, O_RDONLY | O_CLOEXEC));
+ssize_t files_readFileToBufMax(const char* fname, uint8_t* buf, size_t fileMaxSz) {
+ int fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
if (fd == -1) {
- PLOG_W("Couldn't open '%s' for R/O", fileName);
+ PLOG_W("Couldn't open '%s' for R/O", fname);
return -1;
}
ssize_t readSz = files_readFromFd(fd, buf, fileMaxSz);
if (readSz < 0) {
- LOG_W("Couldn't read '%s' to a buf", fileName);
+ LOG_W("Couldn't read '%s' to a buf", fname);
}
close(fd);
- LOG_D("Read '%zu' bytes from '%s'", readSz, fileName);
+ LOG_D("Read %zu bytes (%zu requested) from '%s'", (size_t)readSz, fileMaxSz, fname);
return readSz;
}
-bool files_writeBufToFile(const char* fileName, const uint8_t* buf, size_t fileSz, int flags) {
- int fd = TEMP_FAILURE_RETRY(open(fileName, flags, 0644));
+bool files_writeBufToFile(const char* fname, const uint8_t* buf, size_t fileSz, int flags) {
+ int fd = TEMP_FAILURE_RETRY(open(fname, flags, 0644));
if (fd == -1) {
- PLOG_W("Couldn't open '%s' for R/W", fileName);
+ PLOG_W("Couldn't create/open '%s' for R/W", fname);
return false;
}
bool ret = files_writeToFd(fd, buf, fileSz);
- if (ret == false) {
- PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", fileSz, fileName, fd);
- unlink(fileName);
+ if (!ret) {
+ PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", fileSz, fname, fd);
+ unlink(fname);
} else {
- LOG_D("Written '%zu' bytes to '%s'", fileSz, fileName);
+ LOG_D("Written '%zu' bytes to '%s'", fileSz, fname);
}
close(fd);
return ret;
}
+bool files_writeStrToFile(const char* fname, const char* str, int flags) {
+ return files_writeBufToFile(fname, (uint8_t*)str, strlen(str), flags);
+}
+
int files_writeBufToTmpFile(const char* dir, const uint8_t* buf, size_t fileSz, int flags) {
char template[PATH_MAX];
snprintf(template, sizeof(template), "%s/hfuzz.XXXXXX", dir);
@@ -147,8 +157,8 @@ ssize_t files_readFromFdSeek(int fd, uint8_t* buf, size_t fileSz, off_t off) {
return files_readFromFd(fd, buf, fileSz);
}
-bool files_exists(const char* fileName) {
- return (access(fileName, F_OK) != -1);
+bool files_exists(const char* fname) {
+ return (access(fname, F_OK) != -1);
}
bool files_writePatternToFd(int fd, off_t size, unsigned char p) {
@@ -203,93 +213,24 @@ const char* files_basename(const char* path) {
return base ? base + 1 : path;
}
-/*
- * dstExists argument can be used by caller for cases where existing destination
- * file requires special handling (e.g. save unique crashes)
- */
-bool files_copyFile(const char* source, const char* destination, bool* dstExists, bool try_link) {
- if (dstExists) {
- *dstExists = false;
- }
-
- if (try_link) {
- if (link(source, destination) == 0) {
- return true;
- } else {
- if (errno == EEXIST) {
- // Should kick-in before MAC, so avoid the hassle
- if (dstExists) *dstExists = true;
- return false;
- } else {
- PLOG_D("Couldn't link '%s' as '%s'", source, destination);
- /*
- * Don't fail yet as we might have a running env which doesn't allow
- * hardlinks (e.g. SELinux)
- */
- }
- }
- }
- // Now try with a verbose POSIX alternative
- int inFD, outFD, dstOpenFlags;
- mode_t dstFilePerms;
-
- // O_EXCL is important for saving unique crashes
- dstOpenFlags = O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL;
- dstFilePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
-
- inFD = TEMP_FAILURE_RETRY(open(source, O_RDONLY | O_CLOEXEC));
- if (inFD == -1) {
- PLOG_D("Couldn't open '%s' source", source);
- return false;
- }
-
- struct stat inSt;
- if (fstat(inFD, &inSt) == -1) {
- PLOG_W("Couldn't fstat(fd='%d' fileName='%s')", inFD, source);
- close(inFD);
- return false;
- }
-
- outFD = TEMP_FAILURE_RETRY(open(destination, dstOpenFlags, dstFilePerms));
- if (outFD == -1) {
- if (errno == EEXIST) {
- if (dstExists) *dstExists = true;
- }
- PLOG_D("Couldn't open '%s' destination", destination);
- close(inFD);
- return false;
- }
-
- uint8_t* inFileBuf = malloc(inSt.st_size);
- if (!inFileBuf) {
- PLOG_W("malloc(%zu) failed", (size_t)inSt.st_size);
- close(inFD);
- close(outFD);
- return false;
+/* Zero all bytes in the file */
+bool files_resetFile(int fd, size_t sz) {
+#if defined(_HF_ARCH_LINUX)
+ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, (off_t)0, (off_t)sz) != -1) {
+ return true;
}
+ PLOG_W("fallocate(fd=%d, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, sz=%zu)", fd, sz);
+#endif /* defined(_HF_ARCH_LINUX) */
- ssize_t readSz = files_readFromFd(inFD, inFileBuf, (size_t)inSt.st_size);
- if (readSz < 0) {
- PLOG_W("Couldn't read '%s' to a buf", source);
- free(inFileBuf);
- close(inFD);
- close(outFD);
+ /* Fallback mode */
+ if (ftruncate(fd, (off_t)0) == -1) {
+ PLOG_W("ftruncate(fd=%d, sz=0)", fd);
return false;
}
-
- if (files_writeToFd(outFD, inFileBuf, readSz) == false) {
- PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", (size_t)readSz, destination,
- outFD);
- unlink(destination);
- free(inFileBuf);
- close(inFD);
- close(outFD);
+ if (ftruncate(fd, (off_t)sz) == -1) {
+ PLOG_W("ftruncate(fd=%d, sz=%zu)", fd, sz);
return false;
}
-
- free(inFileBuf);
- close(inFD);
- close(outFD);
return true;
}
@@ -306,7 +247,7 @@ size_t files_parseSymbolFilter(const char* srcFile, char*** filterList) {
return 0;
}
- char* lineptr = NULL;
+ char* lineptr = NULL;
size_t symbolsRead = 0, n = 0;
for (;;) {
if (getline(&lineptr, &n, f) == -1) {
@@ -340,27 +281,27 @@ size_t files_parseSymbolFilter(const char* srcFile, char*** filterList) {
return symbolsRead;
}
-uint8_t* files_mapFile(const char* fileName, off_t* fileSz, int* fd, bool isWritable) {
+uint8_t* files_mapFile(const char* fname, off_t* fileSz, int* fd, bool isWritable) {
int mmapProt = PROT_READ;
if (isWritable) {
mmapProt |= PROT_WRITE;
}
- if ((*fd = TEMP_FAILURE_RETRY(open(fileName, O_RDONLY))) == -1) {
- PLOG_W("Couldn't open() '%s' file in R/O mode", fileName);
+ if ((*fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY))) == -1) {
+ PLOG_W("Couldn't open() '%s' file in R/O mode", fname);
return NULL;
}
struct stat st;
if (fstat(*fd, &st) == -1) {
- PLOG_W("Couldn't stat() the '%s' file", fileName);
+ PLOG_W("Couldn't stat() the '%s' file", fname);
close(*fd);
return NULL;
}
uint8_t* buf;
if ((buf = mmap(NULL, st.st_size, mmapProt, MAP_PRIVATE, *fd, 0)) == MAP_FAILED) {
- PLOG_W("Couldn't mmap() the '%s' file", fileName);
+ PLOG_W("Couldn't mmap() the '%s' file", fname);
close(*fd);
return NULL;
}
@@ -369,79 +310,129 @@ uint8_t* files_mapFile(const char* fileName, off_t* fileSz, int* fd, bool isWrit
return buf;
}
-uint8_t* files_mapFileShared(const char* fileName, off_t* fileSz, int* fd) {
- if ((*fd = TEMP_FAILURE_RETRY(open(fileName, O_RDONLY))) == -1) {
- PLOG_W("Couldn't open() '%s' file in R/O mode", fileName);
- return NULL;
- }
+/* mmap flags for various OSs, when mmap'ing a temporary file or a shared mem */
+int files_getTmpMapFlags(int flag, bool nocore) {
+#if defined(MAP_NOSYNC)
+ /*
+ * Some kind of bug in FreeBSD kernel. Without this flag, the shm_open() memory will cause a lot
+ * of troubles to the calling process when mmap()'d
+ */
+ flag |= MAP_NOSYNC;
+#endif /* defined(MAP_NOSYNC) */
+#if defined(MAP_HASSEMAPHORE)
+ /* Our shared/mmap'd pages can have mutexes in them */
+ flag |= MAP_HASSEMAPHORE;
+#endif /* defined(MAP_HASSEMAPHORE) */
+ if (nocore) {
+#if defined(MAP_CONCEAL)
+ flag |= MAP_CONCEAL;
+#endif /* defined(MAP_CONCEAL) */
+#if defined(MAP_NOCORE)
+ flag |= MAP_NOCORE;
+#endif /* defined(MAP_NOCORE) */
+ }
+ return flag;
+}
- struct stat st;
- if (fstat(*fd, &st) == -1) {
- PLOG_W("Couldn't stat() the '%s' file", fileName);
- close(*fd);
- return NULL;
- }
+int files_createSharedMem(size_t sz, const char* name, bool exportmap) {
+ int fd = -1;
- uint8_t* buf;
- if ((buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, *fd, 0)) == MAP_FAILED) {
- PLOG_W("Couldn't mmap() the '%s' file", fileName);
- close(*fd);
- return NULL;
+ if (exportmap) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "./%s", name);
+ if ((fd = open(path, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)) == -1) {
+ PLOG_W("open('%s')", path);
+ return -1;
+ }
}
- *fileSz = st.st_size;
- return buf;
-}
-
-void* files_mapSharedMem(size_t sz, int* fd, const char* name, const char* dir) {
- *fd = -1;
-
#if defined(_HF_ARCH_LINUX)
+ if (fd == -1) {
+ fd = syscall(__NR_memfd_create, name, (uintptr_t)(MFD_CLOEXEC));
+ }
+#endif /* defined(_HF_ARCH_LINUX) */
-#if !defined(MFD_CLOEXEC) /* sys/memfd.h is not always present */
-#define MFD_CLOEXEC 0x0001U
-#endif /* !defined(MFD_CLOEXEC) */
-
-#if !defined(__NR_memfd_create)
-#if defined(__x86_64__)
-#define __NR_memfd_create 319
-#endif /* defined(__x86_64__) */
-#endif /* !defined(__NR_memfd_create) */
-
-#if defined(__NR_memfd_create)
- *fd = syscall(__NR_memfd_create, name, (uintptr_t)MFD_CLOEXEC);
-#endif /* defined__NR_memfd_create) */
+/* SHM_ANON is available with some *BSD OSes */
+#if defined(SHM_ANON)
+ if (fd == -1) {
+ if ((fd = shm_open(SHM_ANON, O_RDWR, 0600)) == -1) {
+ PLOG_W("shm_open(SHM_ANON, O_RDWR, 0600)");
+ }
+ }
+#endif /* defined(SHM_ANON) */
-#endif /* defined(_HF_ARCH_LINUX) */
+/* Use regular shm_open */
+#if !defined(_HF_ARCH_DARWIN) && !defined(__ANDROID__)
+ /* shm objects under MacOSX are 'a-typical' */
+ if (fd == -1) {
+ char tmpname[PATH_MAX];
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ snprintf(tmpname, sizeof(tmpname), "/%s%lx%lx%d", name, (unsigned long)tv.tv_sec,
+ (unsigned long)tv.tv_usec, (int)getpid());
+ if ((fd = shm_open(tmpname, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) {
+ PLOG_W("shm_open('%s', O_RDWR|O_CREAT|O_EXCL, 0600)", tmpname);
+ } else {
+ shm_unlink(tmpname);
+ }
+ }
+#endif /* !defined(_HF_ARCH_DARWIN) && !defined(__ANDROID__) */
- if (*fd == -1) {
+ /* As the last resort, create a file in /tmp */
+ if (fd == -1) {
char template[PATH_MAX];
- snprintf(template, sizeof(template), "%s/%s.XXXXXX", dir, name);
- if ((*fd = mkostemp(template, O_CLOEXEC)) == -1) {
+ snprintf(template, sizeof(template), "/tmp/%s.XXXXXX", name);
+ if ((fd = mkostemp(template, O_CLOEXEC)) == -1) {
PLOG_W("mkstemp('%s')", template);
- return NULL;
+ return -1;
}
unlink(template);
}
- if (TEMP_FAILURE_RETRY(ftruncate(*fd, sz)) == -1) {
- PLOG_W("ftruncate(%d, %zu)", *fd, sz);
- close(*fd);
- *fd = -1;
+
+ if (TEMP_FAILURE_RETRY(ftruncate(fd, sz)) == -1) {
+ PLOG_W("ftruncate(%d, %zu)", fd, sz);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void* files_mapSharedMem(size_t sz, int* fd, const char* name, bool nocore, bool exportmap) {
+ *fd = files_createSharedMem(sz, name, exportmap);
+ if (*fd == -1) {
return NULL;
}
- void* ret = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+
+ int mflags = files_getTmpMapFlags(MAP_SHARED, /* nocore= */ true);
+ void* ret = mmap(NULL, sz, PROT_READ | PROT_WRITE, mflags, *fd, 0);
if (ret == MAP_FAILED) {
PLOG_W("mmap(sz=%zu, fd=%d)", sz, *fd);
*fd = -1;
close(*fd);
return NULL;
}
+ if (posix_madvise(ret, sz, POSIX_MADV_RANDOM) == -1) {
+ PLOG_W("posix_madvise(sz=%zu, POSIX_MADV_RANDOM)", sz);
+ }
+ if (nocore) {
+#if defined(MADV_DONTDUMP)
+ if (madvise(ret, sz, MADV_DONTDUMP) == -1) {
+ PLOG_W("madvise(sz=%zu, MADV_DONTDUMP)", sz);
+ }
+#endif /* defined(MADV_DONTDUMP) */
+#if defined(MADV_NOCORE)
+ if (madvise(ret, sz, MADV_NOCORE) == -1) {
+ PLOG_W("madvise(sz=%zu, MADV_NOCORE)", sz);
+ }
+#endif /* defined(MADV_NOCORE) */
+ }
return ret;
}
sa_family_t files_sockFamily(int sock) {
struct sockaddr addr;
- socklen_t addrlen = sizeof(addr);
+ socklen_t addrlen = sizeof(addr);
if (getsockname(sock, &addr, &addrlen) == -1) {
PLOG_W("getsockname(sock=%d)", sock);
@@ -451,7 +442,7 @@ sa_family_t files_sockFamily(int sock) {
return addr.sa_family;
}
-const char* files_sockAddrToStr(const struct sockaddr* sa) {
+const char* files_sockAddrToStr(const struct sockaddr* sa, const socklen_t len) {
static __thread char str[4096];
if (sa->sa_family == AF_INET) {
@@ -473,6 +464,33 @@ const char* files_sockAddrToStr(const struct sockaddr* sa) {
return str;
}
+ if (sa->sa_family == AF_UNIX) {
+ if ((size_t)len <= offsetof(struct sockaddr_un, sun_path)) {
+ snprintf(str, sizeof(str), "unix:<struct too short at %u bytes>", (unsigned)len);
+ return str;
+ }
+
+ struct sockaddr_un* sun = (struct sockaddr_un*)sa;
+ int pathlen;
+
+ if (sun->sun_path[0] == '\0') {
+ /* Abstract socket
+ *
+ * TODO: Handle null bytes in sun->sun_path (they have no
+ * special significance unlike in C char arrays, see unix(7))
+ */
+ pathlen = strnlen(&sun->sun_path[1], len - offsetof(struct sockaddr_un, sun_path) - 1);
+
+ snprintf(str, sizeof(str), "unix:abstract:%-*s", pathlen, &sun->sun_path[1]);
+ return str;
+ }
+
+ pathlen = strnlen(sun->sun_path, len - offsetof(struct sockaddr_un, sun_path));
+
+ snprintf(str, sizeof(str), "unix:%-*s", pathlen, sun->sun_path);
+ return str;
+ }
+
snprintf(str, sizeof(str), "Unsupported sockaddr family=%d", (int)sa->sa_family);
return str;
}