Skip to content

Commit c224b83

Browse files
authored
Merge pull request #126 from DeterminateSystems/release-v3.6.7/84df8907-9f0b-49ea-826a-e1f36408f399
Release v3.6.7
2 parents 5dea62b + 404d824 commit c224b83

File tree

9 files changed

+120
-37
lines changed

9 files changed

+120
-37
lines changed

.version-determinate

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.6.6
1+
3.6.7

doc/manual/source/SUMMARY.md.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
- [Contributing](development/contributing.md)
130130
- [Determinate Nix Release Notes](release-notes-determinate/index.md)
131131
- [Changes between Nix and Determinate Nix](release-notes-determinate/changes.md)<!-- next -->
132+
- [Release 3.6.7 (2025-06-24)](release-notes-determinate/rl-3.6.7.md)
132133
- [Release 3.6.6 (2025-06-17)](release-notes-determinate/rl-3.6.6.md)
133134
- [Release 3.6.5 (2025-06-16)](release-notes-determinate/rl-3.6.5.md)
134135
- [Release 3.6.2 (2025-06-02)](release-notes-determinate/rl-3.6.2.md)

doc/manual/source/release-notes-determinate/changes.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changes between Nix and Determinate Nix
22

3-
This section lists the differences between upstream Nix 2.29 and Determinate Nix 3.6.6.<!-- differences -->
3+
This section lists the differences between upstream Nix 2.29 and Determinate Nix 3.6.7.<!-- differences -->
44

55
* In Determinate Nix, flakes are stable. You no longer need to enable the `flakes` experimental feature.
66

@@ -74,3 +74,5 @@ This section lists the differences between upstream Nix 2.29 and Determinate Nix
7474
* Improve caching of inputs in dry-run mode by @edolstra in [DeterminateSystems/nix-src#98](https://github.com/DeterminateSystems/nix-src/pull/98)
7575

7676
<!-- Determinate Nix version 3.6.6 -->
77+
78+
<!-- Determinate Nix version 3.6.7 -->
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Release 3.6.7 (2025-06-24)
2+
3+
* Based on [upstream Nix 2.29.1](../release-notes/rl-2.29.md).
4+
5+
## What's Changed
6+
7+
### Security contents
8+
9+
* Patched against GHSA-g948-229j-48j3
10+
11+
### Lazy trees:
12+
13+
* Lazy trees now produces `flake.lock` files with NAR hashes unless `lazy-locks` is set to `true` by @edolstra in [DeterminateSystems/nix-src#113](https://github.com/DeterminateSystems/nix-src/pull/113)
14+
* Improved caching with lazy-trees when using --impure, with enhanced testing by @edolstra in [DeterminateSystems/nix-src#117](https://github.com/DeterminateSystems/nix-src/pull/117)
15+
16+
17+
**Full Changelog**: [v3.6.6...v3.6.7](https://github.com/DeterminateSystems/nix-src/compare/v3.6.6...v3.6.7)

src/libstore/local-store.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ LocalStore::LocalStore(ref<const Config> config)
247247
else if (curSchema == 0) { /* new store */
248248
curSchema = nixSchemaVersion;
249249
openDB(*state, true);
250-
writeFile(schemaPath, fmt("%1%", curSchema), 0666, true);
250+
writeFile(schemaPath, fmt("%1%", curSchema), 0666, FsSync::Yes);
251251
}
252252

253253
else if (curSchema < nixSchemaVersion) {
@@ -298,7 +298,7 @@ LocalStore::LocalStore(ref<const Config> config)
298298
txn.commit();
299299
}
300300

301-
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true);
301+
writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, FsSync::Yes);
302302

303303
lockFile(globalLock.get(), ltRead, true);
304304
}

src/libstore/unix/build/derivation-builder.cc

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ class DerivationBuilderImpl : public DerivationBuilder, DerivationBuilderParams
129129
*/
130130
Path topTmpDir;
131131

132+
/**
133+
* The file descriptor of the temporary directory.
134+
*/
135+
AutoCloseFD tmpDirFd;
136+
132137
/**
133138
* The path of the temporary directory in the sandbox.
134139
*/
@@ -325,9 +330,24 @@ class DerivationBuilderImpl : public DerivationBuilder, DerivationBuilderParams
325330

326331
/**
327332
* Make a file owned by the builder.
333+
*
334+
* SAFETY: this function is prone to TOCTOU as it receives a path and not a descriptor.
335+
* It's only safe to call in a child of a directory only visible to the owner.
328336
*/
329337
void chownToBuilder(const Path & path);
330338

339+
/**
340+
* Make a file owned by the builder addressed by its file descriptor.
341+
*/
342+
void chownToBuilder(int fd, const Path & path);
343+
344+
/**
345+
* Create a file in `tmpDir` owned by the builder.
346+
*/
347+
void writeBuilderFile(
348+
const std::string & name,
349+
std::string_view contents);
350+
331351
/**
332352
* Run the builder's process.
333353
*/
@@ -900,7 +920,14 @@ void DerivationBuilderImpl::startBuilder()
900920
} else {
901921
tmpDir = topTmpDir;
902922
}
903-
chownToBuilder(tmpDir);
923+
924+
/* The TOCTOU between the previous mkdir call and this open call is unavoidable due to
925+
POSIX semantics.*/
926+
tmpDirFd = AutoCloseFD{open(tmpDir.c_str(), O_RDONLY | O_NOFOLLOW | O_DIRECTORY)};
927+
if (!tmpDirFd)
928+
throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDir);
929+
930+
chownToBuilder(tmpDirFd.get(), tmpDir);
904931

