Skip to content

Commit 12b9372

Browse files
committed
prevent symlink loop recursion in directory traversal
1 parent a63c5f0 commit 12b9372

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

fuzzing/replay/file_util.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ absl::Status YieldFiles(
7070
absl::string_view path,
7171
absl::FunctionRef<void(absl::string_view, const struct stat&)> callback) {
7272
struct stat path_stat;
73-
if (stat(std::string(path).c_str(), &path_stat) < 0) {
73+
// Use lstat so symlink metadata is visible for recursion decisions.
74+
if (lstat(std::string(path).c_str(), &path_stat) < 0) {
7475
return ErrnoStatus(absl::StrCat("could not stat ", path), errno);
7576
}
76-
if (S_ISDIR(path_stat.st_mode)) {
77+
// Do not recurse into symlinked directories to avoid traversal loops.
78+
if (S_ISDIR(path_stat.st_mode) && !S_ISLNK(path_stat.st_mode)) {
7779
return TraverseDirectory(path, callback);
7880
}
7981
callback(path, path_stat);

fuzzing/replay/file_util_test.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
#include <sys/stat.h>
1818
#include <sys/types.h>
19+
#include <unistd.h>
1920

21+
#include <cerrno>
2022
#include <cstdlib>
23+
#include <cstring>
2124
#include <functional>
2225
#include <string>
2326
#include <vector>
@@ -113,6 +116,31 @@ TEST(YieldFilesTest, YieldsHiddenFilesAndDirs) {
113116
EXPECT_THAT(collected_paths, testing::SizeIs(2));
114117
}
115118

119+
TEST(YieldFilesTest, DoesNotRecurseThroughSymlinkLoop) {
120+
const std::string root_dir =
121+
absl::StrCat(getenv("TEST_TMPDIR"), "/symlink-loop-root");
122+
ASSERT_EQ(mkdir(root_dir.c_str(), 0755), 0);
123+
const std::string dir_a = absl::StrCat(root_dir, "/dirA");
124+
ASSERT_EQ(mkdir(dir_a.c_str(), 0755), 0);
125+
const std::string dir_b = absl::StrCat(root_dir, "/dirB");
126+
ASSERT_EQ(mkdir(dir_b.c_str(), 0755), 0);
127+
128+
const std::string link_to_b = absl::StrCat(dir_a, "/toB");
129+
if (symlink(dir_b.c_str(), link_to_b.c_str()) != 0) {
130+
GTEST_SKIP() << "symlink unsupported in this environment: "
131+
<< std::strerror(errno);
132+
}
133+
const std::string link_to_a = absl::StrCat(dir_b, "/toA");
134+
ASSERT_EQ(symlink(dir_a.c_str(), link_to_a.c_str()), 0);
135+
136+
std::vector<std::string> collected_paths;
137+
const absl::Status status =
138+
YieldFiles(root_dir, CollectPathsCallback(&collected_paths));
139+
EXPECT_TRUE(status.ok());
140+
EXPECT_GT(collected_paths.size(), 0);
141+
EXPECT_LE(collected_paths.size(), 6);
142+
}
143+
116144
} // namespace
117145

118146
} // namespace fuzzing

0 commit comments

Comments
 (0)