From a9adc94231d970ad6e9d20677498047143ef53fa Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 31 Jul 2020 21:07:04 -0400 Subject: [PATCH] Fixes for 4.9 Signed-off-by: Sasha Levin --- ...elease-allocated-buffer-if-timed-out.patch | 34 ++ ...elease-allocated-buffer-if-timed-out.patch | 51 +++ ...se-all-allocated-memory-if-sha-type-.patch | 41 +++ ...ck-if-file-namelen-exceeds-max-value.patch | 38 +++ ...ck-memory-boundary-by-insane-namelen.patch | 51 +++ ...oid-memory-leakage-in-f2fs_listxattr.patch | 65 ++++ ...vent-memory-leak-in-cx23888_ir_probe.patch | 40 +++ ...-unimac-fix-potential-null-dereferen.patch | 49 +++ ...ibsas-direct-call-probe-and-destruct.patch | 304 ++++++++++++++++++ queue-4.9/series | 12 + ...allocation-state-mismatch-corruption.patch | 187 +++++++++++ ...all-xfs_da_shrink_inode-with-null-bp.patch | 48 +++ ...ached-inodes-are-free-when-allocated.patch | 160 +++++++++ 13 files changed, 1080 insertions(+) create mode 100644 queue-4.9/ath9k-release-allocated-buffer-if-timed-out.patch create mode 100644 queue-4.9/ath9k_htc-release-allocated-buffer-if-timed-out.patch create mode 100644 queue-4.9/crypto-ccp-release-all-allocated-memory-if-sha-type-.patch create mode 100644 queue-4.9/f2fs-check-if-file-namelen-exceeds-max-value.patch create mode 100644 queue-4.9/f2fs-check-memory-boundary-by-insane-namelen.patch create mode 100644 queue-4.9/f2fs-fix-to-avoid-memory-leakage-in-f2fs_listxattr.patch create mode 100644 queue-4.9/media-rc-prevent-memory-leak-in-cx23888_ir_probe.patch create mode 100644 queue-4.9/net-phy-mdio-bcm-unimac-fix-potential-null-dereferen.patch create mode 100644 queue-4.9/scsi-libsas-direct-call-probe-and-destruct.patch create mode 100644 queue-4.9/series create mode 100644 queue-4.9/xfs-catch-inode-allocation-state-mismatch-corruption.patch create mode 100644 queue-4.9/xfs-don-t-call-xfs_da_shrink_inode-with-null-bp.patch create mode 100644 queue-4.9/xfs-validate-cached-inodes-are-free-when-allocated.patch diff --git a/queue-4.9/ath9k-release-allocated-buffer-if-timed-out.patch b/queue-4.9/ath9k-release-allocated-buffer-if-timed-out.patch new file mode 100644 index 00000000000..39707601248 --- /dev/null +++ b/queue-4.9/ath9k-release-allocated-buffer-if-timed-out.patch @@ -0,0 +1,34 @@ +From 5b03b6fdf4a70cc5361b6d654c051dc5d525012a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 6 Sep 2019 13:59:30 -0500 +Subject: ath9k: release allocated buffer if timed out + +From: Navid Emamdoost + +[ Upstream commit 728c1e2a05e4b5fc52fab3421dce772a806612a2 ] + +In ath9k_wmi_cmd, the allocated network buffer needs to be released +if timeout happens. Otherwise memory will be leaked. + +Signed-off-by: Navid Emamdoost +Signed-off-by: Kalle Valo +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath9k/wmi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c +index 8f14897ae5a33..f100533eb7adc 100644 +--- a/drivers/net/wireless/ath/ath9k/wmi.c ++++ b/drivers/net/wireless/ath/ath9k/wmi.c +@@ -340,6 +340,7 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, + ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n", + wmi_cmd_to_name(cmd_id)); + mutex_unlock(&wmi->op_mutex); ++ kfree_skb(skb); + return -ETIMEDOUT; + } + +-- +2.25.1 + diff --git a/queue-4.9/ath9k_htc-release-allocated-buffer-if-timed-out.patch b/queue-4.9/ath9k_htc-release-allocated-buffer-if-timed-out.patch new file mode 100644 index 00000000000..5a584669b3d --- /dev/null +++ b/queue-4.9/ath9k_htc-release-allocated-buffer-if-timed-out.patch @@ -0,0 +1,51 @@ +From b6f37104af8f8c58e466299a3e6d8b652d88c8a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 6 Sep 2019 13:26:03 -0500 +Subject: ath9k_htc: release allocated buffer if timed out + +From: Navid Emamdoost + +[ Upstream commit 853acf7caf10b828102d92d05b5c101666a6142b ] + +In htc_config_pipe_credits, htc_setup_complete, and htc_connect_service +if time out happens, the allocated buffer needs to be released. +Otherwise there will be memory leak. + +Signed-off-by: Navid Emamdoost +Signed-off-by: Kalle Valo +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath9k/htc_hst.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c +index 257b6ee51e54b..1af216aa5adae 100644 +--- a/drivers/net/wireless/ath/ath9k/htc_hst.c ++++ b/drivers/net/wireless/ath/ath9k/htc_hst.c +@@ -175,6 +175,7 @@ static int htc_config_pipe_credits(struct htc_target *target) + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC credit config timeout\n"); ++ kfree_skb(skb); + return -ETIMEDOUT; + } + +@@ -211,6 +212,7 @@ static int htc_setup_complete(struct htc_target *target) + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC start timeout\n"); ++ kfree_skb(skb); + return -ETIMEDOUT; + } + +@@ -284,6 +286,7 @@ int htc_connect_service(struct htc_target *target, + if (!time_left) { + dev_err(target->dev, "Service connection timeout for: %d\n", + service_connreq->service_id); ++ kfree_skb(skb); + return -ETIMEDOUT; + } + +-- +2.25.1 + diff --git a/queue-4.9/crypto-ccp-release-all-allocated-memory-if-sha-type-.patch b/queue-4.9/crypto-ccp-release-all-allocated-memory-if-sha-type-.patch new file mode 100644 index 00000000000..dae1bb99ae9 --- /dev/null +++ b/queue-4.9/crypto-ccp-release-all-allocated-memory-if-sha-type-.patch @@ -0,0 +1,41 @@ +From b59926f166cd343271286aa69c5495b29f50ff6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Sep 2019 11:04:48 -0500 +Subject: crypto: ccp - Release all allocated memory if sha type is invalid + +From: Navid Emamdoost + +[ Upstream commit 128c66429247add5128c03dc1e144ca56f05a4e2 ] + +Release all allocated memory if sha type is invalid: +In ccp_run_sha_cmd, if the type of sha is invalid, the allocated +hmac_buf should be released. + +v2: fix the goto. + +Signed-off-by: Navid Emamdoost +Acked-by: Gary R Hook +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +--- + drivers/crypto/ccp/ccp-ops.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c +index 7d4cd518e6022..723f0a0cb2b5b 100644 +--- a/drivers/crypto/ccp/ccp-ops.c ++++ b/drivers/crypto/ccp/ccp-ops.c +@@ -1216,8 +1216,9 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) + digest_size); + break; + default: ++ kfree(hmac_buf); + ret = -EINVAL; +- goto e_ctx; ++ goto e_data; + } + + memset(&hmac_cmd, 0, sizeof(hmac_cmd)); +-- +2.25.1 + diff --git a/queue-4.9/f2fs-check-if-file-namelen-exceeds-max-value.patch b/queue-4.9/f2fs-check-if-file-namelen-exceeds-max-value.patch new file mode 100644 index 00000000000..dd040083787 --- /dev/null +++ b/queue-4.9/f2fs-check-if-file-namelen-exceeds-max-value.patch @@ -0,0 +1,38 @@ +From bbb65221d2ea643cc8f5237a19a488d10e455498 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 7 Jan 2019 15:02:34 +0800 +Subject: f2fs: check if file namelen exceeds max value + +From: Sheng Yong + +[ Upstream commit 720db068634c91553a8e1d9a0fcd8c7050e06d2b ] + +Dentry bitmap is not enough to detect incorrect dentries. So this patch +also checks the namelen value of a dentry. + +Signed-off-by: Gong Chen +Signed-off-by: Sheng Yong +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +--- + fs/f2fs/dir.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c +index c452069b5e644..8dd83f024cf26 100644 +--- a/fs/f2fs/dir.c ++++ b/fs/f2fs/dir.c +@@ -845,7 +845,8 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, + + /* check memory boundary before moving forward */ + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); +- if (unlikely(bit_pos > d->max)) { ++ if (unlikely(bit_pos > d->max || ++ le16_to_cpu(de->name_len) > F2FS_NAME_LEN)) { + f2fs_msg(F2FS_I_SB(d->inode)->sb, KERN_WARNING, + "%s: corrupted namelen=%d, run fsck to fix.", + __func__, le16_to_cpu(de->name_len)); +-- +2.25.1 + diff --git a/queue-4.9/f2fs-check-memory-boundary-by-insane-namelen.patch b/queue-4.9/f2fs-check-memory-boundary-by-insane-namelen.patch new file mode 100644 index 00000000000..162e839025f --- /dev/null +++ b/queue-4.9/f2fs-check-memory-boundary-by-insane-namelen.patch @@ -0,0 +1,51 @@ +From 0d565aa43ac4df4d3bf3f0106cec18c108b601e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Nov 2018 12:40:30 -0800 +Subject: f2fs: check memory boundary by insane namelen + +From: Jaegeuk Kim + +[ Upstream commit 4e240d1bab1ead280ddf5eb05058dba6bbd57d10 ] + +If namelen is corrupted to have very long value, fill_dentries can copy +wrong memory area. + +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +--- + fs/f2fs/dir.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c +index b414892be08b7..c452069b5e644 100644 +--- a/fs/f2fs/dir.c ++++ b/fs/f2fs/dir.c +@@ -843,6 +843,16 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, + de_name.name = d->filename[bit_pos]; + de_name.len = le16_to_cpu(de->name_len); + ++ /* check memory boundary before moving forward */ ++ bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ++ if (unlikely(bit_pos > d->max)) { ++ f2fs_msg(F2FS_I_SB(d->inode)->sb, KERN_WARNING, ++ "%s: corrupted namelen=%d, run fsck to fix.", ++ __func__, le16_to_cpu(de->name_len)); ++ set_sbi_flag(sbi, SBI_NEED_FSCK); ++ return -EINVAL; ++ } ++ + if (f2fs_encrypted_inode(d->inode)) { + int save_len = fstr->len; + int err; +@@ -861,7 +871,6 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, + le32_to_cpu(de->ino), d_type)) + return true; + +- bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + ctx->pos = start_pos + bit_pos; + } + return false; +-- +2.25.1 + diff --git a/queue-4.9/f2fs-fix-to-avoid-memory-leakage-in-f2fs_listxattr.patch b/queue-4.9/f2fs-fix-to-avoid-memory-leakage-in-f2fs_listxattr.patch new file mode 100644 index 00000000000..77b6ae4b709 --- /dev/null +++ b/queue-4.9/f2fs-fix-to-avoid-memory-leakage-in-f2fs_listxattr.patch @@ -0,0 +1,65 @@ +From bb2b2d2daae30ce2b40049e92bf9331b1fada4a4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 18 Oct 2019 14:56:22 +0800 +Subject: f2fs: fix to avoid memory leakage in f2fs_listxattr + +From: Randall Huang + +[ Upstream commit 688078e7f36c293dae25b338ddc9e0a2790f6e06 ] + +In f2fs_listxattr, there is no boundary check before +memcpy e_name to buffer. +If the e_name_len is corrupted, +unexpected memory contents may be returned to the buffer. + +Signed-off-by: Randall Huang +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +--- + fs/f2fs/xattr.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c +index 3e1c0280f8661..50fe6840d593a 100644 +--- a/fs/f2fs/xattr.c ++++ b/fs/f2fs/xattr.c +@@ -404,8 +404,9 @@ cleanup: + ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) + { + struct inode *inode = d_inode(dentry); ++ nid_t xnid = F2FS_I(inode)->i_xattr_nid; + struct f2fs_xattr_entry *entry; +- void *base_addr; ++ void *base_addr, *last_base_addr; + int error = 0; + size_t rest = buffer_size; + +@@ -413,6 +414,8 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) + if (error) + return error; + ++ last_base_addr = (void *)base_addr + XATTR_SIZE(xnid, inode); ++ + list_for_each_xattr(entry, base_addr) { + const struct xattr_handler *handler = + f2fs_xattr_handler(entry->e_name_index); +@@ -420,6 +423,15 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) + size_t prefix_len; + size_t size; + ++ if ((void *)(entry) + sizeof(__u32) > last_base_addr || ++ (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { ++ f2fs_err(F2FS_I_SB(inode), "inode (%lu) has corrupted xattr", ++ inode->i_ino); ++ set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); ++ error = -EFSCORRUPTED; ++ goto cleanup; ++ } ++ + if (!handler || (handler->list && !handler->list(dentry))) + continue; + +-- +2.25.1 + diff --git a/queue-4.9/media-rc-prevent-memory-leak-in-cx23888_ir_probe.patch b/queue-4.9/media-rc-prevent-memory-leak-in-cx23888_ir_probe.patch new file mode 100644 index 00000000000..701ce0912fa --- /dev/null +++ b/queue-4.9/media-rc-prevent-memory-leak-in-cx23888_ir_probe.patch @@ -0,0 +1,40 @@ +From 47ea70bc39b7e25d3461eb528c6f080a8712a11c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Sep 2019 12:02:41 -0300 +Subject: media: rc: prevent memory leak in cx23888_ir_probe + +From: Navid Emamdoost + +[ Upstream commit a7b2df76b42bdd026e3106cf2ba97db41345a177 ] + +In cx23888_ir_probe if kfifo_alloc fails the allocated memory for state +should be released. + +Signed-off-by: Navid Emamdoost +Signed-off-by: Sean Young +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Sasha Levin +--- + drivers/media/pci/cx23885/cx23888-ir.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c +index c1aa888af7054..83864a99d3a66 100644 +--- a/drivers/media/pci/cx23885/cx23888-ir.c ++++ b/drivers/media/pci/cx23885/cx23888-ir.c +@@ -1179,8 +1179,11 @@ int cx23888_ir_probe(struct cx23885_dev *dev) + return -ENOMEM; + + spin_lock_init(&state->rx_kfifo_lock); +- if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL)) ++ if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, ++ GFP_KERNEL)) { ++ kfree(state); + return -ENOMEM; ++ } + + state->dev = dev; + sd = &state->sd; +-- +2.25.1 + diff --git a/queue-4.9/net-phy-mdio-bcm-unimac-fix-potential-null-dereferen.patch b/queue-4.9/net-phy-mdio-bcm-unimac-fix-potential-null-dereferen.patch new file mode 100644 index 00000000000..d54d92ee0c8 --- /dev/null +++ b/queue-4.9/net-phy-mdio-bcm-unimac-fix-potential-null-dereferen.patch @@ -0,0 +1,49 @@ +From 3f952b05f6698a550deac29838daa04c46ff5d8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 11 Jan 2018 11:21:51 +0000 +Subject: net: phy: mdio-bcm-unimac: fix potential NULL dereference in + unimac_mdio_probe() + +From: Wei Yongjun + +[ Upstream commit 297a6961ffb8ff4dc66c9fbf53b924bd1dda05d5 ] + +platform_get_resource() may fail and return NULL, so we should +better check it's return value to avoid a NULL pointer dereference +a bit later in the code. + +This is detected by Coccinelle semantic patch. + +@@ +expression pdev, res, n, t, e, e1, e2; +@@ + +res = platform_get_resource(pdev, t, n); ++ if (!res) ++ return -EINVAL; +... when != res == NULL +e = devm_ioremap(e1, res->start, e2); + +Signed-off-by: Wei Yongjun +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/phy/mdio-bcm-unimac.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c +index 8c73b2e771ddd..e6ff731d753d9 100644 +--- a/drivers/net/phy/mdio-bcm-unimac.c ++++ b/drivers/net/phy/mdio-bcm-unimac.c +@@ -177,6 +177,8 @@ static int unimac_mdio_probe(struct platform_device *pdev) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r) ++ return -EINVAL; + + /* Just ioremap, as this MDIO block is usually integrated into an + * Ethernet MAC controller register range +-- +2.25.1 + diff --git a/queue-4.9/scsi-libsas-direct-call-probe-and-destruct.patch b/queue-4.9/scsi-libsas-direct-call-probe-and-destruct.patch new file mode 100644 index 00000000000..4245d541827 --- /dev/null +++ b/queue-4.9/scsi-libsas-direct-call-probe-and-destruct.patch @@ -0,0 +1,304 @@ +From 9854eab640d983890a37e6c2bbb06ffd6219e103 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 Dec 2017 17:42:09 +0800 +Subject: scsi: libsas: direct call probe and destruct + +From: Jason Yan + +[ Upstream commit 0558f33c06bb910e2879e355192227a8e8f0219d ] + +In commit 87c8331fcf72 ("[SCSI] libsas: prevent domain rediscovery +competing with ata error handling") introduced disco mutex to prevent +rediscovery competing with ata error handling and put the whole +revalidation in the mutex. But the rphy add/remove needs to wait for the +error handling which also grabs the disco mutex. This may leads to dead +lock.So the probe and destruct event were introduce to do the rphy +add/remove asynchronously and out of the lock. + +The asynchronously processed workers makes the whole discovery process +not atomic, the other events may interrupt the process. For example, +if a loss of signal event inserted before the probe event, the +sas_deform_port() is called and the port will be deleted. + +And sas_port_delete() may run before the destruct event, but the +port-x:x is the top parent of end device or expander. This leads to +a kernel WARNING such as: + +[ 82.042979] sysfs group 'power' not found for kobject 'phy-1:0:22' +[ 82.042983] ------------[ cut here ]------------ +[ 82.042986] WARNING: CPU: 54 PID: 1714 at fs/sysfs/group.c:237 +sysfs_remove_group+0x94/0xa0 +[ 82.043059] Call trace: +[ 82.043082] [] sysfs_remove_group+0x94/0xa0 +[ 82.043085] [] dpm_sysfs_remove+0x60/0x70 +[ 82.043086] [] device_del+0x138/0x308 +[ 82.043089] [] sas_phy_delete+0x38/0x60 +[ 82.043091] [] do_sas_phy_delete+0x6c/0x80 +[ 82.043093] [] device_for_each_child+0x58/0xa0 +[ 82.043095] [] sas_remove_children+0x40/0x50 +[ 82.043100] [] sas_destruct_devices+0x64/0xa0 +[ 82.043102] [] process_one_work+0x1fc/0x4b0 +[ 82.043104] [] worker_thread+0x50/0x490 +[ 82.043105] [] kthread+0xfc/0x128 +[ 82.043107] [] ret_from_fork+0x10/0x50 + +Make probe and destruct a direct call in the disco and revalidate function, +but put them outside the lock. The whole discovery or revalidate won't +be interrupted by other events. And the DISCE_PROBE and DISCE_DESTRUCT +event are deleted as a result of the direct call. + +Introduce a new list to destruct the sas_port and put the port delete after +the destruct. This makes sure the right order of destroying the sysfs +kobject and fix the warning above. + +In sas_ex_revalidate_domain() have a loop to find all broadcasted +device, and sometimes we have a chance to find the same expander twice. +Because the sas_port will be deleted at the end of the whole revalidate +process, sas_port with the same name cannot be added before this. +Otherwise the sysfs will complain of creating duplicate filename. Since +the LLDD will send broadcast for every device change, we can only +process one expander's revalidation. + +[mkp: kbuild test robot warning] + +Signed-off-by: Jason Yan +CC: John Garry +CC: Johannes Thumshirn +CC: Ewan Milne +CC: Christoph Hellwig +CC: Tomas Henzl +CC: Dan Williams +Reviewed-by: Hannes Reinecke +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libsas/sas_ata.c | 1 - + drivers/scsi/libsas/sas_discover.c | 32 +++++++++++++++++------------- + drivers/scsi/libsas/sas_expander.c | 8 +++----- + drivers/scsi/libsas/sas_internal.h | 1 + + drivers/scsi/libsas/sas_port.c | 3 +++ + include/scsi/libsas.h | 3 +-- + include/scsi/scsi_transport_sas.h | 1 + + 7 files changed, 27 insertions(+), 22 deletions(-) + +diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c +index 87f5e694dbedd..dbe8c5ed4afc2 100644 +--- a/drivers/scsi/libsas/sas_ata.c ++++ b/drivers/scsi/libsas/sas_ata.c +@@ -729,7 +729,6 @@ int sas_discover_sata(struct domain_device *dev) + if (res) + return res; + +- sas_discover_event(dev->port, DISCE_PROBE); + return 0; + } + +diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c +index b200edc665a58..d6365e2fcc603 100644 +--- a/drivers/scsi/libsas/sas_discover.c ++++ b/drivers/scsi/libsas/sas_discover.c +@@ -221,13 +221,9 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) + } + } + +-static void sas_probe_devices(struct work_struct *work) ++static void sas_probe_devices(struct asd_sas_port *port) + { + struct domain_device *dev, *n; +- struct sas_discovery_event *ev = to_sas_discovery_event(work); +- struct asd_sas_port *port = ev->port; +- +- clear_bit(DISCE_PROBE, &port->disc.pending); + + /* devices must be domain members before link recovery and probe */ + list_for_each_entry(dev, &port->disco_list, disco_list_node) { +@@ -303,7 +299,6 @@ int sas_discover_end_dev(struct domain_device *dev) + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; +- sas_discover_event(dev->port, DISCE_PROBE); + + return 0; + } +@@ -362,13 +357,9 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d + sas_put_device(dev); + } + +-static void sas_destruct_devices(struct work_struct *work) ++void sas_destruct_devices(struct asd_sas_port *port) + { + struct domain_device *dev, *n; +- struct sas_discovery_event *ev = to_sas_discovery_event(work); +- struct asd_sas_port *port = ev->port; +- +- clear_bit(DISCE_DESTRUCT, &port->disc.pending); + + list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { + list_del_init(&dev->disco_list_node); +@@ -379,6 +370,16 @@ static void sas_destruct_devices(struct work_struct *work) + } + } + ++static void sas_destruct_ports(struct asd_sas_port *port) ++{ ++ struct sas_port *sas_port, *p; ++ ++ list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) { ++ list_del_init(&sas_port->del_list); ++ sas_port_delete(sas_port); ++ } ++} ++ + void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) + { + if (!test_bit(SAS_DEV_DESTROY, &dev->state) && +@@ -393,7 +394,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) + if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + sas_rphy_unlink(dev->rphy); + list_move_tail(&dev->disco_list_node, &port->destroy_list); +- sas_discover_event(dev->port, DISCE_DESTRUCT); + } + } + +@@ -499,6 +499,8 @@ static void sas_discover_domain(struct work_struct *work) + port->port_dev = NULL; + } + ++ sas_probe_devices(port); ++ + SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id, + task_pid_nr(current), error); + } +@@ -532,6 +534,10 @@ static void sas_revalidate_domain(struct work_struct *work) + port->id, task_pid_nr(current), res); + out: + mutex_unlock(&ha->disco_mutex); ++ ++ sas_destruct_devices(port); ++ sas_destruct_ports(port); ++ sas_probe_devices(port); + } + + /* ---------- Events ---------- */ +@@ -587,10 +593,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) + static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { + [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, + [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, +- [DISCE_PROBE] = sas_probe_devices, + [DISCE_SUSPEND] = sas_suspend_devices, + [DISCE_RESUME] = sas_resume_devices, +- [DISCE_DESTRUCT] = sas_destruct_devices, + }; + + disc->pending = 0; +diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c +index 7e8274938a3ee..ff3289a41c157 100644 +--- a/drivers/scsi/libsas/sas_expander.c ++++ b/drivers/scsi/libsas/sas_expander.c +@@ -1935,7 +1935,8 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, + sas_port_delete_phy(phy->port, phy->phy); + sas_device_set_phy(found, phy->port); + if (phy->port->num_phys == 0) +- sas_port_delete(phy->port); ++ list_add_tail(&phy->port->del_list, ++ &parent->port->sas_port_del_list); + phy->port = NULL; + } + } +@@ -2145,7 +2146,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) + struct domain_device *dev = NULL; + + res = sas_find_bcast_dev(port_dev, &dev); +- while (res == 0 && dev) { ++ if (res == 0 && dev) { + struct expander_device *ex = &dev->ex_dev; + int i = 0, phy_id; + +@@ -2157,9 +2158,6 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) + res = sas_rediscover(dev, phy_id); + i = phy_id + 1; + } while (i < ex->num_phys); +- +- dev = NULL; +- res = sas_find_bcast_dev(port_dev, &dev); + } + return res; + } +diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h +index 9cf0bc260b0e7..2cbbd113d8984 100644 +--- a/drivers/scsi/libsas/sas_internal.h ++++ b/drivers/scsi/libsas/sas_internal.h +@@ -98,6 +98,7 @@ int sas_try_ata_reset(struct asd_sas_phy *phy); + void sas_hae_reset(struct work_struct *work); + + void sas_free_device(struct kref *kref); ++void sas_destruct_devices(struct asd_sas_port *port); + + #ifdef CONFIG_SCSI_SAS_HOST_SMP + extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, +diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c +index d3c5297c6c89e..5d3244c8f2801 100644 +--- a/drivers/scsi/libsas/sas_port.c ++++ b/drivers/scsi/libsas/sas_port.c +@@ -66,6 +66,7 @@ static void sas_resume_port(struct asd_sas_phy *phy) + rc = sas_notify_lldd_dev_found(dev); + if (rc) { + sas_unregister_dev(port, dev); ++ sas_destruct_devices(port); + continue; + } + +@@ -219,6 +220,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) + + if (port->num_phys == 1) { + sas_unregister_domain_devices(port, gone); ++ sas_destruct_devices(port); + sas_port_delete(port->port); + port->port = NULL; + } else { +@@ -323,6 +325,7 @@ static void sas_init_port(struct asd_sas_port *port, + INIT_LIST_HEAD(&port->dev_list); + INIT_LIST_HEAD(&port->disco_list); + INIT_LIST_HEAD(&port->destroy_list); ++ INIT_LIST_HEAD(&port->sas_port_del_list); + spin_lock_init(&port->phy_list_lock); + INIT_LIST_HEAD(&port->phy_list); + port->ha = sas_ha; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 706a7017885c2..8a27e35b20803 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -87,10 +87,8 @@ enum discover_event { + DISCE_DISCOVER_DOMAIN = 0U, + DISCE_REVALIDATE_DOMAIN = 1, + DISCE_PORT_GONE = 2, +- DISCE_PROBE = 3, + DISCE_SUSPEND = 4, + DISCE_RESUME = 5, +- DISCE_DESTRUCT = 6, + DISC_NUM_EVENTS = 7, + }; + +@@ -269,6 +267,7 @@ struct asd_sas_port { + struct list_head dev_list; + struct list_head disco_list; + struct list_head destroy_list; ++ struct list_head sas_port_del_list; + enum sas_linkrate linkrate; + + struct sas_work work; +diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h +index 73d870918939e..2cf88fa91edbb 100644 +--- a/include/scsi/scsi_transport_sas.h ++++ b/include/scsi/scsi_transport_sas.h +@@ -154,6 +154,7 @@ struct sas_port { + + struct mutex phy_list_mutex; + struct list_head phy_list; ++ struct list_head del_list; /* libsas only */ + }; + + #define dev_to_sas_port(d) \ +-- +2.25.1 + diff --git a/queue-4.9/series b/queue-4.9/series new file mode 100644 index 00000000000..6f35880be26 --- /dev/null +++ b/queue-4.9/series @@ -0,0 +1,12 @@ +xfs-catch-inode-allocation-state-mismatch-corruption.patch +xfs-validate-cached-inodes-are-free-when-allocated.patch +xfs-don-t-call-xfs_da_shrink_inode-with-null-bp.patch +net-phy-mdio-bcm-unimac-fix-potential-null-dereferen.patch +crypto-ccp-release-all-allocated-memory-if-sha-type-.patch +media-rc-prevent-memory-leak-in-cx23888_ir_probe.patch +ath9k_htc-release-allocated-buffer-if-timed-out.patch +ath9k-release-allocated-buffer-if-timed-out.patch +f2fs-check-memory-boundary-by-insane-namelen.patch +f2fs-check-if-file-namelen-exceeds-max-value.patch +f2fs-fix-to-avoid-memory-leakage-in-f2fs_listxattr.patch +scsi-libsas-direct-call-probe-and-destruct.patch diff --git a/queue-4.9/xfs-catch-inode-allocation-state-mismatch-corruption.patch b/queue-4.9/xfs-catch-inode-allocation-state-mismatch-corruption.patch new file mode 100644 index 00000000000..65a318be21d --- /dev/null +++ b/queue-4.9/xfs-catch-inode-allocation-state-mismatch-corruption.patch @@ -0,0 +1,187 @@ +From edbfe4dee34459bd4414bb0fa15233e771d63dad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 23 Mar 2018 10:22:53 -0700 +Subject: xfs: catch inode allocation state mismatch corruption + +From: Dave Chinner + +[ Upstream commit ee457001ed6c6f31ddad69c24c1da8f377d8472d ] + +We recently came across a V4 filesystem causing memory corruption +due to a newly allocated inode being setup twice and being added to +the superblock inode list twice. From code inspection, the only way +this could happen is if a newly allocated inode was not marked as +free on disk (i.e. di_mode wasn't zero). + +Running the metadump on an upstream debug kernel fails during inode +allocation like so: + +XFS: Assertion failed: ip->i_d.di_nblocks == 0, file: fs/xfs/xfs_inod= +e.c, line: 838 + ------------[ cut here ]------------ +kernel BUG at fs/xfs/xfs_message.c:114! +invalid opcode: 0000 [#1] PREEMPT SMP +CPU: 11 PID: 3496 Comm: mkdir Not tainted 4.16.0-rc5-dgc #442 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/0= +1/2014 +RIP: 0010:assfail+0x28/0x30 +RSP: 0018:ffffc9000236fc80 EFLAGS: 00010202 +RAX: 00000000ffffffea RBX: 0000000000004000 RCX: 0000000000000000 +RDX: 00000000ffffffc0 RSI: 000000000000000a RDI: ffffffff8227211b +RBP: ffffc9000236fce8 R08: 0000000000000000 R09: 0000000000000000 +R10: 0000000000000bec R11: f000000000000000 R12: ffffc9000236fd30 +R13: ffff8805c76bab80 R14: ffff8805c77ac800 R15: ffff88083fb12e10 +FS: 00007fac8cbff040(0000) GS:ffff88083fd00000(0000) knlGS:0000000000000= +000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007fffa6783ff8 CR3: 00000005c6e2b003 CR4: 00000000000606e0 +Call Trace: + xfs_ialloc+0x383/0x570 + xfs_dir_ialloc+0x6a/0x2a0 + xfs_create+0x412/0x670 + xfs_generic_create+0x1f7/0x2c0 + ? capable_wrt_inode_uidgid+0x3f/0x50 + vfs_mkdir+0xfb/0x1b0 + SyS_mkdir+0xcf/0xf0 + do_syscall_64+0x73/0x1a0 + entry_SYSCALL_64_after_hwframe+0x42/0xb7 + +Extracting the inode number we crashed on from an event trace and +looking at it with xfs_db: + +xfs_db> inode 184452204 +xfs_db> p +core.magic = 0x494e +core.mode = 0100644 +core.version = 2 +core.format = 2 (extents) +core.nlinkv2 = 1 +core.onlink = 0 +..... + +Confirms that it is not a free inode on disk. xfs_repair +also trips over this inode: + +..... +zero length extent (off = 0, fsbno = 0) in ino 184452204 +correcting nextents for inode 184452204 +bad attribute fork in inode 184452204, would clear attr fork +bad nblocks 1 for inode 184452204, would reset to 0 +bad anextents 1 for inode 184452204, would reset to 0 +imap claims in-use inode 184452204 is free, would correct imap +would have cleared inode 184452204 +..... +disconnected inode 184452204, would move to lost+found + +And so we have a situation where the directory structure and the +inobt thinks the inode is free, but the inode on disk thinks it is +still in use. Where this corruption came from is not possible to +diagnose, but we can detect it and prevent the kernel from oopsing +on lookup. The reproducer now results in: + +$ sudo mkdir /mnt/scratch/{0,1,2,3,4,5}{0,1,2,3,4,5} +mkdir: cannot create directory =E2=80=98/mnt/scratch/00=E2=80=99: File ex= +ists +mkdir: cannot create directory =E2=80=98/mnt/scratch/01=E2=80=99: File ex= +ists +mkdir: cannot create directory =E2=80=98/mnt/scratch/03=E2=80=99: Structu= +re needs cleaning +mkdir: cannot create directory =E2=80=98/mnt/scratch/04=E2=80=99: Input/o= +utput error +mkdir: cannot create directory =E2=80=98/mnt/scratch/05=E2=80=99: Input/o= +utput error +.... + +And this corruption shutdown: + +[ 54.843517] XFS (loop0): Corruption detected! Free inode 0xafe846c not= + marked free on disk +[ 54.845885] XFS (loop0): Internal error xfs_trans_cancel at line 1023 = +of file fs/xfs/xfs_trans.c. Caller xfs_create+0x425/0x670 +[ 54.848994] CPU: 10 PID: 3541 Comm: mkdir Not tainted 4.16.0-rc5-dgc #= +443 +[ 54.850753] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIO= +S 1.10.2-1 04/01/2014 +[ 54.852859] Call Trace: +[ 54.853531] dump_stack+0x85/0xc5 +[ 54.854385] xfs_trans_cancel+0x197/0x1c0 +[ 54.855421] xfs_create+0x425/0x670 +[ 54.856314] xfs_generic_create+0x1f7/0x2c0 +[ 54.857390] ? capable_wrt_inode_uidgid+0x3f/0x50 +[ 54.858586] vfs_mkdir+0xfb/0x1b0 +[ 54.859458] SyS_mkdir+0xcf/0xf0 +[ 54.860254] do_syscall_64+0x73/0x1a0 +[ 54.861193] entry_SYSCALL_64_after_hwframe+0x42/0xb7 +[ 54.862492] RIP: 0033:0x7fb73bddf547 +[ 54.863358] RSP: 002b:00007ffdaa553338 EFLAGS: 00000246 ORIG_RAX: 0000= +000000000053 +[ 54.865133] RAX: ffffffffffffffda RBX: 00007ffdaa55449a RCX: 00007fb73= +bddf547 +[ 54.866766] RDX: 0000000000000001 RSI: 00000000000001ff RDI: 00007ffda= +a55449a +[ 54.868432] RBP: 00007ffdaa55449a R08: 00000000000001ff R09: 00005623a= +8670dd0 +[ 54.870110] R10: 00007fb73be72d5b R11: 0000000000000246 R12: 000000000= +00001ff +[ 54.871752] R13: 00007ffdaa5534b0 R14: 0000000000000000 R15: 00007ffda= +a553500 +[ 54.873429] XFS (loop0): xfs_do_force_shutdown(0x8) called from line 1= +024 of file fs/xfs/xfs_trans.c. Return address = ffffffff814cd050 +[ 54.882790] XFS (loop0): Corruption of in-memory data detected. Shutt= +ing down filesystem +[ 54.884597] XFS (loop0): Please umount the filesystem and rectify the = +problem(s) + +Note that this crash is only possible on v4 filesystemsi or v5 +filesystems mounted with the ikeep mount option. For all other V5 +filesystems, this problem cannot occur because we don't read inodes +we are allocating from disk - we simply overwrite them with the new +inode information. + +Signed-Off-By: Dave Chinner +Reviewed-by: Carlos Maiolino +Tested-by: Carlos Maiolino +Reviewed-by: Darrick J. Wong +Signed-off-by: Darrick J. Wong +Signed-off-by: Sasha Levin +--- + fs/xfs/xfs_icache.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c +index 86a4911520cc5..57ec10809f4bf 100644 +--- a/fs/xfs/xfs_icache.c ++++ b/fs/xfs/xfs_icache.c +@@ -471,7 +471,28 @@ xfs_iget_cache_miss( + + trace_xfs_iget_miss(ip); + +- if ((VFS_I(ip)->i_mode == 0) && !(flags & XFS_IGET_CREATE)) { ++ ++ /* ++ * If we are allocating a new inode, then check what was returned is ++ * actually a free, empty inode. If we are not allocating an inode, ++ * the check we didn't find a free inode. ++ */ ++ if (flags & XFS_IGET_CREATE) { ++ if (VFS_I(ip)->i_mode != 0) { ++ xfs_warn(mp, ++"Corruption detected! Free inode 0x%llx not marked free on disk", ++ ino); ++ error = -EFSCORRUPTED; ++ goto out_destroy; ++ } ++ if (ip->i_d.di_nblocks != 0) { ++ xfs_warn(mp, ++"Corruption detected! Free inode 0x%llx has blocks allocated!", ++ ino); ++ error = -EFSCORRUPTED; ++ goto out_destroy; ++ } ++ } else if (VFS_I(ip)->i_mode == 0) { + error = -ENOENT; + goto out_destroy; + } +-- +2.25.1 + diff --git a/queue-4.9/xfs-don-t-call-xfs_da_shrink_inode-with-null-bp.patch b/queue-4.9/xfs-don-t-call-xfs_da_shrink_inode-with-null-bp.patch new file mode 100644 index 00000000000..c165c24aa33 --- /dev/null +++ b/queue-4.9/xfs-don-t-call-xfs_da_shrink_inode-with-null-bp.patch @@ -0,0 +1,48 @@ +From 7c90bf4b5d92891a3feb7cffaba2d0a26a925805 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 Jun 2018 09:53:49 -0700 +Subject: xfs: don't call xfs_da_shrink_inode with NULL bp + +From: Eric Sandeen + +[ Upstream commit bb3d48dcf86a97dc25fe9fc2c11938e19cb4399a ] + +xfs_attr3_leaf_create may have errored out before instantiating a buffer, +for example if the blkno is out of range. In that case there is no work +to do to remove it, and in fact xfs_da_shrink_inode will lead to an oops +if we try. + +This also seems to fix a flaw where the original error from +xfs_attr3_leaf_create gets overwritten in the cleanup case, and it +removes a pointless assignment to bp which isn't used after this. + +Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=199969 +Reported-by: Xu, Wen +Tested-by: Xu, Wen +Signed-off-by: Eric Sandeen +Reviewed-by: Darrick J. Wong +Signed-off-by: Darrick J. Wong +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_attr_leaf.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c +index c6c15e5717e42..70da4113c2baf 100644 +--- a/fs/xfs/libxfs/xfs_attr_leaf.c ++++ b/fs/xfs/libxfs/xfs_attr_leaf.c +@@ -785,9 +785,8 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args) + ASSERT(blkno == 0); + error = xfs_attr3_leaf_create(args, blkno, &bp); + if (error) { +- error = xfs_da_shrink_inode(args, 0, bp); +- bp = NULL; +- if (error) ++ /* xfs_attr3_leaf_create may not have instantiated a block */ ++ if (bp && (xfs_da_shrink_inode(args, 0, bp) != 0)) + goto out; + xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */ + memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */ +-- +2.25.1 + diff --git a/queue-4.9/xfs-validate-cached-inodes-are-free-when-allocated.patch b/queue-4.9/xfs-validate-cached-inodes-are-free-when-allocated.patch new file mode 100644 index 00000000000..7bb59e33e67 --- /dev/null +++ b/queue-4.9/xfs-validate-cached-inodes-are-free-when-allocated.patch @@ -0,0 +1,160 @@ +From dbac2f0f4265973be4a983b7d661eecf6f2e8445 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Apr 2018 17:17:34 -0700 +Subject: xfs: validate cached inodes are free when allocated + +From: Dave Chinner + +[ Upstream commit afca6c5b2595fc44383919fba740c194b0b76aff ] + +A recent fuzzed filesystem image cached random dcache corruption +when the reproducer was run. This often showed up as panics in +lookup_slow() on a null inode->i_ops pointer when doing pathwalks. + +BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 +.... +Call Trace: + lookup_slow+0x44/0x60 + walk_component+0x3dd/0x9f0 + link_path_walk+0x4a7/0x830 + path_lookupat+0xc1/0x470 + filename_lookup+0x129/0x270 + user_path_at_empty+0x36/0x40 + path_listxattr+0x98/0x110 + SyS_listxattr+0x13/0x20 + do_syscall_64+0xf5/0x280 + entry_SYSCALL_64_after_hwframe+0x42/0xb7 + +but had many different failure modes including deadlocks trying to +lock the inode that was just allocated or KASAN reports of +use-after-free violations. + +The cause of the problem was a corrupt INOBT on a v4 fs where the +root inode was marked as free in the inobt record. Hence when we +allocated an inode, it chose the root inode to allocate, found it in +the cache and re-initialised it. + +We recently fixed a similar inode allocation issue caused by inobt +record corruption problem in xfs_iget_cache_miss() in commit +ee457001ed6c ("xfs: catch inode allocation state mismatch +corruption"). This change adds similar checks to the cache-hit path +to catch it, and turns the reproducer into a corruption shutdown +situation. + +Reported-by: Wen Xu +Signed-Off-By: Dave Chinner +Reviewed-by: Christoph Hellwig +Reviewed-by: Carlos Maiolino +Reviewed-by: Darrick J. Wong +[darrick: fix typos in comment] +Signed-off-by: Darrick J. Wong +Signed-off-by: Sasha Levin +--- + fs/xfs/xfs_icache.c | 73 +++++++++++++++++++++++++++++---------------- + 1 file changed, 48 insertions(+), 25 deletions(-) + +diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c +index 57ec10809f4bf..69c112ddb544d 100644 +--- a/fs/xfs/xfs_icache.c ++++ b/fs/xfs/xfs_icache.c +@@ -307,6 +307,46 @@ xfs_reinit_inode( + return error; + } + ++/* ++ * If we are allocating a new inode, then check what was returned is ++ * actually a free, empty inode. If we are not allocating an inode, ++ * then check we didn't find a free inode. ++ * ++ * Returns: ++ * 0 if the inode free state matches the lookup context ++ * -ENOENT if the inode is free and we are not allocating ++ * -EFSCORRUPTED if there is any state mismatch at all ++ */ ++static int ++xfs_iget_check_free_state( ++ struct xfs_inode *ip, ++ int flags) ++{ ++ if (flags & XFS_IGET_CREATE) { ++ /* should be a free inode */ ++ if (VFS_I(ip)->i_mode != 0) { ++ xfs_warn(ip->i_mount, ++"Corruption detected! Free inode 0x%llx not marked free! (mode 0x%x)", ++ ip->i_ino, VFS_I(ip)->i_mode); ++ return -EFSCORRUPTED; ++ } ++ ++ if (ip->i_d.di_nblocks != 0) { ++ xfs_warn(ip->i_mount, ++"Corruption detected! Free inode 0x%llx has blocks allocated!", ++ ip->i_ino); ++ return -EFSCORRUPTED; ++ } ++ return 0; ++ } ++ ++ /* should be an allocated inode */ ++ if (VFS_I(ip)->i_mode == 0) ++ return -ENOENT; ++ ++ return 0; ++} ++ + /* + * Check the validity of the inode we just found it the cache + */ +@@ -356,12 +396,12 @@ xfs_iget_cache_hit( + } + + /* +- * If lookup is racing with unlink return an error immediately. ++ * Check the inode free state is valid. This also detects lookup ++ * racing with unlinks. + */ +- if (VFS_I(ip)->i_mode == 0 && !(flags & XFS_IGET_CREATE)) { +- error = -ENOENT; ++ error = xfs_iget_check_free_state(ip, flags); ++ if (error) + goto out_error; +- } + + /* + * If IRECLAIMABLE is set, we've torn down the VFS inode already. +@@ -473,29 +513,12 @@ xfs_iget_cache_miss( + + + /* +- * If we are allocating a new inode, then check what was returned is +- * actually a free, empty inode. If we are not allocating an inode, +- * the check we didn't find a free inode. ++ * Check the inode free state is valid. This also detects lookup ++ * racing with unlinks. + */ +- if (flags & XFS_IGET_CREATE) { +- if (VFS_I(ip)->i_mode != 0) { +- xfs_warn(mp, +-"Corruption detected! Free inode 0x%llx not marked free on disk", +- ino); +- error = -EFSCORRUPTED; +- goto out_destroy; +- } +- if (ip->i_d.di_nblocks != 0) { +- xfs_warn(mp, +-"Corruption detected! Free inode 0x%llx has blocks allocated!", +- ino); +- error = -EFSCORRUPTED; +- goto out_destroy; +- } +- } else if (VFS_I(ip)->i_mode == 0) { +- error = -ENOENT; ++ error = xfs_iget_check_free_state(ip, flags); ++ if (error) + goto out_destroy; +- } + + /* + * Preload the radix tree so we can insert safely under the +-- +2.25.1 + -- 2.47.3