905932
for (auto & [outputName, status] : initialOutputs) {
906933
/* Set scratch path we'll actually use during the build.
@@ -1485,9 +1512,7 @@ void DerivationBuilderImpl::initTmpDir()
14851512
} else {
14861513
auto hash = hashString(HashAlgorithm::SHA256, i.first);
14871514
std::string fn = ".attr-" + hash.to_string(HashFormat::Nix32, false);
1488-
Path p = tmpDir + "/" + fn;
1489-
writeFile(p, rewriteStrings(i.second, inputRewrites));
1490-
chownToBuilder(p);
1515+
writeBuilderFile(fn, rewriteStrings(i.second, inputRewrites));
14911516
env[i.first + "Path"] = tmpDirInSandbox + "/" + fn;
14921517
}
14931518
}
@@ -1596,11 +1621,9 @@ void DerivationBuilderImpl::writeStructuredAttrs()
15961621

15971622
auto jsonSh = StructuredAttrs::writeShell(json);
15981623

1599-
writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites));
1600-
chownToBuilder(tmpDir + "/.attrs.sh");
1624+
writeBuilderFile(".attrs.sh", rewriteStrings(jsonSh, inputRewrites));
16011625
env["NIX_ATTRS_SH_FILE"] = tmpDirInSandbox + "/.attrs.sh";
1602-
writeFile(tmpDir + "/.attrs.json", rewriteStrings(json.dump(), inputRewrites));
1603-
chownToBuilder(tmpDir + "/.attrs.json");
1626+
writeBuilderFile(".attrs.json", rewriteStrings(json.dump(), inputRewrites));
16041627
env["NIX_ATTRS_JSON_FILE"] = tmpDirInSandbox + "/.attrs.json";
16051628
}
16061629
}
@@ -1854,6 +1877,24 @@ void setupSeccomp()
18541877
#endif
18551878
}
18561879

1880+
void DerivationBuilderImpl::chownToBuilder(int fd, const Path & path)
1881+
{
1882+
if (!buildUser) return;
1883+
if (fchown(fd, buildUser->getUID(), buildUser->getGID()) == -1)
1884+
throw SysError("cannot change ownership of file '%1%'", path);
1885+
}
1886+
1887+
void DerivationBuilderImpl::writeBuilderFile(
1888+
const std::string & name,
1889+
std::string_view contents)
1890+
{
1891+
auto path = std::filesystem::path(tmpDir) / name;
1892+
AutoCloseFD fd{openat(tmpDirFd.get(), name.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC | O_EXCL | O_NOFOLLOW, 0666)};
1893+
if (!fd)
1894+
throw SysError("creating file %s", path);
1895+
writeFile(fd, path, contents);
1896+
chownToBuilder(fd.get(), path);
1897+
}
18571898

18581899
void DerivationBuilderImpl::runChild()
18591900
{
@@ -3065,6 +3106,15 @@ void DerivationBuilderImpl::checkOutputs(const std::map<std::string, ValidPathIn
30653106
void DerivationBuilderImpl::deleteTmpDir(bool force)
30663107
{
30673108
if (topTmpDir != "") {
3109+
/* As an extra precaution, even in the event of `deletePath` failing to
3110+
* clean up, the `tmpDir` will be chowned as if we were to move
3111+
* it inside the Nix store.
3112+
*
3113+
* This hardens against an attack which smuggles a file descriptor
3114+
* to make use of the temporary directory.
3115+
*/
3116+
chmod(topTmpDir.c_str(), 0000);
3117+
30683118
/* Don't keep temporary directories for builtins because they
30693119
might have privileged stuff (like a copy of netrc). */
30703120
if (settings.keepFailed && !force && !drv.isBuiltin()) {

src/libutil/file-content-address.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void restorePath(
9393
{
9494
switch (method) {
9595
case FileSerialisationMethod::Flat:
96-
writeFile(path, source, 0666, startFsync);
96+
writeFile(path, source, 0666, startFsync ? FsSync::Yes : FsSync::No);
9797
break;
9898
case FileSerialisationMethod::NixArchive:
9999
restorePath(path, source, startFsync);

src/libutil/file-system.cc

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ void readFile(const Path & path, Sink & sink, bool memory_map)
303303
}
304304

305305

306-
void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
306+
void writeFile(const Path & path, std::string_view s, mode_t mode, FsSync sync)
307307
{
308308
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
309309
// TODO
@@ -313,22 +313,29 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
313313
, mode));
314314
if (!fd)
315315
throw SysError("opening file '%1%'", path);
316+
317+
writeFile(fd, path, s, mode, sync);
318+
319+
/* Close explicitly to propagate the exceptions. */
320+
fd.close();
321+
}
322+
323+
void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode, FsSync sync)
324+
{
325+
assert(fd);
316326
try {
317327
writeFull(fd.get(), s);
328+
329+
if (sync == FsSync::Yes)
330+
fd.fsync();
331+
318332
} catch (Error & e) {
319-
e.addTrace({}, "writing file '%1%'", path);
333+
e.addTrace({}, "writing file '%1%'", origPath);
320334
throw;
321335
}
322-
if (sync)
323-
fd.fsync();
324-
// Explicitly close to make sure exceptions are propagated.
325-
fd.close();
326-
if (sync)
327-
syncParent(path);
328336
}
329337

330-
331-
void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
338+
void writeFile(const Path & path, Source & source, mode_t mode, FsSync sync)
332339
{
333340
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
334341
// TODO
@@ -352,11 +359,11 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
352359
e.addTrace({}, "writing file '%1%'", path);
353360
throw;
354361
}
355-
if (sync)
362+
if (sync == FsSync::Yes)
356363
fd.fsync();
357364
// Explicitly close to make sure exceptions are propagated.
358365
fd.close();
359-
if (sync)
366+
if (sync == FsSync::Yes)
360367
syncParent(path);
361368
}
362369

