diff options
author | jilles <jilles@FreeBSD.org> | 2014-02-07 13:40:22 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2014-02-07 13:40:22 +0000 |
commit | 8479f1f726231aa2254c246e4b505d17ad724234 (patch) | |
tree | d44fb10280f0cdfd1f165f45ddbb26e53b536712 /lib | |
parent | 3fd6ea64d7c6ad6dfd667d4f2e59083edd9fce71 (diff) | |
download | FreeBSD-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.c | 12 |
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); } |