Index: include/sanitizer/common_interface_defs.h =================================================================== --- include/sanitizer/common_interface_defs.h +++ include/sanitizer/common_interface_defs.h @@ -24,13 +24,20 @@ #ifdef __cplusplus extern "C" { #endif + struct __sanitizer_sandbox_arguments { + int coverage_sandboxed; + int coverage_fd; + size_t coverage_max_block_size; + }; + // Tell the tools to write their reports to "path." instead of stderr. void __sanitizer_set_report_path(const char *path); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. - void __sanitizer_sandbox_on_notify(void *reserved); + void __sanitizer_sandbox_on_notify( + struct __sanitizer_sandbox_arguments *args); // This function is called by the tool when it has just finished reporting // an error. 'error_summary' is a one-line string that summarizes Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -183,7 +183,8 @@ bool StackSizeIsUnlimited(); void SetStackSizeLimitInBytes(uptr limit); void AdjustStackSize(void *attr); -void PrepareForSandboxing(); +void PrepareForSandboxing(struct __sanitizer_sandbox_arguments *args); +void CovPrepareForSandboxing(struct __sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); void InitTlsSize(); Index: lib/sanitizer_common/sanitizer_common.cc =================================================================== --- lib/sanitizer_common/sanitizer_common.cc +++ lib/sanitizer_common/sanitizer_common.cc @@ -300,9 +300,9 @@ } } -void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { - (void)reserved; - PrepareForSandboxing(); +void NOINLINE +__sanitizer_sandbox_on_notify(struct __sanitizer_sandbox_arguments *args) { + PrepareForSandboxing(args); if (sandboxing_callback) sandboxing_callback(); } Index: lib/sanitizer_common/sanitizer_coverage.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage.cc +++ lib/sanitizer_common/sanitizer_coverage.cc @@ -51,6 +51,10 @@ static uptr *pc_array; static atomic_uintptr_t pc_array_index; +static bool cov_sandboxed = false; +static int cov_fd = -1; +static uptr cov_max_block_size = 0; + namespace __sanitizer { // Simply add the pc into the vector under lock. If the function is called more @@ -71,8 +75,55 @@ return a < b; } +struct CovHeader { + int pid; + unsigned int module_length; + uptr data_size; +} __attribute__((packed)); + +static void CovWritePacked(int pid, const char *module, const void *blob, + uptr blob_size) { + CHECK_GE(cov_fd, 0); + unsigned module_length = internal_strlen(module); + CovHeader header; + header.pid = pid; + header.module_length = module_length; + header.data_size = blob_size; + + if (cov_max_block_size == 0) { + // Writing to a file. Just go ahead. + internal_write(cov_fd, &header, sizeof(header)); + internal_write(cov_fd, module, module_length); + internal_write(cov_fd, blob, blob_size); + } else { + // Writing to a socket. We want to split the blob into appropriately sized + // messages. + InternalScopedBuffer block(cov_max_block_size); + uptr header_size_with_module = sizeof(header) + module_length; + CHECK_LT(header_size_with_module, cov_max_block_size); + uptr max_payload_size = cov_max_block_size - header_size_with_module; + char *block_bytes = block.data(); + internal_memcpy(block_bytes, &header, sizeof(header)); + block_bytes += sizeof(header); + internal_memcpy(block_bytes, module, module_length); + block_bytes += module_length; + char *blob_bytes = (char *)blob; + while (blob_size > 0) { + uptr payload_size = Min(blob_size, max_payload_size); + blob_size -= payload_size; + internal_memcpy(block_bytes, blob_bytes, payload_size); + blob_bytes += payload_size; + // Note: this assumes proper alignment of block.data(). + ((CovHeader *)block.data())->data_size = payload_size; + internal_write(cov_fd, block.data(), + header_size_with_module + payload_size); + } + } +} + // Dump the coverage on disk. -void CovDump() { +static void CovDump() { + if (!common_flags()->coverage) return; #if !SANITIZER_WINDOWS if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; @@ -81,7 +132,7 @@ InternalMmapVector offsets(size); const uptr *vb = pc_array; const uptr *ve = vb + size; - MemoryMappingLayout proc_maps(/*cache_enabled*/false); + MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr mb, me, off, prot; InternalScopedBuffer module(4096); InternalScopedBuffer path(4096 * 2); @@ -102,22 +153,57 @@ offsets.push_back(static_cast(diff)); } char *module_name = StripModuleName(module.data()); - internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", - module_name, internal_getpid()); - InternalFree(module_name); - uptr fd = OpenFile(path.data(), true); - if (internal_iserror(fd)) { - Report(" CovDump: failed to open %s for writing\n", path.data()); + if (cov_sandboxed) { + CovWritePacked(internal_getpid(), module_name, offsets.data(), + offsets.size() * sizeof(u32)); + VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb); } else { - internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); - internal_close(fd); - VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); + // One file per module per process. + internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", + module_name, internal_getpid()); + uptr fd = OpenFile(path.data(), true); + if (internal_iserror(fd)) { + Report(" CovDump: failed to open %s for writing\n", path.data()); + } else { + internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); + internal_close(fd); + VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), + vb - old_vb); + } } + InternalFree(module_name); } } + if (cov_fd >= 0) + internal_close(cov_fd); #endif // !SANITIZER_WINDOWS } +static void OpenPackedFileForWriting() { + CHECK(cov_fd < 0); // NOLINT + InternalScopedBuffer path(1024); + internal_snprintf((char *)path.data(), path.size(), "%zd.sancov.packed", + internal_getpid()); + uptr fd = OpenFile(path.data(), true); + if (internal_iserror(fd)) { + Report(" Coverage: failed to open %s for writing\n", path.data()); + Die(); + } + cov_fd = fd; +} + +void CovPrepareForSandboxing(struct __sanitizer_sandbox_arguments *args) { + if (!args) return; + if (!common_flags()->coverage) return; + cov_sandboxed = args->coverage_sandboxed; + if (!cov_sandboxed) return; + cov_fd = args->coverage_fd; + cov_max_block_size = args->coverage_max_block_size; + if (cov_fd < 0) + // Pre-open the file now. The sandbox won't allow us to do it later. + OpenPackedFileForWriting(); +} + } // namespace __sanitizer extern "C" { Index: lib/sanitizer_common/sanitizer_internal_defs.h =================================================================== --- lib/sanitizer_common/sanitizer_internal_defs.h +++ lib/sanitizer_common/sanitizer_internal_defs.h @@ -91,11 +91,15 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_report_path(const char *path); - // Notify the tools that the sandbox is going to be turned on. The reserved - // parameter will be used in the future to hold a structure with functions - // that the tools may call to bypass the sandbox. - SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE - void __sanitizer_sandbox_on_notify(void *reserved); + struct __sanitizer_sandbox_arguments { + int coverage_sandboxed; + int coverage_fd; + __sanitizer::uptr coverage_max_block_size; + }; + + // Notify the tools that the sandbox is going to be turned on. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void + __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); // This function is called by the tool when it has just finished reporting // an error. 'error_summary' is a one-line string that summarizes Index: lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux.cc +++ lib/sanitizer_common/sanitizer_linux.cc @@ -390,7 +390,7 @@ } #endif // SANITIZER_GO -void PrepareForSandboxing() { +void PrepareForSandboxing(struct __sanitizer_sandbox_arguments *args) { // Some kinds of sandboxes may forbid filesystem access, so we won't be able // to read the file mappings from /proc/self/maps. Luckily, neither the // process will be able to load additional libraries, so it's fine to use the @@ -401,6 +401,7 @@ if (Symbolizer *sym = Symbolizer::GetOrNull()) sym->PrepareForSandboxing(); #endif + CovPrepareForSandboxing(args); } enum MutexState { Index: lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_mac.cc +++ lib/sanitizer_common/sanitizer_mac.cc @@ -191,7 +191,8 @@ UNIMPLEMENTED(); } -void PrepareForSandboxing() { +void PrepareForSandboxing(struct __sanitizer_sandbox_arguments *args) { + (void)args; // Nothing here for now. } Index: lib/sanitizer_common/sanitizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_win.cc +++ lib/sanitizer_common/sanitizer_win.cc @@ -192,7 +192,8 @@ UNIMPLEMENTED(); } -void PrepareForSandboxing() { +void PrepareForSandboxing(struct __sanitizer_sandbox_arguments *args) { + (void)args; // Nothing here for now. } Index: lib/sanitizer_common/scripts/sancov.py =================================================================== --- lib/sanitizer_common/scripts/sancov.py +++ lib/sanitizer_common/scripts/sancov.py @@ -4,6 +4,7 @@ # We need to merge these integers into a set and then # either print them (as hex) or dump them into another file. import array +import struct import sys prog_name = ""; @@ -11,16 +12,16 @@ def Usage(): print >> sys.stderr, "Usage: \n" + \ " " + prog_name + " merge file1 [file2 ...] > output\n" \ - " " + prog_name + " print file1 [file2 ...]\n" + " " + prog_name + " print file1 [file2 ...]\n" \ + " " + prog_name + " unpack file1 [file2 ...]\n" exit(1) def ReadOneFile(path): - f = open(path, mode="rb") - f.seek(0, 2) - size = f.tell() - f.seek(0, 0) - s = set(array.array('I', f.read(size))) - f.close() + with open(path, mode="rb") as f: + f.seek(0, 2) + size = f.tell() + f.seek(0, 0) + s = set(array.array('I', f.read(size))) print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) return s @@ -44,6 +45,35 @@ a = array.array('I', s) a.tofile(sys.stdout) +def UnpackOneFile(path): + + with open(path, mode="rb") as f: + print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) + while True: + header = f.read(16) + if not header: return + if len(header) < 16: + break + pid, module_length, blob_size = struct.unpack('iIL', header) + module = f.read(module_length) + blob = f.read(blob_size) + assert(len(module) == module_length) + assert(len(blob) == blob_size) + extracted_file = "%s.%d.sancov" % (module, pid) + print >> sys.stderr, "%s: extracting %s" % \ + (prog_name, extracted_file) + with open(extracted_file, 'wb') as f2: + # The packed file may contain multiple blobs for the same pid/module + # pair. Append to the end of the file instead of overwriting. + f2.seek(0, 2) + f2.write(blob) + # fail + raise Exception('Error reading file %s' % path) + +def Unpack(files): + for f in files: + UnpackOneFile(f) + if __name__ == '__main__': prog_name = sys.argv[0] if len(sys.argv) <= 2: @@ -52,5 +82,8 @@ PrintFiles(sys.argv[2:]) elif sys.argv[1] == "merge": MergeAndPrint(sys.argv[2:]) + elif sys.argv[1] == "unpack": + Unpack(sys.argv[2:]) else: Usage() +