diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index 8860948ef9ca..33db2e9dc908 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -3,6 +3,8 @@ # Makefile for the linux ext2-filesystem routines. # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_EXT2_FS) += ext2.o ext2-y := balloc.o dir.o file.o ialloc.o inode.o \ diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index adf0f31fbddd..80acc1e19387 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -334,6 +334,7 @@ search_reserve_window(struct rb_root *root, ext2_fsblk_t goal) */ void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) { struct rb_root *root = &EXT2_SB(sb)->s_rsv_window_root; struct rb_node *node = &rsv->rsv_node; @@ -373,6 +374,7 @@ void ext2_rsv_window_add(struct super_block *sb, */ static void rsv_window_remove(struct super_block *sb, struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) { rsv->rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; rsv->rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; @@ -414,6 +416,7 @@ static inline int rsv_is_empty(struct ext2_reserve_window *rsv) * Needs truncate_mutex protection prior to calling this function. */ void ext2_init_block_alloc_info(struct inode *inode) + __must_hold(&EXT2_I(inode)->truncate_mutex) { struct ext2_inode_info *ei = EXT2_I(inode); struct ext2_block_alloc_info *block_i; @@ -758,6 +761,7 @@ static int find_next_reservable_window( struct super_block * sb, ext2_fsblk_t start_block, ext2_fsblk_t last_block) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) { struct rb_node *next; struct ext2_reserve_window_node *rsv, *prev; diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index cf2796e5703e..7bdada93dd06 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -77,8 +77,8 @@ struct ext2_sb_info { unsigned long s_gdb_count; /* Number of group descriptor blocks */ unsigned long s_desc_per_block; /* Number of group descriptors per block */ unsigned long s_groups_count; /* Number of groups in the fs */ - unsigned long s_overhead_last; /* Last calculated overhead */ - unsigned long s_blocks_last; /* Last seen block count */ + unsigned long s_overhead_last __guarded_by(&s_lock); /* Last calculated overhead */ + unsigned long s_blocks_last __guarded_by(&s_lock); /* Last seen block count */ struct buffer_head * s_sbh; /* Buffer containing the super block */ struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */ struct buffer_head ** s_group_desc; @@ -86,14 +86,14 @@ struct ext2_sb_info { unsigned long s_sb_block; kuid_t s_resuid; kgid_t s_resgid; - unsigned short s_mount_state; + unsigned short s_mount_state __guarded_by(&s_lock); unsigned short s_pad; int s_addr_per_block_bits; int s_desc_per_block_bits; int s_inode_size; int s_first_ino; spinlock_t s_next_gen_lock; - u32 s_next_generation; + u32 s_next_generation __guarded_by(&s_next_gen_lock); unsigned long s_dir_count; u8 *s_debts; struct percpu_counter s_freeblocks_counter; @@ -102,7 +102,7 @@ struct ext2_sb_info { struct blockgroup_lock *s_blockgroup_lock; /* root of the per fs reservation window tree */ spinlock_t s_rsv_window_lock; - struct rb_root s_rsv_window_root; + struct rb_root s_rsv_window_root __guarded_by(&s_rsv_window_lock); struct ext2_reserve_window_node s_rsv_window_head; /* * s_lock protects against concurrent modifications of s_mount_state, @@ -710,8 +710,10 @@ extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, struct buffer_head ** bh); extern void ext2_discard_reservation (struct inode *); extern int ext2_should_retry_alloc(struct super_block *sb, int *retries); -extern void ext2_init_block_alloc_info(struct inode *); -extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv); +extern void ext2_init_block_alloc_info(struct inode *inode) + __must_hold(&EXT2_I(inode)->truncate_mutex); +extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock); /* dir.c */ int ext2_add_link(struct dentry *, struct inode *); @@ -761,7 +763,8 @@ extern __printf(3, 4) void ext2_error(struct super_block *, const char *, const char *, ...); extern __printf(3, 4) void ext2_msg(struct super_block *, const char *, const char *, ...); -extern void ext2_update_dynamic_rev (struct super_block *sb); +extern void ext2_update_dynamic_rev(struct super_block *sb) + __must_hold(&EXT2_SB(sb)->s_lock); extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es, int wait); diff --git a/fs/ext2/file.c b/fs/ext2/file.c index b9020df7d89e..67fe423c3828 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -135,6 +135,13 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) int ret2; iocb->ki_flags &= ~IOCB_DIRECT; + + /* + * Prevent concurrent direct I/O and buffered I/O to the same file + * range. Wait for in-flight DIO to finish before dirtying pages. + */ + inode_dio_wait(inode); + pos = iocb->ki_pos; status = generic_perform_write(iocb, from); if (unlikely(status < 0)) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index b52ded333aa9..12ac4cfe1500 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -327,6 +327,7 @@ static ext2_fsblk_t ext2_find_near(struct inode *inode, Indirect *ind) static inline ext2_fsblk_t ext2_find_goal(struct inode *inode, long block, Indirect *partial) + __must_hold(&EXT2_I(inode)->truncate_mutex) { struct ext2_block_alloc_info *block_i; @@ -397,6 +398,7 @@ ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks, static int ext2_alloc_blocks(struct inode *inode, ext2_fsblk_t goal, int indirect_blks, int blks, ext2_fsblk_t new_blocks[4], int *err) + __must_hold(&EXT2_I(inode)->truncate_mutex) { int target, i; unsigned long count = 0; @@ -477,6 +479,7 @@ static int ext2_alloc_blocks(struct inode *inode, static int ext2_alloc_branch(struct inode *inode, int indirect_blks, int *blks, ext2_fsblk_t goal, int *offsets, Indirect *branch) + __must_hold(&EXT2_I(inode)->truncate_mutex) { int blocksize = inode->i_sb->s_blocksize; int i, n = 0; @@ -558,6 +561,7 @@ static int ext2_alloc_branch(struct inode *inode, */ static void ext2_splice_branch(struct inode *inode, long block, Indirect *where, int num, int blks) + __must_hold(&EXT2_I(inode)->truncate_mutex) { int i; struct ext2_block_alloc_info *block_i; @@ -1002,6 +1006,7 @@ static Indirect *ext2_find_shared(struct inode *inode, int offsets[4], Indirect chain[4], __le32 *top) + __must_hold(&EXT2_I(inode)->truncate_mutex) { Indirect *partial, *p; int k, err; @@ -1057,6 +1062,7 @@ static Indirect *ext2_find_shared(struct inode *inode, * appropriately. */ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) + __must_hold(&EXT2_I(inode)->truncate_mutex) { ext2_fsblk_t block_to_free = 0, count = 0; ext2_fsblk_t nr; @@ -1097,6 +1103,7 @@ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) * appropriately. */ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth) + __must_hold(&EXT2_I(inode)->truncate_mutex) { struct buffer_head * bh; ext2_fsblk_t nr; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index a40f530872a4..fb217ef2d69f 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -127,6 +127,7 @@ void ext2_msg(struct super_block *sb, const char *prefix, * This must be called with sbi->s_lock held. */ void ext2_update_dynamic_rev(struct super_block *sb) + __must_hold(&EXT2_SB(sb)->s_lock) { struct ext2_super_block *es = EXT2_SB(sb)->s_es; @@ -633,6 +634,7 @@ static int ext2_parse_param(struct fs_context *fc, struct fs_parameter *param) static int ext2_setup_super (struct super_block * sb, struct ext2_super_block * es, int read_only) + __must_hold(&EXT2_SB(sb)->s_lock) { int res = 0; struct ext2_sb_info *sbi = EXT2_SB(sb); @@ -894,7 +896,7 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; - spin_lock_init(&sbi->s_lock); + guard(spinlock_init)(&EXT2_SB(sb)->s_lock); ret = -EINVAL; /* @@ -1126,23 +1128,26 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) goto failed_mount2; } sbi->s_gdb_count = db_count; - sbi->s_next_generation = get_random_u32(); spin_lock_init(&sbi->s_next_gen_lock); + scoped_guard(spinlock, &sbi->s_next_gen_lock) + sbi->s_next_generation = get_random_u32(); /* per filesystem reservation list head & lock */ spin_lock_init(&sbi->s_rsv_window_lock); - sbi->s_rsv_window_root = RB_ROOT; - /* - * Add a single, static dummy reservation to the start of the - * reservation window list --- it gives us a placeholder for - * append-at-start-of-list which makes the allocation logic - * _much_ simpler. - */ - sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; - sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; - sbi->s_rsv_window_head.rsv_alloc_hit = 0; - sbi->s_rsv_window_head.rsv_goal_size = 0; - ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); + scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) { + sbi->s_rsv_window_root = RB_ROOT; + /* + * Add a single, static dummy reservation to the start of the + * reservation window list --- it gives us a placeholder for + * append-at-start-of-list which makes the allocation logic + * _much_ simpler. + */ + sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; + sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; + sbi->s_rsv_window_head.rsv_alloc_hit = 0; + sbi->s_rsv_window_head.rsv_goal_size = 0; + ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); + } err = percpu_counter_init(&sbi->s_freeblocks_counter, ext2_count_free_blocks(sb), GFP_KERNEL); diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c index f9869d62b850..2d23abaeb874 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -39,7 +39,7 @@ static DEFINE_MUTEX(zisofs_zlib_lock); */ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, loff_t block_end, int pcount, - struct page **pages, unsigned poffset, + struct folio **folios, unsigned int poffset, int *errp) { unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1]; @@ -66,11 +66,12 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, if (block_size == 0) { for ( i = 0 ; i < pcount ; i++ ) { unsigned int off = i ? 0 : poffset; + struct folio *folio = folios[i]; - if (!pages[i]) + if (!folio) continue; - memzero_page(pages[i], off, PAGE_SIZE - off); - SetPageUptodate(pages[i]); + folio_zero_range(folio, off, folio_size(folio) - off); + folio_mark_uptodate(folio); } return (((loff_t)pcount) << PAGE_SHIFT) - poffset; } @@ -119,11 +120,12 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, while (curpage < pcount && curbh < haveblocks && zerr != Z_STREAM_END) { + struct folio *folio = folios[curpage]; + if (!stream.avail_out) { - if (pages[curpage]) { - stream.next_out = kmap_local_page(pages[curpage]) - + poffset; - stream.avail_out = PAGE_SIZE - poffset; + if (folio) { + stream.next_out = kmap_local_folio(folio, poffset); + stream.avail_out = folio_size(folio) - poffset; poffset = 0; } else { stream.next_out = (void *)&zisofs_sink_page; @@ -173,9 +175,9 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, if (!stream.avail_out) { /* This page completed */ - if (pages[curpage]) { - flush_dcache_page(pages[curpage]); - SetPageUptodate(pages[curpage]); + if (folio) { + flush_dcache_folio(folio); + folio_mark_uptodate(folio); } if (stream.next_out != (unsigned char *)zisofs_sink_page) { kunmap_local(stream.next_out); @@ -206,7 +208,7 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, * fills in other pages if we have data for them. */ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, - struct page **pages) + struct folio **folios) { loff_t start_off, end_off; loff_t block_start, block_end; @@ -221,14 +223,14 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, int err; loff_t ret; - BUG_ON(!pages[full_page]); + BUG_ON(!folios[full_page]); /* * We want to read at least 'full_page' page. Because we have to * uncompress the whole compression block anyway, fill the surrounding * pages with the data we have anyway... */ - start_off = page_offset(pages[full_page]); + start_off = folio_pos(folios[full_page]); end_off = min_t(loff_t, start_off + PAGE_SIZE, inode->i_size); cstart_block = start_off >> zisofs_block_shift; @@ -267,9 +269,9 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, } err = 0; ret = zisofs_uncompress_block(inode, block_start, block_end, - pcount, pages, poffset, &err); + pcount, folios, poffset, &err); poffset += ret; - pages += poffset >> PAGE_SHIFT; + folios += poffset >> PAGE_SHIFT; pcount -= poffset >> PAGE_SHIFT; full_page -= poffset >> PAGE_SHIFT; poffset &= ~PAGE_MASK; @@ -289,9 +291,11 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, cstart_block++; } - if (poffset && *pages) { - memzero_page(*pages, poffset, PAGE_SIZE - poffset); - SetPageUptodate(*pages); + if (poffset && *folios) { + struct folio *folio = *folios; + + folio_zero_range(folio, poffset, folio_size(folio) - poffset); + folio_mark_uptodate(folio); } brelse(bh); return 0; @@ -312,7 +316,7 @@ static int zisofs_read_folio(struct file *file, struct folio *folio) unsigned int zisofs_pages_per_cblock = PAGE_SHIFT <= zisofs_block_shift ? (1 << (zisofs_block_shift - PAGE_SHIFT)) : 0; - struct page **pages; + struct folio **folios; pgoff_t index = folio->index, end_index; end_index = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; @@ -336,33 +340,38 @@ static int zisofs_read_folio(struct file *file, struct folio *folio) full_page = 0; pcount = 1; } - pages = kzalloc_objs(*pages, - max_t(unsigned int, zisofs_pages_per_cblock, 1)); - if (!pages) { + folios = kzalloc_objs(*folios, + max_t(unsigned int, zisofs_pages_per_cblock, 1)); + if (!folios) { folio_unlock(folio); return -ENOMEM; } - pages[full_page] = &folio->page; + folios[full_page] = folio; for (i = 0; i < pcount; i++, index++) { - if (i != full_page) - pages[i] = grab_cache_page_nowait(mapping, index); + if (i == full_page) + continue; + folios[i] = __filemap_get_folio(mapping, index, + FGP_LOCK | FGP_CREAT | FGP_NOWAIT, + mapping_gfp_mask(mapping)); + if (IS_ERR(folios[i])) + folios[i] = NULL; } - err = zisofs_fill_pages(inode, full_page, pcount, pages); + err = zisofs_fill_pages(inode, full_page, pcount, folios); - /* Release any residual pages, do not SetPageUptodate */ + /* Release any residual folios, do not mark them uptodate */ for (i = 0; i < pcount; i++) { - if (pages[i]) { - flush_dcache_page(pages[i]); - unlock_page(pages[i]); + if (folios[i]) { + flush_dcache_folio(folios[i]); + folio_unlock(folios[i]); if (i != full_page) - put_page(pages[i]); + folio_put(folios[i]); } - } + } /* At this point, err contains 0 or -EIO depending on the "critical" page */ - kfree(pages); + kfree(folios); return err; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 28741251d56e..a6cfb8699481 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -186,7 +186,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, map = 1; if (sbi->s_rock) { - len = get_rock_ridge_filename(de, tmpname, inode); + len = get_rock_ridge_filename(de, tmpname, NAME_MAX + 1, + inode); if (len != 0) { /* may be -1 */ p = tmpname; map = 0; @@ -195,7 +196,9 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, if (map) { #ifdef CONFIG_JOLIET if (sbi->s_joliet_level) { - len = get_joliet_filename(de, tmpname, inode); + len = get_joliet_filename(de, tmpname, + JOLIET_NAME_MAX + 1, + inode); p = tmpname; } else #endif @@ -233,7 +236,12 @@ static int isofs_readdir(struct file *file, struct dir_context *ctx) char *tmpname; struct inode *inode = file_inode(file); - tmpname = kmalloc(1024, GFP_KERNEL); + /* + * Rockridge can produce names of NAME_MAX size, Acorn extensions at + * most 227 chars. + */ + BUILD_BUG_ON(JOLIET_NAME_MAX < NAME_MAX || JOLIET_NAME_MAX < 227); + tmpname = kmalloc(JOLIET_NAME_MAX + 1, GFP_KERNEL); if (tmpname == NULL) return -ENOMEM; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 337836a0a170..4824e42995cc 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -821,6 +821,8 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc) if (!sb_set_blocksize(s, orig_zonesize)) goto out_freesbi; + sbi->s_session_start = (sector_t)vol_desc_start << + (ISOFS_BLOCK_BITS - s->s_blocksize_bits); sbi->s_nls_iocharset = NULL; #ifdef CONFIG_JOLIET @@ -1010,7 +1012,8 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) buf->f_files = ISOFS_SB(sb)->s_ninodes; buf->f_ffree = 0; buf->f_fsid = u64_to_fsid(id); - buf->f_namelen = NAME_MAX; + buf->f_namelen = ISOFS_SB(sb)->s_joliet_level ? + JOLIET_NAME_MAX : NAME_MAX; return 0; } @@ -1171,10 +1174,9 @@ static int isofs_read_level3_size(struct inode *inode) unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); int high_sierra = ISOFS_SB(inode->i_sb)->s_high_sierra; struct buffer_head *bh = NULL; - unsigned long block, offset, block_saved, offset_saved; + unsigned long block, offset; int i = 0; int more_entries = 0; - struct iso_directory_record *tmpde = NULL; struct iso_inode_info *ei = ISOFS_I(inode); inode->i_size = 0; @@ -1198,9 +1200,17 @@ static int isofs_read_level3_size(struct inode *inode) goto out_noread; } de = (struct iso_directory_record *) (bh->b_data + offset); - de_len = *(unsigned char *) de; - if (de_len == 0) { + /* + * If we are at the end of a block (or at its zero-padded + * tail), move on to the next block. A zero length byte at + * the start of a block means the whole block is empty; + * count that towards the same limit as sections below, or a + * chain of empty blocks could be walked without bound. + */ + if (offset >= bufsize || de->length[0] == 0) { + if (offset == 0 && ++i > 100) + goto out_toomany; brelse(bh); bh = NULL; ++block; @@ -1208,60 +1218,36 @@ static int isofs_read_level3_size(struct inode *inode) continue; } - block_saved = block; - offset_saved = offset; - offset += de_len; - - /* Make sure we have a full directory entry */ - if (offset >= bufsize) { - int slop = bufsize - offset + de_len; - if (!tmpde) { - tmpde = kmalloc(256, GFP_KERNEL); - if (!tmpde) - goto out_nomem; - } - memcpy(tmpde, de, slop); - offset &= bufsize - 1; - block++; + if (!isofs_dir_record_valid(de, offset, bufsize)) { + printk(KERN_NOTICE "iso9660: Corrupted directory entry in block %lu of inode %llu\n", + block, inode->i_ino); brelse(bh); - bh = NULL; - if (offset) { - bh = sb_bread(inode->i_sb, block); - if (!bh) - goto out_noread; - memcpy((void *)tmpde+slop, bh->b_data, offset); - } - de = tmpde; + return -EIO; } + /* Save the first continuation directory entry in the inode */ + if (more_entries && !ei->i_next_section_block) { + ei->i_next_section_block = block; + ei->i_next_section_offset = offset; + } + de_len = de->length[0]; + offset += de_len; inode->i_size += isonum_733(de->size); - if (i == 1) { - ei->i_next_section_block = block_saved; - ei->i_next_section_offset = offset_saved; - } - more_entries = de->flags[-high_sierra] & 0x80; - i++; - if (i > 100) + if (++i > 100) goto out_toomany; } while (more_entries); out: - kfree(tmpde); brelse(bh); return 0; -out_nomem: - brelse(bh); - return -ENOMEM; - out_noread: printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block); - kfree(tmpde); return -EIO; out_toomany: - printk(KERN_INFO "%s: More than 100 file sections ?!?, aborting...\n" + printk(KERN_INFO "%s: More than 100 file sections/empty blocks ?!?, aborting...\n" "isofs_read_level3_size: inode=%llu\n", __func__, inode->i_ino); goto out; diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index dacb9cdae4fd..a2d28a23e892 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -35,6 +35,8 @@ struct isofs_sb_info { unsigned long s_firstdatazone; unsigned long s_log_zone_size; unsigned long s_max_size; + /* Session start in filesystem block units. */ + sector_t s_session_start; int s_rock_offset; /* offset of SUSP fields within SU area */ s32 s_sbsector; @@ -113,13 +115,23 @@ struct timespec64 iso_date(u8 *p, int flags); 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 *); +int get_rock_ridge_filename(struct iso_directory_record *de, char *retname, + int retnamesize, struct inode *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 *); +/* + * The longest name the Joliet converter returns, in bytes. A directory + * record is at most 255 bytes long, which leaves room for 111 UTF-16 units + * of name, and no character set needs more than three bytes for one unit. + */ +#define JOLIET_NAME_MAX \ + ((255 - sizeof(struct iso_directory_record)) / 2 * 3) + +int get_joliet_filename(struct iso_directory_record *de, unsigned char *outname, + int outsize, struct inode *inode); int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int flags); diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index c0f04a1e7f69..d37e67c5e36f 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -15,19 +15,26 @@ * Convert Unicode 16 to UTF-8 or ASCII. */ static int -uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls) +uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls, + int outsize) { __be16 *ip, ch; - unsigned char *op; + unsigned char *op, *end; ip = uni; op = ascii; + end = ascii + outsize - 1; /* leave room for the terminator */ while ((ch = get_unaligned(ip)) && len) { int llen; - llen = nls->uni2char(be16_to_cpu(ch), op, NLS_MAX_CHARSET_SIZE); + + if (op >= end) + break; + llen = nls->uni2char(be16_to_cpu(ch), op, end - op); if (llen > 0) op += llen; + else if (llen == -ENAMETOOLONG) + break; else *op++ = '?'; ip++; @@ -38,21 +45,27 @@ uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls) return (op - ascii); } +/* + * Convert the Joliet name of @de into @outname, a buffer of @outsize bytes. + * The result is at most @outsize - 1 bytes long; a longer name is cut at a + * character boundary. + */ int -get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode) +get_joliet_filename(struct iso_directory_record *de, unsigned char *outname, + int outsize, struct inode *inode) { struct nls_table *nls; - unsigned char len = 0; + int len = 0; nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset; if (!nls) { len = utf16s_to_utf8s((const wchar_t *) de->name, de->name_len[0] >> 1, UTF16_BIG_ENDIAN, - outname, PAGE_SIZE); + outname, outsize - 1); } else { len = uni16_to_x8(outname, (__be16 *) de->name, - de->name_len[0] >> 1, nls); + de->name_len[0] >> 1, nls, outsize); } if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) len -= 2; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 4fba1bf7f016..de797e567343 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -104,12 +104,14 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, dpnt = de->name; if (sbi->s_rock && - ((i = get_rock_ridge_filename(de, tmpname, dir)))) { + ((i = get_rock_ridge_filename(de, tmpname, NAME_MAX + 1, + dir)))) { dlen = i; /* possibly -1 */ dpnt = tmpname; #ifdef CONFIG_JOLIET } else if (sbi->s_joliet_level) { - dlen = get_joliet_filename(de, tmpname, dir); + dlen = get_joliet_filename(de, tmpname, + JOLIET_NAME_MAX + 1, dir); dpnt = tmpname; #endif } else if (sbi->s_mapping == 'a') { @@ -155,7 +157,7 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned i struct inode *inode; char *tmpname; - tmpname = kmalloc(1024, GFP_USER); + tmpname = kmalloc(JOLIET_NAME_MAX + 1, GFP_USER); if (!tmpname) return ERR_PTR(-ENOMEM); diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 2628f31bd3a5..5a5984b72224 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -9,6 +9,7 @@ #include #include +#include #include "isofs.h" #include "rock.h" @@ -84,6 +85,10 @@ static void init_rock_state(struct rock_state *rs, struct inode *inode) */ static int rock_continue(struct rock_state *rs) { + struct super_block *sb = rs->inode->i_sb; + struct isofs_sb_info *sbi = ISOFS_SB(sb); + sector_t session_end = sbi->s_session_start + + ((sector_t)sbi->s_nzones << (ISOFS_BLOCK_BITS - sb->s_blocksize_bits)); int ret = 1; int blocksize = 1 << rs->inode->i_blkbits; const int min_de_size = offsetof(struct rock_ridge, u); @@ -101,11 +106,13 @@ static int rock_continue(struct rock_state *rs) goto out; } - if ((unsigned)rs->cont_extent >= ISOFS_SB(rs->inode->i_sb)->s_nzones) { + if (rs->cont_extent && + (rs->cont_extent < sbi->s_session_start || + rs->cont_extent >= session_end)) { printk(KERN_NOTICE "rock: corrupted directory entry. " "extent=%u out of volume (nzones=%lu)\n", (unsigned)rs->cont_extent, - ISOFS_SB(rs->inode->i_sb)->s_nzones); + sbi->s_nzones); ret = -EIO; goto out; } @@ -202,10 +209,14 @@ static int rock_check_overflow(struct rock_state *rs, int sig) } /* + * Build the Rock Ridge name of @de in @retname, a buffer of @retnamesize + * bytes. From the first NM entry that does not fit along with the + * terminator, the rest of the name is dropped. + * * return length of name field; 0: not found, -1: to be ignored */ int get_rock_ridge_filename(struct iso_directory_record *de, - char *retname, struct inode *inode) + char *retname, int retnamesize, struct inode *inode) { struct rock_state rs; struct rock_ridge *rr; @@ -280,7 +291,7 @@ int get_rock_ridge_filename(struct iso_directory_record *de, break; } len = rr->len - 5; - if (retnamlen + len > NAME_MAX) { + if (retnamlen + len >= retnamesize) { truncate = 1; break; } diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index f55810c59b1b..a2b276bcd0a8 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -171,6 +171,10 @@ static int ocfs2_local_check_quota_file(struct super_block *sb, int type) struct ocfs2_disk_dqheader *dqhead; int status, ret = 0; + /* OCFS2 quota format is supported only for OCFS2 filesystems */ + if (sb->s_magic != OCFS2_SUPER_MAGIC) + goto out_err; + /* First check whether we understand local quota file */ status = ocfs2_read_quota_block(linode, 0, &bh); if (status) { diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index a3742737f873..3ad19d7e472f 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -809,6 +809,7 @@ static unsigned long dqcache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { struct dquot *dquot; + unsigned long orig_nr_to_scan = sc->nr_to_scan; unsigned long freed = 0; spin_lock(&dq_list_lock); @@ -822,6 +823,21 @@ dqcache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) freed++; } spin_unlock(&dq_list_lock); + + sc->nr_scanned = orig_nr_to_scan - sc->nr_to_scan; + + /* + * DQST_FREE_DQUOTS is a percpu counter, so count_objects() can + * report a stale/approximate value that is still positive even + * though free_dquots is actually empty by the time we get here. + * When that happens sc->nr_scanned comes back 0 and we have + * nothing further to offer this reclaim pass, so tell + * do_shrink_slab() to stop calling us instead of letting it burn + * through its one-shot scan budget against an empty list. + */ + if (sc->nr_scanned == 0) + return SHRINK_STOP; + return freed; } diff --git a/fs/udf/inode.c b/fs/udf/inode.c index e45e546a739a..bacf2a86161e 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -372,8 +372,8 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map) map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc, offset); map->oflags |= UDF_BLK_MAPPED; - ret = 0; } + ret = 0; out_read: up_read(&iinfo->i_data_sem); brelse(epos.bh); @@ -1336,6 +1336,26 @@ int udf_setsize(struct inode *inode, loff_t newsize) return err; } +/* + * Verify validity of struct deviceSpec on disk. udf_get_extendedattr() has + * already verified the generic header and made sure attribute fits in the + * inode so we just have to make sure attribute space is large enough for + * deviceSpec struct and required impUse information. + */ +static bool udf_device_spec_valid(struct deviceSpec *dsea) +{ + u32 attr_length, imp_use_length; + + attr_length = le32_to_cpu(dsea->attrLength); + imp_use_length = le32_to_cpu(dsea->impUseLength); + if (attr_length < sizeof(struct deviceSpec) || + imp_use_length < sizeof(struct regid) || + imp_use_length > attr_length - sizeof(struct deviceSpec)) + return false; + + return true; +} + /* * Maximum length of linked list formed by ICB hierarchy. The chosen number is * arbitrary - just that we hopefully don't limit any real use of rewritten @@ -1654,13 +1674,19 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { struct deviceSpec *dsea = (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); - if (dsea) { - init_special_inode(inode, inode->i_mode, + + if (IS_ERR(dsea)) { + ret = PTR_ERR(dsea); + goto out; + } + /* Device inodes must have a device spec attribute */ + if (!dsea || !udf_device_spec_valid(dsea)) { + ret = -EFSCORRUPTED; + goto out; + } + init_special_inode(inode, inode->i_mode, MKDEV(le32_to_cpu(dsea->majorDeviceIdent), le32_to_cpu(dsea->minorDeviceIdent))); - /* Developer ID ??? */ - } else - goto out; } ret = 0; out: @@ -1757,6 +1783,7 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc) struct udf_sb_info *sbi = UDF_SB(inode->i_sb); unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; struct udf_inode_info *iinfo = UDF_I(inode); + int err; bh = sb_getblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0)); @@ -1816,11 +1843,21 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc) struct regid *eid; struct deviceSpec *dsea = (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); + + /* Validity of extended attrs was checked on load */ + if (WARN_ON_ONCE(IS_ERR(dsea))) { + err = PTR_ERR(dsea); + goto out_unlock; + } if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, sizeof(struct deviceSpec) + sizeof(struct regid), 12, 0x3); + if (IS_ERR(dsea)) { + err = PTR_ERR(dsea); + goto out_unlock; + } dsea->attrType = cpu_to_le32(12); dsea->attrSubtype = 1; dsea->attrLength = cpu_to_le32( @@ -1962,6 +1999,11 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc) set_inode_metadata_writeback(inode); return 0; + +out_unlock: + unlock_buffer(bh); + brelse(bh); + return err; } struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino, diff --git a/fs/udf/misc.c b/fs/udf/misc.c index 6928e378fbbd..a2084dfbfbd6 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -58,7 +58,7 @@ struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size, cpu_to_le16(TAG_IDENT_EAHD) || le32_to_cpu(eahd->descTag.tagLocation) != iinfo->i_location.logicalBlockNum) - return NULL; + return ERR_PTR(-EFSCORRUPTED); } else { struct udf_sb_info *sbi = UDF_SB(inode->i_sb); @@ -122,7 +122,7 @@ struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size, return (struct genericFormat *)&ea[offset]; } - return NULL; + return ERR_PTR(-ENOSPC); } struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, @@ -144,7 +144,7 @@ struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, cpu_to_le16(TAG_IDENT_EAHD) || le32_to_cpu(eahd->descTag.tagLocation) != iinfo->i_location.logicalBlockNum) - return NULL; + return ERR_PTR(-EFSCORRUPTED); if (type < 2048) offset = sizeof(struct extendedAttrHeaderDesc); @@ -153,16 +153,17 @@ struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, else offset = le32_to_cpu(eahd->appAttrLocation); - while (offset + sizeof(*gaf) < iinfo->i_lenEAttr) { + while (offset < + iinfo->i_lenEAttr - sizeof(struct genericFormat)) { uint32_t attrLength; gaf = (struct genericFormat *)&ea[offset]; attrLength = le32_to_cpu(gaf->attrLength); /* Detect undersized elements and buffer overflows */ - if ((attrLength < sizeof(*gaf)) || - (attrLength > (iinfo->i_lenEAttr - offset))) - break; + if (attrLength < sizeof(struct genericFormat) || + attrLength > iinfo->i_lenEAttr - offset) + return ERR_PTR(-EFSCORRUPTED); if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) diff --git a/fs/udf/super.c b/fs/udf/super.c index 2ba5973ef4dd..5351755aca3e 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1617,7 +1617,10 @@ static bool udf_lvid_valid(struct super_block *sb, parts = le32_to_cpu(lvid->numOfPartitions); impuselen = le32_to_cpu(lvid->lengthOfImpUse); - if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize || + if (sizeof(struct logicalVolIntegrityDescImpUse) > impuselen || + impuselen >= sb->s_blocksize) + return false; + if (parts >= sb->s_blocksize || sizeof(struct logicalVolIntegrityDesc) + impuselen + 2 * parts * sizeof(u32) > sb->s_blocksize) return false; diff --git a/include/linux/once_lite.h b/include/linux/once_lite.h index 236592c4eeb1..5e2b67039aaf 100644 --- a/include/linux/once_lite.h +++ b/include/linux/once_lite.h @@ -10,19 +10,21 @@ #define DO_ONCE_LITE(func, ...) \ DO_ONCE_LITE_IF(true, func, ##__VA_ARGS__) -#define __ONCE_LITE_IF(condition) \ +#define __ONCE_LITE() \ ({ \ static bool __section(".data..once") __already_done; \ - bool __ret_cond = !!(condition); \ bool __ret_once = false; \ \ - if (unlikely(__ret_cond) && unlikely(!__already_done)) {\ + if (unlikely(!__already_done)) { \ __already_done = true; \ __ret_once = true; \ } \ unlikely(__ret_once); \ }) +#define __ONCE_LITE_IF(condition) \ + (unlikely(condition) && __ONCE_LITE()) + #define DO_ONCE_LITE_IF(condition, func, ...) \ ({ \ bool __ret_do_once = !!(condition); \ diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c index d0b92d6da50f..a3fa4bd93e75 100644 --- a/mm/shmem_quota.c +++ b/mm/shmem_quota.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,10 @@ struct quota_id { static int shmem_check_quota_file(struct super_block *sb, int type) { + /* Verify enabling happens on tmpfs superblock */ + if (sb->s_magic != TMPFS_MAGIC) + return 0; + /* There is no real quota file, nothing to do */ return 1; }