From 32a02a9c6bad489ce7c02f3f0b306a8fc1e67fd5 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Tue, 18 Oct 2022 15:20:49 -0700 Subject: [PATCH] [Debuginfod] DEBUGINFOD_HEADERS_FILE environment variable This change adds a DEBUGINFOD_HEADERS_FILE environment variable provides a file containing HTTP headers to attach to outgoing HTTP requests, one per line. This allows a file permissioned with OS access control mechanisms to supply bearer credentials for Debuginfod requests. This matches the mechanism recently added to elfutils' libdebuginfod. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D136303 --- llvm/include/llvm/Debuginfod/HTTPClient.h | 1 + llvm/lib/Debuginfod/Debuginfod.cpp | 41 ++++++++++++++++++++++ llvm/lib/Debuginfod/HTTPClient.cpp | 6 ++++ .../llvm-debuginfod-find/Inputs/capture_req.py | 23 ++++++++++++ .../test/tools/llvm-debuginfod-find/Inputs/headers | 12 +++++++ llvm/test/tools/llvm-debuginfod-find/headers.test | 27 ++++++++++++++ 6 files changed, 110 insertions(+) create mode 100644 llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py create mode 100644 llvm/test/tools/llvm-debuginfod-find/Inputs/headers create mode 100644 llvm/test/tools/llvm-debuginfod-find/headers.test diff --git a/llvm/include/llvm/Debuginfod/HTTPClient.h b/llvm/include/llvm/Debuginfod/HTTPClient.h index 6c94961..1c9f719 100644 --- a/llvm/include/llvm/Debuginfod/HTTPClient.h +++ b/llvm/include/llvm/Debuginfod/HTTPClient.h @@ -27,6 +27,7 @@ enum class HTTPMethod { GET }; /// A stateless description of an outbound HTTP request. struct HTTPRequest { SmallString<128> Url; + SmallVector Headers; HTTPMethod Method = HTTPMethod::GET; bool FollowRedirects = true; HTTPRequest(StringRef Url); diff --git a/llvm/lib/Debuginfod/Debuginfod.cpp b/llvm/lib/Debuginfod/Debuginfod.cpp index ee5cc51..f20b5bc 100644 --- a/llvm/lib/Debuginfod/Debuginfod.cpp +++ b/llvm/lib/Debuginfod/Debuginfod.cpp @@ -22,6 +22,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" @@ -34,6 +35,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/xxhash.h" @@ -169,6 +171,44 @@ Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) { return Error::success(); } +// An over-accepting simplification of the HTTP RFC 7230 spec. +static bool isHeader(StringRef S) { + StringRef Name; + StringRef Value; + std::tie(Name, Value) = S.split(':'); + if (Name.empty() || Value.empty()) + return false; + return all_of(Name, [](char C) { return llvm::isPrint(C) && C != ' '; }) && + all_of(Value, [](char C) { return llvm::isPrint(C) || C == '\t'; }); +} + +static SmallVector getHeaders() { + const char *Filename = getenv("DEBUGINFOD_HEADERS_FILE"); + if (!Filename) + return {}; + ErrorOr> HeadersFile = + MemoryBuffer::getFile(Filename, /*IsText=*/true); + if (!HeadersFile) + return {}; + + SmallVector Headers; + uint64_t LineNumber = 0; + for (StringRef Line : llvm::split((*HeadersFile)->getBuffer(), '\n')) { + LineNumber++; + if (!isHeader(Line)) { + if (!all_of(Line, llvm::isSpace)) + WithColor::warning() + << "could not parse debuginfod header: " << Filename << ':' + << LineNumber << '\n'; + continue; + } + if (Line.back() == '\r') + Line = Line.drop_back(); + Headers.emplace_back(Line); + } + return Headers; +} + Expected getCachedOrDownloadArtifact( StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath, ArrayRef DebuginfodUrls, std::chrono::milliseconds Timeout) { @@ -214,6 +254,7 @@ Expected getCachedOrDownloadArtifact( StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); }, Client); HTTPRequest Request(ArtifactUrl); + Request.Headers = getHeaders(); Error Err = Client.perform(Request, Handler); if (Err) return std::move(Err); diff --git a/llvm/lib/Debuginfod/HTTPClient.cpp b/llvm/lib/Debuginfod/HTTPClient.cpp index 3376eaa..f9201e4 100644 --- a/llvm/lib/Debuginfod/HTTPClient.cpp +++ b/llvm/lib/Debuginfod/HTTPClient.cpp @@ -111,9 +111,15 @@ Error HTTPClient::perform(const HTTPRequest &Request, curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str()); curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects); + curl_slist *Headers = nullptr; + for (const std::string &Header : Request.Headers) + Headers = curl_slist_append(Headers, Header.c_str()); + curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers); + CurlHTTPRequest CurlRequest(Handler); curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest); CURLcode CurlRes = curl_easy_perform(Curl); + curl_slist_free_all(Headers); if (CurlRes != CURLE_OK) return joinErrors(std::move(CurlRequest.ErrorState), createStringError(errc::io_error, diff --git a/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py b/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py new file mode 100644 index 0000000..56fa2d0 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/Inputs/capture_req.py @@ -0,0 +1,23 @@ +import http.server +import os +import subprocess +import sys +import threading + +class TrivialHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(501) + + def log_request(self, *args, **kwargs): + print(self.requestline) + print(self.headers) + +httpd = http.server.HTTPServer(('', 0), TrivialHandler) +port = httpd.socket.getsockname()[1] + +try: + t = threading.Thread(target=httpd.serve_forever).start() + os.environ['DEBUGINFOD_URLS'] =f'http://localhost:{port}' + subprocess.run(sys.argv[1:], capture_output = True) +finally: + httpd.shutdown() diff --git a/llvm/test/tools/llvm-debuginfod-find/Inputs/headers b/llvm/test/tools/llvm-debuginfod-find/Inputs/headers new file mode 100644 index 0000000..9f66ac2 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/Inputs/headers @@ -0,0 +1,12 @@ + + +A: +:A +: +A :B + +A:B +C: D +E:F +hi!$: j k + diff --git a/llvm/test/tools/llvm-debuginfod-find/headers.test b/llvm/test/tools/llvm-debuginfod-find/headers.test new file mode 100644 index 0000000..6fe814d --- /dev/null +++ b/llvm/test/tools/llvm-debuginfod-find/headers.test @@ -0,0 +1,27 @@ +REQUIRES: curl + +RUN: %python %S/Inputs/capture_req.py llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix NO-HEADERS %s +RUN: DEBUGINFOD_HEADERS_FILE=bad %python %S/Inputs/capture_req.py \ +RUN: llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix NO-HEADERS %s +RUN: DEBUGINFOD_HEADERS_FILE=%S/Inputs/headers %python %S/Inputs/capture_req.py \ +RUN: llvm-debuginfod-find --debuginfo 0 \ +RUN: | FileCheck --check-prefix HEADERS %s +RUN: DEBUGINFOD_HEADERS_FILE=%S/Inputs/headers DEBUGINFOD_URLS=fake not llvm-debuginfod-find --debuginfo 0 2>&1 \ +RUN: | FileCheck --check-prefix ERR -DHEADER_FILE=%S/Inputs/headers %s + +NO-HEADERS: Accept: */* +NO-HEADERS-NOT: {{.}} + +HEADERS: Accept: */* +HEADERS-NEXT: A: B +HEADERS-NEXT: C: D +HEADERS-NEXT: E: F +HEADERS-NEXT: hi!$: j k +HEADERS-NOT: {{.}} + +ERR: warning: could not parse debuginfod header: [[HEADER_FILE]]:3 +ERR-NEXT: warning: could not parse debuginfod header: [[HEADER_FILE]]:4 +ERR-NEXT: warning: could not parse debuginfod header: [[HEADER_FILE]]:5 +ERR-NEXT: warning: could not parse debuginfod header: [[HEADER_FILE]]:6 -- 2.7.4