mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
isofs: validate directory records consistently
isofs_export_get_parent() assumes that the first two directory records are valid "." and ".." entries. A malformed image can provide an invalid length for the first entry, causing the computed ".." offset to point outside the received block. Add a shared directory record validator and use it in NFS get_parent, readdir and lookup. This keeps the basic directory record length checks consistent across all directory users before they consume the name field or use one record length to find the next entry. Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Link: https://patch.msgid.link/20260728074349.417508-1-chenyichong@uniontech.com Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
04b7d6a32e
commit
e2ee4078ec
|
|
@ -149,10 +149,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *file,
|
|||
}
|
||||
de = tmpde;
|
||||
}
|
||||
/* Basic sanity check, whether name doesn't exceed dir entry */
|
||||
if (de_len < sizeof(struct iso_directory_record) ||
|
||||
de_len < de->name_len[0] +
|
||||
sizeof(struct iso_directory_record)) {
|
||||
if (!isofs_dir_record_valid(de, de == tmpde ? 0 : offset_saved,
|
||||
de == tmpde ? de_len : bufsize)) {
|
||||
printk(KERN_NOTICE "iso9660: Corrupted directory entry"
|
||||
" in block %lu of inode %llu\n", block,
|
||||
inode->i_ino);
|
||||
|
|
@ -300,4 +298,3 @@ const struct inode_operations isofs_dir_inode_operations =
|
|||
.fileattr_get = isofs_fileattr_get,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -83,13 +83,21 @@ static struct dentry *isofs_export_get_parent(struct dentry *child)
|
|||
|
||||
/* This is the "." entry. */
|
||||
de = (struct iso_directory_record*)bh->b_data;
|
||||
if (!isofs_dir_record_valid(de, 0, child_inode->i_sb->s_blocksize) ||
|
||||
isonum_711(de->name_len) != 1 || de->name[0] != 0) {
|
||||
printk(KERN_ERR "isofs: Unable to find the \".\" directory for NFS.\n");
|
||||
rv = ERR_PTR(-EACCES);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The ".." entry is always the second entry. */
|
||||
parent_offset = (unsigned long)isonum_711(de->length);
|
||||
de = (struct iso_directory_record*)(bh->b_data + parent_offset);
|
||||
|
||||
/* Verify it is in fact the ".." entry. */
|
||||
if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) {
|
||||
if (!isofs_dir_record_valid(de, parent_offset,
|
||||
child_inode->i_sb->s_blocksize) ||
|
||||
isonum_711(de->name_len) != 1 || de->name[0] != 1) {
|
||||
printk(KERN_ERR "isofs: Unable to find the \"..\" "
|
||||
"directory for NFS.\n");
|
||||
rv = ERR_PTR(-EACCES);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ struct inode; /* To make gcc happy */
|
|||
extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated);
|
||||
extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
|
||||
extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
|
||||
bool isofs_dir_record_valid(struct iso_directory_record *de,
|
||||
unsigned long offset,
|
||||
unsigned long bufsize);
|
||||
|
||||
int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *);
|
||||
int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,26 @@
|
|||
#include <linux/gfp.h>
|
||||
#include "isofs.h"
|
||||
|
||||
bool isofs_dir_record_valid(struct iso_directory_record *de,
|
||||
unsigned long offset,
|
||||
unsigned long bufsize)
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned int name_len;
|
||||
unsigned long min_len = offsetof(struct iso_directory_record, name);
|
||||
|
||||
if (offset > bufsize || bufsize - offset < min_len)
|
||||
return false;
|
||||
|
||||
len = isonum_711(de->length);
|
||||
name_len = isonum_711(de->name_len);
|
||||
if (len < min_len || name_len > len - min_len)
|
||||
return false;
|
||||
if (len > bufsize - offset)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
|
||||
{
|
||||
|
|
@ -88,16 +108,16 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry,
|
|||
de = tmpde;
|
||||
}
|
||||
|
||||
dlen = de->name_len[0];
|
||||
dpnt = de->name;
|
||||
/* Basic sanity check, whether name doesn't exceed dir entry */
|
||||
if (de_len < dlen + sizeof(struct iso_directory_record)) {
|
||||
if (!isofs_dir_record_valid(de, de == tmpde ? 0 : offset_saved,
|
||||
de == tmpde ? de_len : bufsize)) {
|
||||
printk(KERN_NOTICE "iso9660: Corrupted directory entry"
|
||||
" in block %lu of inode %llu\n", block,
|
||||
dir->i_ino);
|
||||
brelse(bh);
|
||||
return 0;
|
||||
}
|
||||
dlen = de->name_len[0];
|
||||
dpnt = de->name;
|
||||
|
||||
if (sbi->s_rock &&
|
||||
((i = get_rock_ridge_filename(de, tmpname, dir)))) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user