Commit 6e0362b3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-for-linux-4.10-rc5-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "I have a few more patches this week -- one to make the behavior of a
  quota id ioctl consistent with the other filesystems, and the rest
  improve validation of i_mode & i_size values coming into xfs so that
  we don't read off the ends of arrays or crash when handed garbage disk
  data.

  Summary:
   - inode i_mode sanitization
   - prevent overflows in getnextquota
   - minor build fixes"

* tag 'xfs-for-linux-4.10-rc5-1' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: fix xfs_mode_to_ftype() prototype
  xfs: don't wrap ID in xfs_dq_get_next_id
  xfs: sanity check inode di_mode
  xfs: sanity check inode mode when creating new dentry
  xfs: replace xfs_mode_to_ftype table with switch statement
  xfs: add missing include dependencies to xfs_dir2.h
  xfs: sanity check directory inode di_size
  xfs: make the ASSERT() condition likely
parents 81aaeaac fd29f7af
...@@ -36,21 +36,29 @@ ...@@ -36,21 +36,29 @@
struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR }; struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
/* /*
* @mode, if set, indicates that the type field needs to be set up. * Convert inode mode to directory entry filetype
* This uses the transformation from file mode to DT_* as defined in linux/fs.h
* for file type specification. This will be propagated into the directory
* structure if appropriate for the given operation and filesystem config.
*/ */
const unsigned char xfs_mode_to_ftype[S_IFMT >> S_SHIFT] = { unsigned char xfs_mode_to_ftype(int mode)
[0] = XFS_DIR3_FT_UNKNOWN, {
[S_IFREG >> S_SHIFT] = XFS_DIR3_FT_REG_FILE, switch (mode & S_IFMT) {
[S_IFDIR >> S_SHIFT] = XFS_DIR3_FT_DIR, case S_IFREG:
[S_IFCHR >> S_SHIFT] = XFS_DIR3_FT_CHRDEV, return XFS_DIR3_FT_REG_FILE;
[S_IFBLK >> S_SHIFT] = XFS_DIR3_FT_BLKDEV, case S_IFDIR:
[S_IFIFO >> S_SHIFT] = XFS_DIR3_FT_FIFO, return XFS_DIR3_FT_DIR;
[S_IFSOCK >> S_SHIFT] = XFS_DIR3_FT_SOCK, case S_IFCHR:
[S_IFLNK >> S_SHIFT] = XFS_DIR3_FT_SYMLINK, return XFS_DIR3_FT_CHRDEV;
}; case S_IFBLK:
return XFS_DIR3_FT_BLKDEV;
case S_IFIFO:
return XFS_DIR3_FT_FIFO;
case S_IFSOCK:
return XFS_DIR3_FT_SOCK;
case S_IFLNK:
return XFS_DIR3_FT_SYMLINK;
default:
return XFS_DIR3_FT_UNKNOWN;
}
}
/* /*
* ASCII case-insensitive (ie. A-Z) support for directories that was * ASCII case-insensitive (ie. A-Z) support for directories that was
...@@ -631,7 +639,8 @@ xfs_dir2_isblock( ...@@ -631,7 +639,8 @@ xfs_dir2_isblock(
if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
return rval; return rval;
rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize; rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
ASSERT(rval == 0 || args->dp->i_d.di_size == args->geo->blksize); if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize)
return -EFSCORRUPTED;
*vp = rval; *vp = rval;
return 0; return 0;
} }
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#ifndef __XFS_DIR2_H__ #ifndef __XFS_DIR2_H__
#define __XFS_DIR2_H__ #define __XFS_DIR2_H__
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
struct xfs_defer_ops; struct xfs_defer_ops;
struct xfs_da_args; struct xfs_da_args;
struct xfs_inode; struct xfs_inode;
...@@ -32,10 +35,9 @@ struct xfs_dir2_data_unused; ...@@ -32,10 +35,9 @@ struct xfs_dir2_data_unused;
extern struct xfs_name xfs_name_dotdot; extern struct xfs_name xfs_name_dotdot;
/* /*
* directory filetype conversion tables. * Convert inode mode to directory entry filetype
*/ */
#define S_SHIFT 12 extern unsigned char xfs_mode_to_ftype(int mode);
extern const unsigned char xfs_mode_to_ftype[];
/* /*
* directory operations vector for encode/decode routines * directory operations vector for encode/decode routines
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "xfs_icache.h" #include "xfs_icache.h"
#include "xfs_trans.h" #include "xfs_trans.h"
#include "xfs_ialloc.h" #include "xfs_ialloc.h"
#include "xfs_dir2.h"
/* /*
* Check that none of the inode's in the buffer have a next * Check that none of the inode's in the buffer have a next
...@@ -386,6 +387,7 @@ xfs_dinode_verify( ...@@ -386,6 +387,7 @@ xfs_dinode_verify(
xfs_ino_t ino, xfs_ino_t ino,
struct xfs_dinode *dip) struct xfs_dinode *dip)
{ {
uint16_t mode;
uint16_t flags; uint16_t flags;
uint64_t flags2; uint64_t flags2;
...@@ -396,8 +398,12 @@ xfs_dinode_verify( ...@@ -396,8 +398,12 @@ xfs_dinode_verify(
if (be64_to_cpu(dip->di_size) & (1ULL << 63)) if (be64_to_cpu(dip->di_size) & (1ULL << 63))
return false; return false;
/* No zero-length symlinks. */ mode = be16_to_cpu(dip->di_mode);
if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0) if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
return false;
/* No zero-length symlinks/dirs. */
if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0)
return false; return false;
/* only version 3 or greater inodes are extensively verified here */ /* only version 3 or greater inodes are extensively verified here */
......
...@@ -710,6 +710,10 @@ xfs_dq_get_next_id( ...@@ -710,6 +710,10 @@ xfs_dq_get_next_id(
/* Simple advance */ /* Simple advance */
next_id = *id + 1; next_id = *id + 1;
/* If we'd wrap past the max ID, stop */
if (next_id < *id)
return -ENOENT;
/* If new ID is within the current chunk, advancing it sufficed */ /* If new ID is within the current chunk, advancing it sufficed */
if (next_id % mp->m_quotainfo->qi_dqperchunk) { if (next_id % mp->m_quotainfo->qi_dqperchunk) {
*id = next_id; *id = next_id;
......
...@@ -97,13 +97,28 @@ xfs_init_security( ...@@ -97,13 +97,28 @@ xfs_init_security(
static void static void
xfs_dentry_to_name( xfs_dentry_to_name(
struct xfs_name *namep,
struct dentry *dentry)
{
namep->name = dentry->d_name.name;
namep->len = dentry->d_name.len;
namep->type = XFS_DIR3_FT_UNKNOWN;
}
static int
xfs_dentry_mode_to_name(
struct xfs_name *namep, struct xfs_name *namep,
struct dentry *dentry, struct dentry *dentry,
int mode) int mode)
{ {
namep->name = dentry->d_name.name; namep->name = dentry->d_name.name;
namep->len = dentry->d_name.len; namep->len = dentry->d_name.len;
namep->type = xfs_mode_to_ftype[(mode & S_IFMT) >> S_SHIFT]; namep->type = xfs_mode_to_ftype(mode);
if (unlikely(namep->type == XFS_DIR3_FT_UNKNOWN))
return -EFSCORRUPTED;
return 0;
} }
STATIC void STATIC void
...@@ -119,7 +134,7 @@ xfs_cleanup_inode( ...@@ -119,7 +134,7 @@ xfs_cleanup_inode(
* xfs_init_security we must back out. * xfs_init_security we must back out.
* ENOSPC can hit here, among other things. * ENOSPC can hit here, among other things.
*/ */
xfs_dentry_to_name(&teardown, dentry, 0); xfs_dentry_to_name(&teardown, dentry);
xfs_remove(XFS_I(dir), &teardown, XFS_I(inode)); xfs_remove(XFS_I(dir), &teardown, XFS_I(inode));
} }
...@@ -154,8 +169,12 @@ xfs_generic_create( ...@@ -154,8 +169,12 @@ xfs_generic_create(
if (error) if (error)
return error; return error;
/* Verify mode is valid also for tmpfile case */
error = xfs_dentry_mode_to_name(&name, dentry, mode);
if (unlikely(error))
goto out_free_acl;
if (!tmpfile) { if (!tmpfile) {
xfs_dentry_to_name(&name, dentry, mode);
error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip);
} else { } else {
error = xfs_create_tmpfile(XFS_I(dir), dentry, mode, &ip); error = xfs_create_tmpfile(XFS_I(dir), dentry, mode, &ip);
...@@ -248,7 +267,7 @@ xfs_vn_lookup( ...@@ -248,7 +267,7 @@ xfs_vn_lookup(
if (dentry->d_name.len >= MAXNAMELEN) if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
xfs_dentry_to_name(&name, dentry, 0); xfs_dentry_to_name(&name, dentry);
error = xfs_lookup(XFS_I(dir), &name, &cip, NULL); error = xfs_lookup(XFS_I(dir), &name, &cip, NULL);
if (unlikely(error)) { if (unlikely(error)) {
if (unlikely(error != -ENOENT)) if (unlikely(error != -ENOENT))
...@@ -275,7 +294,7 @@ xfs_vn_ci_lookup( ...@@ -275,7 +294,7 @@ xfs_vn_ci_lookup(
if (dentry->d_name.len >= MAXNAMELEN) if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
xfs_dentry_to_name(&xname, dentry, 0); xfs_dentry_to_name(&xname, dentry);
error = xfs_lookup(XFS_I(dir), &xname, &ip, &ci_name); error = xfs_lookup(XFS_I(dir), &xname, &ip, &ci_name);
if (unlikely(error)) { if (unlikely(error)) {
if (unlikely(error != -ENOENT)) if (unlikely(error != -ENOENT))
...@@ -310,7 +329,9 @@ xfs_vn_link( ...@@ -310,7 +329,9 @@ xfs_vn_link(
struct xfs_name name; struct xfs_name name;
int error; int error;
xfs_dentry_to_name(&name, dentry, inode->i_mode); error = xfs_dentry_mode_to_name(&name, dentry, inode->i_mode);
if (unlikely(error))
return error;
error = xfs_link(XFS_I(dir), XFS_I(inode), &name); error = xfs_link(XFS_I(dir), XFS_I(inode), &name);
if (unlikely(error)) if (unlikely(error))
...@@ -329,7 +350,7 @@ xfs_vn_unlink( ...@@ -329,7 +350,7 @@ xfs_vn_unlink(
struct xfs_name name; struct xfs_name name;
int error; int error;
xfs_dentry_to_name(&name, dentry, 0); xfs_dentry_to_name(&name, dentry);
error = xfs_remove(XFS_I(dir), &name, XFS_I(d_inode(dentry))); error = xfs_remove(XFS_I(dir), &name, XFS_I(d_inode(dentry)));
if (error) if (error)
...@@ -359,7 +380,9 @@ xfs_vn_symlink( ...@@ -359,7 +380,9 @@ xfs_vn_symlink(
mode = S_IFLNK | mode = S_IFLNK |
(irix_symlink_mode ? 0777 & ~current_umask() : S_IRWXUGO); (irix_symlink_mode ? 0777 & ~current_umask() : S_IRWXUGO);
xfs_dentry_to_name(&name, dentry, mode); error = xfs_dentry_mode_to_name(&name, dentry, mode);
if (unlikely(error))
goto out;
error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip); error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip);
if (unlikely(error)) if (unlikely(error))
...@@ -395,6 +418,7 @@ xfs_vn_rename( ...@@ -395,6 +418,7 @@ xfs_vn_rename(
{ {
struct inode *new_inode = d_inode(ndentry); struct inode *new_inode = d_inode(ndentry);
int omode = 0; int omode = 0;
int error;
struct xfs_name oname; struct xfs_name oname;
struct xfs_name nname; struct xfs_name nname;
...@@ -405,8 +429,14 @@ xfs_vn_rename( ...@@ -405,8 +429,14 @@ xfs_vn_rename(
if (flags & RENAME_EXCHANGE) if (flags & RENAME_EXCHANGE)
omode = d_inode(ndentry)->i_mode; omode = d_inode(ndentry)->i_mode;
xfs_dentry_to_name(&oname, odentry, omode); error = xfs_dentry_mode_to_name(&oname, odentry, omode);
xfs_dentry_to_name(&nname, ndentry, d_inode(odentry)->i_mode); if (omode && unlikely(error))
return error;
error = xfs_dentry_mode_to_name(&nname, ndentry,
d_inode(odentry)->i_mode);
if (unlikely(error))
return error;
return xfs_rename(XFS_I(odir), &oname, XFS_I(d_inode(odentry)), return xfs_rename(XFS_I(odir), &oname, XFS_I(d_inode(odentry)),
XFS_I(ndir), &nname, XFS_I(ndir), &nname,
......
...@@ -331,11 +331,11 @@ static inline __uint64_t howmany_64(__uint64_t x, __uint32_t y) ...@@ -331,11 +331,11 @@ static inline __uint64_t howmany_64(__uint64_t x, __uint32_t y)
} }
#define ASSERT_ALWAYS(expr) \ #define ASSERT_ALWAYS(expr) \
(unlikely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__)) (likely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
#ifdef DEBUG #ifdef DEBUG
#define ASSERT(expr) \ #define ASSERT(expr) \
(unlikely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__)) (likely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
#ifndef STATIC #ifndef STATIC
# define STATIC noinline # define STATIC noinline
...@@ -346,7 +346,7 @@ static inline __uint64_t howmany_64(__uint64_t x, __uint32_t y) ...@@ -346,7 +346,7 @@ static inline __uint64_t howmany_64(__uint64_t x, __uint32_t y)
#ifdef XFS_WARN #ifdef XFS_WARN
#define ASSERT(expr) \ #define ASSERT(expr) \
(unlikely(expr) ? (void)0 : asswarn(#expr, __FILE__, __LINE__)) (likely(expr) ? (void)0 : asswarn(#expr, __FILE__, __LINE__))
#ifndef STATIC #ifndef STATIC
# define STATIC static noinline # define STATIC static noinline
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment