From 0f200edbb36daf6c986e761054e924d4f960e44e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Mar 2017 13:32:39 +0200 Subject: [PATCH] 4.4-stable patches added patches: libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch raid10-increment-write-counter-after-bio-is-split.patch usb-usbtmc-add-missing-endpoint-sanity-check.patch xfs-don-t-allow-di_size-with-high-bit-set.patch xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch --- ...t-weight-to-in-when-osd-is-destroyed.patch | 38 ++ ...fix-dumpit-error-path-rtnl-deadlocks.patch | 330 ++++++++++++++++++ ...ent-write-counter-after-bio-is-split.patch | 42 +++ queue-4.4/series | 6 + ...mc-add-missing-endpoint-sanity-check.patch | 64 ++++ ...on-t-allow-di_size-with-high-bit-set.patch | 42 +++ ..._extent_forks-inline-extent-handling.patch | 97 +++++ 7 files changed, 619 insertions(+) create mode 100644 queue-4.4/libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch create mode 100644 queue-4.4/nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch create mode 100644 queue-4.4/raid10-increment-write-counter-after-bio-is-split.patch create mode 100644 queue-4.4/usb-usbtmc-add-missing-endpoint-sanity-check.patch create mode 100644 queue-4.4/xfs-don-t-allow-di_size-with-high-bit-set.patch create mode 100644 queue-4.4/xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch diff --git a/queue-4.4/libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch b/queue-4.4/libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch new file mode 100644 index 00000000000..07d52b4fd43 --- /dev/null +++ b/queue-4.4/libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch @@ -0,0 +1,38 @@ +From b581a5854eee4b7851dedb0f8c2ceb54fb902c06 Mon Sep 17 00:00:00 2001 +From: Ilya Dryomov +Date: Wed, 1 Mar 2017 17:33:27 +0100 +Subject: libceph: don't set weight to IN when OSD is destroyed + +From: Ilya Dryomov + +commit b581a5854eee4b7851dedb0f8c2ceb54fb902c06 upstream. + +Since ceph.git commit 4e28f9e63644 ("osd/OSDMap: clear osd_info, +osd_xinfo on osd deletion"), weight is set to IN when OSD is deleted. +This changes the result of applying an incremental for clients, not +just OSDs. Because CRUSH computations are obviously affected, +pre-4e28f9e63644 servers disagree with post-4e28f9e63644 clients on +object placement, resulting in misdirected requests. + +Mirrors ceph.git commit a6009d1039a55e2c77f431662b3d6cc5a8e8e63f. + +Fixes: 930c53286977 ("libceph: apply new_state before new_up_client on incrementals") +Link: http://tracker.ceph.com/issues/19122 +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +Signed-off-by: Greg Kroah-Hartman + +--- + net/ceph/osdmap.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/net/ceph/osdmap.c ++++ b/net/ceph/osdmap.c +@@ -1265,7 +1265,6 @@ static int decode_new_up_state_weight(vo + if ((map->osd_state[osd] & CEPH_OSD_EXISTS) && + (xorstate & CEPH_OSD_EXISTS)) { + pr_info("osd%d does not exist\n", osd); +- map->osd_weight[osd] = CEPH_OSD_IN; + ret = set_primary_affinity(map, osd, + CEPH_OSD_DEFAULT_PRIMARY_AFFINITY); + if (ret) diff --git a/queue-4.4/nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch b/queue-4.4/nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch new file mode 100644 index 00000000000..9b7d0ce1c71 --- /dev/null +++ b/queue-4.4/nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch @@ -0,0 +1,330 @@ +From ea90e0dc8cecba6359b481e24d9c37160f6f524f Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Wed, 15 Mar 2017 14:26:04 +0100 +Subject: nl80211: fix dumpit error path RTNL deadlocks + +From: Johannes Berg + +commit ea90e0dc8cecba6359b481e24d9c37160f6f524f upstream. + +Sowmini pointed out Dmitry's RTNL deadlock report to me, and it turns out +to be perfectly accurate - there are various error paths that miss unlock +of the RTNL. + +To fix those, change the locking a bit to not be conditional in all those +nl80211_prepare_*_dump() functions, but make those require the RTNL to +start with, and fix the buggy error paths. This also let me use sparse +(by appropriately overriding the rtnl_lock/rtnl_unlock functions) to +validate the changes. + +Reported-by: Sowmini Varadhan +Reported-by: Dmitry Vyukov +Signed-off-by: Johannes Berg +Signed-off-by: Greg Kroah-Hartman + +--- + net/wireless/nl80211.c | 121 +++++++++++++++++++++---------------------------- + 1 file changed, 53 insertions(+), 68 deletions(-) + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -492,21 +492,17 @@ static int nl80211_prepare_wdev_dump(str + { + int err; + +- rtnl_lock(); +- + if (!cb->args[0]) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) +- goto out_unlock; ++ return err; + + *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), + nl80211_fam.attrbuf); +- if (IS_ERR(*wdev)) { +- err = PTR_ERR(*wdev); +- goto out_unlock; +- } ++ if (IS_ERR(*wdev)) ++ return PTR_ERR(*wdev); + *rdev = wiphy_to_rdev((*wdev)->wiphy); + /* 0 is the first index - add 1 to parse only once */ + cb->args[0] = (*rdev)->wiphy_idx + 1; +@@ -516,10 +512,8 @@ static int nl80211_prepare_wdev_dump(str + struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); + struct wireless_dev *tmp; + +- if (!wiphy) { +- err = -ENODEV; +- goto out_unlock; +- } ++ if (!wiphy) ++ return -ENODEV; + *rdev = wiphy_to_rdev(wiphy); + *wdev = NULL; + +@@ -530,21 +524,11 @@ static int nl80211_prepare_wdev_dump(str + } + } + +- if (!*wdev) { +- err = -ENODEV; +- goto out_unlock; +- } ++ if (!*wdev) ++ return -ENODEV; + } + + return 0; +- out_unlock: +- rtnl_unlock(); +- return err; +-} +- +-static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev) +-{ +- rtnl_unlock(); + } + + /* IE validation */ +@@ -3884,9 +3868,10 @@ static int nl80211_dump_station(struct s + int sta_idx = cb->args[2]; + int err; + ++ rtnl_lock(); + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (err) +- return err; ++ goto out_err; + + if (!wdev->netdev) { + err = -EINVAL; +@@ -3922,7 +3907,7 @@ static int nl80211_dump_station(struct s + cb->args[2] = sta_idx; + err = skb->len; + out_err: +- nl80211_finish_wdev_dump(rdev); ++ rtnl_unlock(); + + return err; + } +@@ -4639,9 +4624,10 @@ static int nl80211_dump_mpath(struct sk_ + int path_idx = cb->args[2]; + int err; + ++ rtnl_lock(); + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (err) +- return err; ++ goto out_err; + + if (!rdev->ops->dump_mpath) { + err = -EOPNOTSUPP; +@@ -4675,7 +4661,7 @@ static int nl80211_dump_mpath(struct sk_ + cb->args[2] = path_idx; + err = skb->len; + out_err: +- nl80211_finish_wdev_dump(rdev); ++ rtnl_unlock(); + return err; + } + +@@ -4835,9 +4821,10 @@ static int nl80211_dump_mpp(struct sk_bu + int path_idx = cb->args[2]; + int err; + ++ rtnl_lock(); + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (err) +- return err; ++ goto out_err; + + if (!rdev->ops->dump_mpp) { + err = -EOPNOTSUPP; +@@ -4870,7 +4857,7 @@ static int nl80211_dump_mpp(struct sk_bu + cb->args[2] = path_idx; + err = skb->len; + out_err: +- nl80211_finish_wdev_dump(rdev); ++ rtnl_unlock(); + return err; + } + +@@ -6806,9 +6793,12 @@ static int nl80211_dump_scan(struct sk_b + int start = cb->args[2], idx = 0; + int err; + ++ rtnl_lock(); + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); +- if (err) ++ if (err) { ++ rtnl_unlock(); + return err; ++ } + + wdev_lock(wdev); + spin_lock_bh(&rdev->bss_lock); +@@ -6831,7 +6821,7 @@ static int nl80211_dump_scan(struct sk_b + wdev_unlock(wdev); + + cb->args[2] = idx; +- nl80211_finish_wdev_dump(rdev); ++ rtnl_unlock(); + + return skb->len; + } +@@ -6915,9 +6905,10 @@ static int nl80211_dump_survey(struct sk + int res; + bool radio_stats; + ++ rtnl_lock(); + res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (res) +- return res; ++ goto out_err; + + /* prepare_wdev_dump parsed the attributes */ + radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; +@@ -6958,7 +6949,7 @@ static int nl80211_dump_survey(struct sk + cb->args[2] = survey_idx; + res = skb->len; + out_err: +- nl80211_finish_wdev_dump(rdev); ++ rtnl_unlock(); + return res; + } + +@@ -10158,17 +10149,13 @@ static int nl80211_prepare_vendor_dump(s + void *data = NULL; + unsigned int data_len = 0; + +- rtnl_lock(); +- + if (cb->args[0]) { + /* subtract the 1 again here */ + struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); + struct wireless_dev *tmp; + +- if (!wiphy) { +- err = -ENODEV; +- goto out_unlock; +- } ++ if (!wiphy) ++ return -ENODEV; + *rdev = wiphy_to_rdev(wiphy); + *wdev = NULL; + +@@ -10189,13 +10176,11 @@ static int nl80211_prepare_vendor_dump(s + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) +- goto out_unlock; ++ return err; + + if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] || +- !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { +- err = -EINVAL; +- goto out_unlock; +- } ++ !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) ++ return -EINVAL; + + *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), + nl80211_fam.attrbuf); +@@ -10204,10 +10189,8 @@ static int nl80211_prepare_vendor_dump(s + + *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), + nl80211_fam.attrbuf); +- if (IS_ERR(*rdev)) { +- err = PTR_ERR(*rdev); +- goto out_unlock; +- } ++ if (IS_ERR(*rdev)) ++ return PTR_ERR(*rdev); + + vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); +@@ -10220,19 +10203,15 @@ static int nl80211_prepare_vendor_dump(s + if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) + continue; + +- if (!vcmd->dumpit) { +- err = -EOPNOTSUPP; +- goto out_unlock; +- } ++ if (!vcmd->dumpit) ++ return -EOPNOTSUPP; + + vcmd_idx = i; + break; + } + +- if (vcmd_idx < 0) { +- err = -EOPNOTSUPP; +- goto out_unlock; +- } ++ if (vcmd_idx < 0) ++ return -EOPNOTSUPP; + + if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); +@@ -10249,9 +10228,6 @@ static int nl80211_prepare_vendor_dump(s + + /* keep rtnl locked in successful case */ + return 0; +- out_unlock: +- rtnl_unlock(); +- return err; + } + + static int nl80211_vendor_cmd_dump(struct sk_buff *skb, +@@ -10266,9 +10242,10 @@ static int nl80211_vendor_cmd_dump(struc + int err; + struct nlattr *vendor_data; + ++ rtnl_lock(); + err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev); + if (err) +- return err; ++ goto out; + + vcmd_idx = cb->args[2]; + data = (void *)cb->args[3]; +@@ -10277,18 +10254,26 @@ static int nl80211_vendor_cmd_dump(struc + + if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV)) { +- if (!wdev) +- return -EINVAL; ++ if (!wdev) { ++ err = -EINVAL; ++ goto out; ++ } + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && +- !wdev->netdev) +- return -EINVAL; ++ !wdev->netdev) { ++ err = -EINVAL; ++ goto out; ++ } + + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { + if (wdev->netdev && +- !netif_running(wdev->netdev)) +- return -ENETDOWN; +- if (!wdev->netdev && !wdev->p2p_started) +- return -ENETDOWN; ++ !netif_running(wdev->netdev)) { ++ err = -ENETDOWN; ++ goto out; ++ } ++ if (!wdev->netdev && !wdev->p2p_started) { ++ err = -ENETDOWN; ++ goto out; ++ } + } + } + diff --git a/queue-4.4/raid10-increment-write-counter-after-bio-is-split.patch b/queue-4.4/raid10-increment-write-counter-after-bio-is-split.patch new file mode 100644 index 00000000000..05a70607e70 --- /dev/null +++ b/queue-4.4/raid10-increment-write-counter-after-bio-is-split.patch @@ -0,0 +1,42 @@ +From 9b622e2bbcf049c82e2550d35fb54ac205965f50 Mon Sep 17 00:00:00 2001 +From: Tomasz Majchrzak +Date: Thu, 28 Jul 2016 10:28:25 +0200 +Subject: raid10: increment write counter after bio is split + +From: Tomasz Majchrzak + +commit 9b622e2bbcf049c82e2550d35fb54ac205965f50 upstream. + +md pending write counter must be incremented after bio is split, +otherwise it gets decremented too many times in end bio callback and +becomes negative. + +Signed-off-by: Tomasz Majchrzak +Reviewed-by: Artur Paszkiewicz +Signed-off-by: Shaohua Li +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/raid10.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/md/raid10.c ++++ b/drivers/md/raid10.c +@@ -1072,6 +1072,8 @@ static void __make_request(struct mddev + int max_sectors; + int sectors; + ++ md_write_start(mddev, bio); ++ + /* + * Register the new request and wait if the reconstruction + * thread has put up a bar for new requests. +@@ -1455,8 +1457,6 @@ static void make_request(struct mddev *m + return; + } + +- md_write_start(mddev, bio); +- + do { + + /* diff --git a/queue-4.4/series b/queue-4.4/series index 68c9a5a3f5e..f5a0fc329fb 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -46,3 +46,9 @@ arm-at91-pm-cpu_idle-switch-ddr-to-power-down-mode.patch arm-dts-at91-sama5d2-add-dma-properties-to-uart-nodes.patch cpufreq-restore-policy-min-max-limits-on-cpu-online.patch libceph-force-gfp_noio-for-socket-allocations.patch +raid10-increment-write-counter-after-bio-is-split.patch +libceph-don-t-set-weight-to-in-when-osd-is-destroyed.patch +xfs-don-t-allow-di_size-with-high-bit-set.patch +xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch +nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch +usb-usbtmc-add-missing-endpoint-sanity-check.patch diff --git a/queue-4.4/usb-usbtmc-add-missing-endpoint-sanity-check.patch b/queue-4.4/usb-usbtmc-add-missing-endpoint-sanity-check.patch new file mode 100644 index 00000000000..6b38c5b7969 --- /dev/null +++ b/queue-4.4/usb-usbtmc-add-missing-endpoint-sanity-check.patch @@ -0,0 +1,64 @@ +From 687e0687f71ec00e0132a21fef802dee88c2f1ad Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Tue, 14 Mar 2017 17:55:45 +0100 +Subject: USB: usbtmc: add missing endpoint sanity check + +From: Johan Hovold + +commit 687e0687f71ec00e0132a21fef802dee88c2f1ad upstream. + +USBTMC devices are required to have a bulk-in and a bulk-out endpoint, +but the driver failed to verify this, something which could lead to the +endpoint addresses being taken from uninitialised memory. + +Make sure to zero all private data as part of allocation, and add the +missing endpoint sanity check. + +Note that this also addresses a more recently introduced issue, where +the interrupt-in-presence flag would also be uninitialised whenever the +optional interrupt-in endpoint is not present. This in turn could lead +to an interrupt urb being allocated, initialised and submitted based on +uninitialised values. + +Fixes: dbf3e7f654c0 ("Implement an ioctl to support the USMTMC-USB488 READ_STATUS_BYTE operation.") +Fixes: 5b775f672cc9 ("USB: add USB test and measurement class driver") +Signed-off-by: Johan Hovold +[ johan: backport to v4.4 ] +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/class/usbtmc.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/usb/class/usbtmc.c ++++ b/drivers/usb/class/usbtmc.c +@@ -1105,7 +1105,7 @@ static int usbtmc_probe(struct usb_inter + + dev_dbg(&intf->dev, "%s called\n", __func__); + +- data = kmalloc(sizeof(*data), GFP_KERNEL); ++ data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + +@@ -1163,6 +1163,12 @@ static int usbtmc_probe(struct usb_inter + } + } + ++ if (!data->bulk_out || !data->bulk_in) { ++ dev_err(&intf->dev, "bulk endpoints not found\n"); ++ retcode = -ENODEV; ++ goto err_put; ++ } ++ + retcode = get_capabilities(data); + if (retcode) + dev_err(&intf->dev, "can't read capabilities\n"); +@@ -1186,6 +1192,7 @@ static int usbtmc_probe(struct usb_inter + error_register: + sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); ++err_put: + kref_put(&data->kref, usbtmc_delete); + return retcode; + } diff --git a/queue-4.4/xfs-don-t-allow-di_size-with-high-bit-set.patch b/queue-4.4/xfs-don-t-allow-di_size-with-high-bit-set.patch new file mode 100644 index 00000000000..8f0036b6061 --- /dev/null +++ b/queue-4.4/xfs-don-t-allow-di_size-with-high-bit-set.patch @@ -0,0 +1,42 @@ +From ef388e2054feedaeb05399ed654bdb06f385d294 Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Mon, 5 Dec 2016 12:38:38 +1100 +Subject: xfs: don't allow di_size with high bit set + +From: Darrick J. Wong + +commit ef388e2054feedaeb05399ed654bdb06f385d294 upstream. + +The on-disk field di_size is used to set i_size, which is a signed +integer of loff_t. If the high bit of di_size is set, we'll end up with +a negative i_size, which will cause all sorts of problems. Since the +VFS won't let us create a file with such length, we should catch them +here in the verifier too. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Cc: Nikolay Borisov +Signed-off-by: Greg Kroah-Hartman + +--- + fs/xfs/libxfs/xfs_inode_buf.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/fs/xfs/libxfs/xfs_inode_buf.c ++++ b/fs/xfs/libxfs/xfs_inode_buf.c +@@ -299,6 +299,14 @@ xfs_dinode_verify( + if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC)) + return false; + ++ /* don't allow invalid i_size */ ++ if (be64_to_cpu(dip->di_size) & (1ULL << 63)) ++ return false; ++ ++ /* No zero-length symlinks. */ ++ if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0) ++ return false; ++ + /* only version 3 or greater inodes are extensively verified here */ + if (dip->di_version < 3) + return true; diff --git a/queue-4.4/xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch b/queue-4.4/xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch new file mode 100644 index 00000000000..e589315fabf --- /dev/null +++ b/queue-4.4/xfs-fix-up-xfs_swap_extent_forks-inline-extent-handling.patch @@ -0,0 +1,97 @@ +From 4dfce57db6354603641132fac3c887614e3ebe81 Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Tue, 8 Nov 2016 12:55:18 +1100 +Subject: xfs: fix up xfs_swap_extent_forks inline extent handling + +From: Eric Sandeen + +commit 4dfce57db6354603641132fac3c887614e3ebe81 upstream. + +There have been several reports over the years of NULL pointer +dereferences in xfs_trans_log_inode during xfs_fsr processes, +when the process is doing an fput and tearing down extents +on the temporary inode, something like: + +BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 +PID: 29439 TASK: ffff880550584fa0 CPU: 6 COMMAND: "xfs_fsr" + [exception RIP: xfs_trans_log_inode+0x10] + #9 [ffff8800a57bbbe0] xfs_bunmapi at ffffffffa037398e [xfs] +#10 [ffff8800a57bbce8] xfs_itruncate_extents at ffffffffa0391b29 [xfs] +#11 [ffff8800a57bbd88] xfs_inactive_truncate at ffffffffa0391d0c [xfs] +#12 [ffff8800a57bbdb8] xfs_inactive at ffffffffa0392508 [xfs] +#13 [ffff8800a57bbdd8] xfs_fs_evict_inode at ffffffffa035907e [xfs] +#14 [ffff8800a57bbe00] evict at ffffffff811e1b67 +#15 [ffff8800a57bbe28] iput at ffffffff811e23a5 +#16 [ffff8800a57bbe58] dentry_kill at ffffffff811dcfc8 +#17 [ffff8800a57bbe88] dput at ffffffff811dd06c +#18 [ffff8800a57bbea8] __fput at ffffffff811c823b +#19 [ffff8800a57bbef0] ____fput at ffffffff811c846e +#20 [ffff8800a57bbf00] task_work_run at ffffffff81093b27 +#21 [ffff8800a57bbf30] do_notify_resume at ffffffff81013b0c +#22 [ffff8800a57bbf50] int_signal at ffffffff8161405d + +As it turns out, this is because the i_itemp pointer, along +with the d_ops pointer, has been overwritten with zeros +when we tear down the extents during truncate. When the in-core +inode fork on the temporary inode used by xfs_fsr was originally +set up during the extent swap, we mistakenly looked at di_nextents +to determine whether all extents fit inline, but this misses extents +generated by speculative preallocation; we should be using if_bytes +instead. + +This mistake corrupts the in-memory inode, and code in +xfs_iext_remove_inline eventually gets bad inputs, causing +it to memmove and memset incorrect ranges; this became apparent +because the two values in ifp->if_u2.if_inline_ext[1] contained +what should have been in d_ops and i_itemp; they were memmoved due +to incorrect array indexing and then the original locations +were zeroed with memset, again due to an array overrun. + +Fix this by properly using i_df.if_bytes to determine the number +of extents, not di_nextents. + +Thanks to dchinner for looking at this with me and spotting the +root cause. + +[nborisov: backported to 4.4] + +Cc: stable@vger.kernel.org +Signed-off-by: Eric Sandeen +Reviewed-by: Brian Foster +Signed-off-by: Dave Chinner +Signed-off-by: Nikolay Borisov +Signed-off-by: Greg Kroah-Hartman +-- + fs/xfs/xfs_bmap_util.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/fs/xfs/xfs_bmap_util.c ++++ b/fs/xfs/xfs_bmap_util.c +@@ -1713,6 +1713,7 @@ xfs_swap_extents( + xfs_trans_t *tp; + xfs_bstat_t *sbp = &sxp->sx_stat; + xfs_ifork_t *tempifp, *ifp, *tifp; ++ xfs_extnum_t nextents; + int src_log_flags, target_log_flags; + int error = 0; + int aforkblks = 0; +@@ -1899,7 +1900,8 @@ xfs_swap_extents( + * pointer. Otherwise it's already NULL or + * pointing to the extent. + */ +- if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) { ++ nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); ++ if (nextents <= XFS_INLINE_EXTS) { + ifp->if_u1.if_extents = + ifp->if_u2.if_inline_ext; + } +@@ -1918,7 +1920,8 @@ xfs_swap_extents( + * pointer. Otherwise it's already NULL or + * pointing to the extent. + */ +- if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) { ++ nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); ++ if (nextents <= XFS_INLINE_EXTS) { + tifp->if_u1.if_extents = + tifp->if_u2.if_inline_ext; + } -- 2.47.3