diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2020-06-07 17:40:12 +0100 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2020-06-07 17:40:12 +0100 |
commit | 582af5cb2973430c5a6b19f9d346a74977b9b7a5 (patch) | |
tree | 6e9be52f6bd7c69e858fcead9a183193c9c71992 | |
parent | 5ab00918b76039702d3d07f14ad8f2a05989a21d (diff) | |
download | linux-stable-queue-582af5cb2973430c5a6b19f9d346a74977b9b7a5.tar.gz |
Add various security fixes
44 files changed, 4169 insertions, 0 deletions
diff --git a/queue-3.16/drivers-usb-core-don-t-disable-irqs-in-usb_sg_wait-during-urb-submit.patch b/queue-3.16/drivers-usb-core-don-t-disable-irqs-in-usb_sg_wait-during-urb-submit.patch new file mode 100644 index 00000000..0e29e9f1 --- /dev/null +++ b/queue-3.16/drivers-usb-core-don-t-disable-irqs-in-usb_sg_wait-during-urb-submit.patch @@ -0,0 +1,76 @@ +From: David Mosberger <davidm@egauge.net> +Date: Tue, 8 Mar 2016 14:42:48 -0700 +Subject: drivers: usb: core: Don't disable irqs in usb_sg_wait() during URB submit. + +commit 98b74b0ee57af1bcb6e8b2e76e707a71c5ef8ec9 upstream. + +usb_submit_urb() may take quite long to execute. For example, a +single sg list may have 30 or more entries, possibly leading to that +many calls to DMA-map pages. This can cause interrupt latency of +several hundred micro-seconds. + +Avoid the problem by releasing the io->lock spinlock and re-enabling +interrupts before calling usb_submit_urb(). This opens races with +usb_sg_cancel() and sg_complete(). Handle those races by using +usb_block_urb() to stop URBs from being submitted after +usb_sg_cancel() or sg_complete() with error. + +Note that usb_unlink_urb() is guaranteed to return -ENODEV if +!io->urbs[i]->dev and since the -ENODEV case is already handled, +we don't have to check for !io->urbs[i]->dev explicitly. + +Before this change, reading 512MB from an ext3 filesystem on a USB +memory stick showed a throughput of 12 MB/s with about 500 missed +deadlines. + +With this change, reading the same file gave the same throughput but +only one or two missed deadlines. + +Signed-off-by: David Mosberger <davidm@egauge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/usb/core/message.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -306,9 +306,10 @@ static void sg_complete(struct urb *urb) + */ + spin_unlock(&io->lock); + for (i = 0, found = 0; i < io->entries; i++) { +- if (!io->urbs[i] || !io->urbs[i]->dev) ++ if (!io->urbs[i]) + continue; + if (found) { ++ usb_block_urb(io->urbs[i]); + retval = usb_unlink_urb(io->urbs[i]); + if (retval != -EINPROGRESS && + retval != -ENODEV && +@@ -519,12 +520,10 @@ void usb_sg_wait(struct usb_sg_request * + int retval; + + io->urbs[i]->dev = io->dev; +- retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC); +- +- /* after we submit, let completions or cancellations fire; +- * we handshake using io->status. +- */ + spin_unlock_irq(&io->lock); ++ ++ retval = usb_submit_urb(io->urbs[i], GFP_NOIO); ++ + switch (retval) { + /* maybe we retrying will recover */ + case -ENXIO: /* hc didn't queue this one */ +@@ -594,8 +593,8 @@ void usb_sg_cancel(struct usb_sg_request + for (i = 0; i < io->entries; i++) { + int retval; + +- if (!io->urbs[i]->dev) +- continue; ++ usb_block_urb(io->urbs[i]); ++ + retval = usb_unlink_urb(io->urbs[i]); + if (retval != -EINPROGRESS + && retval != -ENODEV diff --git a/queue-3.16/drivers-usb-core-minimize-irq-disabling-in-usb_sg_cancel.patch b/queue-3.16/drivers-usb-core-minimize-irq-disabling-in-usb_sg_cancel.patch new file mode 100644 index 00000000..67bf14db --- /dev/null +++ b/queue-3.16/drivers-usb-core-minimize-irq-disabling-in-usb_sg_cancel.patch @@ -0,0 +1,68 @@ +From: David Mosberger <davidm@egauge.net> +Date: Tue, 8 Mar 2016 14:42:49 -0700 +Subject: drivers: usb: core: Minimize irq disabling in usb_sg_cancel() + +commit 5f2e5fb873e269fcb806165715d237f0de4ecf1d upstream. + +Restructure usb_sg_cancel() so we don't have to disable interrupts +while cancelling the URBs. + +Suggested-by: Alan Stern <stern@rowland.harvard.edu> +Signed-off-by: David Mosberger <davidm@egauge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/usb/core/message.c | 37 +++++++++++++++++-------------------- + 1 file changed, 17 insertions(+), 20 deletions(-) + +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -581,31 +581,28 @@ EXPORT_SYMBOL_GPL(usb_sg_wait); + void usb_sg_cancel(struct usb_sg_request *io) + { + unsigned long flags; ++ int i, retval; + + spin_lock_irqsave(&io->lock, flags); ++ if (io->status) { ++ spin_unlock_irqrestore(&io->lock, flags); ++ return; ++ } ++ /* shut everything down */ ++ io->status = -ECONNRESET; ++ spin_unlock_irqrestore(&io->lock, flags); + +- /* shut everything down, if it didn't already */ +- if (!io->status) { +- int i; +- +- io->status = -ECONNRESET; +- spin_unlock(&io->lock); +- for (i = 0; i < io->entries; i++) { +- int retval; +- +- usb_block_urb(io->urbs[i]); ++ for (i = io->entries - 1; i >= 0; --i) { ++ usb_block_urb(io->urbs[i]); + +- retval = usb_unlink_urb(io->urbs[i]); +- if (retval != -EINPROGRESS +- && retval != -ENODEV +- && retval != -EBUSY +- && retval != -EIDRM) +- dev_warn(&io->dev->dev, "%s, unlink --> %d\n", +- __func__, retval); +- } +- spin_lock(&io->lock); ++ retval = usb_unlink_urb(io->urbs[i]); ++ if (retval != -EINPROGRESS ++ && retval != -ENODEV ++ && retval != -EBUSY ++ && retval != -EIDRM) ++ dev_warn(&io->dev->dev, "%s, unlink --> %d\n", ++ __func__, retval); + } +- spin_unlock_irqrestore(&io->lock, flags); + } + EXPORT_SYMBOL_GPL(usb_sg_cancel); + diff --git a/queue-3.16/ext4-add-cond_resched-to-ext4_protect_reserved_inode.patch b/queue-3.16/ext4-add-cond_resched-to-ext4_protect_reserved_inode.patch new file mode 100644 index 00000000..fbdcca3e --- /dev/null +++ b/queue-3.16/ext4-add-cond_resched-to-ext4_protect_reserved_inode.patch @@ -0,0 +1,60 @@ +From: Shijie Luo <luoshijie1@huawei.com> +Date: Mon, 10 Feb 2020 20:17:52 -0500 +Subject: ext4: add cond_resched() to ext4_protect_reserved_inode + +commit af133ade9a40794a37104ecbcc2827c0ea373a3c upstream. + +When journal size is set too big by "mkfs.ext4 -J size=", or when +we mount a crafted image to make journal inode->i_size too big, +the loop, "while (i < num)", holds cpu too long. This could cause +soft lockup. + +[ 529.357541] Call trace: +[ 529.357551] dump_backtrace+0x0/0x198 +[ 529.357555] show_stack+0x24/0x30 +[ 529.357562] dump_stack+0xa4/0xcc +[ 529.357568] watchdog_timer_fn+0x300/0x3e8 +[ 529.357574] __hrtimer_run_queues+0x114/0x358 +[ 529.357576] hrtimer_interrupt+0x104/0x2d8 +[ 529.357580] arch_timer_handler_virt+0x38/0x58 +[ 529.357584] handle_percpu_devid_irq+0x90/0x248 +[ 529.357588] generic_handle_irq+0x34/0x50 +[ 529.357590] __handle_domain_irq+0x68/0xc0 +[ 529.357593] gic_handle_irq+0x6c/0x150 +[ 529.357595] el1_irq+0xb8/0x140 +[ 529.357599] __ll_sc_atomic_add_return_acquire+0x14/0x20 +[ 529.357668] ext4_map_blocks+0x64/0x5c0 [ext4] +[ 529.357693] ext4_setup_system_zone+0x330/0x458 [ext4] +[ 529.357717] ext4_fill_super+0x2170/0x2ba8 [ext4] +[ 529.357722] mount_bdev+0x1a8/0x1e8 +[ 529.357746] ext4_mount+0x44/0x58 [ext4] +[ 529.357748] mount_fs+0x50/0x170 +[ 529.357752] vfs_kern_mount.part.9+0x54/0x188 +[ 529.357755] do_mount+0x5ac/0xd78 +[ 529.357758] ksys_mount+0x9c/0x118 +[ 529.357760] __arm64_sys_mount+0x28/0x38 +[ 529.357764] el0_svc_common+0x78/0x130 +[ 529.357766] el0_svc_handler+0x38/0x78 +[ 529.357769] el0_svc+0x8/0xc +[ 541.356516] watchdog: BUG: soft lockup - CPU#0 stuck for 23s! [mount:18674] + +Link: https://lore.kernel.org/r/20200211011752.29242-1-luoshijie1@huawei.com +Reviewed-by: Jan Kara <jack@suse.cz> +Signed-off-by: Shijie Luo <luoshijie1@huawei.com> +Signed-off-by: Theodore Ts'o <tytso@mit.edu> +Cc: stable@kernel.org +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + fs/ext4/block_validity.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -153,6 +153,7 @@ static int ext4_protect_reserved_inode(s + return PTR_ERR(inode); + num = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + while (i < num) { ++ cond_resched(); + map.m_lblk = i; + map.m_len = num - i; + n = ext4_map_blocks(NULL, inode, &map, 0); diff --git a/queue-3.16/ext4-don-t-perform-block-validity-checks-on-the-journal-inode.patch b/queue-3.16/ext4-don-t-perform-block-validity-checks-on-the-journal-inode.patch new file mode 100644 index 00000000..f59646eb --- /dev/null +++ b/queue-3.16/ext4-don-t-perform-block-validity-checks-on-the-journal-inode.patch @@ -0,0 +1,50 @@ +From: Theodore Ts'o <tytso@mit.edu> +Date: Wed, 22 May 2019 10:27:01 -0400 +Subject: ext4: don't perform block validity checks on the journal inode + +commit 0a944e8a6c66ca04c7afbaa17e22bf208a8b37f0 upstream. + +Since the journal inode is already checked when we added it to the +block validity's system zone, if we check it again, we'll just trigger +a failure. + +This was causing failures like this: + +[ 53.897001] EXT4-fs error (device sda): ext4_find_extent:909: inode +#8: comm jbd2/sda-8: pblk 121667583 bad header/extent: invalid extent entries - magic f30a, entries 8, max 340(340), depth 0(0) +[ 53.931430] jbd2_journal_bmap: journal block not found at offset 49 on sda-8 +[ 53.938480] Aborting journal on device sda-8. + +... but only if the system was under enough memory pressure that +logical->physical mapping for the journal inode gets pushed out of the +extent cache. (This is why it wasn't noticed earlier.) + +Fixes: 345c0dbf3a30 ("ext4: protect journal inode's blocks using block_validity") +Reported-by: Dan Rue <dan.rue@linaro.org> +Signed-off-by: Theodore Ts'o <tytso@mit.edu> +Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org> +[bwh: Backported to 3.16: Use EXT4_HAS_COMPAT_FEATURE()] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/fs/ext4/extents.c ++++ b/fs/ext4/extents.c +@@ -503,10 +503,15 @@ __read_extent_tree_block(const char *fun + } + if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE)) + return bh; +- err = __ext4_ext_check(function, line, inode, +- ext_block_hdr(bh), depth, pblk); +- if (err) +- goto errout; ++ if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb, ++ EXT4_FEATURE_COMPAT_HAS_JOURNAL) || ++ (inode->i_ino != ++ le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) { ++ err = __ext4_ext_check(function, line, inode, ++ ext_block_hdr(bh), depth, pblk); ++ if (err) ++ goto errout; ++ } + set_buffer_verified(bh); + /* + * If this is a leaf block, cache all of its entries diff --git a/queue-3.16/ext4-fix-block-validity-checks-for-journal-inodes-using-indirect-blocks.patch b/queue-3.16/ext4-fix-block-validity-checks-for-journal-inodes-using-indirect-blocks.patch new file mode 100644 index 00000000..ca1424aa --- /dev/null +++ b/queue-3.16/ext4-fix-block-validity-checks-for-journal-inodes-using-indirect-blocks.patch @@ -0,0 +1,37 @@ +From: Theodore Ts'o <tytso@mit.edu> +Date: Wed, 15 May 2019 00:51:19 -0400 +Subject: ext4: fix block validity checks for journal inodes using indirect blocks + +commit 170417c8c7bb2cbbdd949bf5c443c0c8f24a203b upstream. + +Commit 345c0dbf3a30 ("ext4: protect journal inode's blocks using +block_validity") failed to add an exception for the journal inode in +ext4_check_blockref(), which is the function used by ext4_get_branch() +for indirect blocks. This caused attempts to read from the ext3-style +journals to fail with: + +[ 848.968550] EXT4-fs error (device sdb7): ext4_get_branch:171: inode #8: block 30343695: comm jbd2/sdb7-8: invalid block + +Fix this by adding the missing exception check. + +Fixes: 345c0dbf3a30 ("ext4: protect journal inode's blocks using block_validity") +Reported-by: Arthur Marsh <arthur.marsh@internode.on.net> +Signed-off-by: Theodore Ts'o <tytso@mit.edu> +[bwh: Backported to 3.16: Use EXT4_HAS_COMPAT_FEATURE] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -277,6 +277,12 @@ int ext4_check_blockref(const char *func + __le32 *bref = p; + unsigned int blk; + ++ if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, ++ EXT4_FEATURE_COMPAT_HAS_JOURNAL) && ++ (inode->i_ino == ++ le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) ++ return 0; ++ + while (bref < p+max) { + blk = le32_to_cpu(*bref++); + if (blk && diff --git a/queue-3.16/ext4-make-checks-for-metadata_csum-feature-safer.patch b/queue-3.16/ext4-make-checks-for-metadata_csum-feature-safer.patch new file mode 100644 index 00000000..4e4aa6ee --- /dev/null +++ b/queue-3.16/ext4-make-checks-for-metadata_csum-feature-safer.patch @@ -0,0 +1,48 @@ +From: Tahsin Erdogan <tahsin@google.com> +Date: Thu, 17 May 2018 18:23:04 +0200 +Subject: ext4: Make checks for metadata_csum feature safer + +This is just a small part of commit dec214d00e0d7 "ext4: xattr inode +deduplication" that makes checks for metadata_csum feature safer and is +actually needed by following fixes. + +Signed-off-by: Tahsin Erdogan <tahsin@google.com> +Acked-by: Jan Kara <jack@suse.cz> +[bwh: Ported to 3.16: Use EXT4_HAS_RO_COMPAT_FEATURE()] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -2411,21 +2411,24 @@ extern void ext4_group_desc_csum_set(str + extern int ext4_register_li_request(struct super_block *sb, + ext4_group_t first_not_zeroed); + +-static inline int ext4_has_group_desc_csum(struct super_block *sb) +-{ +- return EXT4_HAS_RO_COMPAT_FEATURE(sb, +- EXT4_FEATURE_RO_COMPAT_GDT_CSUM) || +- (EXT4_SB(sb)->s_chksum_driver != NULL); +-} +- + static inline int ext4_has_metadata_csum(struct super_block *sb) + { + WARN_ON_ONCE(EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + !EXT4_SB(sb)->s_chksum_driver); + +- return (EXT4_SB(sb)->s_chksum_driver != NULL); ++ return EXT4_HAS_RO_COMPAT_FEATURE(sb, ++ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && ++ (EXT4_SB(sb)->s_chksum_driver != NULL); ++} ++ ++static inline int ext4_has_group_desc_csum(struct super_block *sb) ++{ ++ return EXT4_HAS_RO_COMPAT_FEATURE(sb, ++ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) || ++ ext4_has_metadata_csum(sb); + } ++ + static inline ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es) + { + return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) | diff --git a/queue-3.16/ext4-protect-journal-inode-s-blocks-using-block_validity.patch b/queue-3.16/ext4-protect-journal-inode-s-blocks-using-block_validity.patch new file mode 100644 index 00000000..ba8b9471 --- /dev/null +++ b/queue-3.16/ext4-protect-journal-inode-s-blocks-using-block_validity.patch @@ -0,0 +1,98 @@ +From: Theodore Ts'o <tytso@mit.edu> +Date: Tue, 9 Apr 2019 23:37:08 -0400 +Subject: ext4: protect journal inode's blocks using block_validity + +commit 345c0dbf3a30872d9b204db96b5857cd00808cae upstream. + +Add the blocks which belong to the journal inode to block_validity's +system zone so attempts to deallocate or overwrite the journal due a +corrupted file system where the journal blocks are also claimed by +another inode. + +Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=202879 +Signed-off-by: Theodore Ts'o <tytso@mit.edu> +Cc: stable@kernel.org +[bwh: Backported to 3.16: + - Use EXT4_HAS_COMPAT_FEATURE() + - Use EIO instead of EFSCORRUPTED] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -137,6 +137,48 @@ static void debug_print_tree(struct ext4 + printk("\n"); + } + ++static int ext4_protect_reserved_inode(struct super_block *sb, u32 ino) ++{ ++ struct inode *inode; ++ struct ext4_sb_info *sbi = EXT4_SB(sb); ++ struct ext4_map_blocks map; ++ u32 i = 0, err = 0, num, n; ++ ++ if ((ino < EXT4_ROOT_INO) || ++ (ino > le32_to_cpu(sbi->s_es->s_inodes_count))) ++ return -EINVAL; ++ inode = ext4_iget(sb, ino, EXT4_IGET_SPECIAL); ++ if (IS_ERR(inode)) ++ return PTR_ERR(inode); ++ num = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++ while (i < num) { ++ map.m_lblk = i; ++ map.m_len = num - i; ++ n = ext4_map_blocks(NULL, inode, &map, 0); ++ if (n < 0) { ++ err = n; ++ break; ++ } ++ if (n == 0) { ++ i++; ++ } else { ++ if (!ext4_data_block_valid(sbi, map.m_pblk, n)) { ++ ext4_error(sb, "blocks %llu-%llu from inode %u " ++ "overlap system zone", map.m_pblk, ++ map.m_pblk + map.m_len - 1, ino); ++ err = -EIO; ++ break; ++ } ++ err = add_system_zone(sbi, map.m_pblk, n); ++ if (err < 0) ++ break; ++ i += n; ++ } ++ } ++ iput(inode); ++ return err; ++} ++ + int ext4_setup_system_zone(struct super_block *sb) + { + ext4_group_t ngroups = ext4_get_groups_count(sb); +@@ -171,6 +213,13 @@ int ext4_setup_system_zone(struct super_ + if (ret) + return ret; + } ++ if (EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL) && ++ sbi->s_es->s_journal_inum) { ++ ret = ext4_protect_reserved_inode(sb, ++ le32_to_cpu(sbi->s_es->s_journal_inum)); ++ if (ret) ++ return ret; ++ } + + if (test_opt(sb, DEBUG)) + debug_print_tree(EXT4_SB(sb)); +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -411,6 +411,11 @@ static int __check_block_validity(struct + unsigned int line, + struct ext4_map_blocks *map) + { ++ if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, ++ EXT4_FEATURE_COMPAT_HAS_JOURNAL) && ++ (inode->i_ino == ++ le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) ++ return 0; + if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk, + map->m_len)) { + ext4_error_inode(inode, func, line, map->m_pblk, diff --git a/queue-3.16/ext4-unsigned-int-compared-against-zero.patch b/queue-3.16/ext4-unsigned-int-compared-against-zero.patch new file mode 100644 index 00000000..15772dc6 --- /dev/null +++ b/queue-3.16/ext4-unsigned-int-compared-against-zero.patch @@ -0,0 +1,31 @@ +From: Colin Ian King <colin.king@canonical.com> +Date: Fri, 10 May 2019 22:06:38 -0400 +Subject: ext4: unsigned int compared against zero + +commit fbbbbd2f28aec991f3fbc248df211550fbdfd58c upstream. + +There are two cases where u32 variables n and err are being checked +for less than zero error values, the checks is always false because +the variables are not signed. Fix this by making the variables ints. + +Addresses-Coverity: ("Unsigned compared against 0") +Fixes: 345c0dbf3a30 ("ext4: protect journal inode's blocks using block_validity") +Signed-off-by: Colin Ian King <colin.king@canonical.com> +Signed-off-by: Theodore Ts'o <tytso@mit.edu> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + fs/ext4/block_validity.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -142,7 +142,8 @@ static int ext4_protect_reserved_inode(s + struct inode *inode; + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_map_blocks map; +- u32 i = 0, err = 0, num, n; ++ u32 i = 0, num; ++ int err = 0, n; + + if ((ino < EXT4_ROOT_INO) || + (ino > le32_to_cpu(sbi->s_es->s_inodes_count))) diff --git a/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_cmd_append_vsie_tlv.patch b/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_cmd_append_vsie_tlv.patch new file mode 100644 index 00000000..137f542d --- /dev/null +++ b/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_cmd_append_vsie_tlv.patch @@ -0,0 +1,38 @@ +From: Qing Xu <m1s5p6688@gmail.com> +Date: Thu, 2 Jan 2020 10:39:27 +0800 +Subject: mwifiex: Fix possible buffer overflows in mwifiex_cmd_append_vsie_tlv() + +commit b70261a288ea4d2f4ac7cd04be08a9f0f2de4f4d upstream. + +mwifiex_cmd_append_vsie_tlv() calls memcpy() without checking +the destination size may trigger a buffer overflower, +which a local user could use to cause denial of service +or the execution of arbitrary code. +Fix it by putting the length check before calling memcpy(). + +Signed-off-by: Qing Xu <m1s5p6688@gmail.com> +Signed-off-by: Kalle Valo <kvalo@codeaurora.org> +[bwh: Backported to 3.16: + - Use dev_info() instead of mwifiex_dbg() + - Adjust filename] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/net/wireless/mwifiex/scan.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/wireless/mwifiex/scan.c ++++ b/drivers/net/wireless/mwifiex/scan.c +@@ -2267,6 +2267,13 @@ mwifiex_cmd_append_vsie_tlv(struct mwifi + vs_param_set->header.len = + cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) + & 0x00FF) + 2); ++ if (le16_to_cpu(vs_param_set->header.len) > ++ MWIFIEX_MAX_VSIE_LEN) { ++ dev_info(priv->adapter->dev, ++ "Invalid param length!\n"); ++ break; ++ } ++ + memcpy(vs_param_set->ie, priv->vs_ie[id].ie, + le16_to_cpu(vs_param_set->header.len)); + *buffer += le16_to_cpu(vs_param_set->header.len) + diff --git a/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_ret_wmm_get_status.patch b/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_ret_wmm_get_status.patch new file mode 100644 index 00000000..63c8f105 --- /dev/null +++ b/queue-3.16/mwifiex-fix-possible-buffer-overflows-in-mwifiex_ret_wmm_get_status.patch @@ -0,0 +1,33 @@ +From: Qing Xu <m1s5p6688@gmail.com> +Date: Thu, 2 Jan 2020 10:39:26 +0800 +Subject: mwifiex: Fix possible buffer overflows in mwifiex_ret_wmm_get_status() + +commit 3a9b153c5591548612c3955c9600a98150c81875 upstream. + +mwifiex_ret_wmm_get_status() calls memcpy() without checking the +destination size.Since the source is given from remote AP which +contains illegal wmm elements , this may trigger a heap buffer +overflow. +Fix it by putting the length check before calling memcpy(). + +Signed-off-by: Qing Xu <m1s5p6688@gmail.com> +Signed-off-by: Kalle Valo <kvalo@codeaurora.org> +[bwh: Backported to 3.16: adjust filename] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/net/wireless/mwifiex/wmm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/mwifiex/wmm.c ++++ b/drivers/net/wireless/mwifiex/wmm.c +@@ -791,6 +791,10 @@ int mwifiex_ret_wmm_get_status(struct mw + wmm_param_ie->qos_info_bitmap & + IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK); + ++ if (wmm_param_ie->vend_hdr.len + 2 > ++ sizeof(struct ieee_types_wmm_parameter)) ++ break; ++ + memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. + wmm_ie, wmm_param_ie, + wmm_param_ie->vend_hdr.len + 2); diff --git a/queue-3.16/scsi-mptfusion-add-bounds-check-in-mptctl_hp_targetinfo.patch b/queue-3.16/scsi-mptfusion-add-bounds-check-in-mptctl_hp_targetinfo.patch new file mode 100644 index 00000000..2f1bee1d --- /dev/null +++ b/queue-3.16/scsi-mptfusion-add-bounds-check-in-mptctl_hp_targetinfo.patch @@ -0,0 +1,32 @@ +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Thu, 25 Jan 2018 17:27:27 +0300 +Subject: scsi: mptfusion: Add bounds check in mptctl_hp_targetinfo() + +commit a7043e9529f3c367cc4d82997e00be034cbe57ca upstream. + +My static checker complains about an out of bounds read: + + drivers/message/fusion/mptctl.c:2786 mptctl_hp_targetinfo() + error: buffer overflow 'hd->sel_timeout' 255 <= u32max. + +It's true that we probably should have a bounds check here. + +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/message/fusion/mptctl.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/message/fusion/mptctl.c ++++ b/drivers/message/fusion/mptctl.c +@@ -2698,6 +2698,8 @@ mptctl_hp_targetinfo(unsigned long arg) + __FILE__, __LINE__, iocnum); + return -ENODEV; + } ++ if (karg.hdr.id >= MPT_MAX_FC_DEVICES) ++ return -EINVAL; + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n", + ioc->name)); + diff --git a/queue-3.16/scsi-mptfusion-fix-double-fetch-bug-in-ioctl.patch b/queue-3.16/scsi-mptfusion-fix-double-fetch-bug-in-ioctl.patch new file mode 100644 index 00000000..7a98c275 --- /dev/null +++ b/queue-3.16/scsi-mptfusion-fix-double-fetch-bug-in-ioctl.patch @@ -0,0 +1,573 @@ +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Tue, 14 Jan 2020 15:34:14 +0300 +Subject: scsi: mptfusion: Fix double fetch bug in ioctl + +commit 28d76df18f0ad5bcf5fa48510b225f0ed262a99b upstream. + +Tom Hatskevich reported that we look up "iocp" then, in the called +functions we do a second copy_from_user() and look it up again. +The problem that could cause is: + +drivers/message/fusion/mptctl.c + 674 /* All of these commands require an interrupt or + 675 * are unknown/illegal. + 676 */ + 677 if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) + ^^^^ +We take this lock. + + 678 return ret; + 679 + 680 if (cmd == MPTFWDOWNLOAD) + 681 ret = mptctl_fw_download(arg); + ^^^ +Then the user memory changes and we look up "iocp" again but a different +one so now we are holding the incorrect lock and have a race condition. + + 682 else if (cmd == MPTCOMMAND) + 683 ret = mptctl_mpt_command(arg); + +The security impact of this bug is not as bad as it could have been +because these operations are all privileged and root already has +enormous destructive power. But it's still worth fixing. + +This patch passes the "iocp" pointer to the functions to avoid the +second lookup. That deletes 100 lines of code from the driver so +it's a nice clean up as well. + +Link: https://lore.kernel.org/r/20200114123414.GA7957@kadam +Reported-by: Tom Hatskevich <tom2001tom.23@gmail.com> +Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/message/fusion/mptctl.c | 213 ++++++++------------------------ + 1 file changed, 50 insertions(+), 163 deletions(-) + +--- a/drivers/message/fusion/mptctl.c ++++ b/drivers/message/fusion/mptctl.c +@@ -100,19 +100,19 @@ struct buflist { + * Function prototypes. Called from OS entry point mptctl_ioctl. + * arg contents specific to function. + */ +-static int mptctl_fw_download(unsigned long arg); +-static int mptctl_getiocinfo(unsigned long arg, unsigned int cmd); +-static int mptctl_gettargetinfo(unsigned long arg); +-static int mptctl_readtest(unsigned long arg); +-static int mptctl_mpt_command(unsigned long arg); +-static int mptctl_eventquery(unsigned long arg); +-static int mptctl_eventenable(unsigned long arg); +-static int mptctl_eventreport(unsigned long arg); +-static int mptctl_replace_fw(unsigned long arg); +- +-static int mptctl_do_reset(unsigned long arg); +-static int mptctl_hp_hostinfo(unsigned long arg, unsigned int cmd); +-static int mptctl_hp_targetinfo(unsigned long arg); ++static int mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_getiocinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); ++static int mptctl_gettargetinfo(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_readtest(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_mpt_command(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_eventquery(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_eventenable(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_eventreport(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_replace_fw(MPT_ADAPTER *iocp, unsigned long arg); ++ ++static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg); ++static int mptctl_hp_hostinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); ++static int mptctl_hp_targetinfo(MPT_ADAPTER *iocp, unsigned long arg); + + static int mptctl_probe(struct pci_dev *, const struct pci_device_id *); + static void mptctl_remove(struct pci_dev *); +@@ -123,8 +123,8 @@ static long compat_mpctl_ioctl(struct fi + /* + * Private function calls. + */ +-static int mptctl_do_mpt_command(struct mpt_ioctl_command karg, void __user *mfPtr); +-static int mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen); ++static int mptctl_do_mpt_command(MPT_ADAPTER *iocp, struct mpt_ioctl_command karg, void __user *mfPtr); ++static int mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen); + static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags, + struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); + static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, +@@ -656,19 +656,19 @@ __mptctl_ioctl(struct file *file, unsign + * by TM and FW reloads. + */ + if ((cmd & ~IOCSIZE_MASK) == (MPTIOCINFO & ~IOCSIZE_MASK)) { +- return mptctl_getiocinfo(arg, _IOC_SIZE(cmd)); ++ return mptctl_getiocinfo(iocp, arg, _IOC_SIZE(cmd)); + } else if (cmd == MPTTARGETINFO) { +- return mptctl_gettargetinfo(arg); ++ return mptctl_gettargetinfo(iocp, arg); + } else if (cmd == MPTTEST) { +- return mptctl_readtest(arg); ++ return mptctl_readtest(iocp, arg); + } else if (cmd == MPTEVENTQUERY) { +- return mptctl_eventquery(arg); ++ return mptctl_eventquery(iocp, arg); + } else if (cmd == MPTEVENTENABLE) { +- return mptctl_eventenable(arg); ++ return mptctl_eventenable(iocp, arg); + } else if (cmd == MPTEVENTREPORT) { +- return mptctl_eventreport(arg); ++ return mptctl_eventreport(iocp, arg); + } else if (cmd == MPTFWREPLACE) { +- return mptctl_replace_fw(arg); ++ return mptctl_replace_fw(iocp, arg); + } + + /* All of these commands require an interrupt or +@@ -678,15 +678,15 @@ __mptctl_ioctl(struct file *file, unsign + return ret; + + if (cmd == MPTFWDOWNLOAD) +- ret = mptctl_fw_download(arg); ++ ret = mptctl_fw_download(iocp, arg); + else if (cmd == MPTCOMMAND) +- ret = mptctl_mpt_command(arg); ++ ret = mptctl_mpt_command(iocp, arg); + else if (cmd == MPTHARDRESET) +- ret = mptctl_do_reset(arg); ++ ret = mptctl_do_reset(iocp, arg); + else if ((cmd & ~IOCSIZE_MASK) == (HP_GETHOSTINFO & ~IOCSIZE_MASK)) +- ret = mptctl_hp_hostinfo(arg, _IOC_SIZE(cmd)); ++ ret = mptctl_hp_hostinfo(iocp, arg, _IOC_SIZE(cmd)); + else if (cmd == HP_GETTARGETINFO) +- ret = mptctl_hp_targetinfo(arg); ++ ret = mptctl_hp_targetinfo(iocp, arg); + else + ret = -EINVAL; + +@@ -705,11 +705,10 @@ mptctl_ioctl(struct file *file, unsigned + return ret; + } + +-static int mptctl_do_reset(unsigned long arg) ++static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg) + { + struct mpt_ioctl_diag_reset __user *urinfo = (void __user *) arg; + struct mpt_ioctl_diag_reset krinfo; +- MPT_ADAPTER *iocp; + + if (copy_from_user(&krinfo, urinfo, sizeof(struct mpt_ioctl_diag_reset))) { + printk(KERN_ERR MYNAM "%s@%d::mptctl_do_reset - " +@@ -718,12 +717,6 @@ static int mptctl_do_reset(unsigned long + return -EFAULT; + } + +- if (mpt_verify_adapter(krinfo.hdr.iocnum, &iocp) < 0) { +- printk(KERN_DEBUG MYNAM "%s@%d::mptctl_do_reset - ioc%d not found!\n", +- __FILE__, __LINE__, krinfo.hdr.iocnum); +- return -ENODEV; /* (-6) No such device or address */ +- } +- + dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "mptctl_do_reset called.\n", + iocp->name)); + +@@ -754,7 +747,7 @@ static int mptctl_do_reset(unsigned long + * -ENOMSG if FW upload returned bad status + */ + static int +-mptctl_fw_download(unsigned long arg) ++mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg) + { + struct mpt_fw_xfer __user *ufwdl = (void __user *) arg; + struct mpt_fw_xfer kfwdl; +@@ -766,7 +759,7 @@ mptctl_fw_download(unsigned long arg) + return -EFAULT; + } + +- return mptctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen); ++ return mptctl_do_fw_download(iocp, kfwdl.bufp, kfwdl.fwlen); + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +@@ -784,11 +777,10 @@ mptctl_fw_download(unsigned long arg) + * -ENOMSG if FW upload returned bad status + */ + static int +-mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen) ++mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen) + { + FWDownload_t *dlmsg; + MPT_FRAME_HDR *mf; +- MPT_ADAPTER *iocp; + FWDownloadTCSGE_t *ptsge; + MptSge_t *sgl, *sgIn; + char *sgOut; +@@ -808,17 +800,10 @@ mptctl_do_fw_download(int ioc, char __us + pFWDownloadReply_t ReplyMsg = NULL; + unsigned long timeleft; + +- if (mpt_verify_adapter(ioc, &iocp) < 0) { +- printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n", +- ioc); +- return -ENODEV; /* (-6) No such device or address */ +- } else { +- +- /* Valid device. Get a message frame and construct the FW download message. +- */ +- if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) +- return -EAGAIN; +- } ++ /* Valid device. Get a message frame and construct the FW download message. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) ++ return -EAGAIN; + + dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT + "mptctl_do_fwdl called. mptctl_id = %xh.\n", iocp->name, mptctl_id)); +@@ -826,8 +811,6 @@ mptctl_do_fw_download(int ioc, char __us + iocp->name, ufwbuf)); + dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "DbG: kfwdl.fwlen = %d\n", + iocp->name, (int)fwlen)); +- dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "DbG: kfwdl.ioc = %04xh\n", +- iocp->name, ioc)); + + dlmsg = (FWDownload_t*) mf; + ptsge = (FWDownloadTCSGE_t *) &dlmsg->SGL; +@@ -1234,13 +1217,11 @@ kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_ + * -ENODEV if no such device/adapter + */ + static int +-mptctl_getiocinfo (unsigned long arg, unsigned int data_size) ++mptctl_getiocinfo (MPT_ADAPTER *ioc, unsigned long arg, unsigned int data_size) + { + struct mpt_ioctl_iocinfo __user *uarg = (void __user *) arg; + struct mpt_ioctl_iocinfo *karg; +- MPT_ADAPTER *ioc; + struct pci_dev *pdev; +- int iocnum; + unsigned int port; + int cim_rev; + struct scsi_device *sdev; +@@ -1276,14 +1257,6 @@ mptctl_getiocinfo (unsigned long arg, un + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg->hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_getiocinfo() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- kfree(karg); +- return -ENODEV; +- } +- + /* Verify the data transfer size is correct. */ + if (karg->hdr.maxDataSize != data_size) { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_getiocinfo - " +@@ -1389,15 +1362,13 @@ mptctl_getiocinfo (unsigned long arg, un + * -ENODEV if no such device/adapter + */ + static int +-mptctl_gettargetinfo (unsigned long arg) ++mptctl_gettargetinfo (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_targetinfo __user *uarg = (void __user *) arg; + struct mpt_ioctl_targetinfo karg; +- MPT_ADAPTER *ioc; + VirtDevice *vdevice; + char *pmem; + int *pdata; +- int iocnum; + int numDevices = 0; + int lun; + int maxWordsLeft; +@@ -1412,13 +1383,6 @@ mptctl_gettargetinfo (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_gettargetinfo() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_gettargetinfo called.\n", + ioc->name)); + /* Get the port number and set the maximum number of bytes +@@ -1514,12 +1478,10 @@ mptctl_gettargetinfo (unsigned long arg) + * -ENODEV if no such device/adapter + */ + static int +-mptctl_readtest (unsigned long arg) ++mptctl_readtest (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_test __user *uarg = (void __user *) arg; + struct mpt_ioctl_test karg; +- MPT_ADAPTER *ioc; +- int iocnum; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_test))) { + printk(KERN_ERR MYNAM "%s@%d::mptctl_readtest - " +@@ -1528,13 +1490,6 @@ mptctl_readtest (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_readtest() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_readtest called.\n", + ioc->name)); + /* Fill in the data and return the structure to the calling +@@ -1575,12 +1530,10 @@ mptctl_readtest (unsigned long arg) + * -ENODEV if no such device/adapter + */ + static int +-mptctl_eventquery (unsigned long arg) ++mptctl_eventquery (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_eventquery __user *uarg = (void __user *) arg; + struct mpt_ioctl_eventquery karg; +- MPT_ADAPTER *ioc; +- int iocnum; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventquery))) { + printk(KERN_ERR MYNAM "%s@%d::mptctl_eventquery - " +@@ -1589,13 +1542,6 @@ mptctl_eventquery (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_eventquery() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventquery called.\n", + ioc->name)); + karg.eventEntries = MPTCTL_EVENT_LOG_SIZE; +@@ -1614,12 +1560,10 @@ mptctl_eventquery (unsigned long arg) + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int +-mptctl_eventenable (unsigned long arg) ++mptctl_eventenable (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_eventenable __user *uarg = (void __user *) arg; + struct mpt_ioctl_eventenable karg; +- MPT_ADAPTER *ioc; +- int iocnum; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventenable))) { + printk(KERN_ERR MYNAM "%s@%d::mptctl_eventenable - " +@@ -1628,13 +1572,6 @@ mptctl_eventenable (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_eventenable() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventenable called.\n", + ioc->name)); + if (ioc->events == NULL) { +@@ -1662,12 +1599,10 @@ mptctl_eventenable (unsigned long arg) + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int +-mptctl_eventreport (unsigned long arg) ++mptctl_eventreport (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_eventreport __user *uarg = (void __user *) arg; + struct mpt_ioctl_eventreport karg; +- MPT_ADAPTER *ioc; +- int iocnum; + int numBytes, maxEvents, max; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventreport))) { +@@ -1677,12 +1612,6 @@ mptctl_eventreport (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_eventreport() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventreport called.\n", + ioc->name)); + +@@ -1716,12 +1645,10 @@ mptctl_eventreport (unsigned long arg) + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int +-mptctl_replace_fw (unsigned long arg) ++mptctl_replace_fw (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_replace_fw __user *uarg = (void __user *) arg; + struct mpt_ioctl_replace_fw karg; +- MPT_ADAPTER *ioc; +- int iocnum; + int newFwSize; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_replace_fw))) { +@@ -1731,13 +1658,6 @@ mptctl_replace_fw (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_replace_fw() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_replace_fw called.\n", + ioc->name)); + /* If caching FW, Free the old FW image +@@ -1789,12 +1709,10 @@ mptctl_replace_fw (unsigned long arg) + * -ENOMEM if memory allocation error + */ + static int +-mptctl_mpt_command (unsigned long arg) ++mptctl_mpt_command (MPT_ADAPTER *ioc, unsigned long arg) + { + struct mpt_ioctl_command __user *uarg = (void __user *) arg; + struct mpt_ioctl_command karg; +- MPT_ADAPTER *ioc; +- int iocnum; + int rc; + + +@@ -1805,14 +1723,7 @@ mptctl_mpt_command (unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_mpt_command() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- +- rc = mptctl_do_mpt_command (karg, &uarg->MF); ++ rc = mptctl_do_mpt_command (ioc, karg, &uarg->MF); + + return rc; + } +@@ -1830,9 +1741,8 @@ mptctl_mpt_command (unsigned long arg) + * -EPERM if SCSI I/O and target is untagged + */ + static int +-mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) ++mptctl_do_mpt_command (MPT_ADAPTER *ioc, struct mpt_ioctl_command karg, void __user *mfPtr) + { +- MPT_ADAPTER *ioc; + MPT_FRAME_HDR *mf = NULL; + MPIHeader_t *hdr; + char *psge; +@@ -1841,7 +1751,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + dma_addr_t dma_addr_in; + dma_addr_t dma_addr_out; + int sgSize = 0; /* Num SG elements */ +- int iocnum, flagsLength; ++ int flagsLength; + int sz, rc = 0; + int msgContext; + u16 req_idx; +@@ -1856,13 +1766,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + bufIn.kptr = bufOut.kptr = NULL; + bufIn.len = bufOut.len = 0; + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_do_mpt_command() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } +- + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); +@@ -2418,17 +2321,15 @@ done_free_mem: + * -ENOMEM if memory allocation error + */ + static int +-mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) ++mptctl_hp_hostinfo(MPT_ADAPTER *ioc, unsigned long arg, unsigned int data_size) + { + hp_host_info_t __user *uarg = (void __user *) arg; +- MPT_ADAPTER *ioc; + struct pci_dev *pdev; + char *pbuf=NULL; + dma_addr_t buf_dma; + hp_host_info_t karg; + CONFIGPARMS cfg; + ConfigPageHeader_t hdr; +- int iocnum; + int rc, cim_rev; + ToolboxIstwiReadWriteRequest_t *IstwiRWRequest; + MPT_FRAME_HDR *mf = NULL; +@@ -2452,12 +2353,6 @@ mptctl_hp_hostinfo(unsigned long arg, un + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_hp_hostinfo() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": mptctl_hp_hostinfo called.\n", + ioc->name)); + +@@ -2670,15 +2565,13 @@ retry_wait: + * -ENOMEM if memory allocation error + */ + static int +-mptctl_hp_targetinfo(unsigned long arg) ++mptctl_hp_targetinfo(MPT_ADAPTER *ioc, unsigned long arg) + { + hp_target_info_t __user *uarg = (void __user *) arg; + SCSIDevicePage0_t *pg0_alloc; + SCSIDevicePage3_t *pg3_alloc; +- MPT_ADAPTER *ioc; + MPT_SCSI_HOST *hd = NULL; + hp_target_info_t karg; +- int iocnum; + int data_sz; + dma_addr_t page_dma; + CONFIGPARMS cfg; +@@ -2692,12 +2585,6 @@ mptctl_hp_targetinfo(unsigned long arg) + return -EFAULT; + } + +- if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || +- (ioc == NULL)) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_hp_targetinfo() @%d - ioc%d not found!\n", +- __FILE__, __LINE__, iocnum); +- return -ENODEV; +- } + if (karg.hdr.id >= MPT_MAX_FC_DEVICES) + return -EINVAL; + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n", +@@ -2865,7 +2752,7 @@ compat_mptfwxfer_ioctl(struct file *filp + kfw.fwlen = kfw32.fwlen; + kfw.bufp = compat_ptr(kfw32.bufp); + +- ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen); ++ ret = mptctl_do_fw_download(iocp, kfw.bufp, kfw.fwlen); + + mutex_unlock(&iocp->ioctl_cmds.mutex); + +@@ -2919,7 +2806,7 @@ compat_mpt_command(struct file *filp, un + + /* Pass new structure to do_mpt_command + */ +- ret = mptctl_do_mpt_command (karg, &uarg->MF); ++ ret = mptctl_do_mpt_command (iocp, karg, &uarg->MF); + + mutex_unlock(&iocp->ioctl_cmds.mutex); + diff --git a/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_common_write.patch b/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_common_write.patch new file mode 100644 index 00000000..c869cda4 --- /dev/null +++ b/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_common_write.patch @@ -0,0 +1,34 @@ +From: Li Bin <huawei.libin@huawei.com> +Date: Mon, 13 Apr 2020 19:29:21 +0800 +Subject: scsi: sg: add sg_remove_request in sg_common_write + +commit 849f8583e955dbe3a1806e03ecacd5e71cce0a08 upstream. + +If the dxfer_len is greater than 256M then the request is invalid and we +need to call sg_remove_request in sg_common_write. + +Link: https://lore.kernel.org/r/1586777361-17339-1-git-send-email-huawei.libin@huawei.com +Fixes: f930c7043663 ("scsi: sg: only check for dxfer_len greater than 256M") +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Li Bin <huawei.libin@huawei.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -808,8 +808,10 @@ sg_common_write(Sg_fd * sfp, Sg_request + SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", + (int) cmnd[0], (int) hp->cmd_len)); + +- if (hp->dxfer_len >= SZ_256M) ++ if (hp->dxfer_len >= SZ_256M) { ++ sg_remove_request(sfp, srp); + return -EINVAL; ++ } + + k = sg_start_req(srp, cmnd); + if (k) { diff --git a/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_write.patch b/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_write.patch new file mode 100644 index 00000000..4329e8d0 --- /dev/null +++ b/queue-3.16/scsi-sg-add-sg_remove_request-in-sg_write.patch @@ -0,0 +1,36 @@ +From: Wu Bo <wubo40@huawei.com> +Date: Tue, 14 Apr 2020 10:13:28 +0800 +Subject: scsi: sg: add sg_remove_request in sg_write + +commit 83c6f2390040f188cc25b270b4befeb5628c1aee upstream. + +If the __copy_from_user function failed we need to call sg_remove_request +in sg_write. + +Link: https://lore.kernel.org/r/610618d9-e983-fd56-ed0f-639428343af7@huawei.com +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Wu Bo <wubo40@huawei.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +[groeck: Backport to v5.4.y and older kernels] +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -696,8 +696,10 @@ sg_write(struct file *filp, const char _ + hp->flags = input_size; /* structure abuse ... */ + hp->pack_id = old_hdr.pack_id; + hp->usr_ptr = NULL; +- if (__copy_from_user(cmnd, buf, cmd_size)) ++ if (__copy_from_user(cmnd, buf, cmd_size)) { ++ sg_remove_request(sfp, srp); + return -EFAULT; ++ } + /* + * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV, + * but is is possible that the app intended SG_DXFER_TO_DEV, because there diff --git a/queue-3.16/scsi-sg-change-next_cmd_len-handling-to-mirror-upstream.patch b/queue-3.16/scsi-sg-change-next_cmd_len-handling-to-mirror-upstream.patch new file mode 100644 index 00000000..a1785d57 --- /dev/null +++ b/queue-3.16/scsi-sg-change-next_cmd_len-handling-to-mirror-upstream.patch @@ -0,0 +1,47 @@ +From: Ben Hutchings <ben@decadent.org.uk> +Date: Thu, 28 May 2020 18:54:25 +0100 +Subject: scsi: sg: Change next_cmd_len handling to mirror upstream + +Change the type of next_cmd_len to unsigned char, done in upstream +commit 65c26a0f3969 "sg: relax 16 byte cdb restriction". + +Move the range check from sg_write() to sg_ioctl(), which was done by +that commit and commit bf33f87dd04c "scsi: sg: check length passed to +SG_NEXT_CMD_LEN". Continue limiting the command length to +MAX_COMMAND_SIZE (16). + +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -160,7 +160,7 @@ typedef struct sg_fd { /* holds the sta + char low_dma; /* as in parent but possibly overridden to 1 */ + char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ + char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ +- char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ ++ unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ + char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ + char mmap_called; /* 0 -> mmap() never called on this fd */ + struct kref f_ref; +@@ -653,12 +653,6 @@ sg_write(struct file *filp, const char _ + buf += SZ_SG_HEADER; + __get_user(opcode, buf); + if (sfp->next_cmd_len > 0) { +- if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { +- SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); +- sfp->next_cmd_len = 0; +- sg_remove_request(sfp, srp); +- return -EIO; +- } + cmd_size = sfp->next_cmd_len; + sfp->next_cmd_len = 0; /* reset so only this write() effected */ + } else { +@@ -1045,6 +1039,8 @@ sg_ioctl(struct file *filp, unsigned int + result = get_user(val, ip); + if (result) + return result; ++ if (val > MAX_COMMAND_SIZE) ++ return -ENOMEM; + sfp->next_cmd_len = (val > 0) ? val : 0; + return 0; + case SG_GET_VERSION_NUM: diff --git a/queue-3.16/scsi-sg-check-for-valid-direction-before-starting-the-request.patch b/queue-3.16/scsi-sg-check-for-valid-direction-before-starting-the-request.patch new file mode 100644 index 00000000..e28fb991 --- /dev/null +++ b/queue-3.16/scsi-sg-check-for-valid-direction-before-starting-the-request.patch @@ -0,0 +1,95 @@ +From: Johannes Thumshirn <jthumshirn@suse.de> +Date: Fri, 7 Apr 2017 09:34:15 +0200 +Subject: scsi: sg: check for valid direction before starting the request + +commit 28676d869bbb5257b5f14c0c95ad3af3a7019dd5 upstream. + +Check for a valid direction before starting the request, otherwise we +risk running into an assertion in the scsi midlayer checking for valid +requests. + +[mkp: fixed typo] + +Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de> +Link: http://www.spinics.net/lists/linux-scsi/msg104400.html +Reported-by: Dmitry Vyukov <dvyukov@google.com> +Signed-off-by: Hannes Reinecke <hare@suse.com> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 46 ++++++++++++++++++++++++++++++++++------------ + 1 file changed, 34 insertions(+), 12 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -701,18 +701,14 @@ sg_write(struct file *filp, const char _ + * is a non-zero input_size, so emit a warning. + */ + if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) { +- static char cmd[TASK_COMM_LEN]; +- if (strcmp(current->comm, cmd)) { +- printk_ratelimited(KERN_WARNING +- "sg_write: data in/out %d/%d bytes " +- "for SCSI command 0x%x-- guessing " +- "data in;\n program %s not setting " +- "count and/or reply_len properly\n", +- old_hdr.reply_len - (int)SZ_SG_HEADER, +- input_size, (unsigned int) cmnd[0], +- current->comm); +- strcpy(cmd, current->comm); +- } ++ printk_ratelimited(KERN_WARNING ++ "sg_write: data in/out %d/%d bytes " ++ "for SCSI command 0x%x-- guessing " ++ "data in;\n program %s not setting " ++ "count and/or reply_len properly\n", ++ old_hdr.reply_len - (int)SZ_SG_HEADER, ++ input_size, (unsigned int) cmnd[0], ++ current->comm); + } + k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); + return (k < 0) ? k : count; +@@ -790,6 +786,29 @@ sg_new_write(Sg_fd *sfp, struct file *fi + return count; + } + ++static bool sg_is_valid_dxfer(sg_io_hdr_t *hp) ++{ ++ switch (hp->dxfer_direction) { ++ case SG_DXFER_NONE: ++ if (hp->dxferp || hp->dxfer_len > 0) ++ return false; ++ return true; ++ case SG_DXFER_TO_DEV: ++ case SG_DXFER_FROM_DEV: ++ case SG_DXFER_TO_FROM_DEV: ++ if (!hp->dxferp || hp->dxfer_len == 0) ++ return false; ++ return true; ++ case SG_DXFER_UNKNOWN: ++ if ((!hp->dxferp && hp->dxfer_len) || ++ (hp->dxferp && hp->dxfer_len == 0)) ++ return false; ++ return true; ++ default: ++ return false; ++ } ++} ++ + static int + sg_common_write(Sg_fd * sfp, Sg_request * srp, + unsigned char *cmnd, int timeout, int blocking) +@@ -809,6 +828,9 @@ sg_common_write(Sg_fd * sfp, Sg_request + SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", + (int) cmnd[0], (int) hp->cmd_len)); + ++ if (!sg_is_valid_dxfer(hp)) ++ return -EINVAL; ++ + k = sg_start_req(srp, cmnd); + if (k) { + SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k)); diff --git a/queue-3.16/scsi-sg-close-race-condition-in-sg_remove_sfp_usercontext.patch b/queue-3.16/scsi-sg-close-race-condition-in-sg_remove_sfp_usercontext.patch new file mode 100644 index 00000000..762e1049 --- /dev/null +++ b/queue-3.16/scsi-sg-close-race-condition-in-sg_remove_sfp_usercontext.patch @@ -0,0 +1,91 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 7 Apr 2017 09:34:17 +0200 +Subject: scsi: sg: close race condition in sg_remove_sfp_usercontext() + +commit 97d27b0dd015e980ade63fda111fd1353276e28b upstream. + +sg_remove_sfp_usercontext() is clearing any sg requests, but needs to +take 'rq_list_lock' when modifying the list. + +Reported-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -561,6 +561,7 @@ sg_read(struct file *filp, char __user * + } else + count = (old_hdr->result == 0) ? 0 : -EIO; + sg_finish_rem_req(srp); ++ sg_remove_request(sfp, srp); + retval = count; + free_old_hdr: + kfree(old_hdr); +@@ -601,6 +602,7 @@ sg_new_read(Sg_fd * sfp, char __user *bu + } + err_out: + err2 = sg_finish_rem_req(srp); ++ sg_remove_request(sfp, srp); + return err ? : err2 ? : count; + } + +@@ -835,6 +837,7 @@ sg_common_write(Sg_fd * sfp, Sg_request + if (k) { + SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k)); + sg_finish_rem_req(srp); ++ sg_remove_request(sfp, srp); + return k; /* probably out of space --> ENOMEM */ + } + if (atomic_read(&sdp->detaching)) { +@@ -844,6 +847,7 @@ sg_common_write(Sg_fd * sfp, Sg_request + } + + sg_finish_rem_req(srp); ++ sg_remove_request(sfp, srp); + return -ENODEV; + } + +@@ -1367,6 +1371,7 @@ sg_rq_end_io_usercontext(struct work_str + struct sg_fd *sfp = srp->parentfp; + + sg_finish_rem_req(srp); ++ sg_remove_request(sfp, srp); + kref_put(&sfp->f_ref, sg_remove_sfp); + } + +@@ -1876,8 +1881,6 @@ sg_finish_rem_req(Sg_request *srp) + else + sg_remove_scat(req_schp); + +- sg_remove_request(sfp, srp); +- + return ret; + } + +@@ -2211,12 +2214,17 @@ sg_remove_sfp_usercontext(struct work_st + struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); + struct sg_device *sdp = sfp->parentdp; + Sg_request *srp; ++ unsigned long iflags; + + /* Cleanup any responses which were never read(). */ ++ write_lock_irqsave(&sfp->rq_list_lock, iflags); + while (!list_empty(&sfp->rq_list)) { + srp = list_first_entry(&sfp->rq_list, Sg_request, entry); + sg_finish_rem_req(srp); ++ list_del(&srp->entry); ++ srp->parentfp = NULL; + } ++ write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + + if (sfp->reserve.bufflen > 0) { + SCSI_LOG_TIMEOUT(6, diff --git a/queue-3.16/scsi-sg-disable-set_force_low_dma.patch b/queue-3.16/scsi-sg-disable-set_force_low_dma.patch new file mode 100644 index 00000000..fb03f518 --- /dev/null +++ b/queue-3.16/scsi-sg-disable-set_force_low_dma.patch @@ -0,0 +1,109 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 7 Apr 2017 09:34:12 +0200 +Subject: scsi: sg: disable SET_FORCE_LOW_DMA + +commit 745dfa0d8ec26b24f3304459ff6e9eacc5c8351b upstream. + +The ioctl SET_FORCE_LOW_DMA has never worked since the initial git +check-in, and the respective setting is nowadays handled correctly. So +disable it entirely. + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +[bwh: Backported to 3.16: adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 30 +++++++++--------------------- + include/scsi/sg.h | 1 - + 2 files changed, 9 insertions(+), 22 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -157,7 +157,6 @@ typedef struct sg_fd { /* holds the sta + struct list_head rq_list; /* head of request list */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ +- char low_dma; /* as in parent but possibly overridden to 1 */ + char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ + char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ + unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ +@@ -963,24 +962,14 @@ sg_ioctl(struct file *filp, unsigned int + /* strange ..., for backward compatibility */ + return sfp->timeout_user; + case SG_SET_FORCE_LOW_DMA: +- result = get_user(val, ip); +- if (result) +- return result; +- if (val) { +- sfp->low_dma = 1; +- if ((0 == sfp->low_dma) && !sfp->res_in_use) { +- val = (int) sfp->reserve.bufflen; +- sg_remove_scat(&sfp->reserve); +- sg_build_reserve(sfp, val); +- } +- } else { +- if (atomic_read(&sdp->detaching)) +- return -ENODEV; +- sfp->low_dma = sdp->device->host->unchecked_isa_dma; +- } ++ /* ++ * N.B. This ioctl never worked properly, but failed to ++ * return an error value. So returning '0' to keep compability ++ * with legacy applications. ++ */ + return 0; + case SG_GET_LOW_DMA: +- return put_user((int) sfp->low_dma, ip); ++ return put_user((int) sdp->device->host->unchecked_isa_dma, ip); + case SG_GET_SCSI_ID: + if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t))) + return -EFAULT; +@@ -1890,6 +1879,7 @@ sg_build_indirect(Sg_scatter_hold * schp + int sg_tablesize = sfp->parentdp->sg_tablesize; + int blk_size = buff_size, order; + gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; ++ struct sg_device *sdp = sfp->parentdp; + + if (blk_size < 0) + return -EFAULT; +@@ -1914,7 +1904,7 @@ sg_build_indirect(Sg_scatter_hold * schp + scatter_elem_sz_prev = num; + } + +- if (sfp->low_dma) ++ if (sdp->device->host->unchecked_isa_dma) + gfp_mask |= GFP_DMA; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) +@@ -2168,8 +2158,6 @@ sg_add_sfp(Sg_device * sdp, int dev) + sfp->timeout = SG_DEFAULT_TIMEOUT; + sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER; + sfp->force_packid = SG_DEF_FORCE_PACK_ID; +- sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? +- sdp->device->host->unchecked_isa_dma : 1; + sfp->cmd_q = SG_DEF_COMMAND_Q; + sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; + sfp->parentdp = sdp; +@@ -2627,7 +2615,7 @@ static void sg_proc_debug_helper(struct + jiffies_to_msecs(fp->timeout), + fp->reserve.bufflen, + (int) fp->reserve.k_use_sg, +- (int) fp->low_dma); ++ (int) sdp->device->host->unchecked_isa_dma); + seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", + (int) fp->cmd_q, (int) fp->force_packid, + (int) fp->keep_orphan); +--- a/include/scsi/sg.h ++++ b/include/scsi/sg.h +@@ -234,7 +234,6 @@ typedef struct sg_req_info { /* used by + #define SG_DEFAULT_RETRIES 0 + + /* Defaults, commented if they differ from original sg driver */ +-#define SG_DEF_FORCE_LOW_DMA 0 /* was 1 -> memory below 16MB on i386 */ + #define SG_DEF_FORCE_PACK_ID 0 + #define SG_DEF_KEEP_ORPHAN 0 + #define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */ diff --git a/queue-3.16/scsi-sg-don-t-return-bogus-sg_requests.patch b/queue-3.16/scsi-sg-don-t-return-bogus-sg_requests.patch new file mode 100644 index 00000000..af84e81d --- /dev/null +++ b/queue-3.16/scsi-sg-don-t-return-bogus-sg_requests.patch @@ -0,0 +1,45 @@ +From: Johannes Thumshirn <jthumshirn@suse.de> +Date: Wed, 10 May 2017 09:53:40 +0200 +Subject: scsi: sg: don't return bogus Sg_requests + +commit 48ae8484e9fc324b4968d33c585e54bc98e44d61 upstream. + +If the list search in sg_get_rq_mark() fails to find a valid request, we +return a bogus element. This then can later lead to a GPF in +sg_remove_scat(). + +So don't return bogus Sg_requests in sg_get_rq_mark() but NULL in case +the list search doesn't find a valid request. + +Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de> +Reported-by: Andrey Konovalov <andreyknvl@google.com> +Cc: Hannes Reinecke <hare@suse.de> +Cc: Christoph Hellwig <hch@lst.de> +Cc: Doug Gilbert <dgilbert@interlog.com> +Reviewed-by: Hannes Reinecke <hare@suse.de> +Acked-by: Doug Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Cc: Tony Battersby <tonyb@cybernetics.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -2085,11 +2085,12 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) + if ((1 == resp->done) && (!resp->sg_io_owned) && + ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { + resp->done = 2; /* guard against other readers */ +- break; ++ write_unlock_irqrestore(&sfp->rq_list_lock, iflags); ++ return resp; + } + } + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); +- return resp; ++ return NULL; + } + + /* always adds to end of list */ diff --git a/queue-3.16/scsi-sg-factor-out-sg_fill_request_table.patch b/queue-3.16/scsi-sg-factor-out-sg_fill_request_table.patch new file mode 100644 index 00000000..6d6e86ab --- /dev/null +++ b/queue-3.16/scsi-sg-factor-out-sg_fill_request_table.patch @@ -0,0 +1,103 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 15 Sep 2017 14:05:15 +0200 +Subject: scsi: sg: factor out sg_fill_request_table() + +commit 4759df905a474d245752c9dc94288e779b8734dd upstream. + +Factor out sg_fill_request_table() for better readability. + +[mkp: typos, applied by hand] + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 61 +++++++++++++++++++++++++++-------------------- + 1 file changed, 35 insertions(+), 26 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -870,6 +870,40 @@ static int max_sectors_bytes(struct requ + return max_sectors << 9; + } + ++static void ++sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) ++{ ++ Sg_request *srp; ++ int val; ++ unsigned int ms; ++ ++ val = 0; ++ list_for_each_entry(srp, &sfp->rq_list, entry) { ++ if (val > SG_MAX_QUEUE) ++ break; ++ memset(&rinfo[val], 0, SZ_SG_REQ_INFO); ++ rinfo[val].req_state = srp->done + 1; ++ rinfo[val].problem = ++ srp->header.masked_status & ++ srp->header.host_status & ++ srp->header.driver_status; ++ if (srp->done) ++ rinfo[val].duration = ++ srp->header.duration; ++ else { ++ ms = jiffies_to_msecs(jiffies); ++ rinfo[val].duration = ++ (ms > srp->header.duration) ? ++ (ms - srp->header.duration) : 0; ++ } ++ rinfo[val].orphan = srp->orphan; ++ rinfo[val].sg_io_owned = srp->sg_io_owned; ++ rinfo[val].pack_id = srp->header.pack_id; ++ rinfo[val].usr_ptr = srp->header.usr_ptr; ++ val++; ++ } ++} ++ + static long + sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) + { +@@ -1063,38 +1097,13 @@ sg_ioctl(struct file *filp, unsigned int + return -EFAULT; + else { + sg_req_info_t *rinfo; +- unsigned int ms; + + rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, + GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + read_lock_irqsave(&sfp->rq_list_lock, iflags); +- val = 0; +- list_for_each_entry(srp, &sfp->rq_list, entry) { +- if (val >= SG_MAX_QUEUE) +- break; +- memset(&rinfo[val], 0, SZ_SG_REQ_INFO); +- rinfo[val].req_state = srp->done + 1; +- rinfo[val].problem = +- srp->header.masked_status & +- srp->header.host_status & +- srp->header.driver_status; +- if (srp->done) +- rinfo[val].duration = +- srp->header.duration; +- else { +- ms = jiffies_to_msecs(jiffies); +- rinfo[val].duration = +- (ms > srp->header.duration) ? +- (ms - srp->header.duration) : 0; +- } +- rinfo[val].orphan = srp->orphan; +- rinfo[val].sg_io_owned = srp->sg_io_owned; +- rinfo[val].pack_id = srp->header.pack_id; +- rinfo[val].usr_ptr = srp->header.usr_ptr; +- val++; +- } ++ sg_fill_request_table(sfp, rinfo); + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + result = __copy_to_user(p, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); diff --git a/queue-3.16/scsi-sg-fix-minor-memory-leak-in-error-path.patch b/queue-3.16/scsi-sg-fix-minor-memory-leak-in-error-path.patch new file mode 100644 index 00000000..31fba5ed --- /dev/null +++ b/queue-3.16/scsi-sg-fix-minor-memory-leak-in-error-path.patch @@ -0,0 +1,30 @@ +From: Tony Battersby <tonyb@cybernetics.com> +Date: Thu, 12 Jul 2018 16:30:45 -0400 +Subject: scsi: sg: fix minor memory leak in error path + +commit c170e5a8d222537e98aa8d4fddb667ff7a2ee114 upstream. + +Fix a minor memory leak when there is an error opening a /dev/sg device. + +Fixes: cc833acbee9d ("sg: O_EXCL and other lock handling") +Cc: <stable@vger.kernel.org> +Reviewed-by: Ewan D. Milne <emilne@redhat.com> +Signed-off-by: Tony Battersby <tonyb@cybernetics.com> +Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -2168,6 +2168,7 @@ sg_add_sfp(Sg_device * sdp, int dev) + write_lock_irqsave(&sdp->sfd_lock, iflags); + if (atomic_read(&sdp->detaching)) { + write_unlock_irqrestore(&sdp->sfd_lock, iflags); ++ kfree(sfp); + return ERR_PTR(-ENODEV); + } + list_add_tail(&sfp->sfd_siblings, &sdp->sfds); diff --git a/queue-3.16/scsi-sg-fix-sg_dxfer_from_dev-transfers.patch b/queue-3.16/scsi-sg-fix-sg_dxfer_from_dev-transfers.patch new file mode 100644 index 00000000..85a60e6e --- /dev/null +++ b/queue-3.16/scsi-sg-fix-sg_dxfer_from_dev-transfers.patch @@ -0,0 +1,43 @@ +From: Johannes Thumshirn <jthumshirn@suse.de> +Date: Fri, 7 Jul 2017 10:56:38 +0200 +Subject: scsi: sg: fix SG_DXFER_FROM_DEV transfers + +commit 68c59fcea1f2c6a54c62aa896cc623c1b5bc9b47 upstream. + +SG_DXFER_FROM_DEV transfers do not necessarily have a dxferp as we set +it to NULL for the old sg_io read/write interface, but must have a +length bigger than 0. This fixes a regression introduced by commit +28676d869bbb ("scsi: sg: check for valid direction before starting the +request") + +Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de> +Fixes: 28676d869bbb ("scsi: sg: check for valid direction before starting the request") +Reported-by: Chris Clayton <chris2553@googlemail.com> +Tested-by: Chris Clayton <chris2553@googlemail.com> +Cc: Douglas Gilbert <dgilbert@interlog.com> +Reviewed-by: Hannes Reinecke <hare@suse.com> +Tested-by: Chris Clayton <chris2553@googlemail.com> +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Cc: Cristian Crinteanu <crinteanu.cristian@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -795,8 +795,11 @@ static bool sg_is_valid_dxfer(sg_io_hdr_ + if (hp->dxferp || hp->dxfer_len > 0) + return false; + return true; +- case SG_DXFER_TO_DEV: + case SG_DXFER_FROM_DEV: ++ if (hp->dxfer_len < 0) ++ return false; ++ return true; ++ case SG_DXFER_TO_DEV: + case SG_DXFER_TO_FROM_DEV: + if (!hp->dxferp || hp->dxfer_len == 0) + return false; diff --git a/queue-3.16/scsi-sg-fix-static-checker-warning-in-sg_is_valid_dxfer.patch b/queue-3.16/scsi-sg-fix-static-checker-warning-in-sg_is_valid_dxfer.patch new file mode 100644 index 00000000..911d97b6 --- /dev/null +++ b/queue-3.16/scsi-sg-fix-static-checker-warning-in-sg_is_valid_dxfer.patch @@ -0,0 +1,41 @@ +From: Johannes Thumshirn <jthumshirn@suse.de> +Date: Mon, 17 Jul 2017 15:11:42 +0200 +Subject: scsi: sg: fix static checker warning in sg_is_valid_dxfer + +commit 14074aba4bcda3764c9a702b276308b89901d5b6 upstream. + +dxfer_len is an unsigned int and we always assign a value > 0 to it, so +it doesn't make any sense to check if it is < 0. We can't really check +dxferp as well as we have both NULL and not NULL cases in the possible +call paths. + +So just return true for SG_DXFER_FROM_DEV transfer in +sg_is_valid_dxfer(). + +Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de> +Reported-by: Colin Ian King <colin.king@canonical.com> +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +Cc: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -796,8 +796,11 @@ static bool sg_is_valid_dxfer(sg_io_hdr_ + return false; + return true; + case SG_DXFER_FROM_DEV: +- if (hp->dxfer_len < 0) +- return false; ++ /* ++ * for SG_DXFER_FROM_DEV we always set dxfer_len to > 0. dxferp ++ * can either be NULL or != NULL so there's no point in checking ++ * it either. So just return true. ++ */ + return true; + case SG_DXFER_TO_DEV: + case SG_DXFER_TO_FROM_DEV: diff --git a/queue-3.16/scsi-sg-fixup-infoleak-when-using-sg_get_request_table.patch b/queue-3.16/scsi-sg-fixup-infoleak-when-using-sg_get_request_table.patch new file mode 100644 index 00000000..b142bb3b --- /dev/null +++ b/queue-3.16/scsi-sg-fixup-infoleak-when-using-sg_get_request_table.patch @@ -0,0 +1,43 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 15 Sep 2017 14:05:16 +0200 +Subject: scsi: sg: fixup infoleak when using SG_GET_REQUEST_TABLE + +commit 3e0097499839e0fe3af380410eababe5a47c4cf9 upstream. + +When calling SG_GET_REQUEST_TABLE ioctl only a half-filled table is +returned; the remaining part will then contain stale kernel memory +information. This patch zeroes out the entire table to avoid this +issue. + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Reviewed-by: Eric Dumazet <edumazet@google.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -881,7 +881,6 @@ sg_fill_request_table(Sg_fd *sfp, sg_req + list_for_each_entry(srp, &sfp->rq_list, entry) { + if (val > SG_MAX_QUEUE) + break; +- memset(&rinfo[val], 0, SZ_SG_REQ_INFO); + rinfo[val].req_state = srp->done + 1; + rinfo[val].problem = + srp->header.masked_status & +@@ -1098,8 +1097,8 @@ sg_ioctl(struct file *filp, unsigned int + else { + sg_req_info_t *rinfo; + +- rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, +- GFP_KERNEL); ++ rinfo = kzalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, ++ GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + read_lock_irqsave(&sfp->rq_list_lock, iflags); diff --git a/queue-3.16/scsi-sg-off-by-one-in-sg_ioctl.patch b/queue-3.16/scsi-sg-off-by-one-in-sg_ioctl.patch new file mode 100644 index 00000000..e91a87a5 --- /dev/null +++ b/queue-3.16/scsi-sg-off-by-one-in-sg_ioctl.patch @@ -0,0 +1,30 @@ +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Thu, 17 Aug 2017 10:09:54 +0300 +Subject: scsi: sg: off by one in sg_ioctl() + +commit bd46fc406b30d1db1aff8dabaff8d18bb423fdcf upstream. + +If "val" is SG_MAX_QUEUE then we are one element beyond the end of the +"rinfo" array so the > should be >=. + +Fixes: 109bade9c625 ("scsi: sg: use standard lists for sg_requests") +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -1072,7 +1072,7 @@ sg_ioctl(struct file *filp, unsigned int + read_lock_irqsave(&sfp->rq_list_lock, iflags); + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { +- if (val > SG_MAX_QUEUE) ++ if (val >= SG_MAX_QUEUE) + break; + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); + rinfo[val].req_state = srp->done + 1; diff --git a/queue-3.16/scsi-sg-only-check-for-dxfer_len-greater-than-256m.patch b/queue-3.16/scsi-sg-only-check-for-dxfer_len-greater-than-256m.patch new file mode 100644 index 00000000..0cd2ce05 --- /dev/null +++ b/queue-3.16/scsi-sg-only-check-for-dxfer_len-greater-than-256m.patch @@ -0,0 +1,82 @@ +From: Johannes Thumshirn <jthumshirn@suse.de> +Date: Thu, 27 Jul 2017 09:11:26 +0200 +Subject: scsi: sg: only check for dxfer_len greater than 256M + +commit f930c7043663188429cd9b254e9d761edfc101ce upstream. + +Don't make any assumptions on the sg_io_hdr_t::dxfer_direction or the +sg_io_hdr_t::dxferp in order to determine if it is a valid request. The +only way we can check for bad requests is by checking if the length +exceeds 256M. + +Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de> +Fixes: 28676d869bbb (scsi: sg: check for valid direction before starting the request) +Reported-by: Jason L Tibbitts III <tibbs@math.uh.edu> +Tested-by: Jason L Tibbitts III <tibbs@math.uh.edu> +Suggested-by: Doug Gilbert <dgilbert@interlog.com> +Cc: Doug Gilbert <dgilbert@interlog.com> +Cc: <stable@vger.kernel.org> +Reviewed-by: Hannes Reinecke <hare@suse.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +[bwh: Backported to 3.16: Include <linux/sizes.h>] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 31 +------------------------------ + 1 file changed, 1 insertion(+), 30 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -54,6 +54,7 @@ static int sg_version_num = 30534; /* 2 + #include <linux/atomic.h> + #include <linux/ratelimit.h> + #include <linux/cred.h> /* for sg_check_file_access() */ ++#include <linux/sizes.h> + + #include "scsi.h" + #include <scsi/scsi_dbg.h> +@@ -788,35 +789,6 @@ sg_new_write(Sg_fd *sfp, struct file *fi + return count; + } + +-static bool sg_is_valid_dxfer(sg_io_hdr_t *hp) +-{ +- switch (hp->dxfer_direction) { +- case SG_DXFER_NONE: +- if (hp->dxferp || hp->dxfer_len > 0) +- return false; +- return true; +- case SG_DXFER_FROM_DEV: +- /* +- * for SG_DXFER_FROM_DEV we always set dxfer_len to > 0. dxferp +- * can either be NULL or != NULL so there's no point in checking +- * it either. So just return true. +- */ +- return true; +- case SG_DXFER_TO_DEV: +- case SG_DXFER_TO_FROM_DEV: +- if (!hp->dxferp || hp->dxfer_len == 0) +- return false; +- return true; +- case SG_DXFER_UNKNOWN: +- if ((!hp->dxferp && hp->dxfer_len) || +- (hp->dxferp && hp->dxfer_len == 0)) +- return false; +- return true; +- default: +- return false; +- } +-} +- + static int + sg_common_write(Sg_fd * sfp, Sg_request * srp, + unsigned char *cmnd, int timeout, int blocking) +@@ -836,7 +808,7 @@ sg_common_write(Sg_fd * sfp, Sg_request + SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", + (int) cmnd[0], (int) hp->cmd_len)); + +- if (!sg_is_valid_dxfer(hp)) ++ if (hp->dxfer_len >= SZ_256M) + return -EINVAL; + + k = sg_start_req(srp, cmnd); diff --git a/queue-3.16/scsi-sg-protect-accesses-to-reserved-page-array.patch b/queue-3.16/scsi-sg-protect-accesses-to-reserved-page-array.patch new file mode 100644 index 00000000..c8d3628a --- /dev/null +++ b/queue-3.16/scsi-sg-protect-accesses-to-reserved-page-array.patch @@ -0,0 +1,169 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 7 Apr 2017 09:34:14 +0200 +Subject: scsi: sg: protect accesses to 'reserved' page array + +commit 1bc0eb0446158cc76562176b80623aa119afee5b upstream. + +The 'reserved' page array is used as a short-cut for mapping data, +saving us to allocate pages per request. However, the 'reserved' array +is only capable of holding one request, so this patch introduces a mutex +for protect 'sg_fd' against concurrent accesses. + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> + +[toddpoynor@google.com: backport to 3.18-4.9, fixup for bad ioctl +SG_SET_FORCE_LOW_DMA code removed in later versions and not modified by +the original patch.] + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Todd Poynor <toddpoynor@google.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 47 ++++++++++++++++++++++++++--------------------- + 1 file changed, 26 insertions(+), 21 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -150,6 +150,7 @@ typedef struct sg_fd { /* holds the sta + struct sg_device *parentdp; /* owning device */ + wait_queue_head_t read_wait; /* queue read until command done */ + rwlock_t rq_list_lock; /* protect access to list in req_arr */ ++ struct mutex f_mutex; /* protect against changes in this fd */ + int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ + int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + Sg_scatter_hold reserve; /* buffer held for this file descriptor */ +@@ -163,6 +164,7 @@ typedef struct sg_fd { /* holds the sta + unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ + char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ + char mmap_called; /* 0 -> mmap() never called on this fd */ ++ char res_in_use; /* 1 -> 'reserve' array in use */ + struct kref f_ref; + struct execute_work ew; + } Sg_fd; +@@ -206,7 +208,6 @@ static void sg_remove_sfp(struct kref *) + static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); + static Sg_request *sg_add_request(Sg_fd * sfp); + static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); +-static int sg_res_in_use(Sg_fd * sfp); + static Sg_device *sg_get_dev(int dev); + static void sg_device_destroy(struct kref *kref); + +@@ -652,6 +653,7 @@ sg_write(struct file *filp, const char _ + } + buf += SZ_SG_HEADER; + __get_user(opcode, buf); ++ mutex_lock(&sfp->f_mutex); + if (sfp->next_cmd_len > 0) { + cmd_size = sfp->next_cmd_len; + sfp->next_cmd_len = 0; /* reset so only this write() effected */ +@@ -660,6 +662,7 @@ sg_write(struct file *filp, const char _ + if ((opcode >= 0xc0) && old_hdr.twelve_byte) + cmd_size = 12; + } ++ mutex_unlock(&sfp->f_mutex); + SCSI_LOG_TIMEOUT(4, printk( + "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size)); + /* Determine buffer size. */ +@@ -758,7 +761,7 @@ sg_new_write(Sg_fd *sfp, struct file *fi + sg_remove_request(sfp, srp); + return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ + } +- if (sg_res_in_use(sfp)) { ++ if (sfp->res_in_use) { + sg_remove_request(sfp, srp); + return -EBUSY; /* reserve buffer already being used */ + } +@@ -933,7 +936,7 @@ sg_ioctl(struct file *filp, unsigned int + return result; + if (val) { + sfp->low_dma = 1; +- if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) { ++ if ((0 == sfp->low_dma) && !sfp->res_in_use) { + val = (int) sfp->reserve.bufflen; + sg_remove_scat(&sfp->reserve); + sg_build_reserve(sfp, val); +@@ -1008,12 +1011,18 @@ sg_ioctl(struct file *filp, unsigned int + return -EINVAL; + val = min_t(int, val, + max_sectors_bytes(sdp->device->request_queue)); ++ mutex_lock(&sfp->f_mutex); + if (val != sfp->reserve.bufflen) { +- if (sg_res_in_use(sfp) || sfp->mmap_called) ++ if (sfp->mmap_called || ++ sfp->res_in_use) { ++ mutex_unlock(&sfp->f_mutex); + return -EBUSY; ++ } ++ + sg_remove_scat(&sfp->reserve); + sg_build_reserve(sfp, val); + } ++ mutex_unlock(&sfp->f_mutex); + return 0; + case SG_GET_RESERVED_SIZE: + val = min_t(int, sfp->reserve.bufflen, +@@ -1752,13 +1761,22 @@ sg_start_req(Sg_request *srp, unsigned c + md = &map_data; + + if (md) { +- if (!sg_res_in_use(sfp) && dxfer_len <= rsv_schp->bufflen) ++ mutex_lock(&sfp->f_mutex); ++ if (dxfer_len <= rsv_schp->bufflen && ++ !sfp->res_in_use) { ++ sfp->res_in_use = 1; + sg_link_reserve(sfp, srp, dxfer_len); +- else { ++ } else if ((hp->flags & SG_FLAG_MMAP_IO) && sfp->res_in_use) { ++ mutex_unlock(&sfp->f_mutex); ++ return -EBUSY; ++ } else { + res = sg_build_indirect(req_schp, sfp, dxfer_len); +- if (res) ++ if (res) { ++ mutex_unlock(&sfp->f_mutex); + return res; ++ } + } ++ mutex_unlock(&sfp->f_mutex); + + md->pages = req_schp->pages; + md->page_order = req_schp->page_order; +@@ -2155,6 +2173,7 @@ sg_add_sfp(Sg_device * sdp, int dev) + rwlock_init(&sfp->rq_list_lock); + + kref_init(&sfp->f_ref); ++ mutex_init(&sfp->f_mutex); + sfp->timeout = SG_DEFAULT_TIMEOUT; + sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER; + sfp->force_packid = SG_DEF_FORCE_PACK_ID; +@@ -2229,20 +2248,6 @@ sg_remove_sfp(struct kref *kref) + schedule_work(&sfp->ew.work); + } + +-static int +-sg_res_in_use(Sg_fd * sfp) +-{ +- const Sg_request *srp; +- unsigned long iflags; +- +- read_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (srp = sfp->headrp; srp; srp = srp->nextrp) +- if (srp->res_used) +- break; +- read_unlock_irqrestore(&sfp->rq_list_lock, iflags); +- return srp ? 1 : 0; +-} +- + #ifdef CONFIG_SCSI_PROC_FS + static int + sg_idr_max_id(int id, void *p, void *data) diff --git a/queue-3.16/scsi-sg-protect-against-races-between-mmap-and-sg_set_reserved_size.patch b/queue-3.16/scsi-sg-protect-against-races-between-mmap-and-sg_set_reserved_size.patch new file mode 100644 index 00000000..7a4b01fd --- /dev/null +++ b/queue-3.16/scsi-sg-protect-against-races-between-mmap-and-sg_set_reserved_size.patch @@ -0,0 +1,58 @@ +From: Todd Poynor <toddpoynor@google.com> +Date: Tue, 15 Aug 2017 22:41:08 -0700 +Subject: scsi: sg: protect against races between mmap() and SG_SET_RESERVED_SIZE + +commit 6a8dadcca81fceff9976e8828cceb072873b7bd5 upstream. + +Take f_mutex around mmap() processing to protect against races with the +SG_SET_RESERVED_SIZE ioctl. Ensure the reserve buffer length remains +consistent during the mapping operation, and set the "mmap called" flag +to prevent further changes to the reserved buffer size as an atomic +operation with the mapping. + +[mkp: fixed whitespace] + +Signed-off-by: Todd Poynor <toddpoynor@google.com> +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -1310,6 +1310,7 @@ sg_mmap(struct file *filp, struct vm_are + unsigned long req_sz, len, sa; + Sg_scatter_hold *rsv_schp; + int k, length; ++ int ret = 0; + + if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) + return -ENXIO; +@@ -1319,8 +1320,11 @@ sg_mmap(struct file *filp, struct vm_are + if (vma->vm_pgoff) + return -EINVAL; /* want no offset */ + rsv_schp = &sfp->reserve; +- if (req_sz > rsv_schp->bufflen) +- return -ENOMEM; /* cannot map more than reserved buffer */ ++ mutex_lock(&sfp->f_mutex); ++ if (req_sz > rsv_schp->bufflen) { ++ ret = -ENOMEM; /* cannot map more than reserved buffer */ ++ goto out; ++ } + + sa = vma->vm_start; + length = 1 << (PAGE_SHIFT + rsv_schp->page_order); +@@ -1334,7 +1338,9 @@ sg_mmap(struct file *filp, struct vm_are + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = sfp; + vma->vm_ops = &sg_mmap_vm_ops; +- return 0; ++out: ++ mutex_unlock(&sfp->f_mutex); ++ return ret; + } + + static void diff --git a/queue-3.16/scsi-sg-re-fix-off-by-one-in-sg_fill_request_table.patch b/queue-3.16/scsi-sg-re-fix-off-by-one-in-sg_fill_request_table.patch new file mode 100644 index 00000000..c8dd90a2 --- /dev/null +++ b/queue-3.16/scsi-sg-re-fix-off-by-one-in-sg_fill_request_table.patch @@ -0,0 +1,35 @@ +From: Ben Hutchings <ben.hutchings@codethink.co.uk> +Date: Sun, 15 Oct 2017 18:16:33 +0100 +Subject: scsi: sg: Re-fix off by one in sg_fill_request_table() + +commit 587c3c9f286cee5c9cac38d28c8ae1875f4ec85b upstream. + +Commit 109bade9c625 ("scsi: sg: use standard lists for sg_requests") +introduced an off-by-one error in sg_ioctl(), which was fixed by commit +bd46fc406b30 ("scsi: sg: off by one in sg_ioctl()"). + +Unfortunately commit 4759df905a47 ("scsi: sg: factor out +sg_fill_request_table()") moved that code, and reintroduced the +bug (perhaps due to a botched rebase). Fix it again. + +Fixes: 4759df905a47 ("scsi: sg: factor out sg_fill_request_table()") +Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -879,7 +879,7 @@ sg_fill_request_table(Sg_fd *sfp, sg_req + + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { +- if (val > SG_MAX_QUEUE) ++ if (val >= SG_MAX_QUEUE) + break; + rinfo[val].req_state = srp->done + 1; + rinfo[val].problem = diff --git a/queue-3.16/scsi-sg-recheck-mmap_io-request-length-with-lock-held.patch b/queue-3.16/scsi-sg-recheck-mmap_io-request-length-with-lock-held.patch new file mode 100644 index 00000000..d40b3a94 --- /dev/null +++ b/queue-3.16/scsi-sg-recheck-mmap_io-request-length-with-lock-held.patch @@ -0,0 +1,45 @@ +From: Todd Poynor <toddpoynor@google.com> +Date: Tue, 15 Aug 2017 21:48:43 -0700 +Subject: scsi: sg: recheck MMAP_IO request length with lock held + +commit 8d26f491116feaa0b16de370b6a7ba40a40fa0b4 upstream. + +Commit 1bc0eb044615 ("scsi: sg: protect accesses to 'reserved' page +array") adds needed concurrency protection for the "reserve" buffer. +Some checks that are initially made outside the lock are replicated once +the lock is taken to ensure the checks and resulting decisions are made +using consistent state. + +The check that a request with flag SG_FLAG_MMAP_IO set fits in the +reserve buffer also needs to be performed again under the lock to ensure +the reserve buffer length compared against matches the value in effect +when the request is linked to the reserve buffer. An -ENOMEM should be +returned in this case, instead of switching over to an indirect buffer +as for non-MMAP_IO requests. + +Signed-off-by: Todd Poynor <toddpoynor@google.com> +Acked-by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -1772,9 +1772,12 @@ sg_start_req(Sg_request *srp, unsigned c + !sfp->res_in_use) { + sfp->res_in_use = 1; + sg_link_reserve(sfp, srp, dxfer_len); +- } else if ((hp->flags & SG_FLAG_MMAP_IO) && sfp->res_in_use) { ++ } else if (hp->flags & SG_FLAG_MMAP_IO) { ++ res = -EBUSY; /* sfp->res_in_use == 1 */ ++ if (dxfer_len > rsv_schp->bufflen) ++ res = -ENOMEM; + mutex_unlock(&sfp->f_mutex); +- return -EBUSY; ++ return res; + } else { + res = sg_build_indirect(req_schp, sfp, dxfer_len); + if (res) { diff --git a/queue-3.16/scsi-sg-remove-save_scat_len.patch b/queue-3.16/scsi-sg-remove-save_scat_len.patch new file mode 100644 index 00000000..892c70ee --- /dev/null +++ b/queue-3.16/scsi-sg-remove-save_scat_len.patch @@ -0,0 +1,37 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 7 Apr 2017 09:34:13 +0200 +Subject: scsi: sg: remove 'save_scat_len' + +commit 136e57bf43dc4babbfb8783abbf707d483cacbe3 upstream. + +Unused. + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -154,7 +154,6 @@ typedef struct sg_fd { /* holds the sta + int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ + int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + Sg_scatter_hold reserve; /* buffer held for this file descriptor */ +- unsigned save_scat_len; /* original length of trunc. scat. element */ + Sg_request *headrp; /* head of request slist, NULL->empty */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ +@@ -2069,7 +2068,6 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_reques + req_schp->pages = NULL; + req_schp->page_order = 0; + req_schp->sglist_len = 0; +- sfp->save_scat_len = 0; + srp->res_used = 0; + /* Called without mutex lock to avoid deadlock */ + sfp->res_in_use = 0; diff --git a/queue-3.16/scsi-sg-reset-res_in_use-after-unlinking-reserved-array.patch b/queue-3.16/scsi-sg-reset-res_in_use-after-unlinking-reserved-array.patch new file mode 100644 index 00000000..cecebf50 --- /dev/null +++ b/queue-3.16/scsi-sg-reset-res_in_use-after-unlinking-reserved-array.patch @@ -0,0 +1,35 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Mon, 24 Apr 2017 10:26:36 +0200 +Subject: scsi: sg: reset 'res_in_use' after unlinking reserved array + +commit e791ce27c3f6a1d3c746fd6a8f8e36c9540ec6f9 upstream. + +Once the reserved page array is unused we can reset the 'res_in_use' +state; here we can do a lazy update without holding the mutex as we only +need to check against concurrent access, not concurrent release. + +[mkp: checkpatch] + +Fixes: 1bc0eb044615 ("scsi: sg: protect accesses to 'reserved' page array") +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Cc: Todd Poynor <toddpoynor@google.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -2062,6 +2062,8 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_reques + req_schp->sglist_len = 0; + sfp->save_scat_len = 0; + srp->res_used = 0; ++ /* Called without mutex lock to avoid deadlock */ ++ sfp->res_in_use = 0; + } + + static Sg_request * diff --git a/queue-3.16/scsi-sg-use-standard-lists-for-sg_requests.patch b/queue-3.16/scsi-sg-use-standard-lists-for-sg_requests.patch new file mode 100644 index 00000000..cafec049 --- /dev/null +++ b/queue-3.16/scsi-sg-use-standard-lists-for-sg_requests.patch @@ -0,0 +1,287 @@ +From: Hannes Reinecke <hare@suse.de> +Date: Fri, 7 Apr 2017 09:34:16 +0200 +Subject: scsi: sg: use standard lists for sg_requests + +commit 109bade9c625c89bb5ea753aaa1a0a97e6fbb548 upstream. + +'Sg_request' is using a private list implementation; convert it to +standard lists. + +Signed-off-by: Hannes Reinecke <hare@suse.com> +Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> +Tested-by: Johannes Thumshirn <jthumshirn@suse.de> +Reviewed-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 147 +++++++++++++++++++--------------------------- + 1 file changed, 61 insertions(+), 86 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -130,7 +130,7 @@ struct sg_device; /* forward declaratio + struct sg_fd; + + typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ +- struct sg_request *nextrp; /* NULL -> tail request (slist) */ ++ struct list_head entry; /* list entry */ + struct sg_fd *parentfp; /* NULL -> not in use */ + Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ + sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */ +@@ -154,7 +154,7 @@ typedef struct sg_fd { /* holds the sta + int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ + int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + Sg_scatter_hold reserve; /* buffer held for this file descriptor */ +- Sg_request *headrp; /* head of request slist, NULL->empty */ ++ struct list_head rq_list; /* head of request list */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ + char low_dma; /* as in parent but possibly overridden to 1 */ +@@ -981,7 +981,7 @@ sg_ioctl(struct file *filp, unsigned int + if (!access_ok(VERIFY_WRITE, ip, sizeof (int))) + return -EFAULT; + read_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (srp = sfp->headrp; srp; srp = srp->nextrp) { ++ list_for_each_entry(srp, &sfp->rq_list, entry) { + if ((1 == srp->done) && (!srp->sg_io_owned)) { + read_unlock_irqrestore(&sfp->rq_list_lock, + iflags); +@@ -994,7 +994,8 @@ sg_ioctl(struct file *filp, unsigned int + return 0; + case SG_GET_NUM_WAITING: + read_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) { ++ val = 0; ++ list_for_each_entry(srp, &sfp->rq_list, entry) { + if ((1 == srp->done) && (!srp->sg_io_owned)) + ++val; + } +@@ -1069,35 +1070,33 @@ sg_ioctl(struct file *filp, unsigned int + if (!rinfo) + return -ENOMEM; + read_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; +- ++val, srp = srp ? srp->nextrp : srp) { ++ val = 0; ++ list_for_each_entry(srp, &sfp->rq_list, entry) { ++ if (val > SG_MAX_QUEUE) ++ break; + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); +- if (srp) { +- rinfo[val].req_state = srp->done + 1; +- rinfo[val].problem = +- srp->header.masked_status & +- srp->header.host_status & +- srp->header.driver_status; +- if (srp->done) +- rinfo[val].duration = +- srp->header.duration; +- else { +- ms = jiffies_to_msecs(jiffies); +- rinfo[val].duration = +- (ms > srp->header.duration) ? +- (ms - srp->header.duration) : 0; +- } +- rinfo[val].orphan = srp->orphan; +- rinfo[val].sg_io_owned = +- srp->sg_io_owned; +- rinfo[val].pack_id = +- srp->header.pack_id; +- rinfo[val].usr_ptr = +- srp->header.usr_ptr; ++ rinfo[val].req_state = srp->done + 1; ++ rinfo[val].problem = ++ srp->header.masked_status & ++ srp->header.host_status & ++ srp->header.driver_status; ++ if (srp->done) ++ rinfo[val].duration = ++ srp->header.duration; ++ else { ++ ms = jiffies_to_msecs(jiffies); ++ rinfo[val].duration = ++ (ms > srp->header.duration) ? ++ (ms - srp->header.duration) : 0; + } ++ rinfo[val].orphan = srp->orphan; ++ rinfo[val].sg_io_owned = srp->sg_io_owned; ++ rinfo[val].pack_id = srp->header.pack_id; ++ rinfo[val].usr_ptr = srp->header.usr_ptr; ++ val++; + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); +- result = __copy_to_user(p, rinfo, ++ result = __copy_to_user(p, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); + result = result ? -EFAULT : 0; + kfree(rinfo); +@@ -1229,7 +1228,7 @@ sg_poll(struct file *filp, poll_table * + return POLLERR; + poll_wait(filp, &sfp->read_wait, wait); + read_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (srp = sfp->headrp; srp; srp = srp->nextrp) { ++ list_for_each_entry(srp, &sfp->rq_list, entry) { + /* if any read waiting, flag it */ + if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) + res = POLLIN | POLLRDNORM; +@@ -2080,7 +2079,7 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) + unsigned long iflags; + + write_lock_irqsave(&sfp->rq_list_lock, iflags); +- for (resp = sfp->headrp; resp; resp = resp->nextrp) { ++ list_for_each_entry(resp, &sfp->rq_list, entry) { + /* look for requests that are ready + not SG_IO owned */ + if ((1 == resp->done) && (!resp->sg_io_owned) && + ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { +@@ -2098,70 +2097,45 @@ sg_add_request(Sg_fd * sfp) + { + int k; + unsigned long iflags; +- Sg_request *resp; + Sg_request *rp = sfp->req_arr; + + write_lock_irqsave(&sfp->rq_list_lock, iflags); +- resp = sfp->headrp; +- if (!resp) { +- memset(rp, 0, sizeof (Sg_request)); +- rp->parentfp = sfp; +- resp = rp; +- sfp->headrp = resp; +- } else { +- if (0 == sfp->cmd_q) +- resp = NULL; /* command queuing disallowed */ +- else { +- for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { +- if (!rp->parentfp) +- break; +- } +- if (k < SG_MAX_QUEUE) { +- memset(rp, 0, sizeof (Sg_request)); +- rp->parentfp = sfp; +- while (resp->nextrp) +- resp = resp->nextrp; +- resp->nextrp = rp; +- resp = rp; +- } else +- resp = NULL; ++ if (!list_empty(&sfp->rq_list)) { ++ if (!sfp->cmd_q) ++ goto out_unlock; ++ ++ for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { ++ if (!rp->parentfp) ++ break; + } ++ if (k >= SG_MAX_QUEUE) ++ goto out_unlock; + } +- if (resp) { +- resp->nextrp = NULL; +- resp->header.duration = jiffies_to_msecs(jiffies); +- } ++ memset(rp, 0, sizeof (Sg_request)); ++ rp->parentfp = sfp; ++ rp->header.duration = jiffies_to_msecs(jiffies); ++ list_add_tail(&rp->entry, &sfp->rq_list); + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); +- return resp; ++ return rp; ++out_unlock: ++ write_unlock_irqrestore(&sfp->rq_list_lock, iflags); ++ return NULL; + } + + /* Return of 1 for found; 0 for not found */ + static int + sg_remove_request(Sg_fd * sfp, Sg_request * srp) + { +- Sg_request *prev_rp; +- Sg_request *rp; + unsigned long iflags; + int res = 0; + +- if ((!sfp) || (!srp) || (!sfp->headrp)) ++ if (!sfp || !srp || list_empty(&sfp->rq_list)) + return res; + write_lock_irqsave(&sfp->rq_list_lock, iflags); +- prev_rp = sfp->headrp; +- if (srp == prev_rp) { +- sfp->headrp = prev_rp->nextrp; +- prev_rp->parentfp = NULL; ++ if (!list_empty(&srp->entry)) { ++ list_del(&srp->entry); ++ srp->parentfp = NULL; + res = 1; +- } else { +- while ((rp = prev_rp->nextrp)) { +- if (srp == rp) { +- prev_rp->nextrp = rp->nextrp; +- rp->parentfp = NULL; +- res = 1; +- break; +- } +- prev_rp = rp; +- } + } + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return res; +@@ -2180,7 +2154,7 @@ sg_add_sfp(Sg_device * sdp, int dev) + + init_waitqueue_head(&sfp->read_wait); + rwlock_init(&sfp->rq_list_lock); +- ++ INIT_LIST_HEAD(&sfp->rq_list); + kref_init(&sfp->f_ref); + mutex_init(&sfp->f_mutex); + sfp->timeout = SG_DEFAULT_TIMEOUT; +@@ -2218,10 +2192,13 @@ sg_remove_sfp_usercontext(struct work_st + { + struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); + struct sg_device *sdp = sfp->parentdp; ++ Sg_request *srp; + + /* Cleanup any responses which were never read(). */ +- while (sfp->headrp) +- sg_finish_rem_req(sfp->headrp); ++ while (!list_empty(&sfp->rq_list)) { ++ srp = list_first_entry(&sfp->rq_list, Sg_request, entry); ++ sg_finish_rem_req(srp); ++ } + + if (sfp->reserve.bufflen > 0) { + SCSI_LOG_TIMEOUT(6, +@@ -2626,7 +2603,7 @@ static int sg_proc_seq_show_devstrs(stru + /* must be called while holding sg_index_lock */ + static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) + { +- int k, m, new_interface, blen, usg; ++ int k, new_interface, blen, usg; + Sg_request *srp; + Sg_fd *fp; + const sg_io_hdr_t *hp; +@@ -2646,13 +2623,11 @@ static void sg_proc_debug_helper(struct + seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", + (int) fp->cmd_q, (int) fp->force_packid, + (int) fp->keep_orphan); +- for (m = 0, srp = fp->headrp; +- srp != NULL; +- ++m, srp = srp->nextrp) { ++ list_for_each_entry(srp, &fp->rq_list, entry) { + hp = &srp->header; + new_interface = (hp->interface_id == '\0') ? 0 : 1; + if (srp->res_used) { +- if (new_interface && ++ if (new_interface && + (SG_FLAG_MMAP_IO & hp->flags)) + cp = " mmap>> "; + else +@@ -2683,7 +2658,7 @@ static void sg_proc_debug_helper(struct + seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, + (int) srp->data.cmd_opcode); + } +- if (0 == m) ++ if (list_empty(&fp->rq_list)) + seq_puts(s, " No requests active\n"); + read_unlock(&fp->rq_list_lock); + } diff --git a/queue-3.16/selinux-cleanup-error-reporting-in-selinux_nlmsg_perm.patch b/queue-3.16/selinux-cleanup-error-reporting-in-selinux_nlmsg_perm.patch new file mode 100644 index 00000000..16b81928 --- /dev/null +++ b/queue-3.16/selinux-cleanup-error-reporting-in-selinux_nlmsg_perm.patch @@ -0,0 +1,38 @@ +From: Richard Guy Briggs <rgb@redhat.com> +Date: Thu, 18 Sep 2014 20:50:17 -0400 +Subject: selinux: cleanup error reporting in selinux_nlmsg_perm() + +commit e173fb2646a832b424c80904c306b816760ce477 upstream. + +Convert audit_log() call to WARN_ONCE(). + +Rename "type=" to nlmsg_type=" to avoid confusion with the audit record +type. + +Added "protocol=" to help track down which protocol (NETLINK_AUDIT?) was used +within the netlink protocol family. + +Signed-off-by: Richard Guy Briggs <rgb@redhat.com> +[Rewrote the patch subject line] +Signed-off-by: Paul Moore <pmoore@redhat.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + security/selinux/hooks.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -4683,10 +4683,9 @@ static int selinux_nlmsg_perm(struct soc + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); + if (err) { + if (err == -EINVAL) { +- audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, +- "SELinux: unrecognized netlink message" +- " type=%hu for sclass=%hu\n", +- nlh->nlmsg_type, sksec->sclass); ++ WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" ++ " protocol=%hu nlmsg_type=%hu sclass=%hu\n", ++ sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); + if (!selinux_enforcing || security_get_allow_unknown()) + err = 0; + } diff --git a/queue-3.16/selinux-convert-warn_once-to-printk-in-selinux_nlmsg_perm.patch b/queue-3.16/selinux-convert-warn_once-to-printk-in-selinux_nlmsg_perm.patch new file mode 100644 index 00000000..93904e49 --- /dev/null +++ b/queue-3.16/selinux-convert-warn_once-to-printk-in-selinux_nlmsg_perm.patch @@ -0,0 +1,37 @@ +From: Richard Guy Briggs <rgb@redhat.com> +Date: Wed, 12 Nov 2014 14:01:34 -0500 +Subject: selinux: convert WARN_ONCE() to printk() in selinux_nlmsg_perm() + +commit d950f84c1c6658faec2ecbf5b09f7e7191953394 upstream. + +Convert WARN_ONCE() to printk() in selinux_nlmsg_perm(). + +After conversion from audit_log() in commit e173fb26, WARN_ONCE() was +deemed too alarmist, so switch it to printk(). + +Signed-off-by: Richard Guy Briggs <rgb@redhat.com> +[PM: Changed to printk(WARNING) so we catch all of the different + invalid netlink messages. In Richard's defense, he brought this + point up earlier, but I didn't understand his point at the time.] +Signed-off-by: Paul Moore <pmoore@redhat.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + security/selinux/hooks.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -4683,9 +4683,10 @@ static int selinux_nlmsg_perm(struct soc + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); + if (err) { + if (err == -EINVAL) { +- WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" +- " protocol=%hu nlmsg_type=%hu sclass=%hu\n", +- sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); ++ printk(KERN_WARNING ++ "SELinux: unrecognized netlink message:" ++ " protocol=%hu nlmsg_type=%hu sclass=%hu\n", ++ sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); + if (!selinux_enforcing || security_get_allow_unknown()) + err = 0; + } diff --git a/queue-3.16/selinux-print-sclass-as-string-when-unrecognized-netlink-message-occurs.patch b/queue-3.16/selinux-print-sclass-as-string-when-unrecognized-netlink-message-occurs.patch new file mode 100644 index 00000000..0150bdd9 --- /dev/null +++ b/queue-3.16/selinux-print-sclass-as-string-when-unrecognized-netlink-message-occurs.patch @@ -0,0 +1,32 @@ +From: Marek Milkovic <mmilkovi@redhat.com> +Date: Thu, 4 Jun 2015 16:22:16 -0400 +Subject: selinux: Print 'sclass' as string when unrecognized netlink message occurs + +commit cded3fffbeab777e6ad2ec05d4a3b62c5caca0f3 upstream. + +This prints the 'sclass' field as string instead of index in unrecognized netlink message. +The textual representation makes it easier to distinguish the right class. + +Signed-off-by: Marek Milkovic <mmilkovi@redhat.com> +Acked-by: Stephen Smalley <sds@tycho.nsa.gov> +[PM: 80-char width fixes] +Signed-off-by: Paul Moore <pmoore@redhat.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + security/selinux/hooks.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -4685,8 +4685,9 @@ static int selinux_nlmsg_perm(struct soc + if (err == -EINVAL) { + printk(KERN_WARNING + "SELinux: unrecognized netlink message:" +- " protocol=%hu nlmsg_type=%hu sclass=%hu\n", +- sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); ++ " protocol=%hu nlmsg_type=%hu sclass=%s\n", ++ sk->sk_protocol, nlh->nlmsg_type, ++ secclass_map[sksec->sclass - 1].name); + if (!selinux_enforcing || security_get_allow_unknown()) + err = 0; + } diff --git a/queue-3.16/selinux-properly-handle-multiple-messages-in-selinux_netlink_send.patch b/queue-3.16/selinux-properly-handle-multiple-messages-in-selinux_netlink_send.patch new file mode 100644 index 00000000..041d4bb7 --- /dev/null +++ b/queue-3.16/selinux-properly-handle-multiple-messages-in-selinux_netlink_send.patch @@ -0,0 +1,104 @@ +From: Paul Moore <paul@paul-moore.com> +Date: Tue, 28 Apr 2020 09:59:02 -0400 +Subject: selinux: properly handle multiple messages in selinux_netlink_send() + +commit fb73974172ffaaf57a7c42f35424d9aece1a5af6 upstream. + +Fix the SELinux netlink_send hook to properly handle multiple netlink +messages in a single sk_buff; each message is parsed and subject to +SELinux access control. Prior to this patch, SELinux only inspected +the first message in the sk_buff. + +Cc: stable@vger.kernel.org +Reported-by: Dmitry Vyukov <dvyukov@google.com> +Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com> +Signed-off-by: Paul Moore <paul@paul-moore.com> +[bwh: Backported to 3.16: adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -4669,39 +4669,59 @@ static int selinux_tun_dev_open(void *se + + static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) + { +- int err = 0; +- u32 perm; ++ int rc = 0; ++ unsigned int msg_len; ++ unsigned int data_len = skb->len; ++ unsigned char *data = skb->data; + struct nlmsghdr *nlh; + struct sk_security_struct *sksec = sk->sk_security; ++ u16 sclass = sksec->sclass; ++ u32 perm; + +- if (skb->len < NLMSG_HDRLEN) { +- err = -EINVAL; +- goto out; +- } +- nlh = nlmsg_hdr(skb); ++ while (data_len >= nlmsg_total_size(0)) { ++ nlh = (struct nlmsghdr *)data; + +- err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); +- if (err) { +- if (err == -EINVAL) { ++ /* NOTE: the nlmsg_len field isn't reliably set by some netlink ++ * users which means we can't reject skb's with bogus ++ * length fields; our solution is to follow what ++ * netlink_rcv_skb() does and simply skip processing at ++ * messages with length fields that are clearly junk ++ */ ++ if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len) ++ return 0; ++ ++ rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); ++ if (rc == 0) { ++ rc = sock_has_perm(current, sk, perm); ++ if (rc) ++ return rc; ++ } else if (rc == -EINVAL) { ++ /* -EINVAL is a missing msg/perm mapping */ + pr_warn_ratelimited("SELinux: unrecognized netlink" +- " message: protocol=%hu nlmsg_type=%hu sclass=%s" +- " pig=%d comm=%s\n", +- sk->sk_protocol, nlh->nlmsg_type, +- secclass_map[sksec->sclass - 1].name, +- task_pid_nr(current), current->comm); +- if (!selinux_enforcing || security_get_allow_unknown()) +- err = 0; ++ " message: protocol=%hu nlmsg_type=%hu sclass=%s" ++ " pid=%d comm=%s\n", ++ sk->sk_protocol, nlh->nlmsg_type, ++ secclass_map[sclass - 1].name, ++ task_pid_nr(current), current->comm); ++ if (selinux_enforcing && !security_get_allow_unknown()) ++ return rc; ++ rc = 0; ++ } else if (rc == -ENOENT) { ++ /* -ENOENT is a missing socket/class mapping, ignore */ ++ rc = 0; ++ } else { ++ return rc; + } + +- /* Ignore */ +- if (err == -ENOENT) +- err = 0; +- goto out; ++ /* move to the next message after applying netlink padding */ ++ msg_len = NLMSG_ALIGN(nlh->nlmsg_len); ++ if (msg_len >= data_len) ++ return 0; ++ data_len -= msg_len; ++ data += msg_len; + } + +- err = sock_has_perm(current, sk, perm); +-out: +- return err; ++ return rc; + } + + #ifdef CONFIG_NETFILTER diff --git a/queue-3.16/selinux-rate-limit-netlink-message-warnings-in-selinux_nlmsg_perm.patch b/queue-3.16/selinux-rate-limit-netlink-message-warnings-in-selinux_nlmsg_perm.patch new file mode 100644 index 00000000..84cc0ed5 --- /dev/null +++ b/queue-3.16/selinux-rate-limit-netlink-message-warnings-in-selinux_nlmsg_perm.patch @@ -0,0 +1,40 @@ +From: Vladis Dronov <vdronov@redhat.com> +Date: Thu, 24 Dec 2015 11:09:41 -0500 +Subject: selinux: rate-limit netlink message warnings in selinux_nlmsg_perm() + +commit 76319946f321e30872dd72af7de867cb26e7a373 upstream. + +Any process is able to send netlink messages with invalid types. +Make the warning rate-limited to prevent too much log spam. + +The warning is supposed to help to find misbehaving programs, so +print the triggering command name and pid. + +Reported-by: Florian Weimer <fweimer@redhat.com> +Signed-off-by: Vladis Dronov <vdronov@redhat.com> +[PM: subject line tweak to make checkpatch.pl happy] +Signed-off-by: Paul Moore <pmoore@redhat.com> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + security/selinux/hooks.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -4683,11 +4683,12 @@ static int selinux_nlmsg_perm(struct soc + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); + if (err) { + if (err == -EINVAL) { +- printk(KERN_WARNING +- "SELinux: unrecognized netlink message:" +- " protocol=%hu nlmsg_type=%hu sclass=%s\n", ++ pr_warn_ratelimited("SELinux: unrecognized netlink" ++ " message: protocol=%hu nlmsg_type=%hu sclass=%s" ++ " pig=%d comm=%s\n", + sk->sk_protocol, nlh->nlmsg_type, +- secclass_map[sksec->sclass - 1].name); ++ secclass_map[sksec->sclass - 1].name, ++ task_pid_nr(current), current->comm); + if (!selinux_enforcing || security_get_allow_unknown()) + err = 0; + } diff --git a/queue-3.16/series b/queue-3.16/series index 4d83b479..445426c6 100644 --- a/queue-3.16/series +++ b/queue-3.16/series @@ -8,3 +8,46 @@ net-sysfs-fix-reference-count-leak-in-rx-netdev_queue_add_kobject.patch net-sysfs-fix-netdev_queue_add_kobject-breakage.patch net-sysfs-call-dev_hold-always-in-netdev_queue_add_kobject.patch net-sysfs-call-dev_hold-always-in-rx_queue_add_kobject.patch +selinux-cleanup-error-reporting-in-selinux_nlmsg_perm.patch +selinux-convert-warn_once-to-printk-in-selinux_nlmsg_perm.patch +selinux-print-sclass-as-string-when-unrecognized-netlink-message-occurs.patch +selinux-rate-limit-netlink-message-warnings-in-selinux_nlmsg_perm.patch +selinux-properly-handle-multiple-messages-in-selinux_netlink_send.patch +drivers-usb-core-don-t-disable-irqs-in-usb_sg_wait-during-urb-submit.patch +drivers-usb-core-minimize-irq-disabling-in-usb_sg_cancel.patch +usb-core-fix-free-while-in-use-bug-in-the-usb-s-glibrary.patch +scsi-mptfusion-add-bounds-check-in-mptctl_hp_targetinfo.patch +scsi-mptfusion-fix-double-fetch-bug-in-ioctl.patch +mwifiex-fix-possible-buffer-overflows-in-mwifiex_cmd_append_vsie_tlv.patch +mwifiex-fix-possible-buffer-overflows-in-mwifiex_ret_wmm_get_status.patch +sg-o_excl-and-other-lock-handling.patch +sg-prevent-integer-overflow-when-converting-from-sectors-to-bytes.patch +scsi-sg-change-next_cmd_len-handling-to-mirror-upstream.patch +scsi-sg-protect-accesses-to-reserved-page-array.patch +scsi-sg-reset-res_in_use-after-unlinking-reserved-array.patch +scsi-sg-protect-against-races-between-mmap-and-sg_set_reserved_size.patch +scsi-sg-recheck-mmap_io-request-length-with-lock-held.patch +scsi-sg-remove-save_scat_len.patch +scsi-sg-use-standard-lists-for-sg_requests.patch +scsi-sg-off-by-one-in-sg_ioctl.patch +scsi-sg-factor-out-sg_fill_request_table.patch +scsi-sg-fixup-infoleak-when-using-sg_get_request_table.patch +scsi-sg-re-fix-off-by-one-in-sg_fill_request_table.patch +scsi-sg-disable-set_force_low_dma.patch +scsi-sg-check-for-valid-direction-before-starting-the-request.patch +scsi-sg-close-race-condition-in-sg_remove_sfp_usercontext.patch +scsi-sg-fix-sg_dxfer_from_dev-transfers.patch +scsi-sg-fix-static-checker-warning-in-sg_is_valid_dxfer.patch +scsi-sg-only-check-for-dxfer_len-greater-than-256m.patch +scsi-sg-don-t-return-bogus-sg_requests.patch +scsi-sg-fix-minor-memory-leak-in-error-path.patch +scsi-sg-add-sg_remove_request-in-sg_common_write.patch +scsi-sg-add-sg_remove_request-in-sg_write.patch +signal-extend-exec_id-to-64bits.patch +usb-gadget-fix-illegal-array-access-in-binding-with-udc.patch +ext4-make-checks-for-metadata_csum-feature-safer.patch +ext4-protect-journal-inode-s-blocks-using-block_validity.patch +ext4-unsigned-int-compared-against-zero.patch +ext4-fix-block-validity-checks-for-journal-inodes-using-indirect-blocks.patch +ext4-don-t-perform-block-validity-checks-on-the-journal-inode.patch +ext4-add-cond_resched-to-ext4_protect_reserved_inode.patch diff --git a/queue-3.16/sg-o_excl-and-other-lock-handling.patch b/queue-3.16/sg-o_excl-and-other-lock-handling.patch new file mode 100644 index 00000000..e8442303 --- /dev/null +++ b/queue-3.16/sg-o_excl-and-other-lock-handling.patch @@ -0,0 +1,920 @@ +From: Douglas Gilbert <dgilbert@interlog.com> +Date: Wed, 25 Jun 2014 14:08:03 +0200 +Subject: sg: O_EXCL and other lock handling + +commit cc833acbee9db5ca8c6162b015b4c93863c6f821 upstream. + +This addresses a problem reported by Vaughan Cao concerning +the correctness of the O_EXCL logic in the sg driver. POSIX +doesn't defined O_EXCL semantics on devices but "allow only +one open file descriptor at a time per sg device" is a rough +definition. The sg driver's semantics have been to wait +on an open() when O_NONBLOCK is not given and there are +O_EXCL headwinds. Nasty things can happen during that wait +such as the device being detached (removed). So multiple +locks are reworked in this patch making it large and hard +to break down into digestible bits. + +This patch is against Linus's current git repository which +doesn't include any sg patches sent in the last few weeks. +Hence this patch touches as little as possible that it +doesn't need to and strips out most SCSI_LOG_TIMEOUT() +changes in v3 because Hannes said he was going to rework all +that stuff. + +The sg3_utils package has several test programs written to +test this patch. See examples/sg_tst_excl*.cpp . + +Not all the locks and flags in sg have been re-worked in +this patch, notably sg_request::done . That can wait for +a follow-up patch if this one meets with approval. + +Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> +Reviewed-by: Hannes Reinecke <hare@suse.de> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 424 +++++++++++++++++++++++++--------------------- + 1 file changed, 230 insertions(+), 194 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -51,6 +51,7 @@ static int sg_version_num = 30534; /* 2 + #include <linux/delay.h> + #include <linux/blktrace_api.h> + #include <linux/mutex.h> ++#include <linux/atomic.h> + #include <linux/ratelimit.h> + #include <linux/cred.h> /* for sg_check_file_access() */ + +@@ -103,18 +104,16 @@ static int scatter_elem_sz_prev = SG_SCA + + #define SG_SECTOR_SZ 512 + +-static int sg_add(struct device *, struct class_interface *); +-static void sg_remove(struct device *, struct class_interface *); +- +-static DEFINE_SPINLOCK(sg_open_exclusive_lock); ++static int sg_add_device(struct device *, struct class_interface *); ++static void sg_remove_device(struct device *, struct class_interface *); + + static DEFINE_IDR(sg_index_idr); + static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock + file descriptor list for device */ + + static struct class_interface sg_interface = { +- .add_dev = sg_add, +- .remove_dev = sg_remove, ++ .add_dev = sg_add_device, ++ .remove_dev = sg_remove_device, + }; + + typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ +@@ -147,8 +146,7 @@ typedef struct sg_request { /* SG_MAX_QU + } Sg_request; + + typedef struct sg_fd { /* holds the state of a file descriptor */ +- /* sfd_siblings is protected by sg_index_lock */ +- struct list_head sfd_siblings; ++ struct list_head sfd_siblings; /* protected by device's sfd_lock */ + struct sg_device *parentdp; /* owning device */ + wait_queue_head_t read_wait; /* queue read until command done */ + rwlock_t rq_list_lock; /* protect access to list in req_arr */ +@@ -171,14 +169,15 @@ typedef struct sg_fd { /* holds the sta + + typedef struct sg_device { /* holds the state of each scsi generic device */ + struct scsi_device *device; +- wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ ++ wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ ++ struct mutex open_rel_lock; /* held when in open() or release() */ + int sg_tablesize; /* adapter's max scatter-gather table size */ + u32 index; /* device index number */ +- /* sfds is protected by sg_index_lock */ + struct list_head sfds; +- volatile char detached; /* 0->attached, 1->detached pending removal */ +- /* exclude protected by sg_open_exclusive_lock */ +- char exclude; /* opened for exclusive access */ ++ rwlock_t sfd_lock; /* protect access to sfd list */ ++ atomic_t detaching; /* 0->device usable, 1->device detaching */ ++ bool exclude; /* 1->open(O_EXCL) succeeded and is active */ ++ int open_cnt; /* count of opens (perhaps < num(sfds) ) */ + char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ + struct gendisk *disk; + struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg<n>] */ +@@ -209,7 +208,7 @@ static Sg_request *sg_add_request(Sg_fd + static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); + static int sg_res_in_use(Sg_fd * sfp); + static Sg_device *sg_get_dev(int dev); +-static void sg_put_dev(Sg_device *sdp); ++static void sg_device_destroy(struct kref *kref); + + #define SZ_SG_HEADER sizeof(struct sg_header) + #define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) +@@ -253,38 +252,43 @@ static int sg_allow_access(struct file * + return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); + } + +-static int get_exclude(Sg_device *sdp) +-{ +- unsigned long flags; +- int ret; +- +- spin_lock_irqsave(&sg_open_exclusive_lock, flags); +- ret = sdp->exclude; +- spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); +- return ret; +-} +- +-static int set_exclude(Sg_device *sdp, char val) ++static int ++open_wait(Sg_device *sdp, int flags) + { +- unsigned long flags; +- +- spin_lock_irqsave(&sg_open_exclusive_lock, flags); +- sdp->exclude = val; +- spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); +- return val; +-} ++ int retval = 0; + +-static int sfds_list_empty(Sg_device *sdp) +-{ +- unsigned long flags; +- int ret; ++ if (flags & O_EXCL) { ++ while (sdp->open_cnt > 0) { ++ mutex_unlock(&sdp->open_rel_lock); ++ retval = wait_event_interruptible(sdp->open_wait, ++ (atomic_read(&sdp->detaching) || ++ !sdp->open_cnt)); ++ mutex_lock(&sdp->open_rel_lock); ++ ++ if (retval) /* -ERESTARTSYS */ ++ return retval; ++ if (atomic_read(&sdp->detaching)) ++ return -ENODEV; ++ } ++ } else { ++ while (sdp->exclude) { ++ mutex_unlock(&sdp->open_rel_lock); ++ retval = wait_event_interruptible(sdp->open_wait, ++ (atomic_read(&sdp->detaching) || ++ !sdp->exclude)); ++ mutex_lock(&sdp->open_rel_lock); ++ ++ if (retval) /* -ERESTARTSYS */ ++ return retval; ++ if (atomic_read(&sdp->detaching)) ++ return -ENODEV; ++ } ++ } + +- read_lock_irqsave(&sg_index_lock, flags); +- ret = list_empty(&sdp->sfds); +- read_unlock_irqrestore(&sg_index_lock, flags); +- return ret; ++ return retval; + } + ++/* Returns 0 on success, else a negated errno value */ + static int + sg_open(struct inode *inode, struct file *filp) + { +@@ -293,17 +297,15 @@ sg_open(struct inode *inode, struct file + struct request_queue *q; + Sg_device *sdp; + Sg_fd *sfp; +- int res; + int retval; + + nonseekable_open(inode, filp); + SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); ++ if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) ++ return -EPERM; /* Can't lock it with read only access */ + sdp = sg_get_dev(dev); +- if (IS_ERR(sdp)) { +- retval = PTR_ERR(sdp); +- sdp = NULL; +- goto sg_put; +- } ++ if (IS_ERR(sdp)) ++ return PTR_ERR(sdp); + + /* This driver's module count bumped by fops_get in <linux/fs.h> */ + /* Prevent the device driver from vanishing while we sleep */ +@@ -315,6 +317,9 @@ sg_open(struct inode *inode, struct file + if (retval) + goto sdp_put; + ++ /* scsi_block_when_processing_errors() may block so bypass ++ * check if O_NONBLOCK. Permits SCSI commands to be issued ++ * during error recovery. Tread carefully. */ + if (!((flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) { + retval = -ENXIO; +@@ -322,65 +327,65 @@ sg_open(struct inode *inode, struct file + goto error_out; + } + +- if (flags & O_EXCL) { +- if (O_RDONLY == (flags & O_ACCMODE)) { +- retval = -EPERM; /* Can't lock it with read only access */ +- goto error_out; +- } +- if (!sfds_list_empty(sdp) && (flags & O_NONBLOCK)) { +- retval = -EBUSY; +- goto error_out; +- } +- res = wait_event_interruptible(sdp->o_excl_wait, +- ((!sfds_list_empty(sdp) || get_exclude(sdp)) ? 0 : set_exclude(sdp, 1))); +- if (res) { +- retval = res; /* -ERESTARTSYS because signal hit process */ +- goto error_out; +- } +- } else if (get_exclude(sdp)) { /* some other fd has an exclusive lock on dev */ +- if (flags & O_NONBLOCK) { +- retval = -EBUSY; +- goto error_out; +- } +- res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp)); +- if (res) { +- retval = res; /* -ERESTARTSYS because signal hit process */ +- goto error_out; ++ mutex_lock(&sdp->open_rel_lock); ++ if (flags & O_NONBLOCK) { ++ if (flags & O_EXCL) { ++ if (sdp->open_cnt > 0) { ++ retval = -EBUSY; ++ goto error_mutex_locked; ++ } ++ } else { ++ if (sdp->exclude) { ++ retval = -EBUSY; ++ goto error_mutex_locked; ++ } + } ++ } else { ++ retval = open_wait(sdp, flags); ++ if (retval) /* -ERESTARTSYS or -ENODEV */ ++ goto error_mutex_locked; + } +- if (sdp->detached) { +- retval = -ENODEV; +- goto error_out; +- } +- if (sfds_list_empty(sdp)) { /* no existing opens on this device */ ++ ++ /* N.B. at this point we are holding the open_rel_lock */ ++ if (flags & O_EXCL) ++ sdp->exclude = true; ++ ++ if (sdp->open_cnt < 1) { /* no existing opens */ + sdp->sgdebug = 0; + q = sdp->device->request_queue; + sdp->sg_tablesize = queue_max_segments(q); + } +- if ((sfp = sg_add_sfp(sdp, dev))) +- filp->private_data = sfp; +- else { +- if (flags & O_EXCL) { +- set_exclude(sdp, 0); /* undo if error */ +- wake_up_interruptible(&sdp->o_excl_wait); +- } +- retval = -ENOMEM; +- goto error_out; ++ sfp = sg_add_sfp(sdp, dev); ++ if (IS_ERR(sfp)) { ++ retval = PTR_ERR(sfp); ++ goto out_undo; + } ++ ++ filp->private_data = sfp; ++ sdp->open_cnt++; ++ mutex_unlock(&sdp->open_rel_lock); ++ + retval = 0; +-error_out: +- if (retval) { +- scsi_autopm_put_device(sdp->device); +-sdp_put: +- scsi_device_put(sdp->device); +- } + sg_put: +- if (sdp) +- sg_put_dev(sdp); ++ kref_put(&sdp->d_ref, sg_device_destroy); + return retval; ++ ++out_undo: ++ if (flags & O_EXCL) { ++ sdp->exclude = false; /* undo if error */ ++ wake_up_interruptible(&sdp->open_wait); ++ } ++error_mutex_locked: ++ mutex_unlock(&sdp->open_rel_lock); ++error_out: ++ scsi_autopm_put_device(sdp->device); ++sdp_put: ++ scsi_device_put(sdp->device); ++ goto sg_put; + } + +-/* Following function was formerly called 'sg_close' */ ++/* Release resources associated with a successful sg_open() ++ * Returns 0 on success, else a negated errno value */ + static int + sg_release(struct inode *inode, struct file *filp) + { +@@ -391,11 +396,20 @@ sg_release(struct inode *inode, struct f + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); + +- set_exclude(sdp, 0); +- wake_up_interruptible(&sdp->o_excl_wait); +- ++ mutex_lock(&sdp->open_rel_lock); + scsi_autopm_put_device(sdp->device); + kref_put(&sfp->f_ref, sg_remove_sfp); ++ sdp->open_cnt--; ++ ++ /* possibly many open()s waiting on exlude clearing, start many; ++ * only open(O_EXCL)s wait on 0==open_cnt so only start one */ ++ if (sdp->exclude) { ++ sdp->exclude = false; ++ wake_up_interruptible_all(&sdp->open_wait); ++ } else if (0 == sdp->open_cnt) { ++ wake_up_interruptible(&sdp->open_wait); ++ } ++ mutex_unlock(&sdp->open_rel_lock); + return 0; + } + +@@ -455,7 +469,7 @@ sg_read(struct file *filp, char __user * + } + srp = sg_get_rq_mark(sfp, req_pack_id); + if (!srp) { /* now wait on packet to arrive */ +- if (sdp->detached) { ++ if (atomic_read(&sdp->detaching)) { + retval = -ENODEV; + goto free_old_hdr; + } +@@ -464,9 +478,9 @@ sg_read(struct file *filp, char __user * + goto free_old_hdr; + } + retval = wait_event_interruptible(sfp->read_wait, +- (sdp->detached || ++ (atomic_read(&sdp->detaching) || + (srp = sg_get_rq_mark(sfp, req_pack_id)))); +- if (sdp->detached) { ++ if (atomic_read(&sdp->detaching)) { + retval = -ENODEV; + goto free_old_hdr; + } +@@ -613,7 +627,7 @@ sg_write(struct file *filp, const char _ + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_write: %s, count=%d\n", + sdp->disk->disk_name, (int) count)); +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + if (!((filp->f_flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) +@@ -806,7 +820,7 @@ sg_common_write(Sg_fd * sfp, Sg_request + sg_finish_rem_req(srp); + return k; /* probably out of space --> ENOMEM */ + } +- if (sdp->detached) { ++ if (atomic_read(&sdp->detaching)) { + if (srp->bio) { + blk_end_request_all(srp->rq, -EIO); + srp->rq = NULL; +@@ -871,7 +885,7 @@ sg_ioctl(struct file *filp, unsigned int + + switch (cmd_in) { + case SG_IO: +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + if (!scsi_block_when_processing_errors(sdp->device)) + return -ENXIO; +@@ -882,8 +896,8 @@ sg_ioctl(struct file *filp, unsigned int + if (result < 0) + return result; + result = wait_event_interruptible(sfp->read_wait, +- (srp_done(sfp, srp) || sdp->detached)); +- if (sdp->detached) ++ (srp_done(sfp, srp) || atomic_read(&sdp->detaching))); ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + write_lock_irq(&sfp->rq_list_lock); + if (srp->done) { +@@ -922,7 +936,7 @@ sg_ioctl(struct file *filp, unsigned int + sg_build_reserve(sfp, val); + } + } else { +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + sfp->low_dma = sdp->device->host->unchecked_isa_dma; + } +@@ -935,7 +949,7 @@ sg_ioctl(struct file *filp, unsigned int + else { + sg_scsi_id_t __user *sg_idp = p; + +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + __put_user((int) sdp->device->host->host_no, + &sg_idp->host_no); +@@ -1077,11 +1091,11 @@ sg_ioctl(struct file *filp, unsigned int + return result; + } + case SG_EMULATED_HOST: +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + return put_user(sdp->device->host->hostt->emulated, ip); + case SG_SCSI_RESET: +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + if (filp->f_flags & O_NONBLOCK) { + if (scsi_host_in_recovery(sdp->device->host)) +@@ -1114,7 +1128,7 @@ sg_ioctl(struct file *filp, unsigned int + return (scsi_reset_provider(sdp->device, val) == + SUCCESS) ? 0 : -EIO; + case SCSI_IOCTL_SEND_COMMAND: +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + if (read_only) { + unsigned char opcode = WRITE_6; +@@ -1136,7 +1150,7 @@ sg_ioctl(struct file *filp, unsigned int + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_TRANSFORM: +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + return -ENODEV; + return scsi_ioctl(sdp->device, cmd_in, p); + case BLKSECTGET: +@@ -1210,7 +1224,7 @@ sg_poll(struct file *filp, poll_table * + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + +- if (sdp->detached) ++ if (atomic_read(&sdp->detaching)) + res |= POLLHUP; + else if (!sfp->cmd_q) { + if (0 == count) +@@ -1309,7 +1323,8 @@ sg_mmap(struct file *filp, struct vm_are + return 0; + } + +-static void sg_rq_end_io_usercontext(struct work_struct *work) ++static void ++sg_rq_end_io_usercontext(struct work_struct *work) + { + struct sg_request *srp = container_of(work, struct sg_request, ew.work); + struct sg_fd *sfp = srp->parentfp; +@@ -1322,7 +1337,8 @@ static void sg_rq_end_io_usercontext(str + * This function is a "bottom half" handler that is called by the mid + * level when a command is completed (or has failed). + */ +-static void sg_rq_end_io(struct request *rq, int uptodate) ++static void ++sg_rq_end_io(struct request *rq, int uptodate) + { + struct sg_request *srp = rq->end_io_data; + Sg_device *sdp; +@@ -1340,8 +1356,8 @@ static void sg_rq_end_io(struct request + return; + + sdp = sfp->parentdp; +- if (unlikely(sdp->detached)) +- printk(KERN_INFO "sg_rq_end_io: device detached\n"); ++ if (unlikely(atomic_read(&sdp->detaching))) ++ pr_info("%s: device detaching\n", __func__); + + sense = rq->sense; + result = rq->errors; +@@ -1364,7 +1380,7 @@ static void sg_rq_end_io(struct request + if ((sdp->sgdebug > 0) && + ((CHECK_CONDITION == srp->header.masked_status) || + (COMMAND_TERMINATED == srp->header.masked_status))) +- __scsi_print_sense("sg_cmd_done", sense, ++ __scsi_print_sense(__func__, sense, + SCSI_SENSE_BUFFERSIZE); + + /* Following if statement is a patch supplied by Eric Youngdale */ +@@ -1423,7 +1439,8 @@ static struct class *sg_sysfs_class; + + static int sg_sysfs_valid = 0; + +-static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) ++static Sg_device * ++sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) + { + struct request_queue *q = scsidp->request_queue; + Sg_device *sdp; +@@ -1433,7 +1450,8 @@ static Sg_device *sg_alloc(struct gendis + + sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL); + if (!sdp) { +- printk(KERN_WARNING "kmalloc Sg_device failure\n"); ++ sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device " ++ "failure\n", __func__); + return ERR_PTR(-ENOMEM); + } + +@@ -1448,8 +1466,9 @@ static Sg_device *sg_alloc(struct gendis + scsidp->type, SG_MAX_DEVS - 1); + error = -ENODEV; + } else { +- printk(KERN_WARNING +- "idr allocation Sg_device failure: %d\n", error); ++ sdev_printk(KERN_WARNING, scsidp, "%s: idr " ++ "allocation Sg_device failure: %d\n", ++ __func__, error); + } + goto out_unlock; + } +@@ -1460,8 +1479,11 @@ static Sg_device *sg_alloc(struct gendis + disk->first_minor = k; + sdp->disk = disk; + sdp->device = scsidp; ++ mutex_init(&sdp->open_rel_lock); + INIT_LIST_HEAD(&sdp->sfds); +- init_waitqueue_head(&sdp->o_excl_wait); ++ init_waitqueue_head(&sdp->open_wait); ++ atomic_set(&sdp->detaching, 0); ++ rwlock_init(&sdp->sfd_lock); + sdp->sg_tablesize = queue_max_segments(q); + sdp->index = k; + kref_init(&sdp->d_ref); +@@ -1479,7 +1501,7 @@ out_unlock: + } + + static int +-sg_add(struct device *cl_dev, struct class_interface *cl_intf) ++sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) + { + struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); + struct gendisk *disk; +@@ -1490,7 +1512,7 @@ sg_add(struct device *cl_dev, struct cla + + disk = alloc_disk(1); + if (!disk) { +- printk(KERN_WARNING "alloc_disk failed\n"); ++ pr_warn("%s: alloc_disk failed\n", __func__); + return -ENOMEM; + } + disk->major = SCSI_GENERIC_MAJOR; +@@ -1498,7 +1520,7 @@ sg_add(struct device *cl_dev, struct cla + error = -ENOMEM; + cdev = cdev_alloc(); + if (!cdev) { +- printk(KERN_WARNING "cdev_alloc failed\n"); ++ pr_warn("%s: cdev_alloc failed\n", __func__); + goto out; + } + cdev->owner = THIS_MODULE; +@@ -1506,7 +1528,7 @@ sg_add(struct device *cl_dev, struct cla + + sdp = sg_alloc(disk, scsidp); + if (IS_ERR(sdp)) { +- printk(KERN_WARNING "sg_alloc failed\n"); ++ pr_warn("%s: sg_alloc failed\n", __func__); + error = PTR_ERR(sdp); + goto out; + } +@@ -1524,22 +1546,20 @@ sg_add(struct device *cl_dev, struct cla + sdp->index), + sdp, "%s", disk->disk_name); + if (IS_ERR(sg_class_member)) { +- printk(KERN_ERR "sg_add: " +- "device_create failed\n"); ++ pr_err("%s: device_create failed\n", __func__); + error = PTR_ERR(sg_class_member); + goto cdev_add_err; + } + error = sysfs_create_link(&scsidp->sdev_gendev.kobj, + &sg_class_member->kobj, "generic"); + if (error) +- printk(KERN_ERR "sg_add: unable to make symlink " +- "'generic' back to sg%d\n", sdp->index); ++ pr_err("%s: unable to make symlink 'generic' back " ++ "to sg%d\n", __func__, sdp->index); + } else +- printk(KERN_WARNING "sg_add: sg_sys Invalid\n"); ++ pr_warn("%s: sg_sys Invalid\n", __func__); + +- sdev_printk(KERN_NOTICE, scsidp, +- "Attached scsi generic sg%d type %d\n", sdp->index, +- scsidp->type); ++ sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d " ++ "type %d\n", sdp->index, scsidp->type); + + dev_set_drvdata(cl_dev, sdp); + +@@ -1558,7 +1578,8 @@ out: + return error; + } + +-static void sg_device_destroy(struct kref *kref) ++static void ++sg_device_destroy(struct kref *kref) + { + struct sg_device *sdp = container_of(kref, struct sg_device, d_ref); + unsigned long flags; +@@ -1580,33 +1601,39 @@ static void sg_device_destroy(struct kre + kfree(sdp); + } + +-static void sg_remove(struct device *cl_dev, struct class_interface *cl_intf) ++static void ++sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) + { + struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); + Sg_device *sdp = dev_get_drvdata(cl_dev); + unsigned long iflags; + Sg_fd *sfp; ++ int val; + +- if (!sdp || sdp->detached) ++ if (!sdp) + return; ++ /* want sdp->detaching non-zero as soon as possible */ ++ val = atomic_inc_return(&sdp->detaching); ++ if (val > 1) ++ return; /* only want to do following once per device */ + +- SCSI_LOG_TIMEOUT(3, printk("sg_remove: %s\n", sdp->disk->disk_name)); ++ SCSI_LOG_TIMEOUT(3, printk("%s: %s\n", __func__, ++ sdp->disk->disk_name)); + +- /* Need a write lock to set sdp->detached. */ +- write_lock_irqsave(&sg_index_lock, iflags); +- sdp->detached = 1; ++ read_lock_irqsave(&sdp->sfd_lock, iflags); + list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { +- wake_up_interruptible(&sfp->read_wait); ++ wake_up_interruptible_all(&sfp->read_wait); + kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); + } +- write_unlock_irqrestore(&sg_index_lock, iflags); ++ wake_up_interruptible_all(&sdp->open_wait); ++ read_unlock_irqrestore(&sdp->sfd_lock, iflags); + + sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); + device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index)); + cdev_del(sdp->cdev); + sdp->cdev = NULL; + +- sg_put_dev(sdp); ++ kref_put(&sdp->d_ref, sg_device_destroy); + } + + module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); +@@ -1676,7 +1703,8 @@ exit_sg(void) + idr_destroy(&sg_index_idr); + } + +-static int sg_start_req(Sg_request *srp, unsigned char *cmd) ++static int ++sg_start_req(Sg_request *srp, unsigned char *cmd) + { + int res; + struct request *rq; +@@ -1778,7 +1806,8 @@ static int sg_start_req(Sg_request *srp, + return res; + } + +-static int sg_finish_rem_req(Sg_request * srp) ++static int ++sg_finish_rem_req(Sg_request *srp) + { + int ret = 0; + +@@ -2115,7 +2144,7 @@ sg_add_sfp(Sg_device * sdp, int dev) + + sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); + if (!sfp) +- return NULL; ++ return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&sfp->read_wait); + rwlock_init(&sfp->rq_list_lock); +@@ -2129,9 +2158,13 @@ sg_add_sfp(Sg_device * sdp, int dev) + sfp->cmd_q = SG_DEF_COMMAND_Q; + sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; + sfp->parentdp = sdp; +- write_lock_irqsave(&sg_index_lock, iflags); ++ write_lock_irqsave(&sdp->sfd_lock, iflags); ++ if (atomic_read(&sdp->detaching)) { ++ write_unlock_irqrestore(&sdp->sfd_lock, iflags); ++ return ERR_PTR(-ENODEV); ++ } + list_add_tail(&sfp->sfd_siblings, &sdp->sfds); +- write_unlock_irqrestore(&sg_index_lock, iflags); ++ write_unlock_irqrestore(&sdp->sfd_lock, iflags); + SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); + if (unlikely(sg_big_buff != def_reserved_size)) + sg_big_buff = def_reserved_size; +@@ -2147,7 +2180,8 @@ sg_add_sfp(Sg_device * sdp, int dev) + return sfp; + } + +-static void sg_remove_sfp_usercontext(struct work_struct *work) ++static void ++sg_remove_sfp_usercontext(struct work_struct *work) + { + struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); + struct sg_device *sdp = sfp->parentdp; +@@ -2171,20 +2205,20 @@ static void sg_remove_sfp_usercontext(st + kfree(sfp); + + scsi_device_put(sdp->device); +- sg_put_dev(sdp); ++ kref_put(&sdp->d_ref, sg_device_destroy); + module_put(THIS_MODULE); + } + +-static void sg_remove_sfp(struct kref *kref) ++static void ++sg_remove_sfp(struct kref *kref) + { + struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref); + struct sg_device *sdp = sfp->parentdp; + unsigned long iflags; + +- write_lock_irqsave(&sg_index_lock, iflags); ++ write_lock_irqsave(&sdp->sfd_lock, iflags); + list_del(&sfp->sfd_siblings); +- write_unlock_irqrestore(&sg_index_lock, iflags); +- wake_up_interruptible(&sdp->o_excl_wait); ++ write_unlock_irqrestore(&sdp->sfd_lock, iflags); + + INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); + schedule_work(&sfp->ew.work); +@@ -2235,7 +2269,8 @@ static Sg_device *sg_lookup_dev(int dev) + return idr_find(&sg_index_idr, dev); + } + +-static Sg_device *sg_get_dev(int dev) ++static Sg_device * ++sg_get_dev(int dev) + { + struct sg_device *sdp; + unsigned long flags; +@@ -2244,8 +2279,8 @@ static Sg_device *sg_get_dev(int dev) + sdp = sg_lookup_dev(dev); + if (!sdp) + sdp = ERR_PTR(-ENXIO); +- else if (sdp->detached) { +- /* If sdp->detached, then the refcount may already be 0, in ++ else if (atomic_read(&sdp->detaching)) { ++ /* If sdp->detaching, then the refcount may already be 0, in + * which case it would be a bug to do kref_get(). + */ + sdp = ERR_PTR(-ENODEV); +@@ -2256,11 +2291,6 @@ static Sg_device *sg_get_dev(int dev) + return sdp; + } + +-static void sg_put_dev(struct sg_device *sdp) +-{ +- kref_put(&sdp->d_ref, sg_device_destroy); +-} +- + #ifdef CONFIG_SCSI_PROC_FS + + static struct proc_dir_entry *sg_proc_sgp = NULL; +@@ -2477,8 +2507,7 @@ static int sg_proc_single_open_version(s + + static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v) + { +- seq_printf(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\t" +- "online\n"); ++ seq_puts(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); + return 0; + } + +@@ -2534,7 +2563,11 @@ static int sg_proc_seq_show_dev(struct s + + read_lock_irqsave(&sg_index_lock, iflags); + sdp = it ? sg_lookup_dev(it->index) : NULL; +- if (sdp && (scsidp = sdp->device) && (!sdp->detached)) ++ if ((NULL == sdp) || (NULL == sdp->device) || ++ (atomic_read(&sdp->detaching))) ++ seq_puts(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); ++ else { ++ scsidp = sdp->device; + seq_printf(s, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + scsidp->host->host_no, scsidp->channel, + scsidp->id, scsidp->lun, (int) scsidp->type, +@@ -2542,8 +2575,7 @@ static int sg_proc_seq_show_dev(struct s + (int) scsidp->queue_depth, + (int) scsidp->device_busy, + (int) scsi_device_online(scsidp)); +- else +- seq_printf(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); ++ } + read_unlock_irqrestore(&sg_index_lock, iflags); + return 0; + } +@@ -2562,11 +2594,12 @@ static int sg_proc_seq_show_devstrs(stru + + read_lock_irqsave(&sg_index_lock, iflags); + sdp = it ? sg_lookup_dev(it->index) : NULL; +- if (sdp && (scsidp = sdp->device) && (!sdp->detached)) ++ scsidp = sdp ? sdp->device : NULL; ++ if (sdp && scsidp && (!atomic_read(&sdp->detaching))) + seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n", + scsidp->vendor, scsidp->model, scsidp->rev); + else +- seq_printf(s, "<no active device>\n"); ++ seq_puts(s, "<no active device>\n"); + read_unlock_irqrestore(&sg_index_lock, iflags); + return 0; + } +@@ -2611,12 +2644,12 @@ static void sg_proc_debug_helper(struct + else + cp = " "; + } +- seq_printf(s, cp); ++ seq_puts(s, cp); + blen = srp->data.bufflen; + usg = srp->data.k_use_sg; +- seq_printf(s, srp->done ? +- ((1 == srp->done) ? "rcv:" : "fin:") +- : "act:"); ++ seq_puts(s, srp->done ? ++ ((1 == srp->done) ? "rcv:" : "fin:") ++ : "act:"); + seq_printf(s, " id=%d blen=%d", + srp->header.pack_id, blen); + if (srp->done) +@@ -2632,7 +2665,7 @@ static void sg_proc_debug_helper(struct + (int) srp->data.cmd_opcode); + } + if (0 == m) +- seq_printf(s, " No requests active\n"); ++ seq_puts(s, " No requests active\n"); + read_unlock(&fp->rq_list_lock); + } + } +@@ -2648,31 +2681,34 @@ static int sg_proc_seq_show_debug(struct + Sg_device *sdp; + unsigned long iflags; + +- if (it && (0 == it->index)) { +- seq_printf(s, "max_active_device=%d(origin 1)\n", +- (int)it->max); +- seq_printf(s, " def_reserved_size=%d\n", sg_big_buff); +- } ++ if (it && (0 == it->index)) ++ seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", ++ (int)it->max, sg_big_buff); + + read_lock_irqsave(&sg_index_lock, iflags); + sdp = it ? sg_lookup_dev(it->index) : NULL; +- if (sdp && !list_empty(&sdp->sfds)) { +- struct scsi_device *scsidp = sdp->device; +- ++ if (NULL == sdp) ++ goto skip; ++ read_lock(&sdp->sfd_lock); ++ if (!list_empty(&sdp->sfds)) { + seq_printf(s, " >>> device=%s ", sdp->disk->disk_name); +- if (sdp->detached) +- seq_printf(s, "detached pending close "); +- else +- seq_printf +- (s, "scsi%d chan=%d id=%d lun=%d em=%d", +- scsidp->host->host_no, +- scsidp->channel, scsidp->id, +- scsidp->lun, +- scsidp->host->hostt->emulated); +- seq_printf(s, " sg_tablesize=%d excl=%d\n", +- sdp->sg_tablesize, get_exclude(sdp)); ++ if (atomic_read(&sdp->detaching)) ++ seq_puts(s, "detaching pending close "); ++ else if (sdp->device) { ++ struct scsi_device *scsidp = sdp->device; ++ ++ seq_printf(s, "%d:%d:%d:%d em=%d", ++ scsidp->host->host_no, ++ scsidp->channel, scsidp->id, ++ scsidp->lun, ++ scsidp->host->hostt->emulated); ++ } ++ seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n", ++ sdp->sg_tablesize, sdp->exclude, sdp->open_cnt); + sg_proc_debug_helper(s, sdp); + } ++ read_unlock(&sdp->sfd_lock); ++skip: + read_unlock_irqrestore(&sg_index_lock, iflags); + return 0; + } diff --git a/queue-3.16/sg-prevent-integer-overflow-when-converting-from-sectors-to-bytes.patch b/queue-3.16/sg-prevent-integer-overflow-when-converting-from-sectors-to-bytes.patch new file mode 100644 index 00000000..c109e224 --- /dev/null +++ b/queue-3.16/sg-prevent-integer-overflow-when-converting-from-sectors-to-bytes.patch @@ -0,0 +1,75 @@ +From: Akinobu Mita <akinobu.mita@gmail.com> +Date: Mon, 2 Jun 2014 22:56:46 +0900 +Subject: sg: prevent integer overflow when converting from sectors to bytes + +commit 46f69e6a6bbbf3858617c8729e31895846c15a79 upstream. + +This prevents integer overflow when converting the request queue's +max_sectors from sectors to bytes. However, this is a preparation for +extending the data type of max_sectors in struct Scsi_Host and +scsi_host_template. So, it is impossible to happen this integer +overflow for now, because SCSI low-level drivers can not specify +max_sectors greater than 0xffff due to the data type limitation. + +Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> +Acked by: Douglas Gilbert <dgilbert@interlog.com> +Signed-off-by: Christoph Hellwig <hch@lst.de> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/scsi/sg.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -865,6 +865,15 @@ static int srp_done(Sg_fd *sfp, Sg_reque + return ret; + } + ++static int max_sectors_bytes(struct request_queue *q) ++{ ++ unsigned int max_sectors = queue_max_sectors(q); ++ ++ max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9); ++ ++ return max_sectors << 9; ++} ++ + static long + sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) + { +@@ -1004,7 +1013,7 @@ sg_ioctl(struct file *filp, unsigned int + if (val < 0) + return -EINVAL; + val = min_t(int, val, +- queue_max_sectors(sdp->device->request_queue) * 512); ++ max_sectors_bytes(sdp->device->request_queue)); + if (val != sfp->reserve.bufflen) { + if (sg_res_in_use(sfp) || sfp->mmap_called) + return -EBUSY; +@@ -1014,7 +1023,7 @@ sg_ioctl(struct file *filp, unsigned int + return 0; + case SG_GET_RESERVED_SIZE: + val = min_t(int, sfp->reserve.bufflen, +- queue_max_sectors(sdp->device->request_queue) * 512); ++ max_sectors_bytes(sdp->device->request_queue)); + return put_user(val, ip); + case SG_SET_COMMAND_Q: + result = get_user(val, ip); +@@ -1154,7 +1163,7 @@ sg_ioctl(struct file *filp, unsigned int + return -ENODEV; + return scsi_ioctl(sdp->device, cmd_in, p); + case BLKSECTGET: +- return put_user(queue_max_sectors(sdp->device->request_queue) * 512, ++ return put_user(max_sectors_bytes(sdp->device->request_queue), + ip); + case BLKTRACESETUP: + return blk_trace_setup(sdp->device->request_queue, +@@ -2170,7 +2179,7 @@ sg_add_sfp(Sg_device * sdp, int dev) + sg_big_buff = def_reserved_size; + + bufflen = min_t(int, sg_big_buff, +- queue_max_sectors(sdp->device->request_queue) * 512); ++ max_sectors_bytes(sdp->device->request_queue)); + sg_build_reserve(sfp, bufflen); + SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", + sfp->reserve.bufflen, sfp->reserve.k_use_sg)); diff --git a/queue-3.16/signal-extend-exec_id-to-64bits.patch b/queue-3.16/signal-extend-exec_id-to-64bits.patch new file mode 100644 index 00000000..43577249 --- /dev/null +++ b/queue-3.16/signal-extend-exec_id-to-64bits.patch @@ -0,0 +1,81 @@ +From: "Eric W. Biederman" <ebiederm@xmission.com> +Date: Mon, 30 Mar 2020 19:01:04 -0500 +Subject: signal: Extend exec_id to 64bits + +commit d1e7fd6462ca9fc76650fbe6ca800e35b24267da upstream. + +Replace the 32bit exec_id with a 64bit exec_id to make it impossible +to wrap the exec_id counter. With care an attacker can cause exec_id +wrap and send arbitrary signals to a newly exec'd parent. This +bypasses the signal sending checks if the parent changes their +credentials during exec. + +The severity of this problem can been seen that in my limited testing +of a 32bit exec_id it can take as little as 19s to exec 65536 times. +Which means that it can take as little as 14 days to wrap a 32bit +exec_id. Adam Zabrocki has succeeded wrapping the self_exe_id in 7 +days. Even my slower timing is in the uptime of a typical server. +Which means self_exec_id is simply a speed bump today, and if exec +gets noticably faster self_exec_id won't even be a speed bump. + +Extending self_exec_id to 64bits introduces a problem on 32bit +architectures where reading self_exec_id is no longer atomic and can +take two read instructions. Which means that is is possible to hit +a window where the read value of exec_id does not match the written +value. So with very lucky timing after this change this still +remains expoiltable. + +I have updated the update of exec_id on exec to use WRITE_ONCE +and the read of exec_id in do_notify_parent to use READ_ONCE +to make it clear that there is no locking between these two +locations. + +Link: https://lore.kernel.org/kernel-hardening/20200324215049.GA3710@pi3.com.pl +Fixes: 2.3.23pre2 +Cc: stable@vger.kernel.org +Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> +[bwh: Backported to 3.16: + - Use ACCESS_ONCE() + - Adjust context] +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + fs/exec.c | 2 +- + include/linux/sched.h | 4 ++-- + kernel/signal.c | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1182,7 +1182,7 @@ void setup_new_exec(struct linux_binprm + + /* An exec changes our domain. We are no longer part of the thread + group */ +- current->self_exec_id++; ++ ACCESS_ONCE(current->self_exec_id) = current->self_exec_id + 1; + flush_signal_handlers(current, 0); + } + EXPORT_SYMBOL(setup_new_exec); +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1427,8 +1427,8 @@ struct task_struct { + struct seccomp seccomp; + + /* Thread group tracking */ +- u32 parent_exec_id; +- u32 self_exec_id; ++ u64 parent_exec_id; ++ u64 self_exec_id; + /* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, + * mempolicy */ + spinlock_t alloc_lock; +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -1679,7 +1679,7 @@ bool do_notify_parent(struct task_struct + * This is only possible if parent == real_parent. + * Check if it has changed security domain. + */ +- if (tsk->parent_exec_id != tsk->parent->self_exec_id) ++ if (tsk->parent_exec_id != ACCESS_ONCE(tsk->parent->self_exec_id)) + sig = SIGCHLD; + } + diff --git a/queue-3.16/usb-core-fix-free-while-in-use-bug-in-the-usb-s-glibrary.patch b/queue-3.16/usb-core-fix-free-while-in-use-bug-in-the-usb-s-glibrary.patch new file mode 100644 index 00000000..e1f081b2 --- /dev/null +++ b/queue-3.16/usb-core-fix-free-while-in-use-bug-in-the-usb-s-glibrary.patch @@ -0,0 +1,88 @@ +From: Alan Stern <stern@rowland.harvard.edu> +Date: Sat, 28 Mar 2020 16:18:11 -0400 +Subject: USB: core: Fix free-while-in-use bug in the USB S-Glibrary + +commit 056ad39ee9253873522f6469c3364964a322912b upstream. + +FuzzUSB (a variant of syzkaller) found a free-while-still-in-use bug +in the USB scatter-gather library: + +BUG: KASAN: use-after-free in atomic_read +include/asm-generic/atomic-instrumented.h:26 [inline] +BUG: KASAN: use-after-free in usb_hcd_unlink_urb+0x5f/0x170 +drivers/usb/core/hcd.c:1607 +Read of size 4 at addr ffff888065379610 by task kworker/u4:1/27 + +CPU: 1 PID: 27 Comm: kworker/u4:1 Not tainted 5.5.11 #2 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS +1.10.2-1ubuntu1 04/01/2014 +Workqueue: scsi_tmf_2 scmd_eh_abort_handler +Call Trace: + __dump_stack lib/dump_stack.c:77 [inline] + dump_stack+0xce/0x128 lib/dump_stack.c:118 + print_address_description.constprop.4+0x21/0x3c0 mm/kasan/report.c:374 + __kasan_report+0x153/0x1cb mm/kasan/report.c:506 + kasan_report+0x12/0x20 mm/kasan/common.c:639 + check_memory_region_inline mm/kasan/generic.c:185 [inline] + check_memory_region+0x152/0x1b0 mm/kasan/generic.c:192 + __kasan_check_read+0x11/0x20 mm/kasan/common.c:95 + atomic_read include/asm-generic/atomic-instrumented.h:26 [inline] + usb_hcd_unlink_urb+0x5f/0x170 drivers/usb/core/hcd.c:1607 + usb_unlink_urb+0x72/0xb0 drivers/usb/core/urb.c:657 + usb_sg_cancel+0x14e/0x290 drivers/usb/core/message.c:602 + usb_stor_stop_transport+0x5e/0xa0 drivers/usb/storage/transport.c:937 + +This bug occurs when cancellation of the S-G transfer races with +transfer completion. When that happens, usb_sg_cancel() may continue +to access the transfer's URBs after usb_sg_wait() has freed them. + +The bug is caused by the fact that usb_sg_cancel() does not take any +sort of reference to the transfer, and so there is nothing to prevent +the URBs from being deallocated while the routine is trying to use +them. The fix is to take such a reference by incrementing the +transfer's io->count field while the cancellation is in progres and +decrementing it afterward. The transfer's URBs are not deallocated +until io->complete is triggered, which happens when io->count reaches +zero. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +Reported-and-tested-by: Kyungtae Kim <kt0755@gmail.com> +CC: <stable@vger.kernel.org> + +Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.2003281615140.14837-100000@netrider.rowland.org +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/usb/core/message.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -584,12 +584,13 @@ void usb_sg_cancel(struct usb_sg_request + int i, retval; + + spin_lock_irqsave(&io->lock, flags); +- if (io->status) { ++ if (io->status || io->count == 0) { + spin_unlock_irqrestore(&io->lock, flags); + return; + } + /* shut everything down */ + io->status = -ECONNRESET; ++ io->count++; /* Keep the request alive until we're done */ + spin_unlock_irqrestore(&io->lock, flags); + + for (i = io->entries - 1; i >= 0; --i) { +@@ -603,6 +604,12 @@ void usb_sg_cancel(struct usb_sg_request + dev_warn(&io->dev->dev, "%s, unlink --> %d\n", + __func__, retval); + } ++ ++ spin_lock_irqsave(&io->lock, flags); ++ io->count--; ++ if (!io->count) ++ complete(&io->complete); ++ spin_unlock_irqrestore(&io->lock, flags); + } + EXPORT_SYMBOL_GPL(usb_sg_cancel); + diff --git a/queue-3.16/usb-gadget-fix-illegal-array-access-in-binding-with-udc.patch b/queue-3.16/usb-gadget-fix-illegal-array-access-in-binding-with-udc.patch new file mode 100644 index 00000000..4d36b48a --- /dev/null +++ b/queue-3.16/usb-gadget-fix-illegal-array-access-in-binding-with-udc.patch @@ -0,0 +1,72 @@ +From: Kyungtae Kim <kt0755@gmail.com> +Date: Sun, 10 May 2020 05:43:34 +0000 +Subject: USB: gadget: fix illegal array access in binding with UDC + +commit 15753588bcd4bbffae1cca33c8ced5722477fe1f upstream. + +FuzzUSB (a variant of syzkaller) found an illegal array access +using an incorrect index while binding a gadget with UDC. + +Reference: https://www.spinics.net/lists/linux-usb/msg194331.html + +This bug occurs when a size variable used for a buffer +is misused to access its strcpy-ed buffer. +Given a buffer along with its size variable (taken from user input), +from which, a new buffer is created using kstrdup(). +Due to the original buffer containing 0 value in the middle, +the size of the kstrdup-ed buffer becomes smaller than that of the original. +So accessing the kstrdup-ed buffer with the same size variable +triggers memory access violation. + +The fix makes sure no zero value in the buffer, +by comparing the strlen() of the orignal buffer with the size variable, +so that the access to the kstrdup-ed buffer is safe. + +BUG: KASAN: slab-out-of-bounds in gadget_dev_desc_UDC_store+0x1ba/0x200 +drivers/usb/gadget/configfs.c:266 +Read of size 1 at addr ffff88806a55dd7e by task syz-executor.0/17208 + +CPU: 2 PID: 17208 Comm: syz-executor.0 Not tainted 5.6.8 #1 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 +Call Trace: + __dump_stack lib/dump_stack.c:77 [inline] + dump_stack+0xce/0x128 lib/dump_stack.c:118 + print_address_description.constprop.4+0x21/0x3c0 mm/kasan/report.c:374 + __kasan_report+0x131/0x1b0 mm/kasan/report.c:506 + kasan_report+0x12/0x20 mm/kasan/common.c:641 + __asan_report_load1_noabort+0x14/0x20 mm/kasan/generic_report.c:132 + gadget_dev_desc_UDC_store+0x1ba/0x200 drivers/usb/gadget/configfs.c:266 + flush_write_buffer fs/configfs/file.c:251 [inline] + configfs_write_file+0x2f1/0x4c0 fs/configfs/file.c:283 + __vfs_write+0x85/0x110 fs/read_write.c:494 + vfs_write+0x1cd/0x510 fs/read_write.c:558 + ksys_write+0x18a/0x220 fs/read_write.c:611 + __do_sys_write fs/read_write.c:623 [inline] + __se_sys_write fs/read_write.c:620 [inline] + __x64_sys_write+0x73/0xb0 fs/read_write.c:620 + do_syscall_64+0x9e/0x510 arch/x86/entry/common.c:294 + entry_SYSCALL_64_after_hwframe+0x49/0xbe + +Signed-off-by: Kyungtae Kim <kt0755@gmail.com> +Reported-and-tested-by: Kyungtae Kim <kt0755@gmail.com> +Cc: Felipe Balbi <balbi@kernel.org> +Cc: stable <stable@vger.kernel.org> +Link: https://lore.kernel.org/r/20200510054326.GA19198@pizza01 +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Ben Hutchings <ben@decadent.org.uk> +--- + drivers/usb/gadget/configfs.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/usb/gadget/configfs.c ++++ b/drivers/usb/gadget/configfs.c +@@ -254,6 +254,9 @@ static ssize_t gadget_dev_desc_UDC_store + char *name; + int ret; + ++ if (strlen(page) < len) ++ return -EOVERFLOW; ++ + name = kstrdup(page, GFP_KERNEL); + if (!name) + return -ENOMEM; |