perf: fix race in build_id_cache__add_s()
PATCH 1/1 perf: fix race in build_id_cache__add_s()
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso)
{
...
if (access(filename, F_OK)) {
^--------------------------------------------------------- [1]
if (is_kallsyms) {
if (copyfile("/proc/kallsyms", filename))
goto out_free;
} else if (link(realname, filename) && copyfile(name, filename))
^-----------------------------^------------- [2]
\------------ [3]
goto out_free;
}...
when multiple instances of perf record get to [1] at more or less same time and run access() one or more may get failure because the file does not exist yet (since the first instance did not have chance to link it yet). at this point the race moves to link() at [2] where first thread to get there links file and goes on but second one gets -EEXIST so it runs copyfile [3] which truncates the file.
recreator:
rm -rf /root/.debug
for cpu in $(awk '/processor/ {print $3}' /proc/cpuinfo); do
perf record -a -v -T -F 1000 -C $cpu \
-o perf-${cpu}.data sleep 5 2> /dev/null &done
wait
and simply search for empty files by
find /lib/modules/uname -r/kernel/* -size 0
--- tools/perf/util/build-id.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/util/build-id.c +2 -1
@@ -293,7 +293,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,if (is_kallsyms) {if (copyfile ("/proc/kallsyms" , filename)) goto out_free; - }else if (link (realname, filename) &©file (name, filename)) + }else if (link (realname, filename) && errno !=EEXIST && +copyfile (name, filename)) goto out_free; }
Signature · 1 line
2.1.0
Quoted 14 lines · Milos Vyletel
int build_id_cache__add_s(const char
*sbuild_id, const char *debugdir,
const char *name, boolis_kallsyms, boolis_vdso)
{
...if (access(filename, F_OK)) { ^--------------------------------------------------------- [1] if (is_kallsyms) { if (copyfile("/proc/kallsyms", filename)) goto out_free; } else if (link(realname, filename) && copyfile(name, filename)) ^-----------------------------^------------- [2] \------------ [3] goto out_free; }
Quoted 7 lines
...
when multiple instances of perf record get to [1] at more or less same time and run
access()one or more may get failure because the file does not exist yet (since the first instance did not have chance to link it yet). at this point the race moves tolink()at [2] where first thread to get there links file and goes on but second one gets-EEXISTso it runs copyfile [3] which truncates the file.
nice.. :-\
in addition we should use some inter-perf lock
on all .debug dir operations
jirka
Em Fri, Mar 20, 2015 at 01:18:07PM +0100, Jiri Olsa escreveu:
Quoted 23 lines · Milos Vyletel
Quoted 14 lines
int build_id_cache__add_s(const char
*sbuild_id, const char *debugdir,
const char *name, boolis_kallsyms, boolis_vdso)
{
...if (access(filename, F_OK)) { ^--------------------------------------------------------- [1] if (is_kallsyms) { if (copyfile("/proc/kallsyms", filename)) goto out_free; } else if (link(realname, filename) && copyfile(name, filename)) ^-----------------------------^------------- [2] \------------ [3] goto out_free; }Quoted 7 lines
...
when multiple instances of perf record get to [1] at more or less same time and run
access()one or more may get failure because the file does not exist yet (since the first instance did not have chance to link it yet). at this point the race moves tolink()at [2] where first thread to get there links file and goes on but second one gets-EEXISTso it runs copyfile [3] which truncates the file.
Quoted 6 lines
nice.. :-\
Acked-by Jiri Olsa <[email protected]>in addition we should use some inter-perf lock
on all .debug dir operations
Yeah, would be nice to have that improved.
Thanks, applied.
- Arnaldo
tip-bot for Milos Vyletel
merge bot[tip:perf/core] perf tools: Fix race in build_id_cache__add_s()
In reply to Milos Vyletelperf tools: Fix race in build_id_cache__add_s()
Commit-ID: 0635b0f71424be7706793ac260d063491a2889a0 Gitweb: http://git.kernel.org/tip/0635b0f71424be7706793ac260d063491a2889a0 Author: Milos Vyletel <[email protected]> AuthorDate: Fri, 20 Mar 2015 11:37:25 +0100 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Fri, 20 Mar 2015 17:49:50 -0300
perf tools: Fix race in build_id_cache__add_s()
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso)
{
...
if (access(filename, F_OK)) {
^--------------------------------------------------------- [1]
if (is_kallsyms) {
if (copyfile("/proc/kallsyms", filename))
goto out_free;
} else if (link(realname, filename) && copyfile(name, filename))
^-----------------------------^------------- [2]
\------------ [3]
goto out_free;
}...
When multiple instances of perf record get to [1] at more or less same time and run access() one or more may get failure because the file does not exist yet (since the first instance did not have chance to link it yet).
At this point the race moves to link() at [2] where first thread to get there links file and goes on but second one gets -EEXIST so it runs copyfile [3] which truncates the file.
reproducer:
rm -rf /root/.debug
for cpu in $(awk '/processor/ {print $3}' /proc/cpuinfo); do
perf record -a -v -T -F 1000 -C $cpu \
-o perf-${cpu}.data sleep 5 2> /dev/null &done
wait
and simply search for empty files by:
find /lib/modules/uname -r/kernel/* -size 0
--- tools/perf/util/build-id.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/util/build-id.c +2 -1
@@ -374,7 +374,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,if (is_kallsyms) {if (copyfile ("/proc/kallsyms" , filename)) goto out_free; - }else if (link (realname, filename) &©file (name, filename)) + }else if (link (realname, filename) && errno !=EEXIST && +copyfile (name, filename)) goto out_free; }