/* * * honggfuzz - file operations * ----------------------------------------- * * Author: Robert Swiecki * * Copyright 2010-2018 by Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * */ #include "libhfcommon/files.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_HF_ARCH_LINUX) #include #endif /* defined(_HF_ARCH_LINUX) */ #include #include #include #include "libhfcommon/common.h" #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)); if (fd == -1) { PLOG_W("Couldn't open '%s' for R/O", fileName); return -1; } ssize_t readSz = files_readFromFd(fd, buf, fileMaxSz); if (readSz < 0) { LOG_W("Couldn't read '%s' to a buf", fileName); } close(fd); LOG_D("Read '%zu' bytes from '%s'", readSz, fileName); 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)); if (fd == -1) { PLOG_W("Couldn't create/open '%s' for R/W", fileName); 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); } else { LOG_D("Written '%zu' bytes to '%s'", fileSz, fileName); } close(fd); return ret; } 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); int fd = mkostemp(template, flags); if (fd == -1) { PLOG_W("mkostemp('%s') failed", template); return -1; } if (unlink(template) == -1) { PLOG_W("unlink('%s')", template); } if (!files_writeToFd(fd, buf, fileSz)) { PLOG_W("Couldn't save data to the temporary file"); close(fd); return -1; } if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { PLOG_W("Couldn't rewind file '%s' fd=%d", template, fd); close(fd); return -1; } return fd; } bool files_writeToFd(int fd, const uint8_t* buf, size_t fileSz) { size_t writtenSz = 0; while (writtenSz < fileSz) { ssize_t sz = TEMP_FAILURE_RETRY(write(fd, &buf[writtenSz], fileSz - writtenSz)); if (sz < 0) { return false; } writtenSz += sz; } return true; } bool files_writeStrToFd(int fd, const char* str) { return files_writeToFd(fd, (const uint8_t*)str, strlen(str)); } ssize_t files_readFromFd(int fd, uint8_t* buf, size_t fileSz) { size_t readSz = 0; while (readSz < fileSz) { ssize_t sz = TEMP_FAILURE_RETRY(read(fd, &buf[readSz], fileSz - readSz)); if (sz == 0) { break; } if (sz < 0) { return -1; } readSz += sz; } return (ssize_t)readSz; } ssize_t files_readFromFdSeek(int fd, uint8_t* buf, size_t fileSz, off_t off) { if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { PLOG_W("lseek(fd=%d, %lld, SEEK_SET)", fd, (long long int)off); return -1; } return files_readFromFd(fd, buf, fileSz); } bool files_exists(const char* fileName) { return (access(fileName, F_OK) != -1); } bool files_writePatternToFd(int fd, off_t size, unsigned char p) { void* buf = malloc(size); if (!buf) { PLOG_W("Couldn't allocate memory"); return false; } memset(buf, p, (size_t)size); int ret = files_writeToFd(fd, buf, size); free(buf); return ret; } bool files_sendToSocketNB(int fd, const uint8_t* buf, size_t fileSz) { size_t writtenSz = 0; while (writtenSz < fileSz) { ssize_t sz = TEMP_FAILURE_RETRY(send(fd, &buf[writtenSz], fileSz - writtenSz, MSG_DONTWAIT)); if (sz < 0) { return false; } writtenSz += sz; } return true; } bool files_sendToSocket(int fd, const uint8_t* buf, size_t fileSz) { int sendFlags = 0; #ifdef _HF_ARCH_DARWIN sendFlags |= SO_NOSIGPIPE; #else sendFlags |= MSG_NOSIGNAL; #endif size_t writtenSz = 0; while (writtenSz < fileSz) { ssize_t sz = send(fd, &buf[writtenSz], fileSz - writtenSz, sendFlags); if (sz < 0 && errno == EINTR) continue; if (sz < 0) return false; writtenSz += sz; } return true; } const char* files_basename(const char* path) { const char* base = strrchr(path, '/'); return base ? base + 1 : path; } /* * Reads symbols from src file (one per line) and append them to filterList. The * total number of added symbols is returned. * * Simple wildcard strings are also supported (e.g. mem*) */ size_t files_parseSymbolFilter(const char* srcFile, char*** filterList) { FILE* f = fopen(srcFile, "rb"); if (f == NULL) { PLOG_W("Couldn't open '%s' - R/O mode", srcFile); return 0; } char* lineptr = NULL; size_t symbolsRead = 0, n = 0; for (;;) { if (getline(&lineptr, &n, f) == -1) { break; } if (strlen(lineptr) < 3) { LOG_F("Input symbol '%s' too short (strlen < 3)", lineptr); symbolsRead = 0; break; } if ((*filterList = (char**)util_Realloc( *filterList, (symbolsRead + 1) * sizeof((*filterList)[0]))) == NULL) { PLOG_W("realloc failed (sz=%zu)", (symbolsRead + 1) * sizeof((*filterList)[0])); symbolsRead = 0; break; } (*filterList)[symbolsRead] = malloc(strlen(lineptr)); if (!(*filterList)[symbolsRead]) { PLOG_E("malloc(%zu) failed", strlen(lineptr)); symbolsRead = 0; break; } snprintf((*filterList)[symbolsRead], strlen(lineptr), "%s", lineptr); symbolsRead++; } LOG_I("%zu filter symbols added to list", symbolsRead); fclose(f); free(lineptr); return symbolsRead; } uint8_t* files_mapFile(const char* fileName, 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); return NULL; } struct stat st; if (fstat(*fd, &st) == -1) { PLOG_W("Couldn't stat() the '%s' file", fileName); 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); close(*fd); return NULL; } *fileSz = st.st_size; return buf; } /* 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) */ /* Avoid mapping the memory lazily */ #if defined(MAP_PREFAULT_READ) flag |= MAP_PREFAULT_READ; #endif /* defined(MAP_PREFAULT_READ) */ #if defined(MAP_POPULATE) flag |= MAP_POPULATE; #endif /* defined(MAP_POPULATE) */ 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; } void* files_mapSharedMem(size_t sz, int* fd, const char* name, bool nocore) { *fd = -1; #if 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) */ #endif /* defined(_HF_ARCH_LINUX) */ /* SHM_ANON is available with some *BSD OSes */ #if defined(SHM_ANON) if (*fd == -1) { if ((*fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600)) == -1) { PLOG_W("shm_open(SHM_ANON, O_RDWR|O_CLOEXEC, 0600)"); } } #endif /* defined(SHM_ANON) */ #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 | O_CLOEXEC, 0600)) == -1) { PLOG_W("shm_open('%s', O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, 0600)", tmpname); } else { shm_unlink(tmpname); } } #endif /* !defined(_HF_ARCH_DARWIN) && !defined(__ANDROID__) */ if (*fd == -1) { char template[PATH_MAX]; snprintf(template, sizeof(template), "/tmp/%s.XXXXXX", name); if ((*fd = mkostemp(template, O_CLOEXEC)) == -1) { PLOG_W("mkstemp('%s')", template); return NULL; } unlink(template); } if (TEMP_FAILURE_RETRY(ftruncate(*fd, sz)) == -1) { PLOG_W("ftruncate(%d, %zu)", *fd, sz); close(*fd); *fd = -1; return NULL; } 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); if (getsockname(sock, &addr, &addrlen) == -1) { PLOG_W("getsockname(sock=%d)", sock); return AF_UNSPEC; } return addr.sa_family; } const char* files_sockAddrToStr(const struct sockaddr* sa) { static __thread char str[4096]; if (sa->sa_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)sa; if (inet_ntop(sin->sin_family, &sin->sin_addr.s_addr, str, sizeof(str))) { util_ssnprintf(str, sizeof(str), "/%hd", ntohs(sin->sin_port)); } else { snprintf(str, sizeof(str), "IPv4 addr conversion failed"); } return str; } if (sa->sa_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; if (inet_ntop(sin6->sin6_family, sin6->sin6_addr.s6_addr, str, sizeof(str))) { util_ssnprintf(str, sizeof(str), "/%hd", ntohs(sin6->sin6_port)); } else { snprintf(str, sizeof(str), "IPv6 addr conversion failed"); } return str; } snprintf(str, sizeof(str), "Unsupported sockaddr family=%d", (int)sa->sa_family); return str; }