Skip to content

Commit 97d5f2e

Browse files
captain5050acmel
authored andcommitted
tools api fs: More thread safety for global filesystem variables
Multiple threads, such as with "perf top", may race to initialize a file system path like hugetlbfs. The racy initialization of the path leads to at least memory leaks. To avoid this initialize each fs for reading the mount point path with pthread_once. Mounting the file system may also be racy, so introduce a mutex over the function. This does mean that the path is being accessed with and without a mutex, which is inherently racy but hopefully benign, especially as there are fewer callers to fs__mount. Remove the fs__entries by directly using global variables, this was done as no argument like the index can be passed to the init once routine. Issue found and tested with "perf top" and address sanitizer. Signed-off-by: Ian Rogers <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 8dc26b6 commit 97d5f2e

File tree

1 file changed

+86
-125
lines changed
  • tools/lib/api/fs

1 file changed

+86
-125
lines changed

tools/lib/api/fs/fs.c

Lines changed: 86 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-License-Identifier: GPL-2.0
2+
#include <assert.h>
23
#include <ctype.h>
34
#include <errno.h>
45
#include <limits.h>
@@ -10,6 +11,7 @@
1011
#include <sys/types.h>
1112
#include <sys/stat.h>
1213
#include <fcntl.h>
14+
#include <pthread.h>
1315
#include <unistd.h>
1416
#include <sys/mount.h>
1517

@@ -43,7 +45,7 @@
4345
#define BPF_FS_MAGIC 0xcafe4a11
4446
#endif
4547

46-
static const char * const sysfs__fs_known_mountpoints[] = {
48+
static const char * const sysfs__known_mountpoints[] = {
4749
"/sys",
4850
0,
4951
};
@@ -86,69 +88,70 @@ static const char * const bpf_fs__known_mountpoints[] = {
8688
};
8789

8890
struct fs {
89-
const char *name;
90-
const char * const *mounts;
91+
const char * const name;
92+
const char * const * const mounts;
9193
char *path;
92-
bool found;
93-
bool checked;
94-
long magic;
95-
};
96-
97-
enum {
98-
FS__SYSFS = 0,
99-
FS__PROCFS = 1,
100-
FS__DEBUGFS = 2,
101-
FS__TRACEFS = 3,
102-
FS__HUGETLBFS = 4,
103-
FS__BPF_FS = 5,
94+
pthread_mutex_t mount_mutex;
95+
const long magic;
10496
};
10597

10698
#ifndef TRACEFS_MAGIC
10799
#define TRACEFS_MAGIC 0x74726163
108100
#endif
109101

110-
static struct fs fs__entries[] = {
111-
[FS__SYSFS] = {
112-
.name = "sysfs",
113-
.mounts = sysfs__fs_known_mountpoints,
114-
.magic = SYSFS_MAGIC,
115-
.checked = false,
116-
},
117-
[FS__PROCFS] = {
118-
.name = "proc",
119-
.mounts = procfs__known_mountpoints,
120-
.magic = PROC_SUPER_MAGIC,
121-
.checked = false,
122-
},
123-
[FS__DEBUGFS] = {
124-
.name = "debugfs",
125-
.mounts = debugfs__known_mountpoints,
126-
.magic = DEBUGFS_MAGIC,
127-
.checked = false,
128-
},
129-
[FS__TRACEFS] = {
130-
.name = "tracefs",
131-
.mounts = tracefs__known_mountpoints,
132-
.magic = TRACEFS_MAGIC,
133-
.checked = false,
134-
},
135-
[FS__HUGETLBFS] = {
136-
.name = "hugetlbfs",
137-
.mounts = hugetlbfs__known_mountpoints,
138-
.magic = HUGETLBFS_MAGIC,
139-
.checked = false,
140-
},
141-
[FS__BPF_FS] = {
142-
.name = "bpf",
143-
.mounts = bpf_fs__known_mountpoints,
144-
.magic = BPF_FS_MAGIC,
145-
.checked = false,
146-
},
147-
};
102+
static void fs__init_once(struct fs *fs);
103+
static const char *fs__mountpoint(const struct fs *fs);
104+
static const char *fs__mount(struct fs *fs);
105+
106+
#define FS(lower_name, fs_name, upper_name) \
107+
static struct fs fs__##lower_name = { \
108+
.name = #fs_name, \
109+
.mounts = lower_name##__known_mountpoints, \
110+
.magic = upper_name##_MAGIC, \
111+
.mount_mutex = PTHREAD_MUTEX_INITIALIZER, \
112+
}; \
113+
\
114+
static void lower_name##_init_once(void) \
115+
{ \
116+
struct fs *fs = &fs__##lower_name; \
117+
\
118+
fs__init_once(fs); \
119+
} \
120+
\
121+
const char *lower_name##__mountpoint(void) \
122+
{ \
123+
static pthread_once_t init_once = PTHREAD_ONCE_INIT; \
124+
struct fs *fs = &fs__##lower_name; \
125+
\
126+
pthread_once(&init_once, lower_name##_init_once); \
127+
return fs__mountpoint(fs); \
128+
} \
129+
\
130+
const char *lower_name##__mount(void) \
131+
{ \
132+
const char *mountpoint = lower_name##__mountpoint(); \
133+
struct fs *fs = &fs__##lower_name; \
134+
\
135+
if (mountpoint) \
136+
return mountpoint; \
137+
\
138+
return fs__mount(fs); \
139+
} \
140+
\
141+
bool lower_name##__configured(void) \
142+
{ \
143+
return lower_name##__mountpoint() != NULL; \
144+
}
145+
146+
FS(sysfs, sysfs, SYSFS);
147+
FS(procfs, procfs, PROC_SUPER);
148+
FS(debugfs, debugfs, DEBUGFS);
149+
FS(tracefs, tracefs, TRACEFS);
150+
FS(hugetlbfs, hugetlbfs, HUGETLBFS);
151+
FS(bpf_fs, bpf, BPF_FS);
148152

149153
static bool fs__read_mounts(struct fs *fs)
150154
{
151-
bool found = false;
152155
char type[100];
153156
FILE *fp;
154157
char path[PATH_MAX + 1];
@@ -157,22 +160,17 @@ static bool fs__read_mounts(struct fs *fs)
157160
if (fp == NULL)
158161
return false;
159162

160-
while (!found &&
161-
fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
163+
while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
162164
path, type) == 2) {
163165

164166
if (strcmp(type, fs->name) == 0) {
165-
free(fs->path);
166167
fs->path = strdup(path);
167-
if (!fs->path)
168-
return false;
169-
found = true;
168+
fclose(fp);
169+
return fs->path != NULL;
170170
}
171171
}
172-
173172
fclose(fp);
174-
fs->checked = true;
175-
return fs->found = found;
173+
return false;
176174
}
177175

