diff --git a/src/libutil/include/nix/util/strings.hh b/src/libutil/include/nix/util/strings.hh index da6decc31..da8409e6a 100644 --- a/src/libutil/include/nix/util/strings.hh +++ b/src/libutil/include/nix/util/strings.hh @@ -166,4 +166,10 @@ public: } }; +/** + * Check that the string does not contain any NUL bytes and return c_str(). + * @throws Error if str contains '\0' bytes. + */ +const char * requireCString(const std::string & str); + } // namespace nix diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index c0c3d6602..91a0f73ec 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -5,6 +5,7 @@ #include "nix/util/strings-inline.hh" #include "nix/util/os-string.hh" #include "nix/util/error.hh" +#include "nix/util/util.hh" namespace nix { @@ -152,4 +153,14 @@ std::string optionalBracket(std::string_view prefix, std::string_view content, s return result; } +const char * requireCString(const std::string & s) +{ + if (std::memchr(s.data(), '\0', s.size())) [[unlikely]] { + using namespace std::string_view_literals; + auto str = replaceStrings(s, "\0"sv, "␀"sv); + throw Error("string '%s' with null (\\0) bytes used where it's not allowed", str); + } + return s.c_str(); +} + } // namespace nix diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 72042901c..0a8b64528 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -327,8 +327,11 @@ Path renderUrlPathEnsureLegal(const std::vector & urlPath) /* This is only really valid for UNIX. Windows has more restrictions. */ if (comp.contains('/')) throw BadURL("URL path component '%s' contains '/', which is not allowed in file names", comp); - if (comp.contains(char(0))) - throw BadURL("URL path component '%s' contains NUL byte which is not allowed", comp); + if (comp.contains(char(0))) { + using namespace std::string_view_literals; + auto str = replaceStrings(comp, "\0"sv, "␀"sv); + throw BadURL("URL path component '%s' contains NUL byte which is not allowed", str); + } } return concatStringsSep("/", urlPath);