urlDecode() silently produces NUL bytes on malformed input
Author: CarstenGrohmannCreated Feb 23, 2026Updated Feb 25, 2026
urlDecode() converts invalid %XX sequences (e.g. %GG) into NUL bytes instead of rejecting or preserving them. A truncated % at end of string is silently dropped. Malformed input can originate from buggy S3 clients, tampered object metadata, or an untrusted S3 backend.
Root Cause:
The hex-decoding ternary in src/string_util.cpp:206,211 falls through to 0x00 for non-hex characters. A truncated % at end of string causes a silent break, dropping the incomplete sequence.
Reproduction:
urlDecode("%GG"); // returns "\x00", expected: error or literal "%GG"
urlDecode("a%2"); // returns "a", silently drops "%2"
urlDecode("a%"); // returns "a", silently drops "%"Proposed Fix:
Two options — feedback welcome:
- Replace with
curl_easy_unescape()— libcurl is already a required dependency and passes invalid sequences through literally. Needs a CURL* handle from the existing. - Fix
urlDecode()in place — add proper error signaling (bool return) and pass invalid%XXthrough literally. No handle needed, but keeps custom code to maintain.
Which approach would you prefer?
Source: s3fs-fuse/s3fs-fuse