Skip to content

Commit bfa3cb2

Browse files
committed
fix: multiple threading and memory issues in pathmatch
1 parent 11b4973 commit bfa3cb2

File tree

1 file changed

+30
-6
lines changed

1 file changed

+30
-6
lines changed

src/Features/Pathmatch.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ struct AutoReadLock {
9898
ReadWriteLock& lock_;
9999
};
100100

101+
/**
102+
* Auto lock for writes on a RW mutex
103+
*/
104+
struct AutoWriteLock {
105+
AutoWriteLock(ReadWriteLock& lock) : lock_(lock) {
106+
lock_.write_lock();
107+
}
108+
~AutoWriteLock() {
109+
lock_.unlock();
110+
}
111+
112+
ReadWriteLock& lock_;
113+
};
114+
101115
////////////////////////////////////////////////////////////////////////////////
102116
// Global db accessors
103117
////////////////////////////////////////////////////////////////////////////////
@@ -142,14 +156,23 @@ static double dc_get_time() {
142156
* then stores off those results.
143157
*/
144158
static dircontext_t *dc_find_or_populate(const char *path) {
159+
auto &db = dir_db();
160+
145161
{
146162
AutoReadLock lock(dir_db_lock());
147-
auto &db = dir_db();
148163
if (auto ent = db.find((char *)path); ent != db.end()) {
149164
return dc_build_around_ent(path, ent->second);
150165
}
151166
}
152167

168+
AutoWriteLock lock(dir_db_lock());
169+
170+
// before we try and cache it, check if it was cached while we were acquiring our
171+
// write lock (avoid TOCTOU condition)
172+
if (auto ent = db.find((char *)path); ent != db.end()) {
173+
return dc_build_around_ent(path, ent->second);
174+
}
175+
153176
// read contents and store into the db.
154177
dirent **namelist = nullptr;
155178
// Grab all dir entries
@@ -172,9 +195,7 @@ static dircontext_t *dc_find_or_populate(const char *path) {
172195
free(namelist);
173196

174197
// Insert into the db
175-
dir_db_lock().write_lock();
176-
dir_db().insert({path, dent});
177-
dir_db_lock().unlock();
198+
db.insert({path, dent});
178199

179200
// Finally build a returnable value
180201
return dc_build_around_ent(path, dent);
@@ -189,6 +210,7 @@ static void dc_close(dircontext_t *context) {
189210

190211
auto old_refs = context->ent->nref.fetch_sub(1); // Dec refcount
191212
if (old_refs == 1) {
213+
delete context->ent;
192214
dir_db().erase(context->key);
193215
}
194216

@@ -326,7 +348,7 @@ static PathMod pathmatchDetour(const char *in, char **out, bool allow_basename_m
326348
if (dir) {
327349
struct dirent *ent;
328350
while (ent = dircache_readdir(dir)) {
329-
if (!strncasecmp(ent->d_name, start, len)) {
351+
if (!strncasecmp(ent->d_name, start, len) && !ent->d_name[len]) {
330352
// fill in the correct capitalization
331353
memcpy(start, ent->d_name, len);
332354
matched = true;
@@ -338,7 +360,9 @@ static PathMod pathmatchDetour(const char *in, char **out, bool allow_basename_m
338360

339361
if (!matched) break; // this component didn't match, so certainly no further ones will
340362

341-
start += len + 1; // next component
363+
// next component
364+
start += len;
365+
if (*start) start += 1; // skip slash
342366
}
343367

344368
*out = buf;

0 commit comments

Comments
 (0)