Skip to content

Commit ff05a94

Browse files
authored
Merge pull request #15850 from NixOS/serialise-fixes-32-bit-and-framed-sink-perf
libutil: Fix various 32 bit size_t truncations from uint64_t, fix inefficiencies in serialisation code
2 parents 1ba67c5 + 0548d85 commit ff05a94

6 files changed

Lines changed: 40 additions & 9 deletions

File tree

src/libutil-tests/serialise.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,19 @@ TEST(readNum, negativeValuesSerialiseWellDefined)
2222
EXPECT_EQ(readNum<uint64_t>(*makeNumSource(int16_t(-1))), std::numeric_limits<uint64_t>::max());
2323
}
2424

25+
TEST(readPadding, works)
26+
{
27+
for (unsigned i = 0; i < 8; ++i)
28+
EXPECT_NO_THROW(readPadding(i, *makeNumSource(0)));
29+
30+
for (unsigned len = 1; len < 8; ++len) {
31+
EXPECT_THROW(readPadding(len, *makeNumSource(~uint64_t(0))), SerialisationError);
32+
unsigned padLen = 8 - len;
33+
for (unsigned byte = 0; byte < padLen; ++byte) {
34+
uint64_t val = uint64_t{1} << (8 * byte);
35+
EXPECT_THROW(readPadding(len, *makeNumSource(val)), SerialisationError);
36+
}
37+
}
38+
}
39+
2540
} // namespace nix

src/libutil/archive.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,14 @@ static void parseContents(CreateRegularFileSink & sink, Source & source)
146146
sink.preallocateContents(size);
147147

148148
if (sink.skipContents) {
149-
source.skip(alignUp(size, 8));
149+
uint64_t left = alignUp(size, 8);
150+
/* Source::skip takes a size_t, which might be narrower on 32 bit systems, so
151+
be careful around truncations. */
152+
while (left) {
153+
size_t toSkip = std::min<uint64_t>(left, std::numeric_limits<size_t>::max());
154+
source.skip(toSkip);
155+
left -= toSkip;
156+
}
150157
return;
151158
}
152159

src/libutil/fs-sink.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ void RestoreRegularFile::preallocateContents(uint64_t len)
267267

268268
#if HAVE_POSIX_FALLOCATE
269269
if (len) {
270+
if (len > std::numeric_limits<off_t>::max())
271+
throw Error("cannot preallocate contents for a file because it's too large");
272+
270273
errno = posix_fallocate(fd.get(), 0, len);
271274
/* Note that EINVAL may indicate that the underlying
272275
filesystem doesn't support preallocation (e.g. on

src/libutil/include/nix/util/serialise.hh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,10 @@ inline uint64_t readLongLong(Source & source)
567567
return readNum<uint64_t>(source);
568568
}
569569

570+
/**
571+
* Read padding bytes to align `len` up to a multiple of 8. Throws
572+
* SerialisationError if any padding byte is non-zero.
573+
*/
570574
void readPadding(size_t len, Source & source);
571575
size_t readString(char * buf, size_t max, Source & source);
572576
std::string readString(Source & source, size_t max = std::numeric_limits<size_t>::max());
@@ -649,8 +653,8 @@ struct FramedSource : Source
649653
auto n = readInt(from);
650654
if (!n)
651655
break;
652-
std::vector<char> data(n);
653-
from(data.data(), n);
656+
pending.resize(n);
657+
from(pending.data(), n);
654658
}
655659
}
656660
} catch (...) {
@@ -669,11 +673,12 @@ struct FramedSource : Source
669673
eof = true;
670674
return 0;
671675
}
672-
pending = std::vector<char>(len);
676+
pending.resize(len);
673677
pos = 0;
674678
from(pending.data(), len);
675679
}
676680

681+
assert(pending.size() > pos); /* Sanity check. */
677682
auto n = std::min(len, pending.size() - pos);
678683
memcpy(data, pending.data() + pos, n);
679684
pos += n;

src/libutil/memory-source-accessor.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ void CreateMemoryRegularFile::isExecutable()
234234

235235
void CreateMemoryRegularFile::preallocateContents(uint64_t len)
236236
{
237+
if (len > std::numeric_limits<decltype(regularFile.contents)::size_type>::max())
238+
throw Error("cannot preallocate contents for a file that is too large to fit in memory");
237239
regularFile.contents.reserve(len);
238240
}
239241

src/libutil/serialise.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,12 +508,11 @@ Sink & operator<<(Sink & sink, const Error & ex)
508508
void readPadding(size_t len, Source & source)
509509
{
510510
if (len % 8) {
511-
char zero[8];
511+
uint64_t zero = 0;
512512
size_t n = 8 - (len % 8);
513-
source(zero, n);
514-
for (unsigned int i = 0; i < n; i++)
515-
if (zero[i])
516-
throw SerialisationError("non-zero padding");
513+
source(reinterpret_cast<char *>(&zero), n);
514+
if (zero)
515+
throw SerialisationError("non-zero padding");
517516
}
518517
}
519518

0 commit comments

Comments
 (0)