summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2014-02-07 13:40:22 +0000
committerjilles <jilles@FreeBSD.org>2014-02-07 13:40:22 +0000
commit8479f1f726231aa2254c246e4b505d17ad724234 (patch)
treed44fb10280f0cdfd1f165f45ddbb26e53b536712 /lib
parent3fd6ea64d7c6ad6dfd667d4f2e59083edd9fce71 (diff)
downloadFreeBSD-src-8479f1f726231aa2254c246e4b505d17ad724234.zip
FreeBSD-src-8479f1f726231aa2254c246e4b505d17ad724234.tar.gz
fts: Fix double-free with conflicting concurrent modifications.
If rare conditions such as concurrent conflicting manipulation of the filesystem occur, fts_read() frees the current FTSENT without adjusting the pointers in the FTS accordingly. A later fts_close() then frees the same FTSENT again. Reported by: pho Tested by: pho MFC after: 1 week
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/gen/fts.c12
1 files changed, 8 insertions, 4 deletions
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index e5f7387..1724f9c0fd 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -406,8 +406,6 @@ fts_read(FTS *sp)
/* Move to the next node on this level. */
next: tmp = p;
if ((p = p->fts_link) != NULL) {
- free(tmp);
-
/*
* If reached the top, return to the original directory (or
* the root of the tree), and load the paths for the next root.
@@ -417,6 +415,7 @@ next: tmp = p;
SET(FTS_STOP);
return (NULL);
}
+ free(tmp);
fts_load(sp, p);
return (sp->fts_cur = p);
}
@@ -426,8 +425,10 @@ next: tmp = p;
* ignore. If followed, get a file descriptor so we can
* get back if necessary.
*/
- if (p->fts_instr == FTS_SKIP)
+ if (p->fts_instr == FTS_SKIP) {
+ free(tmp);
goto next;
+ }
if (p->fts_instr == FTS_FOLLOW) {
p->fts_info = fts_stat(sp, p, 1, -1);
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
@@ -441,6 +442,8 @@ next: tmp = p;
p->fts_instr = FTS_NOINSTR;
}
+ free(tmp);
+
name: t = sp->fts_path + NAPPEND(p->fts_parent);
*t++ = '/';
memmove(t, p->fts_name, p->fts_namelen + 1);
@@ -449,13 +452,13 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
/* Move up to the parent node. */
p = tmp->fts_parent;
- free(tmp);
if (p->fts_level == FTS_ROOTPARENTLEVEL) {
/*
* Done; free everything up and set errno to 0 so the user
* can distinguish between error and EOF.
*/
+ free(tmp);
free(p);
errno = 0;
return (sp->fts_cur = NULL);
@@ -488,6 +491,7 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
SET(FTS_STOP);
return (NULL);
}
+ free(tmp);
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
return (sp->fts_cur = p);
}
OpenPOWER on IntegriCloud