Re: harden against corrupt symlinks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Aug 11, 2006  15:48 -0700, Andrew Morton wrote:
> > > --- linux-2.6/fs/namei.c	2005-05-24 11:13:09.000000000 -0700
> > > +++ linux-2.6/fs/namei.c	2005-05-24 11:13:12.000000000 -0700
> > > @@ -1936,7 +1936,12 @@ int vfs_readlink(struct dentry *dentry, 
> > >  	if (IS_ERR(link))
> > >  		goto out;
> > >  
> > > -	len = strlen(link);
> > > +	len = strnlen(link, PATH_MAX);
> > > +	if (len == PATH_MAX) {
> > > +		len = -ENAMETOOLONG;
> > > +		goto out;
> > > +	}
> > > +
> > >  	if (len > (unsigned) buflen)
> > >  		len = buflen;
> > >  	if (copy_to_user(buffer, link, len))
> > > @@ -1953,6 +1958,11 @@ __vfs_follow_link(struct nameidata *nd, 
> > >  	if (IS_ERR(link))
> > >  		goto fail;
> > >  
> > > +	if (strnlen(link, PATH_MAX) == PATH_MAX) {
> > > +		link = ERR_PTR(-ENAMETOOLONG);
> > > +		goto fail;
> > > +	}
> > > +
> > >  	if (*link == '/') {
> > >  		path_release(nd);
> > >  		if (!walk_init_root(link, nd))
> > 
> 
> Should we instead be treating this as a filesystem driver bug, and fix up
> the filesystem(s) to not return overly-long pathname components?
> 
> Running strnlen() against each component in __vfs_follow_link() looks
> expensive.  Can it be avoided?
> 
> Given that this is the VFS correcting for filesystem misbehaviour, it would
> seem to make sense to perform this correction at a low level, immediately
> after the filesytem driver has returned us the pathname component.  An
> apparently-obvious way of doing this is to wrap ->follow_link.  Can anyone
> think of a smarter way?

#define PATH_MAX        4096    /* # chars in a path name including nul */

One possibility is for the case of long symlinks to always set the last byte
of the page to NUL to ensure that the link is terminated.  This appears
easily doable by having page_getlink() do the NUL termination after kmap()
but before returning, something like:

 static char *page_getlink(struct dentry * dentry, struct page **ppage)
 {
	char *link;
 	page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
 			       NULL);
 	if (IS_ERR(page))
 		goto sync_fail;
 	wait_on_page_locked(page);
 	if (!PageUptodate(page))
 		goto async_fail;
 	*ppage = page;
-	return kmap(page);
+	link = kmap(page);
+	/* PATH_MAX is strictly <= PAGE_SIZE */
+	link[PATH_MAX - 1] = '\0';
+	return link;


Many of the other filesystems that don't use page_follow_link_light()
already do NUL termination themselves.

nfs_follow_link() is very similar, but not identical and needs the same fix.

ocfs2_page_getlink() is an exact duplicate of page_getlink() and the bug
duplication could be avoided if page_getlink() was exported.  Otherwise it
needs the same fix.


I _think_ PATH_MAX is the right thing here (instead of PAGE_SIZE), since
the caller expects at most PATH_MAX in the returned link, and PAGE_SIZE
may be considerably larger.  I don't think PATH_MAX will ever be larger
than PAGE_SIZE.  We could also use min(PAGE_SIZE, PATH_MAX) and it would
be resolved at compile time, but it seems wishy-washy to me.

The other option is to actually get the link length out of the filesystem
itself, and avoid strlen(link) entirely, but that is a more complex change.

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux