From fea4fa0095668e392aa3333450e93afc1784762b Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Tue, 14 Mar 2023 14:36:18 +0100 Subject: send payloads as duplicated handles (#240) * send payloads as duplicated handles if requestor provides process id and allows local file references. * linux/macos fixes * tests * fix access rights when duplicating handle * fix closing of duplicated handles on error * cleanup * changelog --- zenhttp/httpshared.cpp | 154 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 24 deletions(-) (limited to 'zenhttp/httpshared.cpp') diff --git a/zenhttp/httpshared.cpp b/zenhttp/httpshared.cpp index b6346413f..7aade56d2 100644 --- a/zenhttp/httpshared.cpp +++ b/zenhttp/httpshared.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -23,21 +24,23 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { +const std::string_view HandlePrefix(":?#:"); + std::vector -FormatPackageMessage(const CbPackage& Data) +FormatPackageMessage(const CbPackage& Data, int TargetProcessPid) { - return FormatPackageMessage(Data, FormatFlags::kDefault); + return FormatPackageMessage(Data, FormatFlags::kDefault, TargetProcessPid); } CompositeBuffer -FormatPackageMessageBuffer(const CbPackage& Data) +FormatPackageMessageBuffer(const CbPackage& Data, int TargetProcessPid) { - return FormatPackageMessageBuffer(Data, FormatFlags::kDefault); + return FormatPackageMessageBuffer(Data, FormatFlags::kDefault, TargetProcessPid); } CompositeBuffer -FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags) +FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid) { - std::vector Message = FormatPackageMessage(Data, Flags); + std::vector Message = FormatPackageMessage(Data, Flags, TargetProcessPid); std::vector Buffers; @@ -50,8 +53,44 @@ FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags) } std::vector -FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) +FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid) { + void* TargetProcessHandle = nullptr; +#if ZEN_PLATFORM_WINDOWS + std::vector DuplicatedHandles; + auto _ = MakeGuard([&DuplicatedHandles, &TargetProcessHandle]() { + if (TargetProcessHandle == nullptr) + { + return; + } + + for (HANDLE DuplicatedHandle : DuplicatedHandles) + { + HANDLE ClosingHandle; + if (::DuplicateHandle((HANDLE)TargetProcessHandle, + DuplicatedHandle, + GetCurrentProcess(), + &ClosingHandle, + 0, + FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == TRUE) + { + ::CloseHandle(ClosingHandle); + } + } + ::CloseHandle((HANDLE)TargetProcessHandle); + TargetProcessHandle = nullptr; + }); + + if (EnumHasAllFlags(Flags, FormatFlags::kAllowLocalReferences) && TargetProcessPid != 0) + { + TargetProcessHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, TargetProcessPid); + } +#else + ZEN_UNUSED(TargetProcessPid); + void* DuplicatedHandles = nullptr; +#endif // ZEN_PLATFORM_WINDOWS + const std::span& Attachments = Data.GetAttachments(); std::vector ResponseBuffers; @@ -99,10 +138,11 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) tsl::robin_map FileNameMap; - auto IsLocalRef = [&FileNameMap](const CompositeBuffer& AttachmentBinary, - bool DenyPartialLocalReferences, - CbAttachmentReferenceHeader& LocalRef, - std::string& Path8) -> bool { + auto IsLocalRef = [&FileNameMap, &DuplicatedHandles](const CompositeBuffer& AttachmentBinary, + bool DenyPartialLocalReferences, + void* TargetProcessHandle, + CbAttachmentReferenceHeader& LocalRef, + std::string& Path8) -> bool { const SharedBuffer& Segment = AttachmentBinary.GetSegments().front(); IoBufferFileReference Ref; const IoBuffer& SegmentBuffer = Segment.AsIoBuffer(); @@ -123,9 +163,36 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) } else { - ExtendablePathBuilder<256> LocalRefFile; - LocalRefFile.Append(std::filesystem::absolute(PathFromHandle(Ref.FileHandle))); - Path8 = LocalRefFile.ToUtf8(); + bool UseFilePath = true; +#if ZEN_PLATFORM_WINDOWS + if (TargetProcessHandle != nullptr) + { + HANDLE TargetHandle = INVALID_HANDLE_VALUE; + BOOL OK = ::DuplicateHandle(GetCurrentProcess(), + Ref.FileHandle, + (HANDLE)TargetProcessHandle, + &TargetHandle, + FILE_GENERIC_READ, + FALSE, + 0); + if (OK) + { + DuplicatedHandles.push_back(TargetHandle); + Path8 = fmt::format("{}{}", HandlePrefix, reinterpret_cast(TargetHandle)); + UseFilePath = false; + } + } +#else // ZEN_PLATFORM_WINDOWS + ZEN_UNUSED(TargetProcessHandle); + // Not supported on Linux/Mac. Could potentially use pidfd_getfd() but that requires a fairly new Linux kernel/includes and to + // deal with acceess rights etc. +#endif // ZEN_PLATFORM_WINDOWS + if (UseFilePath) + { + ExtendablePathBuilder<256> LocalRefFile; + LocalRefFile.Append(std::filesystem::absolute(PathFromHandle(Ref.FileHandle))); + Path8 = LocalRefFile.ToUtf8(); + } FileNameMap.insert_or_assign(Ref.FileHandle, Path8); } @@ -159,14 +226,18 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) if (MarshalByLocalRef) { - MarshalByLocalRef = IsLocalRef(Compressed, DenyPartialLocalReferences, LocalRef, Path8); + MarshalByLocalRef = IsLocalRef(Compressed, DenyPartialLocalReferences, TargetProcessHandle, LocalRef, Path8); } if (MarshalByLocalRef) { const bool IsCompressed = true; + bool IsHandle = false; +#if ZEN_PLATFORM_WINDOWS + IsHandle = Path8.starts_with(HandlePrefix); +#endif MarshalLocal(Path8, LocalRef, AttachmentHash, IsCompressed); - ZEN_DEBUG("Marshalled '{}' as file of {} bytes", Path8, Compressed.GetSize()); + ZEN_DEBUG("Marshalled '{}' as file {} of {} bytes", Path8, IsHandle ? "handle" : "path", Compressed.GetSize()); } else { @@ -201,14 +272,18 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) if (MarshalByLocalRef) { - MarshalByLocalRef = IsLocalRef(AttachmentBinary, DenyPartialLocalReferences, LocalRef, Path8); + MarshalByLocalRef = IsLocalRef(AttachmentBinary, DenyPartialLocalReferences, TargetProcessHandle, LocalRef, Path8); } if (MarshalByLocalRef) { const bool IsCompressed = false; + bool IsHandle = false; +#if ZEN_PLATFORM_WINDOWS + IsHandle = Path8.starts_with(HandlePrefix); +#endif MarshalLocal(Path8, LocalRef, AttachmentHash, IsCompressed); - ZEN_DEBUG("Marshalled '{}' as file of {} bytes", Path8, AttachmentBinary.GetSize()); + ZEN_DEBUG("Marshalled '{}' as file {} of {} bytes", Path8, IsHandle ? "handle" : "path", AttachmentBinary.GetSize()); } else { @@ -226,6 +301,9 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags) } } FileNameMap.clear(); +#if ZEN_PLATFORM_WINDOWS + DuplicatedHandles.clear(); +#endif // ZEN_PLATFORM_WINDOWS return ResponseBuffers; } @@ -282,6 +360,8 @@ ParsePackageMessage(IoBuffer Payload, std::function PartialFileBuffers; + // TODO: Throwing before this loop completes could result in leaking handles as we might not have picked up all the handles in the + // message for (uint32_t i = 0; i < ChunkCount; ++i) { const CbAttachmentEntry& Entry = AttachmentEntries[i]; @@ -297,20 +377,46 @@ ParsePackageMessage(IoBuffer Payload, std::function= sizeof(CbAttachmentReferenceHeader)); const CbAttachmentReferenceHeader* AttachRefHdr = AttachmentBuffer.Data(); - const char8_t* PathPointer = reinterpret_cast(AttachRefHdr + 1); + const char* PathPointer = reinterpret_cast(AttachRefHdr + 1); ZEN_ASSERT(AttachmentBuffer.Size() >= (sizeof(CbAttachmentReferenceHeader) + AttachRefHdr->AbsolutePathLength)); - - std::filesystem::path Path{std::u8string_view(PathPointer, AttachRefHdr->AbsolutePathLength)}; + std::string_view PathView(PathPointer, AttachRefHdr->AbsolutePathLength); IoBuffer FullFileBuffer; + + std::filesystem::path Path(Utf8ToWide(PathView)); if (auto It = PartialFileBuffers.find(Path.string()); It != PartialFileBuffers.end()) { FullFileBuffer = It->second; } else { - FullFileBuffer = PartialFileBuffers.insert_or_assign(Path.string(), IoBufferBuilder::MakeFromFile(Path)).first->second; + if (PathView.starts_with(HandlePrefix)) + { +#if ZEN_PLATFORM_WINDOWS + std::string_view HandleString(PathView.substr(HandlePrefix.length())); + std::optional HandleNumber(ParseInt(HandleString)); + if (HandleNumber.has_value()) + { + HANDLE FileHandle = HANDLE(HandleNumber.value()); + ULARGE_INTEGER liFileSize; + liFileSize.LowPart = ::GetFileSize(FileHandle, &liFileSize.HighPart); + if (liFileSize.LowPart != INVALID_FILE_SIZE) + { + FullFileBuffer = IoBuffer(IoBuffer::File, (void*)FileHandle, 0, uint64_t(liFileSize.QuadPart)); + PartialFileBuffers.insert_or_assign(Path.string(), FullFileBuffer); + } + } +#else // ZEN_PLATFORM_WINDOWS + // Not supported on Linux/Mac. Could potentially use pidfd_getfd() but that requires a fairly new Linux kernel/includes + // and to deal with acceess rights etc. + ZEN_ASSERT(false); +#endif // ZEN_PLATFORM_WINDOWS + } + else + { + FullFileBuffer = PartialFileBuffers.insert_or_assign(Path.string(), IoBufferBuilder::MakeFromFile(Path)).first->second; + } } if (!FullFileBuffer) @@ -318,7 +424,7 @@ ParsePackageMessage(IoBuffer Payload, std::functionPayloadByteOffset, AttachRefHdr->PayloadByteSize)); } @@ -332,7 +438,7 @@ ParsePackageMessage(IoBuffer Payload, std::functionPayloadByteOffset, AttachRefHdr->PayloadByteSize)); } -- cgit v1.2.3