]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2019 12:57:36 +0000 (05:57 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Mar 2019 12:57:36 +0000 (05:57 -0700)
added patches:
ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
cifs-allow-calling-smb2_xxx_free-null.patch
driver-core-postpone-dma-tear-down-until-after-devres-release.patch
gfs2-fix-missed-wakeups-in-find_insert_glock.patch
staging-erofs-add-error-handling-for-xattr-submodule.patch
staging-erofs-fix-fast-symlink-w-o-xattr-when-fs-xattr-is-on.patch
staging-erofs-fix-memleak-of-inode-s-shared-xattr-array.patch
staging-erofs-fix-race-of-initializing-xattrs-of-a-inode-at-the-same-time.patch
staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch

queue-4.19/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch [new file with mode: 0644]
queue-4.19/cifs-allow-calling-smb2_xxx_free-null.patch [new file with mode: 0644]
queue-4.19/driver-core-postpone-dma-tear-down-until-after-devres-release.patch [new file with mode: 0644]
queue-4.19/gfs2-fix-missed-wakeups-in-find_insert_glock.patch [new file with mode: 0644]
queue-4.19/series
queue-4.19/staging-erofs-add-error-handling-for-xattr-submodule.patch [new file with mode: 0644]
queue-4.19/staging-erofs-fix-fast-symlink-w-o-xattr-when-fs-xattr-is-on.patch [new file with mode: 0644]
queue-4.19/staging-erofs-fix-memleak-of-inode-s-shared-xattr-array.patch [new file with mode: 0644]
queue-4.19/staging-erofs-fix-race-of-initializing-xattrs-of-a-inode-at-the-same-time.patch [new file with mode: 0644]
queue-4.19/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch [new file with mode: 0644]

diff --git a/queue-4.19/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch b/queue-4.19/ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
new file mode 100644 (file)
index 0000000..88a834c
--- /dev/null
@@ -0,0 +1,88 @@
+From ce938231bd3b1d7af3cbd8836f084801090470e1 Mon Sep 17 00:00:00 2001
+From: "Daniel F. Dickinson" <cshored@thecshore.com>
+Date: Sat, 22 Dec 2018 01:09:13 -0500
+Subject: ath9k: Avoid OF no-EEPROM quirks without qca,no-eeprom
+
+From: Daniel F. Dickinson <cshored@thecshore.com>
+
+commit ce938231bd3b1d7af3cbd8836f084801090470e1 upstream.
+
+ath9k_of_init() function[0] was initially written on the assumption that
+if someone had an explicit ath9k OF node that "there must be something
+wrong, why would someone add an OF node if everything is fine"[1]
+(Quoting Martin Blumenstingl <martin.blumenstingl@googlemail.com>)
+
+"it turns out it's not that simple. with your requirements I'm now aware
+of two use-cases where the current code in ath9k_of_init() doesn't work
+without modifications"[1]
+
+The "your requirements" Martin speaks of is the result of the fact that I
+have a device (PowerCloud Systems CR5000) has some kind of default - not
+unique mac address - set and requires to set the correct MAC address via
+mac-address devicetree property, however:
+
+"some cards come with a physical EEPROM chip [or OTP] so "qca,no-eeprom"
+should not be set (your use-case). in this case AH_USE_EEPROM should be
+set (which is the default when there is no OF node)"[1]
+
+The other use case is:
+
+the firmware on some PowerMac G5 seems to add a OF node for the ath9k
+card automatically. depending on the EEPROM on the card AH_NO_EEP_SWAP
+should be unset (which is the default when there is no OF node). see [3]
+
+After this patch to ath9k_of_init() the new behavior will be:
+
+    if there's no OF node then everything is the same as before
+    if there's an empty OF node then ath9k will use the hardware EEPROM
+      (before ath9k would fail to initialize because no EEPROM data was
+      provided by userspace)
+    if there's an OF node with only a MAC address then ath9k will use
+      the MAC address and the hardware EEPROM (see the case above)
+    with "qca,no-eeprom" EEPROM data from userspace will be requested.
+      the behavior here will not change
+[1]
+
+Martin provides additional background on EEPROM swapping[1].
+
+Thanks to Christian Lamparter <chunkeey@gmail.com> for all his help on
+troubleshooting this issue and the basis for this patch.
+
+[0]https://elixir.bootlin.com/linux/v4.20-rc7/source/drivers/net/wireless/ath/ath9k/init.c#L615
+[1]https://github.com/openwrt/openwrt/pull/1645#issuecomment-448027058
+[2]https://github.com/openwrt/openwrt/pull/1613
+[3]https://patchwork.kernel.org/patch/10241731/
+
+Fixes: 138b41253d9c ("ath9k: parse the device configuration from an OF node")
+Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Signed-off-by: Daniel F. Dickinson <cshored@thecshore.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Cc: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/init.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -636,15 +636,15 @@ static int ath9k_of_init(struct ath_soft
+               ret = ath9k_eeprom_request(sc, eeprom_name);
+               if (ret)
+                       return ret;
++
++              ah->ah_flags &= ~AH_USE_EEPROM;
++              ah->ah_flags |= AH_NO_EEP_SWAP;
+       }
+       mac = of_get_mac_address(np);
+       if (mac)
+               ether_addr_copy(common->macaddr, mac);
+-      ah->ah_flags &= ~AH_USE_EEPROM;
+-      ah->ah_flags |= AH_NO_EEP_SWAP;
+-
+       return 0;
+ }
diff --git a/queue-4.19/cifs-allow-calling-smb2_xxx_free-null.patch b/queue-4.19/cifs-allow-calling-smb2_xxx_free-null.patch
new file mode 100644 (file)
index 0000000..4aa54ab
--- /dev/null
@@ -0,0 +1,73 @@
+From 32a1fb36f6e50183871c2c1fcf5493c633e84732 Mon Sep 17 00:00:00 2001
+From: Ronnie Sahlberg <lsahlber@redhat.com>
+Date: Wed, 24 Oct 2018 11:50:33 +1000
+Subject: cifs: allow calling SMB2_xxx_free(NULL)
+
+From: Ronnie Sahlberg <lsahlber@redhat.com>
+
+commit 32a1fb36f6e50183871c2c1fcf5493c633e84732 upstream.
+
+Change these free functions to allow passing NULL as the argument and
+treat it as a no-op just like free(NULL) would.
+Or, if rqst->rq_iov is NULL.
+
+The second scenario could happen for smb2_queryfs() if the call
+to SMB2_query_info_init() fails and we go to qfs_exit to clean up
+and free all resources.
+In that case we have not yet assigned rqst[2].rq_iov and thus
+the rq_iov dereference in SMB2_close_free() will cause a NULL pointer
+dereference.
+
+[ bp: upstream patch also fixes SMB2_set_info_free which was introduced in 4.20 ]
+
+Fixes:  1eb9fb52040f ("cifs: create SMB2_open_init()/SMB2_open_free() helpers")
+
+Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Reviewed-by: Aurelien Aptel <aaptel@suse.com>
+CC: Stable <stable@vger.kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/cifs/smb2pdu.c |   16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -2243,10 +2243,12 @@ SMB2_open_free(struct smb_rqst *rqst)
+ {
+       int i;
+-      cifs_small_buf_release(rqst->rq_iov[0].iov_base);
+-      for (i = 1; i < rqst->rq_nvec; i++)
+-              if (rqst->rq_iov[i].iov_base != smb2_padding)
+-                      kfree(rqst->rq_iov[i].iov_base);
++      if (rqst && rqst->rq_iov) {
++              cifs_small_buf_release(rqst->rq_iov[0].iov_base);
++              for (i = 1; i < rqst->rq_nvec; i++)
++                      if (rqst->rq_iov[i].iov_base != smb2_padding)
++                              kfree(rqst->rq_iov[i].iov_base);
++      }
+ }
+ int
+@@ -2535,7 +2537,8 @@ SMB2_close_init(struct cifs_tcon *tcon,
+ void
+ SMB2_close_free(struct smb_rqst *rqst)
+ {
+-      cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
++      if (rqst && rqst->rq_iov)
++              cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
+ }
+ int
+@@ -2685,7 +2688,8 @@ SMB2_query_info_init(struct cifs_tcon *t
+ void
+ SMB2_query_info_free(struct smb_rqst *rqst)
+ {
+-      cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
++      if (rqst && rqst->rq_iov)
++              cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
+ }
+ static int
diff --git a/queue-4.19/driver-core-postpone-dma-tear-down-until-after-devres-release.patch b/queue-4.19/driver-core-postpone-dma-tear-down-until-after-devres-release.patch
new file mode 100644 (file)
index 0000000..9244c13
--- /dev/null
@@ -0,0 +1,119 @@
+From 376991db4b6464e906d699ef07681e2ffa8ab08c Mon Sep 17 00:00:00 2001
+From: Geert Uytterhoeven <geert+renesas@glider.be>
+Date: Thu, 7 Feb 2019 20:36:53 +0100
+Subject: driver core: Postpone DMA tear-down until after devres release
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Geert Uytterhoeven <geert+renesas@glider.be>
+
+commit 376991db4b6464e906d699ef07681e2ffa8ab08c upstream.
+
+When unbinding the (IOMMU-enabled) R-Car SATA device on Salvator-XS
+(R-Car H3 ES2.0), in preparation of rebinding against vfio-platform for
+device pass-through for virtualization:
+
+    echo ee300000.sata > /sys/bus/platform/drivers/sata_rcar/unbind
+
+the kernel crashes with:
+
+    Unable to handle kernel paging request at virtual address ffffffbf029ffffc
+    Mem abort info:
+      ESR = 0x96000006
+      Exception class = DABT (current EL), IL = 32 bits
+      SET = 0, FnV = 0
+      EA = 0, S1PTW = 0
+    Data abort info:
+      ISV = 0, ISS = 0x00000006
+      CM = 0, WnR = 0
+    swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000007e8c586c
+    [ffffffbf029ffffc] pgd=000000073bfc6003, pud=000000073bfc6003, pmd=0000000000000000
+    Internal error: Oops: 96000006 [#1] SMP
+    Modules linked in:
+    CPU: 0 PID: 1098 Comm: bash Not tainted 5.0.0-rc5-salvator-x-00452-g37596f884f4318ef #287
+    Hardware name: Renesas Salvator-X 2nd version board based on r8a7795 ES2.0+ (DT)
+    pstate: 60400005 (nZCv daif +PAN -UAO)
+    pc : __free_pages+0x8/0x58
+    lr : __dma_direct_free_pages+0x50/0x5c
+    sp : ffffff801268baa0
+    x29: ffffff801268baa0 x28: 0000000000000000
+    x27: ffffffc6f9c60bf0 x26: ffffffc6f9c60bf0
+    x25: ffffffc6f9c60810 x24: 0000000000000000
+    x23: 00000000fffff000 x22: ffffff8012145000
+    x21: 0000000000000800 x20: ffffffbf029fffc8
+    x19: 0000000000000000 x18: ffffffc6f86c42c8
+    x17: 0000000000000000 x16: 0000000000000070
+    x15: 0000000000000003 x14: 0000000000000000
+    x13: ffffff801103d7f8 x12: 0000000000000028
+    x11: ffffff8011117604 x10: 0000000000009ad8
+    x9 : ffffff80110126d0 x8 : ffffffc6f7563000
+    x7 : 6b6b6b6b6b6b6b6b x6 : 0000000000000018
+    x5 : ffffff8011cf3cc8 x4 : 0000000000004000
+    x3 : 0000000000080000 x2 : 0000000000000001
+    x1 : 0000000000000000 x0 : ffffffbf029fffc8
+    Process bash (pid: 1098, stack limit = 0x00000000c38e3e32)
+    Call trace:
+     __free_pages+0x8/0x58
+     __dma_direct_free_pages+0x50/0x5c
+     arch_dma_free+0x1c/0x98
+     dma_direct_free+0x14/0x24
+     dma_free_attrs+0x9c/0xdc
+     dmam_release+0x18/0x20
+     release_nodes+0x25c/0x28c
+     devres_release_all+0x48/0x4c
+     device_release_driver_internal+0x184/0x1f0
+     device_release_driver+0x14/0x1c
+     unbind_store+0x70/0xb8
+     drv_attr_store+0x24/0x34
+     sysfs_kf_write+0x4c/0x64
+     kernfs_fop_write+0x154/0x1c4
+     __vfs_write+0x34/0x164
+     vfs_write+0xb4/0x16c
+     ksys_write+0x5c/0xbc
+     __arm64_sys_write+0x14/0x1c
+     el0_svc_common+0x98/0x114
+     el0_svc_handler+0x1c/0x24
+     el0_svc+0x8/0xc
+    Code: d51b4234 17fffffa a9bf7bfd 910003fd (b9403404)
+    ---[ end trace 8c564cdd3a1a840f ]---
+
+While I've bisected this to commit e8e683ae9a736407 ("iommu/of: Fix
+probe-deferral"), and reverting that commit on post-v5.0-rc4 kernels
+does fix the problem, this turned out to be a red herring.
+
+On arm64, arch_teardown_dma_ops() resets dev->dma_ops to NULL.
+Hence if a driver has used a managed DMA allocation API, the allocated
+DMA memory will be freed using the direct DMA ops, while it may have
+been allocated using a custom DMA ops (iommu_dma_ops in this case).
+
+Fix this by reversing the order of the calls to devres_release_all() and
+arch_teardown_dma_ops().
+
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Acked-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Cc: stable <stable@vger.kernel.org>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+[rm: backport for 4.12-4.19 - kernels before 5.0 will not see
+ the crash above, but may get silent memory corruption instead]
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/base/dd.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/base/dd.c
++++ b/drivers/base/dd.c
+@@ -963,9 +963,9 @@ static void __device_release_driver(stru
+                       drv->remove(dev);
+               device_links_driver_cleanup(dev);
+-              dma_deconfigure(dev);
+               devres_release_all(dev);
++              dma_deconfigure(dev);
+               dev->driver = NULL;
+               dev_set_drvdata(dev, NULL);
+               if (dev->pm_domain && dev->pm_domain->dismiss)
diff --git a/queue-4.19/gfs2-fix-missed-wakeups-in-find_insert_glock.patch b/queue-4.19/gfs2-fix-missed-wakeups-in-find_insert_glock.patch
new file mode 100644 (file)
index 0000000..29404ca
--- /dev/null
@@ -0,0 +1,38 @@
+From 605b0487f0bc1ae9963bf52ece0f5c8055186f81 Mon Sep 17 00:00:00 2001
+From: Andreas Gruenbacher <agruenba@redhat.com>
+Date: Wed, 6 Mar 2019 15:41:57 +0100
+Subject: gfs2: Fix missed wakeups in find_insert_glock
+
+From: Andreas Gruenbacher <agruenba@redhat.com>
+
+commit 605b0487f0bc1ae9963bf52ece0f5c8055186f81 upstream.
+
+Mark Syms has reported seeing tasks that are stuck waiting in
+find_insert_glock.  It turns out that struct lm_lockname contains four padding
+bytes on 64-bit architectures that function glock_waitqueue doesn't skip when
+hashing the glock name.  As a result, we can end up waking up the wrong
+waitqueue, and the waiting tasks may be stuck forever.
+
+Fix that by using ht_parms.key_len instead of sizeof(struct lm_lockname) for
+the key length.
+
+Reported-by: Mark Syms <mark.syms@citrix.com>
+Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
+Signed-off-by: Bob Peterson <rpeterso@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/gfs2/glock.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/gfs2/glock.c
++++ b/fs/gfs2/glock.c
+@@ -107,7 +107,7 @@ static int glock_wake_function(wait_queu
+ static wait_queue_head_t *glock_waitqueue(struct lm_lockname *name)
+ {
+-      u32 hash = jhash2((u32 *)name, sizeof(*name) / 4, 0);
++      u32 hash = jhash2((u32 *)name, ht_parms.key_len / 4, 0);
+       return glock_wait_table + hash_32(hash, GLOCK_WAIT_TABLE_BITS);
+ }
index fefe347bc85919e9c6aac5f90e1c1c34b2524bd3..f7ca9ddac0b23fbf807d9a40dd3dcf70aafb4e2f 100644 (file)
@@ -134,3 +134,12 @@ arm-dts-exynos-fix-max-voltage-for-buck8-regulator-on-odroid-xu3-xu4.patch
 drm-disable-uncached-dma-optimization-for-arm-and-ar.patch
 netfilter-xt_tee-fix-wrong-interface-selection.patch
 netfilter-xt_tee-add-missing-code-to-get-interface-i.patch
+gfs2-fix-missed-wakeups-in-find_insert_glock.patch
+staging-erofs-add-error-handling-for-xattr-submodule.patch
+staging-erofs-fix-fast-symlink-w-o-xattr-when-fs-xattr-is-on.patch
+staging-erofs-fix-memleak-of-inode-s-shared-xattr-array.patch
+staging-erofs-fix-race-of-initializing-xattrs-of-a-inode-at-the-same-time.patch
+staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch
+cifs-allow-calling-smb2_xxx_free-null.patch
+ath9k-avoid-of-no-eeprom-quirks-without-qca-no-eeprom.patch
+driver-core-postpone-dma-tear-down-until-after-devres-release.patch
diff --git a/queue-4.19/staging-erofs-add-error-handling-for-xattr-submodule.patch b/queue-4.19/staging-erofs-add-error-handling-for-xattr-submodule.patch
new file mode 100644 (file)
index 0000000..9a57537
--- /dev/null
@@ -0,0 +1,318 @@
+From foo@baz Tue Mar 12 05:46:41 PDT 2019
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Mon, 11 Mar 2019 14:08:54 +0800
+Subject: staging: erofs: add error handling for xattr submodule
+To: <stable@vger.kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, LKML <linux-kernel@vger.kernel.org>, <linux-erofs@lists.ozlabs.org>, Chao Yu <yuchao0@huawei.com>, Chao Yu <chao@kernel.org>, Miao Xie <miaoxie@huawei.com>, Fang Wei <fangwei1@huawei.com>, Gao Xiang <gaoxiang25@huawei.com>
+Message-ID: <20190311060858.28654-1-gaoxiang25@huawei.com>
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+commit cadf1ccf1b0021d0b7a9347e102ac5258f9f98c8 upstream.
+
+This patch enhances the missing error handling code for
+xattr submodule, which improves the stability for the rare cases.
+
+Reviewed-by: Chao Yu <yuchao0@huawei.com>
+Signed-off-by: Chao Yu <yuchao0@huawei.com>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/erofs/internal.h |    5 +
+ drivers/staging/erofs/xattr.c    |  112 +++++++++++++++++++++++++++------------
+ 2 files changed, 81 insertions(+), 36 deletions(-)
+
+--- a/drivers/staging/erofs/internal.h
++++ b/drivers/staging/erofs/internal.h
+@@ -485,8 +485,9 @@ struct erofs_map_blocks_iter {
+ };
+-static inline struct page *erofs_get_inline_page(struct inode *inode,
+-      erofs_blk_t blkaddr)
++static inline struct page *
++erofs_get_inline_page(struct inode *inode,
++                    erofs_blk_t blkaddr)
+ {
+       return erofs_get_meta_page(inode->i_sb,
+               blkaddr, S_ISDIR(inode->i_mode));
+--- a/drivers/staging/erofs/xattr.c
++++ b/drivers/staging/erofs/xattr.c
+@@ -24,16 +24,25 @@ struct xattr_iter {
+ static inline void xattr_iter_end(struct xattr_iter *it, bool atomic)
+ {
+-      /* only init_inode_xattrs use non-atomic once */
++      /* the only user of kunmap() is 'init_inode_xattrs' */
+       if (unlikely(!atomic))
+               kunmap(it->page);
+       else
+               kunmap_atomic(it->kaddr);
++
+       unlock_page(it->page);
+       put_page(it->page);
+ }
+-static void init_inode_xattrs(struct inode *inode)
++static inline void xattr_iter_end_final(struct xattr_iter *it)
++{
++      if (!it->page)
++              return;
++
++      xattr_iter_end(it, true);
++}
++
++static int init_inode_xattrs(struct inode *inode)
+ {
+       struct xattr_iter it;
+       unsigned i;
+@@ -43,7 +52,7 @@ static void init_inode_xattrs(struct ino
+       bool atomic_map;
+       if (likely(inode_has_inited_xattr(inode)))
+-              return;
++              return 0;
+       vi = EROFS_V(inode);
+       BUG_ON(!vi->xattr_isize);
+@@ -53,7 +62,8 @@ static void init_inode_xattrs(struct ino
+       it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
+       it.page = erofs_get_inline_page(inode, it.blkaddr);
+-      BUG_ON(IS_ERR(it.page));
++      if (IS_ERR(it.page))
++              return PTR_ERR(it.page);
+       /* read in shared xattr array (non-atomic, see kmalloc below) */
+       it.kaddr = kmap(it.page);
+@@ -62,9 +72,12 @@ static void init_inode_xattrs(struct ino
+       ih = (struct erofs_xattr_ibody_header *)(it.kaddr + it.ofs);
+       vi->xattr_shared_count = ih->h_shared_count;
+-      vi->xattr_shared_xattrs = (unsigned *)kmalloc_array(
+-              vi->xattr_shared_count, sizeof(unsigned),
+-              GFP_KERNEL | __GFP_NOFAIL);
++      vi->xattr_shared_xattrs = kmalloc_array(vi->xattr_shared_count,
++                                              sizeof(uint), GFP_KERNEL);
++      if (!vi->xattr_shared_xattrs) {
++              xattr_iter_end(&it, atomic_map);
++              return -ENOMEM;
++      }
+       /* let's skip ibody header */
+       it.ofs += sizeof(struct erofs_xattr_ibody_header);
+@@ -77,7 +90,8 @@ static void init_inode_xattrs(struct ino
+                       it.page = erofs_get_meta_page(inode->i_sb,
+                               ++it.blkaddr, S_ISDIR(inode->i_mode));
+-                      BUG_ON(IS_ERR(it.page));
++                      if (IS_ERR(it.page))
++                              return PTR_ERR(it.page);
+                       it.kaddr = kmap_atomic(it.page);
+                       atomic_map = true;
+@@ -90,6 +104,7 @@ static void init_inode_xattrs(struct ino
+       xattr_iter_end(&it, atomic_map);
+       inode_set_inited_xattr(inode);
++      return 0;
+ }
+ struct xattr_iter_handlers {
+@@ -99,18 +114,25 @@ struct xattr_iter_handlers {
+       void (*value)(struct xattr_iter *, unsigned, char *, unsigned);
+ };
+-static void xattr_iter_fixup(struct xattr_iter *it)
++static inline int xattr_iter_fixup(struct xattr_iter *it)
+ {
+-      if (unlikely(it->ofs >= EROFS_BLKSIZ)) {
+-              xattr_iter_end(it, true);
++      if (it->ofs < EROFS_BLKSIZ)
++              return 0;
+-              it->blkaddr += erofs_blknr(it->ofs);
+-              it->page = erofs_get_meta_page(it->sb, it->blkaddr, false);
+-              BUG_ON(IS_ERR(it->page));
++      xattr_iter_end(it, true);
+-              it->kaddr = kmap_atomic(it->page);
+-              it->ofs = erofs_blkoff(it->ofs);
++      it->blkaddr += erofs_blknr(it->ofs);
++      it->page = erofs_get_meta_page(it->sb, it->blkaddr, false);
++      if (IS_ERR(it->page)) {
++              int err = PTR_ERR(it->page);
++
++              it->page = NULL;
++              return err;
+       }
++
++      it->kaddr = kmap_atomic(it->page);
++      it->ofs = erofs_blkoff(it->ofs);
++      return 0;
+ }
+ static int inline_xattr_iter_begin(struct xattr_iter *it,
+@@ -132,21 +154,24 @@ static int inline_xattr_iter_begin(struc
+       it->ofs = erofs_blkoff(iloc(sbi, vi->nid) + inline_xattr_ofs);
+       it->page = erofs_get_inline_page(inode, it->blkaddr);
+-      BUG_ON(IS_ERR(it->page));
+-      it->kaddr = kmap_atomic(it->page);
++      if (IS_ERR(it->page))
++              return PTR_ERR(it->page);
++      it->kaddr = kmap_atomic(it->page);
+       return vi->xattr_isize - xattr_header_sz;
+ }
+ static int xattr_foreach(struct xattr_iter *it,
+-      struct xattr_iter_handlers *op, unsigned *tlimit)
++      const struct xattr_iter_handlers *op, unsigned int *tlimit)
+ {
+       struct erofs_xattr_entry entry;
+       unsigned value_sz, processed, slice;
+       int err;
+       /* 0. fixup blkaddr, ofs, ipage */
+-      xattr_iter_fixup(it);
++      err = xattr_iter_fixup(it);
++      if (err)
++              return err;
+       /*
+        * 1. read xattr entry to the memory,
+@@ -178,7 +203,9 @@ static int xattr_foreach(struct xattr_it
+               if (it->ofs >= EROFS_BLKSIZ) {
+                       BUG_ON(it->ofs > EROFS_BLKSIZ);
+-                      xattr_iter_fixup(it);
++                      err = xattr_iter_fixup(it);
++                      if (err)
++                              goto out;
+                       it->ofs = 0;
+               }
+@@ -210,7 +237,10 @@ static int xattr_foreach(struct xattr_it
+       while (processed < value_sz) {
+               if (it->ofs >= EROFS_BLKSIZ) {
+                       BUG_ON(it->ofs > EROFS_BLKSIZ);
+-                      xattr_iter_fixup(it);
++
++                      err = xattr_iter_fixup(it);
++                      if (err)
++                              goto out;
+                       it->ofs = 0;
+               }
+@@ -270,7 +300,7 @@ static void xattr_copyvalue(struct xattr
+       memcpy(it->buffer + processed, buf, len);
+ }
+-static struct xattr_iter_handlers find_xattr_handlers = {
++static const struct xattr_iter_handlers find_xattr_handlers = {
+       .entry = xattr_entrymatch,
+       .name = xattr_namematch,
+       .alloc_buffer = xattr_checkbuffer,
+@@ -291,8 +321,11 @@ static int inline_getxattr(struct inode
+               ret = xattr_foreach(&it->it, &find_xattr_handlers, &remaining);
+               if (ret >= 0)
+                       break;
++
++              if (ret != -ENOATTR)    /* -ENOMEM, -EIO, etc. */
++                      break;
+       }
+-      xattr_iter_end(&it->it, true);
++      xattr_iter_end_final(&it->it);
+       return ret < 0 ? ret : it->buffer_size;
+ }
+@@ -315,8 +348,10 @@ static int shared_getxattr(struct inode
+                               xattr_iter_end(&it->it, true);
+                       it->it.page = erofs_get_meta_page(inode->i_sb,
+-                              blkaddr, false);
+-                      BUG_ON(IS_ERR(it->it.page));
++                                                        blkaddr, false);
++                      if (IS_ERR(it->it.page))
++                              return PTR_ERR(it->it.page);
++
+                       it->it.kaddr = kmap_atomic(it->it.page);
+                       it->it.blkaddr = blkaddr;
+               }
+@@ -324,9 +359,12 @@ static int shared_getxattr(struct inode
+               ret = xattr_foreach(&it->it, &find_xattr_handlers, NULL);
+               if (ret >= 0)
+                       break;
++
++              if (ret != -ENOATTR)    /* -ENOMEM, -EIO, etc. */
++                      break;
+       }
+       if (vi->xattr_shared_count)
+-              xattr_iter_end(&it->it, true);
++              xattr_iter_end_final(&it->it);
+       return ret < 0 ? ret : it->buffer_size;
+ }
+@@ -351,7 +389,9 @@ int erofs_getxattr(struct inode *inode,
+       if (unlikely(name == NULL))
+               return -EINVAL;
+-      init_inode_xattrs(inode);
++      ret = init_inode_xattrs(inode);
++      if (ret)
++              return ret;
+       it.index = index;
+@@ -494,7 +534,7 @@ static int xattr_skipvalue(struct xattr_
+       return 1;
+ }
+-static struct xattr_iter_handlers list_xattr_handlers = {
++static const struct xattr_iter_handlers list_xattr_handlers = {
+       .entry = xattr_entrylist,
+       .name = xattr_namelist,
+       .alloc_buffer = xattr_skipvalue,
+@@ -516,7 +556,7 @@ static int inline_listxattr(struct listx
+               if (ret < 0)
+                       break;
+       }
+-      xattr_iter_end(&it->it, true);
++      xattr_iter_end_final(&it->it);
+       return ret < 0 ? ret : it->buffer_ofs;
+ }
+@@ -538,8 +578,10 @@ static int shared_listxattr(struct listx
+                               xattr_iter_end(&it->it, true);
+                       it->it.page = erofs_get_meta_page(inode->i_sb,
+-                              blkaddr, false);
+-                      BUG_ON(IS_ERR(it->it.page));
++                                                        blkaddr, false);
++                      if (IS_ERR(it->it.page))
++                              return PTR_ERR(it->it.page);
++
+                       it->it.kaddr = kmap_atomic(it->it.page);
+                       it->it.blkaddr = blkaddr;
+               }
+@@ -549,7 +591,7 @@ static int shared_listxattr(struct listx
+                       break;
+       }
+       if (vi->xattr_shared_count)
+-              xattr_iter_end(&it->it, true);
++              xattr_iter_end_final(&it->it);
+       return ret < 0 ? ret : it->buffer_ofs;
+ }
+@@ -560,7 +602,9 @@ ssize_t erofs_listxattr(struct dentry *d
+       int ret;
+       struct listxattr_iter it;
+-      init_inode_xattrs(d_inode(dentry));
++      ret = init_inode_xattrs(d_inode(dentry));
++      if (ret)
++              return ret;
+       it.dentry = dentry;
+       it.buffer = buffer;
diff --git a/queue-4.19/staging-erofs-fix-fast-symlink-w-o-xattr-when-fs-xattr-is-on.patch b/queue-4.19/staging-erofs-fix-fast-symlink-w-o-xattr-when-fs-xattr-is-on.patch
new file mode 100644 (file)
index 0000000..7b18aeb
--- /dev/null
@@ -0,0 +1,125 @@
+From foo@baz Tue Mar 12 05:46:41 PDT 2019
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Mon, 11 Mar 2019 14:08:55 +0800
+Subject: staging: erofs: fix fast symlink w/o xattr when fs xattr is on
+To: <stable@vger.kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, LKML <linux-kernel@vger.kernel.org>, <linux-erofs@lists.ozlabs.org>, Chao Yu <yuchao0@huawei.com>, Chao Yu <chao@kernel.org>, Miao Xie <miaoxie@huawei.com>, Fang Wei <fangwei1@huawei.com>, Gao Xiang <gaoxiang25@huawei.com>
+Message-ID: <20190311060858.28654-2-gaoxiang25@huawei.com>
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+commit 7077fffcb0b0b65dc75e341306aeef4d0e7f2ec6 upstream.
+
+Currently, this will hit a BUG_ON for these symlinks as follows:
+
+- kernel message
+------------[ cut here ]------------
+kernel BUG at drivers/staging/erofs/xattr.c:59!
+SMP PTI
+CPU: 1 PID: 1170 Comm: getllxattr Not tainted 4.20.0-rc6+ #92
+Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-2.fc27 04/01/2014
+RIP: 0010:init_inode_xattrs+0x22b/0x270
+Code: 48 0f 45 ea f0 ff 4d 34 74 0d 41 83 4c 24 e0 01 31 c0 e9 00 fe ff ff 48 89 ef e8 e0 31 9e ff eb e9 89 e8 e9 ef fd ff ff 0f 0$
+ <0f> 0b 48 89 ef e8 fb f6 9c ff 48 8b 45 08 a8 01 75 24 f0 ff 4d 34
+RSP: 0018:ffffa03ac026bdf8 EFLAGS: 00010246
+------------[ cut here ]------------
+...
+Call Trace:
+ erofs_listxattr+0x30/0x2c0
+ ? selinux_inode_listxattr+0x5a/0x80
+ ? kmem_cache_alloc+0x33/0x170
+ ? security_inode_listxattr+0x27/0x40
+ listxattr+0xaf/0xc0
+ path_listxattr+0x5a/0xa0
+ do_syscall_64+0x43/0xf0
+ entry_SYSCALL_64_after_hwframe+0x44/0xa9
+...
+---[ end trace 3c24b49408dc0c72 ]---
+
+Fix it by checking ->xattr_isize in init_inode_xattrs(),
+and it also fixes improper return value -ENOTSUPP
+(it should be -ENODATA if xattr is enabled) for those inodes.
+
+Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
+Cc: <stable@vger.kernel.org> # 4.19+
+Reported-by: Li Guifu <bluce.liguifu@huawei.com>
+Tested-by: Li Guifu <bluce.liguifu@huawei.com>
+Reviewed-by: Chao Yu <yuchao0@huawei.com>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/erofs/inode.c |    8 ++++----
+ drivers/staging/erofs/xattr.c |   25 ++++++++++++++++++++-----
+ 2 files changed, 24 insertions(+), 9 deletions(-)
+
+--- a/drivers/staging/erofs/inode.c
++++ b/drivers/staging/erofs/inode.c
+@@ -184,16 +184,16 @@ static int fill_inode(struct inode *inod
+               /* setup the new inode */
+               if (S_ISREG(inode->i_mode)) {
+ #ifdef CONFIG_EROFS_FS_XATTR
+-                      if (vi->xattr_isize)
+-                              inode->i_op = &erofs_generic_xattr_iops;
++                      inode->i_op = &erofs_generic_xattr_iops;
+ #endif
+                       inode->i_fop = &generic_ro_fops;
+               } else if (S_ISDIR(inode->i_mode)) {
+                       inode->i_op =
+ #ifdef CONFIG_EROFS_FS_XATTR
+-                              vi->xattr_isize ? &erofs_dir_xattr_iops :
+-#endif
++                              &erofs_dir_xattr_iops;
++#else
+                               &erofs_dir_iops;
++#endif
+                       inode->i_fop = &erofs_dir_fops;
+               } else if (S_ISLNK(inode->i_mode)) {
+                       /* by default, page_get_link is used for symlink */
+--- a/drivers/staging/erofs/xattr.c
++++ b/drivers/staging/erofs/xattr.c
+@@ -55,7 +55,26 @@ static int init_inode_xattrs(struct inod
+               return 0;
+       vi = EROFS_V(inode);
+-      BUG_ON(!vi->xattr_isize);
++
++      /*
++       * bypass all xattr operations if ->xattr_isize is not greater than
++       * sizeof(struct erofs_xattr_ibody_header), in detail:
++       * 1) it is not enough to contain erofs_xattr_ibody_header then
++       *    ->xattr_isize should be 0 (it means no xattr);
++       * 2) it is just to contain erofs_xattr_ibody_header, which is on-disk
++       *    undefined right now (maybe use later with some new sb feature).
++       */
++      if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
++              errln("xattr_isize %d of nid %llu is not supported yet",
++                    vi->xattr_isize, vi->nid);
++              return -ENOTSUPP;
++      } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
++              if (unlikely(vi->xattr_isize)) {
++                      DBG_BUGON(1);
++                      return -EIO;    /* xattr ondisk layout error */
++              }
++              return -ENOATTR;
++      }
+       sbi = EROFS_I_SB(inode);
+       it.blkaddr = erofs_blknr(iloc(sbi, vi->nid) + vi->inode_isize);
+@@ -414,7 +433,6 @@ static int erofs_xattr_generic_get(const
+               struct dentry *unused, struct inode *inode,
+               const char *name, void *buffer, size_t size)
+ {
+-      struct erofs_vnode *const vi = EROFS_V(inode);
+       struct erofs_sb_info *const sbi = EROFS_I_SB(inode);
+       switch (handler->flags) {
+@@ -432,9 +450,6 @@ static int erofs_xattr_generic_get(const
+               return -EINVAL;
+       }
+-      if (!vi->xattr_isize)
+-              return -ENOATTR;
+-
+       return erofs_getxattr(inode, handler->flags, name, buffer, size);
+ }
diff --git a/queue-4.19/staging-erofs-fix-memleak-of-inode-s-shared-xattr-array.patch b/queue-4.19/staging-erofs-fix-memleak-of-inode-s-shared-xattr-array.patch
new file mode 100644 (file)
index 0000000..6bd6d15
--- /dev/null
@@ -0,0 +1,43 @@
+From foo@baz Tue Mar 12 05:46:41 PDT 2019
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Mon, 11 Mar 2019 14:08:56 +0800
+Subject: staging: erofs: fix memleak of inode's shared xattr array
+To: <stable@vger.kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, LKML <linux-kernel@vger.kernel.org>, <linux-erofs@lists.ozlabs.org>, Chao Yu <yuchao0@huawei.com>, Chao Yu <chao@kernel.org>, Miao Xie <miaoxie@huawei.com>, Fang Wei <fangwei1@huawei.com>, Sheng Yong <shengyong1@huawei.com>, Gao Xiang <gaoxiang25@huawei.com>
+Message-ID: <20190311060858.28654-3-gaoxiang25@huawei.com>
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+From: Sheng Yong <shengyong1@huawei.com>
+
+commit 3b1b5291f79d040d549d7c746669fc30e8045b9b upstream.
+
+If it fails to read a shared xattr page, the inode's shared xattr array
+is not freed. The next time the inode's xattr is accessed, the previously
+allocated array is leaked.
+
+Signed-off-by: Sheng Yong <shengyong1@huawei.com>
+Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
+Cc: <stable@vger.kernel.org> # 4.19+
+Reviewed-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/erofs/xattr.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/staging/erofs/xattr.c
++++ b/drivers/staging/erofs/xattr.c
+@@ -109,8 +109,11 @@ static int init_inode_xattrs(struct inod
+                       it.page = erofs_get_meta_page(inode->i_sb,
+                               ++it.blkaddr, S_ISDIR(inode->i_mode));
+-                      if (IS_ERR(it.page))
++                      if (IS_ERR(it.page)) {
++                              kfree(vi->xattr_shared_xattrs);
++                              vi->xattr_shared_xattrs = NULL;
+                               return PTR_ERR(it.page);
++                      }
+                       it.kaddr = kmap_atomic(it.page);
+                       atomic_map = true;
diff --git a/queue-4.19/staging-erofs-fix-race-of-initializing-xattrs-of-a-inode-at-the-same-time.patch b/queue-4.19/staging-erofs-fix-race-of-initializing-xattrs-of-a-inode-at-the-same-time.patch
new file mode 100644 (file)
index 0000000..a70c3ec
--- /dev/null
@@ -0,0 +1,148 @@
+From foo@baz Tue Mar 12 05:46:41 PDT 2019
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Mon, 11 Mar 2019 14:08:57 +0800
+Subject: staging: erofs: fix race of initializing xattrs of a inode at the same time
+To: <stable@vger.kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, LKML <linux-kernel@vger.kernel.org>, <linux-erofs@lists.ozlabs.org>, Chao Yu <yuchao0@huawei.com>, Chao Yu <chao@kernel.org>, Miao Xie <miaoxie@huawei.com>, Fang Wei <fangwei1@huawei.com>, Gao Xiang <gaoxiang25@huawei.com>
+Message-ID: <20190311060858.28654-4-gaoxiang25@huawei.com>
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+commit 62dc45979f3f8cb0ea67302a93bff686f0c46c5a upstream.
+
+In real scenario, there could be several threads accessing xattrs
+of the same xattr-uninitialized inode, and init_inode_xattrs()
+almost at the same time.
+
+That's actually an unexpected behavior, this patch closes the race.
+
+Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
+Cc: <stable@vger.kernel.org> # 4.19+
+Reviewed-by: Chao Yu <yuchao0@huawei.com>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/erofs/internal.h |   11 +++++++---
+ drivers/staging/erofs/xattr.c    |   41 +++++++++++++++++++++++++++------------
+ 2 files changed, 37 insertions(+), 15 deletions(-)
+
+--- a/drivers/staging/erofs/internal.h
++++ b/drivers/staging/erofs/internal.h
+@@ -327,12 +327,17 @@ static inline erofs_off_t iloc(struct er
+       return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits);
+ }
+-#define inode_set_inited_xattr(inode)   (EROFS_V(inode)->flags |= 1)
+-#define inode_has_inited_xattr(inode)   (EROFS_V(inode)->flags & 1)
++/* atomic flag definitions */
++#define EROFS_V_EA_INITED_BIT 0
++
++/* bitlock definitions (arranged in reverse order) */
++#define EROFS_V_BL_XATTR_BIT  (BITS_PER_LONG - 1)
+ struct erofs_vnode {
+       erofs_nid_t nid;
+-      unsigned int flags;
++
++      /* atomic flags (including bitlocks) */
++      unsigned long flags;
+       unsigned char data_mapping_mode;
+       /* inline size in bytes */
+--- a/drivers/staging/erofs/xattr.c
++++ b/drivers/staging/erofs/xattr.c
+@@ -44,17 +44,24 @@ static inline void xattr_iter_end_final(
+ static int init_inode_xattrs(struct inode *inode)
+ {
++      struct erofs_vnode *const vi = EROFS_V(inode);
+       struct xattr_iter it;
+       unsigned i;
+       struct erofs_xattr_ibody_header *ih;
+       struct erofs_sb_info *sbi;
+-      struct erofs_vnode *vi;
+       bool atomic_map;
++      int ret = 0;
+-      if (likely(inode_has_inited_xattr(inode)))
++      /* the most case is that xattrs of this inode are initialized. */
++      if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))
+               return 0;
+-      vi = EROFS_V(inode);
++      if (wait_on_bit_lock(&vi->flags, EROFS_V_BL_XATTR_BIT, TASK_KILLABLE))
++              return -ERESTARTSYS;
++
++      /* someone has initialized xattrs for us? */
++      if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))
++              goto out_unlock;
+       /*
+        * bypass all xattr operations if ->xattr_isize is not greater than
+@@ -67,13 +74,16 @@ static int init_inode_xattrs(struct inod
+       if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
+               errln("xattr_isize %d of nid %llu is not supported yet",
+                     vi->xattr_isize, vi->nid);
+-              return -ENOTSUPP;
++              ret = -ENOTSUPP;
++              goto out_unlock;
+       } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
+               if (unlikely(vi->xattr_isize)) {
+                       DBG_BUGON(1);
+-                      return -EIO;    /* xattr ondisk layout error */
++                      ret = -EIO;
++                      goto out_unlock;        /* xattr ondisk layout error */
+               }
+-              return -ENOATTR;
++              ret = -ENOATTR;
++              goto out_unlock;
+       }
+       sbi = EROFS_I_SB(inode);
+@@ -81,8 +91,10 @@ static int init_inode_xattrs(struct inod
+       it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
+       it.page = erofs_get_inline_page(inode, it.blkaddr);
+-      if (IS_ERR(it.page))
+-              return PTR_ERR(it.page);
++      if (IS_ERR(it.page)) {
++              ret = PTR_ERR(it.page);
++              goto out_unlock;
++      }
+       /* read in shared xattr array (non-atomic, see kmalloc below) */
+       it.kaddr = kmap(it.page);
+@@ -95,7 +107,8 @@ static int init_inode_xattrs(struct inod
+                                               sizeof(uint), GFP_KERNEL);
+       if (!vi->xattr_shared_xattrs) {
+               xattr_iter_end(&it, atomic_map);
+-              return -ENOMEM;
++              ret = -ENOMEM;
++              goto out_unlock;
+       }
+       /* let's skip ibody header */
+@@ -112,7 +125,8 @@ static int init_inode_xattrs(struct inod
+                       if (IS_ERR(it.page)) {
+                               kfree(vi->xattr_shared_xattrs);
+                               vi->xattr_shared_xattrs = NULL;
+-                              return PTR_ERR(it.page);
++                              ret = PTR_ERR(it.page);
++                              goto out_unlock;
+                       }
+                       it.kaddr = kmap_atomic(it.page);
+@@ -125,8 +139,11 @@ static int init_inode_xattrs(struct inod
+       }
+       xattr_iter_end(&it, atomic_map);
+-      inode_set_inited_xattr(inode);
+-      return 0;
++      set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);
++
++out_unlock:
++      clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
++      return ret;
+ }
+ struct xattr_iter_handlers {
diff --git a/queue-4.19/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch b/queue-4.19/staging-erofs-keep-corrupted-fs-from-crashing-kernel-in-erofs_namei.patch
new file mode 100644 (file)
index 0000000..a424c5c
--- /dev/null
@@ -0,0 +1,318 @@
+From foo@baz Tue Mar 12 05:46:41 PDT 2019
+From: Gao Xiang <gaoxiang25@huawei.com>
+Date: Mon, 11 Mar 2019 14:08:58 +0800
+Subject: staging: erofs: keep corrupted fs from crashing kernel in erofs_namei()
+To: <stable@vger.kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, LKML <linux-kernel@vger.kernel.org>, <linux-erofs@lists.ozlabs.org>, Chao Yu <yuchao0@huawei.com>, Chao Yu <chao@kernel.org>, Miao Xie <miaoxie@huawei.com>, Fang Wei <fangwei1@huawei.com>, Gao Xiang <gaoxiang25@huawei.com>
+Message-ID: <20190311060858.28654-5-gaoxiang25@huawei.com>
+
+From: Gao Xiang <gaoxiang25@huawei.com>
+
+commit 419d6efc50e94bcf5d6b35cd8c71f79edadec564 upstream.
+
+As Al pointed out, "
+... and while we are at it, what happens to
+       unsigned int nameoff = le16_to_cpu(de[mid].nameoff);
+       unsigned int matched = min(startprfx, endprfx);
+
+       struct qstr dname = QSTR_INIT(data + nameoff,
+               unlikely(mid >= ndirents - 1) ?
+                       maxsize - nameoff :
+                       le16_to_cpu(de[mid + 1].nameoff) - nameoff);
+
+       /* string comparison without already matched prefix */
+       int ret = dirnamecmp(name, &dname, &matched);
+if le16_to_cpu(de[...].nameoff) is not monotonically increasing?  I.e.
+what's to prevent e.g. (unsigned)-1 ending up in dname.len?
+
+Corrupted fs image shouldn't oops the kernel.. "
+
+Revisit the related lookup flow to address the issue.
+
+Fixes: d72d1ce60174 ("staging: erofs: add namei functions")
+Cc: <stable@vger.kernel.org> # 4.19+
+Suggested-by: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/erofs/namei.c |  189 ++++++++++++++++++++++--------------------
+ 1 file changed, 100 insertions(+), 89 deletions(-)
+
+--- a/drivers/staging/erofs/namei.c
++++ b/drivers/staging/erofs/namei.c
+@@ -15,74 +15,77 @@
+ #include <trace/events/erofs.h>
+-/* based on the value of qn->len is accurate */
+-static inline int dirnamecmp(struct qstr *qn,
+-      struct qstr *qd, unsigned *matched)
++struct erofs_qstr {
++      const unsigned char *name;
++      const unsigned char *end;
++};
++
++/* based on the end of qn is accurate and it must have the trailing '\0' */
++static inline int dirnamecmp(const struct erofs_qstr *qn,
++                           const struct erofs_qstr *qd,
++                           unsigned int *matched)
+ {
+-      unsigned i = *matched, len = min(qn->len, qd->len);
+-loop:
+-      if (unlikely(i >= len)) {
+-              *matched = i;
+-              if (qn->len < qd->len) {
+-                      /*
+-                       * actually (qn->len == qd->len)
+-                       * when qd->name[i] == '\0'
+-                       */
+-                      return qd->name[i] == '\0' ? 0 : -1;
+-              }
+-              return (qn->len > qd->len);
+-      }
++      unsigned int i = *matched;
+-      if (qn->name[i] != qd->name[i]) {
+-              *matched = i;
+-              return qn->name[i] > qd->name[i] ? 1 : -1;
++      /*
++       * on-disk error, let's only BUG_ON in the debugging mode.
++       * otherwise, it will return 1 to just skip the invalid name
++       * and go on (in consideration of the lookup performance).
++       */
++      DBG_BUGON(qd->name > qd->end);
++
++      /* qd could not have trailing '\0' */
++      /* However it is absolutely safe if < qd->end */
++      while (qd->name + i < qd->end && qd->name[i] != '\0') {
++              if (qn->name[i] != qd->name[i]) {
++                      *matched = i;
++                      return qn->name[i] > qd->name[i] ? 1 : -1;
++              }
++              ++i;
+       }
+-
+-      ++i;
+-      goto loop;
++      *matched = i;
++      /* See comments in __d_alloc on the terminating NUL character */
++      return qn->name[i] == '\0' ? 0 : 1;
+ }
+-static struct erofs_dirent *find_target_dirent(
+-      struct qstr *name,
+-      u8 *data, int maxsize)
++#define nameoff_from_disk(off, sz)    (le16_to_cpu(off) & ((sz) - 1))
++
++static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name,
++                                             u8 *data,
++                                             unsigned int dirblksize,
++                                             const int ndirents)
+ {
+-      unsigned ndirents, head, back;
+-      unsigned startprfx, endprfx;
++      int head, back;
++      unsigned int startprfx, endprfx;
+       struct erofs_dirent *const de = (struct erofs_dirent *)data;
+-      /* make sure that maxsize is valid */
+-      BUG_ON(maxsize < sizeof(struct erofs_dirent));
+-
+-      ndirents = le16_to_cpu(de->nameoff) / sizeof(*de);
+-
+-      /* corrupted dir (may be unnecessary...) */
+-      BUG_ON(!ndirents);
+-
+-      head = 0;
++      /* since the 1st dirent has been evaluated previously */
++      head = 1;
+       back = ndirents - 1;
+       startprfx = endprfx = 0;
+       while (head <= back) {
+-              unsigned mid = head + (back - head) / 2;
+-              unsigned nameoff = le16_to_cpu(de[mid].nameoff);
+-              unsigned matched = min(startprfx, endprfx);
+-
+-              struct qstr dname = QSTR_INIT(data + nameoff,
+-                      unlikely(mid >= ndirents - 1) ?
+-                              maxsize - nameoff :
+-                              le16_to_cpu(de[mid + 1].nameoff) - nameoff);
++              const int mid = head + (back - head) / 2;
++              const int nameoff = nameoff_from_disk(de[mid].nameoff,
++                                                    dirblksize);
++              unsigned int matched = min(startprfx, endprfx);
++              struct erofs_qstr dname = {
++                      .name = data + nameoff,
++                      .end = unlikely(mid >= ndirents - 1) ?
++                              data + dirblksize :
++                              data + nameoff_from_disk(de[mid + 1].nameoff,
++                                                       dirblksize)
++              };
+               /* string comparison without already matched prefix */
+               int ret = dirnamecmp(name, &dname, &matched);
+-              if (unlikely(!ret))
++              if (unlikely(!ret)) {
+                       return de + mid;
+-              else if (ret > 0) {
++              } else if (ret > 0) {
+                       head = mid + 1;
+                       startprfx = matched;
+-              } else if (unlikely(mid < 1))   /* fix "mid" overflow */
+-                      break;
+-              else {
++              } else {
+                       back = mid - 1;
+                       endprfx = matched;
+               }
+@@ -91,12 +94,12 @@ static struct erofs_dirent *find_target_
+       return ERR_PTR(-ENOENT);
+ }
+-static struct page *find_target_block_classic(
+-      struct inode *dir,
+-      struct qstr *name, int *_diff)
++static struct page *find_target_block_classic(struct inode *dir,
++                                            struct erofs_qstr *name,
++                                            int *_ndirents)
+ {
+-      unsigned startprfx, endprfx;
+-      unsigned head, back;
++      unsigned int startprfx, endprfx;
++      int head, back;
+       struct address_space *const mapping = dir->i_mapping;
+       struct page *candidate = ERR_PTR(-ENOENT);
+@@ -105,41 +108,43 @@ static struct page *find_target_block_cl
+       back = inode_datablocks(dir) - 1;
+       while (head <= back) {
+-              unsigned mid = head + (back - head) / 2;
++              const int mid = head + (back - head) / 2;
+               struct page *page = read_mapping_page(mapping, mid, NULL);
+-              if (IS_ERR(page)) {
+-exact_out:
+-                      if (!IS_ERR(candidate)) /* valid candidate */
+-                              put_page(candidate);
+-                      return page;
+-              } else {
+-                      int diff;
+-                      unsigned ndirents, matched;
+-                      struct qstr dname;
++              if (!IS_ERR(page)) {
+                       struct erofs_dirent *de = kmap_atomic(page);
+-                      unsigned nameoff = le16_to_cpu(de->nameoff);
+-
+-                      ndirents = nameoff / sizeof(*de);
++                      const int nameoff = nameoff_from_disk(de->nameoff,
++                                                            EROFS_BLKSIZ);
++                      const int ndirents = nameoff / sizeof(*de);
++                      int diff;
++                      unsigned int matched;
++                      struct erofs_qstr dname;
+-                      /* corrupted dir (should have one entry at least) */
+-                      BUG_ON(!ndirents || nameoff > PAGE_SIZE);
++                      if (unlikely(!ndirents)) {
++                              DBG_BUGON(1);
++                              kunmap_atomic(de);
++                              put_page(page);
++                              page = ERR_PTR(-EIO);
++                              goto out;
++                      }
+                       matched = min(startprfx, endprfx);
+                       dname.name = (u8 *)de + nameoff;
+-                      dname.len = ndirents == 1 ?
+-                              /* since the rest of the last page is 0 */
+-                              EROFS_BLKSIZ - nameoff
+-                              : le16_to_cpu(de[1].nameoff) - nameoff;
++                      if (ndirents == 1)
++                              dname.end = (u8 *)de + EROFS_BLKSIZ;
++                      else
++                              dname.end = (u8 *)de +
++                                      nameoff_from_disk(de[1].nameoff,
++                                                        EROFS_BLKSIZ);
+                       /* string comparison without already matched prefix */
+                       diff = dirnamecmp(name, &dname, &matched);
+                       kunmap_atomic(de);
+                       if (unlikely(!diff)) {
+-                              *_diff = 0;
+-                              goto exact_out;
++                              *_ndirents = 0;
++                              goto out;
+                       } else if (diff > 0) {
+                               head = mid + 1;
+                               startprfx = matched;
+@@ -147,45 +152,51 @@ exact_out:
+                               if (likely(!IS_ERR(candidate)))
+                                       put_page(candidate);
+                               candidate = page;
++                              *_ndirents = ndirents;
+                       } else {
+                               put_page(page);
+-                              if (unlikely(mid < 1))  /* fix "mid" overflow */
+-                                      break;
+-
+                               back = mid - 1;
+                               endprfx = matched;
+                       }
++                      continue;
+               }
++out:          /* free if the candidate is valid */
++              if (!IS_ERR(candidate))
++                      put_page(candidate);
++              return page;
+       }
+-      *_diff = 1;
+       return candidate;
+ }
+ int erofs_namei(struct inode *dir,
+-      struct qstr *name,
+-      erofs_nid_t *nid, unsigned *d_type)
++              struct qstr *name,
++              erofs_nid_t *nid, unsigned int *d_type)
+ {
+-      int diff;
++      int ndirents;
+       struct page *page;
+-      u8 *data;
++      void *data;
+       struct erofs_dirent *de;
++      struct erofs_qstr qn;
+       if (unlikely(!dir->i_size))
+               return -ENOENT;
+-      diff = 1;
+-      page = find_target_block_classic(dir, name, &diff);
++      qn.name = name->name;
++      qn.end = name->name + name->len;
++
++      ndirents = 0;
++      page = find_target_block_classic(dir, &qn, &ndirents);
+       if (unlikely(IS_ERR(page)))
+               return PTR_ERR(page);
+       data = kmap_atomic(page);
+       /* the target page has been mapped */
+-      de = likely(diff) ?
+-              /* since the rest of the last page is 0 */
+-              find_target_dirent(name, data, EROFS_BLKSIZ) :
+-              (struct erofs_dirent *)data;
++      if (ndirents)
++              de = find_target_dirent(&qn, data, EROFS_BLKSIZ, ndirents);
++      else
++              de = (struct erofs_dirent *)data;
+       if (likely(!IS_ERR(de))) {
+               *nid = le64_to_cpu(de->nid);