Skip to content

Commit f64aa9c

Browse files
ryanofskyhebasto
andcommitted
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. Co-authored-by: Hennadii Stepanov <[email protected]>
1 parent 7a4ac71 commit f64aa9c

18 files changed

+83
-59
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: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,30 @@ static inline auto quoted(const std::string& s)
9292
}
9393

9494
// Allow safe path append operations.
95-
static inline path operator+(path p1, path p2)
95+
static inline path operator/(path p1, path p2)
9696
{
97-
p1 += std::move(p2);
97+
p1 /= std::move(p2);
9898
return p1;
9999
}
100+
static inline path operator/(path p1, const char* p2)
101+
{
102+
p1 /= p2;
103+
return p1;
104+
}
105+
static inline path operator+(path p1, const char* p2)
106+
{
107+
p1 += p2;
108+
return p1;
109+
}
110+
static inline path operator+(path p1, path::value_type p2)
111+
{
112+
p1 += p2;
113+
return p1;
114+
}
115+
116+
// Disallow unsafe path append operations.
117+
template<typename T> static inline path operator/(path p1, T p2) = delete;
118+
template<typename T> static inline path operator+(path p1, T p2) = delete;
100119

101120
// Disallow implicit std::string conversion for copy_file
102121
// 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
@@ -3127,7 +3127,7 @@ void CaptureMessageToFile(const CAddress& addr,
31273127
std::string clean_addr = addr.ToString();
31283128
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
31293129

3130-
fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
3130+
fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr);
31313131
fs::create_directories(base_path);
31323132

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

src/qt/guiutil.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ fs::path static StartupShortcutPath()
508508
return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
509509
if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4"
510510
return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk";
511-
return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin (%s).lnk", chain);
511+
return GetSpecialFolderPath(CSIDL_STARTUP) / fs::u8path(strprintf("Bitcoin (%s).lnk", chain));
512512
}
513513

514514
bool GetStartOnSystemStartup()
@@ -589,7 +589,7 @@ fs::path static GetAutostartFilePath()
589589
std::string chain = gArgs.GetChainName();
590590
if (chain == CBaseChainParams::MAIN)
591591
return GetAutostartDir() / "bitcoin.desktop";
592-
return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain);
592+
return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", chain));
593593
}
594594

595595
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
@@ -2049,7 +2049,7 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
20492049
BOOST_CHECK(!ParseFixedPoint("31.999999999999999999999", 3, &amount));
20502050
}
20512051

2052-
static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
2052+
static void TestOtherThread(fs::path dirname, fs::path lockname, bool *result)
20532053
{
20542054
*result = LockDirectory(dirname, lockname);
20552055
}
@@ -2059,7 +2059,7 @@ static constexpr char LockCommand = 'L';
20592059
static constexpr char UnlockCommand = 'U';
20602060
static constexpr char ExitCommand = 'X';
20612061

2062-
[[noreturn]] static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
2062+
[[noreturn]] static void TestOtherProcess(fs::path dirname, fs::path lockname, int fd)
20632063
{
20642064
char ch;
20652065
while (true) {
@@ -2090,7 +2090,7 @@ static constexpr char ExitCommand = 'X';
20902090
BOOST_AUTO_TEST_CASE(test_LockDirectory)
20912091
{
20922092
fs::path dirname = m_args.GetDataDirBase() / "lock_dir";
2093-
const std::string lockname = ".lock";
2093+
const fs::path lockname = ".lock";
20942094
#ifndef WIN32
20952095
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
20962096
// it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined

src/util/getuniquepath.cpp

Lines changed: 1 addition & 1 deletion
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;
1414
}

src/util/system.cpp

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

107-
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
107+
bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
108108
{
109109
LOCK(cs_dir_locks);
110110
fs::path pathLockFile = directory / lockfile_name;
@@ -128,7 +128,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
128128
return true;
129129
}
130130

131-
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
131+
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
132132
{
133133
LOCK(cs_dir_locks);
134134
dir_locks.erase(fs::PathToString(directory / lockfile_name));

0 commit comments

Comments
 (0)