From 0bc0f3f04b1c058fe8495bd76c962ec9d3c901dd Mon Sep 17 00:00:00 2001 From: Noah Sanci Date: Fri, 16 Jul 2021 15:16:20 -0400 Subject: [PATCH] debuginfod: PR28034 - client-side %-escape url characters When requesting some source files, some URL-inconvenient chars sometimes pop up. Example from f33 libstdc++: /buildid/44d8485cb75512c2ca5c8f70afbd475cae30af4f/source/usr/src/debug/ gcc-10.3.1-1.fc33.x86_64/obj-x86_64-redhat-linux/x86_64-redhat-linux/ libstdc++-v3/src/c++11/../../../../../libstdc++-v3/src/c++11/ condition_variable.cc As this URL is passed into debuginfod's handler_cb, it appears that the + signs are helpfully unescaped to spaces by libmicrohttpd, which 'course breaks everything. In order to ensure the server properly parses urls such as this one, %-escape characters on the client side so that the correct url is preserved and properly processed on the server side. https://sourceware.org/bugzilla/show_bug.cgi?id=28034 Signed-off-by: Noah Sanci --- debuginfod/debuginfod-client.c | 52 ++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/debuginfod/debuginfod-client.c b/debuginfod/debuginfod-client.c index 7d4b220f..ed5943f3 100644 --- a/debuginfod/debuginfod-client.c +++ b/debuginfod/debuginfod-client.c @@ -904,16 +904,52 @@ debuginfod_query_server (debuginfod_client *c, if (filename) /* must start with / */ { /* PR28034 escape characters in completed url to %hh format. */ - char *escaped_string; - escaped_string = curl_easy_escape(data[i].handle, filename, 0); - if (!escaped_string) + char escaped_string[PATH_MAX] = {'\0'}; + char *loc = (char *) filename; + char *loc2; + char *tmp; + for(size_t j = 0; j < strlen(filename); ++j) { - rc = -ENOMEM; - goto out2; + loc2 = strstr(loc, "/"); + // If the first character is a '/' + if ( (unsigned long) loc2 - (unsigned long) loc <= 0) + { + strcat(escaped_string, "/"); + loc ++; + } + // If we have reached the end of the path, there won't be a / + // but we still need to process one more string (the file's actual name) + else if (loc2 == NULL) + { + tmp = curl_easy_escape(data[i].handle, loc, 0); + if (!tmp) + { + rc = -ENOMEM; + goto out1; + } + strcat(escaped_string, tmp); + curl_free(tmp); + break; + } + // The default case, when there is a string surrounded by '/'s + else + { + // The third argument to curl_easy_escape isolated the string between the + // '/'s + tmp = curl_easy_escape(data[i].handle, loc, (unsigned long) loc2 - (unsigned long)loc); + if (!tmp) + { + rc = -ENOMEM; + goto out1; + } + strcat(escaped_string, tmp); + strcat(escaped_string, "/"); + curl_free(tmp); + loc = loc2+1; + } } - snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url, - build_id_bytes, type, escaped_string); - curl_free(escaped_string); + snprintf(data[i].url, PATH_MAX, "%s/%s/%s%s", server_url, + build_id_bytes, type, escaped_string); } else snprintf(data[i].url, PATH_MAX, "%s/%s/%s", server_url, build_id_bytes, type); -- 2.31.1