forked from lix-project/lix
Merge pull request #9673 from pennae/drv-parse-opts
optimize derivation parsing
(cherry picked from commit 3511430902941f0f26dc71313a54bb5096f57305)
Change-Id: I00f76dcd464a5811944613731501af504b6e8c29
This commit is contained in:
parent
96f1a404d0
commit
cd326a2aa4
6
doc/manual/rl-next/drv-string-parse-hang.md
Normal file
6
doc/manual/rl-next/drv-string-parse-hang.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
synopsis: Fix handling of truncated `.drv` files.
|
||||||
|
prs: 9673
|
||||||
|
---
|
||||||
|
|
||||||
|
Previously a `.drv` that was truncated in the middle of a string would case nix to enter an infinite loop, eventually exhausting all memory and crashing.
|
|
@ -2,6 +2,7 @@
|
||||||
#include "downstream-placeholder.hh"
|
#include "downstream-placeholder.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "types.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "split.hh"
|
#include "split.hh"
|
||||||
#include "common-protocol.hh"
|
#include "common-protocol.hh"
|
||||||
|
@ -149,31 +150,78 @@ StorePath writeDerivation(Store & store,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read string `s' from stream `str'. */
|
namespace {
|
||||||
static void expect(std::istream & str, std::string_view s)
|
/**
|
||||||
{
|
* This mimics std::istream to some extent. We use this much smaller implementation
|
||||||
for (auto & c : s) {
|
* instead of plain istreams because the sentry object overhead is too high.
|
||||||
if (str.get() != c)
|
*/
|
||||||
throw FormatError("expected string '%1%'", s);
|
struct StringViewStream {
|
||||||
|
std::string_view remaining;
|
||||||
|
|
||||||
|
int peek() const {
|
||||||
|
return remaining.empty() ? EOF : remaining[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get() {
|
||||||
|
if (remaining.empty()) return EOF;
|
||||||
|
char c = remaining[0];
|
||||||
|
remaining.remove_prefix(1);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr struct Escapes {
|
||||||
|
char map[256];
|
||||||
|
constexpr Escapes() {
|
||||||
|
for (int i = 0; i < 256; i++) map[i] = (char) (unsigned char) i;
|
||||||
|
map[(int) (unsigned char) 'n'] = '\n';
|
||||||
|
map[(int) (unsigned char) 'r'] = '\r';
|
||||||
|
map[(int) (unsigned char) 't'] = '\t';
|
||||||
|
}
|
||||||
|
char operator[](char c) const { return map[(unsigned char) c]; }
|
||||||
|
} escapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read string `s' from stream `str'. */
|
||||||
|
static void expect(StringViewStream & str, std::string_view s)
|
||||||
|
{
|
||||||
|
if (!str.remaining.starts_with(s))
|
||||||
|
throw FormatError("expected string '%1%'", s);
|
||||||
|
str.remaining.remove_prefix(s.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read a C-style string from stream `str'. */
|
/* Read a C-style string from stream `str'. */
|
||||||
static std::string parseString(std::istream & str)
|
static BackedStringView parseString(StringViewStream & str)
|
||||||
{
|
{
|
||||||
std::string res;
|
|
||||||
expect(str, "\"");
|
expect(str, "\"");
|
||||||
int c;
|
auto c = str.remaining.begin(), end = str.remaining.end();
|
||||||
while ((c = str.get()) != '"')
|
bool escaped = false;
|
||||||
if (c == '\\') {
|
for (; c != end && *c != '"'; c++) {
|
||||||
c = str.get();
|
if (*c == '\\') {
|
||||||
if (c == 'n') res += '\n';
|
c++;
|
||||||
else if (c == 'r') res += '\r';
|
if (c == end)
|
||||||
else if (c == 't') res += '\t';
|
throw FormatError("unterminated string in derivation");
|
||||||
else res += c;
|
escaped = true;
|
||||||
}
|
}
|
||||||
else res += c;
|
}
|
||||||
|
|
||||||
|
const auto contentLen = c - str.remaining.begin();
|
||||||
|
const auto content = str.remaining.substr(0, contentLen);
|
||||||
|
str.remaining.remove_prefix(contentLen + 1);
|
||||||
|
|
||||||
|
if (!escaped)
|
||||||
|
return content;
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
res.reserve(content.size());
|
||||||
|
for (c = content.begin(), end = content.end(); c != end; c++)
|
||||||
|
if (*c == '\\') {
|
||||||
|
c++;
|
||||||
|
res += escapes[*c];
|
||||||
|
}
|
||||||
|
else res += *c;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,15 +230,15 @@ static void validatePath(std::string_view s) {
|
||||||
throw FormatError("bad path '%1%' in derivation", s);
|
throw FormatError("bad path '%1%' in derivation", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Path parsePath(std::istream & str)
|
static BackedStringView parsePath(StringViewStream & str)
|
||||||
{
|
{
|
||||||
auto s = parseString(str);
|
auto s = parseString(str);
|
||||||
validatePath(s);
|
validatePath(*s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool endOfList(std::istream & str)
|
static bool endOfList(StringViewStream & str)
|
||||||
{
|
{
|
||||||
if (str.peek() == ',') {
|
if (str.peek() == ',') {
|
||||||
str.get();
|
str.get();
|
||||||
|
@ -204,12 +252,12 @@ static bool endOfList(std::istream & str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static StringSet parseStrings(std::istream & str, bool arePaths)
|
static StringSet parseStrings(StringViewStream & str, bool arePaths)
|
||||||
{
|
{
|
||||||
StringSet res;
|
StringSet res;
|
||||||
expect(str, "[");
|
expect(str, "[");
|
||||||
while (!endOfList(str))
|
while (!endOfList(str))
|
||||||
res.insert(arePaths ? parsePath(str) : parseString(str));
|
res.insert((arePaths ? parsePath(str) : parseString(str)).toOwned());
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +310,7 @@ static DerivationOutput parseDerivationOutput(
|
||||||
}
|
}
|
||||||
|
|
||||||
static DerivationOutput parseDerivationOutput(
|
static DerivationOutput parseDerivationOutput(
|
||||||
const Store & store, std::istringstream & str,
|
const Store & store, StringViewStream & str,
|
||||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings)
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings)
|
||||||
{
|
{
|
||||||
expect(str, ","); const auto pathS = parseString(str);
|
expect(str, ","); const auto pathS = parseString(str);
|
||||||
|
@ -270,7 +318,7 @@ static DerivationOutput parseDerivationOutput(
|
||||||
expect(str, ","); const auto hash = parseString(str);
|
expect(str, ","); const auto hash = parseString(str);
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
|
|
||||||
return parseDerivationOutput(store, pathS, hashAlgo, hash, xpSettings);
|
return parseDerivationOutput(store, *pathS, *hashAlgo, *hash, xpSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -292,7 +340,7 @@ enum struct DerivationATermVersion {
|
||||||
|
|
||||||
static DerivedPathMap<StringSet>::ChildNode parseDerivedPathMapNode(
|
static DerivedPathMap<StringSet>::ChildNode parseDerivedPathMapNode(
|
||||||
const Store & store,
|
const Store & store,
|
||||||
std::istringstream & str,
|
StringViewStream & str,
|
||||||
DerivationATermVersion version)
|
DerivationATermVersion version)
|
||||||
{
|
{
|
||||||
DerivedPathMap<StringSet>::ChildNode node;
|
DerivedPathMap<StringSet>::ChildNode node;
|
||||||
|
@ -318,7 +366,7 @@ static DerivedPathMap<StringSet>::ChildNode parseDerivedPathMapNode(
|
||||||
expect(str, ",[");
|
expect(str, ",[");
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "(");
|
expect(str, "(");
|
||||||
auto outputName = parseString(str);
|
auto outputName = parseString(str).toOwned();
|
||||||
expect(str, ",");
|
expect(str, ",");
|
||||||
node.childMap.insert_or_assign(outputName, parseDerivedPathMapNode(store, str, version));
|
node.childMap.insert_or_assign(outputName, parseDerivedPathMapNode(store, str, version));
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
|
@ -344,7 +392,7 @@ Derivation parseDerivation(
|
||||||
Derivation drv;
|
Derivation drv;
|
||||||
drv.name = name;
|
drv.name = name;
|
||||||
|
|
||||||
std::istringstream str(std::move(s));
|
StringViewStream str{s};
|
||||||
expect(str, "D");
|
expect(str, "D");
|
||||||
DerivationATermVersion version;
|
DerivationATermVersion version;
|
||||||
switch (str.peek()) {
|
switch (str.peek()) {
|
||||||
|
@ -355,12 +403,12 @@ Derivation parseDerivation(
|
||||||
case 'r': {
|
case 'r': {
|
||||||
expect(str, "rvWithVersion(");
|
expect(str, "rvWithVersion(");
|
||||||
auto versionS = parseString(str);
|
auto versionS = parseString(str);
|
||||||
if (versionS == "xp-dyn-drv") {
|
if (*versionS == "xp-dyn-drv") {
|
||||||
// Only verison we have so far
|
// Only verison we have so far
|
||||||
version = DerivationATermVersion::DynamicDerivations;
|
version = DerivationATermVersion::DynamicDerivations;
|
||||||
xpSettings.require(Xp::DynamicDerivations);
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
} else {
|
} else {
|
||||||
throw FormatError("Unknown derivation ATerm format version '%s'", versionS);
|
throw FormatError("Unknown derivation ATerm format version '%s'", *versionS);
|
||||||
}
|
}
|
||||||
expect(str, ",");
|
expect(str, ",");
|
||||||
break;
|
break;
|
||||||
|
@ -372,7 +420,7 @@ Derivation parseDerivation(
|
||||||
/* Parse the list of outputs. */
|
/* Parse the list of outputs. */
|
||||||
expect(str, "[");
|
expect(str, "[");
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "("); std::string id = parseString(str);
|
expect(str, "("); std::string id = parseString(str).toOwned();
|
||||||
auto output = parseDerivationOutput(store, str, xpSettings);
|
auto output = parseDerivationOutput(store, str, xpSettings);
|
||||||
drv.outputs.emplace(std::move(id), std::move(output));
|
drv.outputs.emplace(std::move(id), std::move(output));
|
||||||
}
|
}
|
||||||
|
@ -381,28 +429,28 @@ Derivation parseDerivation(
|
||||||
expect(str, ",[");
|
expect(str, ",[");
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "(");
|
expect(str, "(");
|
||||||
Path drvPath = parsePath(str);
|
auto drvPath = parsePath(str);
|
||||||
expect(str, ",");
|
expect(str, ",");
|
||||||
drv.inputDrvs.map.insert_or_assign(store.parseStorePath(drvPath), parseDerivedPathMapNode(store, str, version));
|
drv.inputDrvs.map.insert_or_assign(store.parseStorePath(*drvPath), parseDerivedPathMapNode(store, str, version));
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(str, ","); drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true));
|
expect(str, ","); drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true));
|
||||||
expect(str, ","); drv.platform = parseString(str);
|
expect(str, ","); drv.platform = parseString(str).toOwned();
|
||||||
expect(str, ","); drv.builder = parseString(str);
|
expect(str, ","); drv.builder = parseString(str).toOwned();
|
||||||
|
|
||||||
/* Parse the builder arguments. */
|
/* Parse the builder arguments. */
|
||||||
expect(str, ",[");
|
expect(str, ",[");
|
||||||
while (!endOfList(str))
|
while (!endOfList(str))
|
||||||
drv.args.push_back(parseString(str));
|
drv.args.push_back(parseString(str).toOwned());
|
||||||
|
|
||||||
/* Parse the environment variables. */
|
/* Parse the environment variables. */
|
||||||
expect(str, ",[");
|
expect(str, ",[");
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "("); auto name = parseString(str);
|
expect(str, "("); auto name = parseString(str).toOwned();
|
||||||
expect(str, ","); auto value = parseString(str);
|
expect(str, ","); auto value = parseString(str).toOwned();
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
drv.env[name] = value;
|
drv.env.insert_or_assign(std::move(name), std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
|
|
|
@ -161,7 +161,7 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path)
|
||||||
sink.preallocateContents(size);
|
sink.preallocateContents(size);
|
||||||
|
|
||||||
uint64_t left = size;
|
uint64_t left = size;
|
||||||
std::vector<char> buf(65536);
|
std::array<char, 65536> buf;
|
||||||
|
|
||||||
while (left) {
|
while (left) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
|
@ -78,7 +78,7 @@ void Source::operator () (char * data, size_t len)
|
||||||
void Source::drainInto(Sink & sink)
|
void Source::drainInto(Sink & sink)
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
std::vector<char> buf(8192);
|
std::array<char, 8192> buf;
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t n;
|
size_t n;
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in a new issue