Skip to content

Commit 67ca71e

Browse files
committed
Disallow more unsafe string->path conversions allowed by path append operators
Add more fs::path operator/ and operator+ overloads to prevent unsafe string->path conversions on Windows that would cause strings to be decoded according to the current Windows locale & code page instead of the correct string encoding. Update application code to deal with loss of implicit string->path conversions by calling fs::u8path or fs::PathFromString explicitly, or by just changing variable types from std::string to fs::path to avoid conversions altoghther, or make them happen earlier. In all cases, there's no change in behavior either (1) because strings only contained ASCII characters and would be decoded the same regardless of what encoding was used, or (2) because of the 1:1 mapping between paths and strings using the PathToString and PathFromString functions.
1 parent 08bcfa2 commit 67ca71e

File tree

18 files changed

+91
-61
lines changed

18 files changed

+91
-61
lines changed

src/addrdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
5353
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
5454

5555
// open temp output file, and associate with CAutoFile
56-
fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
56+
fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
5757
FILE *file = fsbridge::fopen(pathTmp, "wb");
5858
CAutoFile fileout(file, SER_DISK, version);
5959
if (fileout.IsNull()) {

src/flatfile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ std::string FlatFilePos::ToString() const
2727

2828
fs::path FlatFileSeq::FileName(const FlatFilePos& pos) const
2929
{
30-
return m_dir / strprintf("%s%05u.dat", m_prefix, pos.nFile);
30+
return m_dir / fs::u8path(strprintf("%s%05u.dat", m_prefix, pos.nFile));
3131
}
3232

3333
FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only)

src/fs.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,35 @@ static inline auto quoted(const std::string& s)
7878
}
7979

8080
// Allow safe path append operations.
81-
static inline path operator+(path p1, path p2)
81+
static inline path operator/(path p1, path p2)
8282
{
83-
p1 += std::move(p2);
83+
p1 /= std::move(p2);
8484
return p1;
8585
}
86+
static inline path operator/(path p1, std::filesystem::path p2)
87+
{
88+
p1 /= std::move(p2);
89+
return p1;
90+
}
91+
static inline path operator/(path p1, const char* p2)
92+
{
93+
p1 /= p2;
94+
return p1;
95+
}
96+
static inline path operator+(path p1, const char* p2)
97+
{
98+
p1 += p2;
99+
return p1;
100+
}
101+
static inline path operator+(path p1, path::value_type p2)
102+
{
103+
p1 += p2;
104+
return p1;
105+
}
106+
107+
// Disallow unsafe path append operations.
108+
template<typename T> static inline path operator/(path p1, T p2) = delete;
109+
template<typename T> static inline path operator+(path p1, T p2) = delete;
86110

87111
// Disallow implicit std::string conversion for copy_file
88112
// to avoid locale-dependent encoding on Windows.

src/index/blockfilterindex.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
100100
const std::string& filter_name = BlockFilterTypeName(filter_type);
101101
if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
102102

103-
fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / filter_name;
103+
fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / fs::u8path(filter_name);
104104
fs::create_directories(path);
105105

106106
m_name = filter_name + " block filter index";

src/net.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3097,7 +3097,7 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
30973097
std::string clean_addr = addr.ToString();
30983098
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
30993099

3100-
fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
3100+
fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr);
31013101
fs::create_directories(base_path);
31023102

31033103
fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");

src/qt/guiutil.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ fs::path static GetAutostartFilePath()
586586
std::string chain = gArgs.GetChainName();
587587
if (chain == CBaseChainParams::MAIN)
588588
return GetAutostartDir() / "bitcoin.desktop";
589-
return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain);
589+
return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", chain));
590590
}
591591

592592
bool GetStartOnSystemStartup()

src/test/util/chainstate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F ma
3030
//
3131
int height;
3232
WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
33-
fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
33+
fs::path snapshot_path = root / fs::u8path(tfm::format("test_snapshot.%d.dat", height));
3434
FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
3535
CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
3636

src/test/util_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,7 +2014,7 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
20142014
BOOST_CHECK(!ParseFixedPoint("31.999999999999999999999", 3, &amount));
20152015
}
20162016

2017-
static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
2017+
static void TestOtherThread(fs::path dirname, fs::path lockname, bool *result)
20182018
{
20192019
*result = LockDirectory(dirname, lockname);
20202020
}
@@ -2024,7 +2024,7 @@ static constexpr char LockCommand = 'L';
20242024
static constexpr char UnlockCommand = 'U';
20252025
static constexpr char ExitCommand = 'X';
20262026

2027-
[[noreturn]] static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
2027+
[[noreturn]] static void TestOtherProcess(fs::path dirname, fs::path lockname, int fd)
20282028
{
20292029
char ch;
20302030
while (true) {
@@ -2055,7 +2055,7 @@ static constexpr char ExitCommand = 'X';
20552055
BOOST_AUTO_TEST_CASE(test_LockDirectory)
20562056
{
20572057
fs::path dirname = m_args.GetDataDirBase() / "lock_dir";
2058-
const std::string lockname = ".lock";
2058+
const fs::path lockname = ".lock";
20592059
#ifndef WIN32
20602060
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
20612061
// it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined

src/util/getuniquepath.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
fs::path GetUniquePath(const fs::path& base)
1010
{
1111
FastRandomContext rnd;
12-
fs::path tmpFile = base / HexStr(rnd.randbytes(8));
12+
fs::path tmpFile = base / fs::u8path(HexStr(rnd.randbytes(8)));
1313
return tmpFile;
14-
}
14+
}

src/util/system.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ static Mutex cs_dir_locks;
9595
*/
9696
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
9797

98-
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
98+
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
9999
{
100100
LOCK(cs_dir_locks);
101101
fs::path pathLockFile = directory / lockfile_name;
@@ -119,7 +119,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
119119
return true;
120120
}
121121

122-
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
122+
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
123123
{
124124
LOCK(cs_dir_locks);
125125
dir_locks.erase(fs::PathToString(directory / lockfile_name));

0 commit comments

Comments
 (0)