-
Notifications
You must be signed in to change notification settings - Fork 0
Backport Lix CVE fixes to Nix 2.3 #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2.3-maintenance
Are you sure you want to change the base?
Changes from all commits
c7745b2
49333a0
e47a472
8251672
305d2e5
70f5c25
cb34f22
2e04bd1
3f3c4e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -266,16 +266,13 @@ bool isLink(const Path & path) | |||||
| } | ||||||
|
|
||||||
|
|
||||||
| DirEntries readDirectory(const Path & path) | ||||||
| DirEntries readDirectory(DIR *dir, const Path & path) | ||||||
| { | ||||||
| DirEntries entries; | ||||||
| entries.reserve(64); | ||||||
|
|
||||||
| AutoCloseDir dir(opendir(path.c_str())); | ||||||
| if (!dir) throw SysError(format("opening directory '%1%'") % path); | ||||||
|
|
||||||
| struct dirent * dirent; | ||||||
| while (errno = 0, dirent = readdir(dir.get())) { /* sic */ | ||||||
| while (errno = 0, dirent = readdir(dir)) { /* sic */ | ||||||
| checkInterrupt(); | ||||||
| string name = dirent->d_name; | ||||||
| if (name == "." || name == "..") continue; | ||||||
|
|
@@ -292,6 +289,14 @@ DirEntries readDirectory(const Path & path) | |||||
| return entries; | ||||||
| } | ||||||
|
|
||||||
| DirEntries readDirectory(const Path & path) | ||||||
| { | ||||||
| AutoCloseDir dir(opendir(path.c_str())); | ||||||
| if (!dir) throw SysError(format("opening directory '%1%'") % path); | ||||||
|
|
||||||
| return readDirectory(dir.get(), path); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| unsigned char getFileType(const Path & path) | ||||||
| { | ||||||
|
|
@@ -338,9 +343,44 @@ void writeFile(const Path & path, const string & s, mode_t mode) | |||||
| AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); | ||||||
| if (!fd) | ||||||
| throw SysError(format("opening file '%1%'") % path); | ||||||
|
|
||||||
| writeFile(fd, s, mode); | ||||||
|
|
||||||
| /* Close explicitly to propagate the exceptions. */ | ||||||
| fd.close(); | ||||||
| } | ||||||
|
|
||||||
| void writeFile(AutoCloseFD & fd, const std::string& s, mode_t mode) | ||||||
| { | ||||||
| assert(fd); | ||||||
| writeFull(fd.get(), s); | ||||||
| } | ||||||
|
|
||||||
| void writeFileAndSync(const Path & path, const std::string& s, mode_t mode) | ||||||
| { | ||||||
| { | ||||||
| AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)}; | ||||||
| if (!fd) | ||||||
| throw SysError("opening file '%1%'", path); | ||||||
|
|
||||||
| writeFile(fd, s, mode); | ||||||
| fd.fsync(); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. couldn't closeForWrite be used here? |
||||||
| /* Close explicitly to ensure that exceptions are propagated. */ | ||||||
| fd.close(); | ||||||
| } | ||||||
|
|
||||||
| syncParent(path); | ||||||
| } | ||||||
|
|
||||||
| static void closeForWrite(const Path & path, AutoCloseFD & fd, bool sync) | ||||||
| { | ||||||
| if (sync) | ||||||
| fd.fsync(); | ||||||
| // Explicitly close to make sure exceptions are propagated. | ||||||
| fd.close(); | ||||||
| if (sync) | ||||||
| syncParent(path); | ||||||
| } | ||||||
|
|
||||||
| void writeFile(const Path & path, Source & source, mode_t mode) | ||||||
| { | ||||||
|
|
@@ -356,8 +396,17 @@ void writeFile(const Path & path, Source & source, mode_t mode) | |||||
| writeFull(fd.get(), (unsigned char *) buf.data(), n); | ||||||
| } catch (EndOfFile &) { break; } | ||||||
| } | ||||||
|
|
||||||
| closeForWrite(path, fd, false); | ||||||
| } | ||||||
|
|
||||||
| void syncParent(const Path & path) | ||||||
| { | ||||||
| AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0); | ||||||
| if (!fd) | ||||||
| throw SysError("opening file '%1%'", path); | ||||||
| fd.fsync(); | ||||||
| } | ||||||
|
|
||||||
| string readLine(int fd) | ||||||
| { | ||||||
|
|
@@ -387,14 +436,16 @@ void writeLine(int fd, string s) | |||||
| } | ||||||
|
|
||||||
|
|
||||||
| static void _deletePath(const Path & path, unsigned long long & bytesFreed) | ||||||
| static void _deletePath(int parentfd, const Path& name, unsigned long long & bytesFreed) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we continuing to use |
||||||
| { | ||||||
| /* This ensures that `name` is an immediate child of `parentfd`. */ | ||||||
| assert(!name.empty() && name.find('/') == std::string::npos && "`name` is an immediate child to `parentfd`"); | ||||||
| checkInterrupt(); | ||||||
|
|
||||||
| struct stat st; | ||||||
| if (lstat(path.c_str(), &st) == -1) { | ||||||
| if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) { | ||||||
| if (errno == ENOENT) return; | ||||||
| throw SysError(format("getting status of '%1%'") % path); | ||||||
| throw SysError("getting status of '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd)); | ||||||
| } | ||||||
|
|
||||||
| if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) | ||||||
|
|
@@ -404,18 +455,41 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed) | |||||
| /* Make the directory accessible. */ | ||||||
| const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR; | ||||||
| if ((st.st_mode & PERM_MASK) != PERM_MASK) { | ||||||
| if (chmod(path.c_str(), st.st_mode | PERM_MASK) == -1) | ||||||
| throw SysError(format("chmod '%1%'") % path); | ||||||
| if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) { | ||||||
| throw SysError("chmod '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd)); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| for (auto & i : readDirectory(path)) | ||||||
| _deletePath(path + "/" + i.name, bytesFreed); | ||||||
| int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW); | ||||||
| if (fd = -1) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd)); | ||||||
| AutoCloseDir dir(fdopendir(fd)); | ||||||
| if (!dir) | ||||||
| throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd)); | ||||||
| for (auto & i : readDirectory(dir.get(), name)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also here we don't have a flag for interruptibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Apparently related to cgroups in Lix, unnecessary.) |
||||||
| _deletePath(dirfd(dir.get()), i.name, bytesFreed); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here… There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Apparently related to cgroups in Lix, unnecessary.) |
||||||
| } | ||||||
|
|
||||||
| int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0; | ||||||
| if (unlinkat(parentfd, name.c_str(), flags) == -1) { | ||||||
| if (errno == ENOENT) return; | ||||||
| throw SysError("cannot unlink '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd)); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (remove(path.c_str()) == -1) { | ||||||
| static void _deletePath(const Path & path, unsigned long long & bytesFreed) | ||||||
| { | ||||||
| Path dir = dirOf(path); | ||||||
| if (dir == "") | ||||||
| dir = "/"; | ||||||
|
|
||||||
| AutoCloseFD dirfd(open(dir.c_str(), O_RDONLY)); | ||||||
| if (!dirfd) { | ||||||
| if (errno == ENOENT) return; | ||||||
| throw SysError(format("cannot unlink '%1%'") % path); | ||||||
| throw SysError(format("opening directory '%1%'") % path); | ||||||
| } | ||||||
|
|
||||||
| _deletePath(dirfd.get(), baseNameOf(path).data(), bytesFreed); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -747,6 +821,28 @@ int AutoCloseFD::get() const | |||||
| return fd; | ||||||
| } | ||||||
|
|
||||||
| std::string guessOrInventPathFromFD(int fd) | ||||||
| { | ||||||
| assert(fd >= 0); | ||||||
| /* On Linux, there's no F_GETPATH available. | ||||||
| * But we can read /proc/ */ | ||||||
| #if __linux__ | ||||||
| try { | ||||||
| return readLink(fmt("/proc/self/fd/%1%", fd).c_str()); | ||||||
| } catch (...) { | ||||||
| } | ||||||
| #elif defined (HAVE_F_GETPATH) && HAVE_F_GETPATH | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This CPP symbol is set by meson in Lix, so I think this is effectively dead code here unless we add it to the autoconf machinery. |
||||||
| std::string fdName(PATH_MAX, '\0'); | ||||||
| if (fcntl(fd, F_GETPATH, fdName.data()) != -1) { | ||||||
| fdName.resize(strlen(fdName.c_str())); | ||||||
| return fdName; | ||||||
| } | ||||||
| #else | ||||||
| #error "No implementation for retrieving file descriptors path." | ||||||
| #endif | ||||||
|
|
||||||
| return fmt("<fd %i>", fd); | ||||||
| } | ||||||
|
|
||||||
| void AutoCloseFD::close() | ||||||
| { | ||||||
|
|
@@ -758,6 +854,19 @@ void AutoCloseFD::close() | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| void AutoCloseFD::fsync() | ||||||
| { | ||||||
| if (fd != -1) { | ||||||
| int result; | ||||||
| #if __APPLE__ | ||||||
| result = ::fcntl(fd, F_FULLFSYNC); | ||||||
| #else | ||||||
| result = ::fsync(fd); | ||||||
| #endif | ||||||
| if (result == -1) | ||||||
| throw SysError("fsync file descriptor %1%", fd); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| AutoCloseFD::operator bool() const | ||||||
| { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,6 +109,15 @@ void writeFile(const Path & path, const string & s, mode_t mode = 0666); | |
|
|
||
| void writeFile(const Path & path, Source & source, mode_t mode = 0666); | ||
|
|
||
| class AutoCloseFD; // forward-declaration needed because this moved around in Lix | ||
| void writeFile(AutoCloseFD & fd, const std::string& s, mode_t mode = 0666); | ||
|
|
||
| /* Write a string to a file and flush the file and its parent directory to disk. */ | ||
| void writeFileAndSync(const Path & path, const std::string& s, mode_t mode = 0666); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know the significance of writeFileUninterruptible? Is that something we also need? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently related to cgroups in Lix, unnecessary. |
||
|
|
||
| /* Flush a file's parent directory to disk */ | ||
| void syncParent(const Path & path); | ||
|
|
||
| /* Read a line from a file descriptor. */ | ||
| string readLine(int fd); | ||
|
|
||
|
|
@@ -186,6 +195,13 @@ public: | |
| operator Path() const { return path; } | ||
| }; | ||
|
|
||
| /* | ||
| * Will attempt to guess *A* path associated that might lead to the same file as used by this | ||
| * file descriptor. | ||
| * | ||
| * The returned string should NEVER be used as a valid path. | ||
| */ | ||
| std::string guessOrInventPathFromFD(int fd); | ||
|
|
||
| class AutoCloseFD | ||
| { | ||
|
|
@@ -202,6 +218,15 @@ public: | |
| explicit operator bool() const; | ||
| int release(); | ||
| void close(); | ||
| void fsync(); | ||
|
|
||
| /* | ||
| * Will attempt to guess *A* path associated that might lead to the same file as used by this | ||
| * file descriptor. | ||
| * | ||
| * The returned string should NEVER be used as a valid path. | ||
| */ | ||
| std::string guessOrInventPath() const { return guessOrInventPathFromFD(fd); } | ||
| }; | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't the fd closed in here, but in the wrapping function? Is the calling function entitled to keep using the fd? A previous specifcally started closing the fd here to “propagate exceptions”, so I'm confused why it's being reverted here…