-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmGWL+AACgkQnJ2qBz9k
 QNnK3ggAtJLlaCwg4TxyIIGEWBP/KBKs2STLf80VDavhxZuplKKZ0o5/9IPZ2uu9
 v9SgME4hnGpUNaECttVWqijT823SiIxA/4uFSBnN1hLlvPj2Wkwl0oRWb6vCFzZ5
 OBUiZrycZKxEq78p2Ao8+FxeRsJ9RuXHnPkZULAqizkEue23d+gW33cO0x//UwB0
 lB6i32yDqCmOAtLaT1Div5ttJ3oM/hGBbOURkh3YB7jV2MxojHX28ZjDv657S30k
 lhpQwvmHF70jM7MPWbxtcbSTBdPKQdaF5+oDqpAFwDQn3nhEk1sjw6IrCUKr2NAK
 Z+YFlRgv5eD+loRPEBVVU/6QrmfSEQ==
 =ueAF
 -----END PGP SIGNATURE-----

Merge tag 'fs_for_v5.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull UDF fix from Jan Kara:
 "A fix for a long-standing UDF bug where we were not properly
  validating directory position inside readdir"

* tag 'fs_for_v5.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  udf: Fix crash after seekdir
This commit is contained in:
Linus Torvalds 2021-11-18 12:31:29 -08:00
commit db850a9b8d
3 changed files with 35 additions and 2 deletions

View file

@ -31,6 +31,7 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/bio.h>
#include <linux/iversion.h>
#include "udf_i.h"
#include "udf_sb.h"
@ -43,7 +44,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
struct fileIdentDesc *fi = NULL;
struct fileIdentDesc cfi;
udf_pblk_t block, iblock;
loff_t nf_pos;
loff_t nf_pos, emit_pos = 0;
int flen;
unsigned char *fname = NULL, *copy_name = NULL;
unsigned char *nameptr;
@ -57,6 +58,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
int i, num, ret = 0;
struct extent_position epos = { NULL, 0, {0, 0} };
struct super_block *sb = dir->i_sb;
bool pos_valid = false;
if (ctx->pos == 0) {
if (!dir_emit_dot(file, ctx))
@ -67,6 +69,21 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
if (nf_pos >= size)
goto out;
/*
* Something changed since last readdir (either lseek was called or dir
* changed)? We need to verify the position correctly points at the
* beginning of some dir entry so that the directory parsing code does
* not get confused. Since UDF does not have any reliable way of
* identifying beginning of dir entry (names are under user control),
* we need to scan the directory from the beginning.
*/
if (!inode_eq_iversion(dir, file->f_version)) {
emit_pos = nf_pos;
nf_pos = 0;
} else {
pos_valid = true;
}
fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
if (!fname) {
ret = -ENOMEM;
@ -122,13 +139,21 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
while (nf_pos < size) {
struct kernel_lb_addr tloc;
loff_t cur_pos = nf_pos;
ctx->pos = (nf_pos >> 2) + 1;
/* Update file position only if we got past the current one */
if (nf_pos >= emit_pos) {
ctx->pos = (nf_pos >> 2) + 1;
pos_valid = true;
}
fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
&elen, &offset);
if (!fi)
goto out;
/* Still not at offset where user asked us to read from? */
if (cur_pos < emit_pos)
continue;
liu = le16_to_cpu(cfi.lengthOfImpUse);
lfi = cfi.lengthFileIdent;
@ -186,8 +211,11 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
} /* end while */
ctx->pos = (nf_pos >> 2) + 1;
pos_valid = true;
out:
if (pos_valid)
file->f_version = inode_query_iversion(dir);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);

View file

@ -30,6 +30,7 @@
#include <linux/sched.h>
#include <linux/crc-itu-t.h>
#include <linux/exportfs.h>
#include <linux/iversion.h>
static inline int udf_match(int len1, const unsigned char *name1, int len2,
const unsigned char *name2)
@ -134,6 +135,8 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
mark_buffer_dirty_inode(fibh->ebh, inode);
mark_buffer_dirty_inode(fibh->sbh, inode);
}
inode_inc_iversion(inode);
return 0;
}

View file

@ -57,6 +57,7 @@
#include <linux/crc-itu-t.h>
#include <linux/log2.h>
#include <asm/byteorder.h>
#include <linux/iversion.h>
#include "udf_sb.h"
#include "udf_i.h"
@ -149,6 +150,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
init_rwsem(&ei->i_data_sem);
ei->cached_extent.lstart = -1;
spin_lock_init(&ei->i_extent_cache_lock);
inode_set_iversion(&ei->vfs_inode, 1);
return &ei->vfs_inode;
}