aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-05-05 16:25:35 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-05-07 22:52:39 -0400
commit99d825822eade8d827a1817357cbf3f889a552d6 (patch)
tree7861ddb5ed4b9c4b72a94e272b646ce9db64a71c
parent6a480a7842545ec520a91730209ec0bae41694c1 (diff)
get_rock_ridge_filename(): handle malformed NM entries
Payloads of NM entries are not supposed to contain NUL. When we run into such, only the part prior to the first NUL goes into the concatenation (i.e. the directory entry name being encoded by a bunch of NM entries). We do stop when the amount collected so far + the claimed amount in the current NM entry exceed 254. So far, so good, but what we return as the total length is the sum of *claimed* sizes, not the actual amount collected. And that can grow pretty large - not unlimited, since you'd need to put CE entries in between to be able to get more than the maximum that could be contained in one isofs directory entry / continuation chunk and we are stop once we'd encountered 32 CEs, but you can get about 8Kb easily. And that's what will be passed to readdir callback as the name length. 8Kb __copy_to_user() from a buffer allocated by __get_free_page() Cc: stable@vger.kernel.org # 0.98pl6+ (yes, really) Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/isofs/rock.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 5384ceb35b1c..98b3eb7d8eaf 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -203,6 +203,8 @@ int get_rock_ridge_filename(struct iso_directory_record *de,
203 int retnamlen = 0; 203 int retnamlen = 0;
204 int truncate = 0; 204 int truncate = 0;
205 int ret = 0; 205 int ret = 0;
206 char *p;
207 int len;
206 208
207 if (!ISOFS_SB(inode->i_sb)->s_rock) 209 if (!ISOFS_SB(inode->i_sb)->s_rock)
208 return 0; 210 return 0;
@@ -267,12 +269,17 @@ repeat:
267 rr->u.NM.flags); 269 rr->u.NM.flags);
268 break; 270 break;
269 } 271 }
270 if ((strlen(retname) + rr->len - 5) >= 254) { 272 len = rr->len - 5;
273 if (retnamlen + len >= 254) {
271 truncate = 1; 274 truncate = 1;
272 break; 275 break;
273 } 276 }
274 strncat(retname, rr->u.NM.name, rr->len - 5); 277 p = memchr(rr->u.NM.name, '\0', len);
275 retnamlen += rr->len - 5; 278 if (unlikely(p))
279 len = p - rr->u.NM.name;
280 memcpy(retname + retnamlen, rr->u.NM.name, len);
281 retnamlen += len;
282 retname[retnamlen] = '\0';
276 break; 283 break;
277 case SIG('R', 'E'): 284 case SIG('R', 'E'):
278 kfree(rs.buffer); 285 kfree(rs.buffer);