178176
static int fs__valid_mount(const char *fs, long magic)
@@ -194,11 +192,9 @@ static bool fs__check_mounts(struct fs *fs)
194192
ptr = fs->mounts;
195193
while (*ptr) {
196194
if (fs__valid_mount(*ptr, fs->magic) == 0) {
197-
free(fs->path);
198195
fs->path = strdup(*ptr);
199196
if (!fs->path)
200197
return false;
201-
fs->found = true;
202198
return true;
203199
}
204200
ptr++;
@@ -236,45 +232,26 @@ static bool fs__env_override(struct fs *fs)
236232
if (!override_path)
237233
return false;
238234

239-
free(fs->path);
240235
fs->path = strdup(override_path);
241236
if (!fs->path)
242237
return false;
243-
fs->found = true;
244-
fs->checked = true;
245238
return true;
246239
}
247240

248-
static const char *fs__get_mountpoint(struct fs *fs)
241+
static void fs__init_once(struct fs *fs)
249242
{
250-
if (fs__env_override(fs))
251-
return fs->path;
252-
253-
if (fs__check_mounts(fs))
254-
return fs->path;
255-
256-
if (fs__read_mounts(fs))
257-
return fs->path;
258-
259-
return NULL;
243+
if (!fs__env_override(fs) &&
244+
!fs__check_mounts(fs) &&
245+
!fs__read_mounts(fs)) {
246+
assert(!fs->path);
247+
} else {
248+
assert(fs->path);
249+
}
260250
}
261251

262-
static const char *fs__mountpoint(int idx)
252+
static const char *fs__mountpoint(const struct fs *fs)
263253
{
264-
struct fs *fs = &fs__entries[idx];
265-
266-
if (fs->found)
267-
return (const char *)fs->path;
268-
269-
/* the mount point was already checked for the mount point
270-
* but and did not exist, so return NULL to avoid scanning again.
271-
* This makes the found and not found paths cost equivalent
272-
* in case of multiple calls.
273-
*/
274-
if (fs->checked)
275-
return NULL;
276-
277-
return fs__get_mountpoint(fs);
254+
return fs->path;
278255
}
279256

280257
static const char *mount_overload(struct fs *fs)
@@ -289,45 +266,29 @@ static const char *mount_overload(struct fs *fs)
289266
return getenv(upper_name) ?: *fs->mounts;
290267
}
291268

292-
static const char *fs__mount(int idx)
269+
static const char *fs__mount(struct fs *fs)
293270
{
294-
struct fs *fs = &fs__entries[idx];
295271
const char *mountpoint;
296272

297-
if (fs__mountpoint(idx))
298-
return (const char *)fs->path;
273+
pthread_mutex_lock(&fs->mount_mutex);
299274

300-
mountpoint = mount_overload(fs);
275+
/* Check if path found inside the mutex to avoid races with other callers of mount. */
276+
mountpoint = fs__mountpoint(fs);
277+
if (mountpoint)
278+
goto out;
301279

302-
if (mount(NULL, mountpoint, fs->name, 0, NULL) < 0)
303-
return NULL;
304-
305-
return fs__check_mounts(fs) ? fs->path : NULL;
306-
}
280+
mountpoint = mount_overload(fs);
307281

308-
#define FS(name, idx) \
309-
const char *name##__mountpoint(void) \
310-
{ \
311-
return fs__mountpoint(idx); \
312-
} \
313-
\
314-
const char *name##__mount(void) \
315-
{ \
316-
return fs__mount(idx); \
317-
} \
318-
\
319-
bool name##__configured(void) \
320-
{ \
321-
return name##__mountpoint() != NULL; \
282+
if (mount(NULL, mountpoint, fs->name, 0, NULL) == 0 &&
283+
fs__valid_mount(mountpoint, fs->magic) == 0) {
284+
fs->path = strdup(mountpoint);
285+
mountpoint = fs->path;
286+
}
287+
out:
288+
pthread_mutex_unlock(&fs->mount_mutex);
289+
return mountpoint;
322290
}
323291

324-
FS(sysfs, FS__SYSFS);
325-
FS(procfs, FS__PROCFS);
326-
FS(debugfs, FS__DEBUGFS);
327-
FS(tracefs, FS__TRACEFS);
328-
FS(hugetlbfs, FS__HUGETLBFS);
329-
FS(bpf_fs, FS__BPF_FS);
330-
331292
int filename__read_int(const char *filename, int *value)
332293
{
333294
char line[64];

0 commit comments

Comments
 (0)