diff options
Diffstat (limited to 'libhfcommon/files.c')
-rw-r--r-- | libhfcommon/files.c | 316 |
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; } |