@@ -419,7 +426,8 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
419426
#ifndef _WIN32
420427
checkInterrupt();
421428

422-
std::string name(baseNameOf(path.native()));
429+
std::string name(path.filename());
430+
assert(name != "." && name != ".." && !name.empty());
423431

424432
struct stat st;
425433
if (fstatat(parentfd, name.c_str(), &st,
@@ -460,7 +468,7 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
460468
throw SysError("chmod %1%", path);
461469
}
462470

463-
int fd = openat(parentfd, path.c_str(), O_RDONLY);
471+
int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
464472
if (fd == -1)
465473
throw SysError("opening directory %1%", path);
466474
AutoCloseDir dir(fdopendir(fd));
@@ -472,7 +480,7 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
472480
checkInterrupt();
473481
std::string childName = dirent->d_name;
474482
if (childName == "." || childName == "..") continue;
475-
_deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed, ex);
483+
_deletePath(dirfd(dir.get()), path / childName, bytesFreed, ex);
476484
}
477485
if (errno) throw SysError("reading directory %1%", path);
478486
}
@@ -497,14 +505,13 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
497505

498506
static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
499507
{
500-
Path dir = dirOf(path.string());
501-
if (dir == "")
502-
dir = "/";
508+
assert(path.is_absolute());
509+
assert(path.parent_path() != path);
503510

504-
AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY));
511+
AutoCloseFD dirfd = toDescriptor(open(path.parent_path().string().c_str(), O_RDONLY));
505512
if (!dirfd) {
506513
if (errno == ENOENT) return;
507-
throw SysError("opening directory '%1%'", path);
514+
throw SysError("opening directory %s", path.parent_path());
508515
}
509516

510517
std::exception_ptr ex;

src/libutil/include/nix/util/file-system.hh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,21 +175,27 @@ std::string readFile(const Path & path);
175175
std::string readFile(const std::filesystem::path & path);
176176
void readFile(const Path & path, Sink & sink, bool memory_map = true);
177177

178+
enum struct FsSync { Yes, No };
179+
178180
/**
179181
* Write a string to a file.
180182
*/
181-
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false);
182-
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, bool sync = false)
183+
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
184+
185+
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No)
183186
{
184187
return writeFile(path.string(), s, mode, sync);
185188
}
186189

187-
void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false);
188-
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, bool sync = false)
190+
void writeFile(const Path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No);
191+
192+
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No)
189193
{
190194
return writeFile(path.string(), source, mode, sync);
191195
}
192196

197+
void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
198+
193199
/**
194200
* Flush a path's parent directory to disk.
195201
*/

0 commit comments

Comments
 (0)