Skip to content

Commit 7e5bc71

Browse files
authored
[libc++] Fix UB in filesystem::__copy for non-existent destination. (#87615)
The lstat/stat/fstat functions have no guarantee whether the `struct stat` buffer is changed or not on failure. The filesystem::__copy function assumes that the `struct stat` buffer is not updated on failure, which is not necessarily correct. It appears that for a non-existing destination `detail::posix_lstat(to, t_st, &m_ec1)` returns a failure indicator and overwrites the `struct stat` buffer with a garbage value, which is accidentally equal to the `f_st` from stack internals from the previous `detail::posix_lstat(from, f_st, &m_ec1)` call. file_type::not_found is a known status, so checking against `if (not status_known(t))` passes spuriously and execution continues. Then the __copy function returns errc::function_not_supported because stats are accidentally equivalent, which is incorrect. Before checking for `detail::stat_equivalent`, we instead need to make sure that the call to lstat/stat/fstat was successful. As a result of `f_st` and `t_st` not being accessed anymore without checking for the lstat/stat/fstat success indicator, it is not needed to zero-initialize them.
1 parent 0c5319e commit 7e5bc71

File tree

1 file changed

+3
-3
lines changed

1 file changed

+3
-3
lines changed

libcxx/src/filesystem/operations.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,20 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
109109
const bool sym_status2 = bool(options & copy_options::copy_symlinks);
110110

111111
error_code m_ec1;
112-
StatT f_st = {};
112+
StatT f_st;
113113
const file_status f =
114114
sym_status || sym_status2 ? detail::posix_lstat(from, f_st, &m_ec1) : detail::posix_stat(from, f_st, &m_ec1);
115115
if (m_ec1)
116116
return err.report(m_ec1);
117117

118-
StatT t_st = {};
118+
StatT t_st;
119119
const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1) : detail::posix_stat(to, t_st, &m_ec1);
120120

121121
if (not status_known(t))
122122
return err.report(m_ec1);
123123

124124
if (!exists(f) || is_other(f) || is_other(t) || (is_directory(f) && is_regular_file(t)) ||
125-
detail::stat_equivalent(f_st, t_st)) {
125+
(exists(t) && detail::stat_equivalent(f_st, t_st))) {
126126
return err.report(errc::function_not_supported);
127127
}
128128

0 commit comments

Comments
 (0)