From: Sasha Levin Date: Mon, 1 Jan 2024 18:11:04 +0000 (-0500) Subject: Fixes for 6.1 X-Git-Tag: v5.10.206~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e66cd1c7bb1cc4ba34c048f6d9bd6fcc47c74e44;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.1 Signed-off-by: Sasha Levin --- diff --git a/queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch b/queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch new file mode 100644 index 00000000000..d0f850145d8 --- /dev/null +++ b/queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch @@ -0,0 +1,40 @@ +From cb680f84a33e0bd3cda8cfe94452f3c15f0e3927 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 Dec 2023 15:50:35 +0200 +Subject: ARM: dts: Fix occasional boot hang for am3 usb + +From: Tony Lindgren + +[ Upstream commit 9b6a51aab5f5f9f71d2fa16e8b4d530e1643dfcb ] + +With subtle timings changes, we can now sometimes get an external abort on +non-linefetch error booting am3 devices at sysc_reset(). This is because +of a missing reset delay needed for the usb target module. + +Looks like we never enabled the delay earlier for am3, although a similar +issue was seen earlier with a similar usb setup for dm814x as described in +commit ebf244148092 ("ARM: OMAP2+: Use srst_udelay for USB on dm814x"). + +Cc: stable@vger.kernel.org +Fixes: 0782e8572ce4 ("ARM: dts: Probe am335x musb with ti-sysc") +Signed-off-by: Tony Lindgren +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/am33xx.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi +index 32d397b3950b9..b2e7f6a710740 100644 +--- a/arch/arm/boot/dts/am33xx.dtsi ++++ b/arch/arm/boot/dts/am33xx.dtsi +@@ -349,6 +349,7 @@ usb: target-module@47400000 { + , + , + ; ++ ti,sysc-delay-us = <2>; + clocks = <&l3s_clkctrl AM3_L3S_USB_OTG_HS_CLKCTRL 0>; + clock-names = "fck"; + #address-cells = <1>; +-- +2.43.0 + diff --git a/queue-6.1/block-renumber-queue_flag_hw_wc.patch b/queue-6.1/block-renumber-queue_flag_hw_wc.patch new file mode 100644 index 00000000000..a9ee3b567e3 --- /dev/null +++ b/queue-6.1/block-renumber-queue_flag_hw_wc.patch @@ -0,0 +1,37 @@ +From 41a9ccbc475d613f073f31681da464dfdaf616eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Dec 2023 08:15:24 +0000 +Subject: block: renumber QUEUE_FLAG_HW_WC + +From: Christoph Hellwig + +[ Upstream commit 02d374f3418df577c850f0cd45c3da9245ead547 ] + +For the QUEUE_FLAG_HW_WC to actually work, it needs to have a separate +number from QUEUE_FLAG_FUA, doh. + +Fixes: 43c9835b144c ("block: don't allow enabling a cache on devices that don't support it") +Signed-off-by: Christoph Hellwig +Link: https://lore.kernel.org/r/20231226081524.180289-1-hch@lst.de +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + include/linux/blkdev.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index 57674b3c58774..07a7eeef47d39 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -565,7 +565,7 @@ struct request_queue { + #define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */ + #define QUEUE_FLAG_ADD_RANDOM 10 /* Contributes to random pool */ + #define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */ +-#define QUEUE_FLAG_HW_WC 18 /* Write back caching supported */ ++#define QUEUE_FLAG_HW_WC 13 /* Write back caching supported */ + #define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */ + #define QUEUE_FLAG_STABLE_WRITES 15 /* don't modify blks until WB is done */ + #define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */ +-- +2.43.0 + diff --git a/queue-6.1/fs-introduce-lock_rename_child-helper.patch b/queue-6.1/fs-introduce-lock_rename_child-helper.patch new file mode 100644 index 00000000000..7275a6ab8cf --- /dev/null +++ b/queue-6.1/fs-introduce-lock_rename_child-helper.patch @@ -0,0 +1,131 @@ +From 1713573bb43a2ac28f0643e58afa5730feba14b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:36 +0900 +Subject: fs: introduce lock_rename_child() helper + +From: Al Viro + +[ Upstream commit 9bc37e04823b5280dd0f22b6680fc23fe81ca325 ] + +Pass the dentry of a source file and the dentry of a destination directory +to lock parent inodes for rename. As soon as this function returns, +->d_parent of the source file dentry is stable and inodes are properly +locked for calling vfs-rename. This helper is needed for ksmbd server. +rename request of SMB protocol has to rename an opened file, no matter +which directory it's in. + +Signed-off-by: Al Viro +Signed-off-by: Namjae Jeon +Signed-off-by: Al Viro +Signed-off-by: Sasha Levin +--- + fs/namei.c | 68 ++++++++++++++++++++++++++++++++++++------- + include/linux/namei.h | 1 + + 2 files changed, 58 insertions(+), 11 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 5e1c2ab2ae709..6daaf84567195 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2980,20 +2980,10 @@ static inline int may_create(struct user_namespace *mnt_userns, + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); + } + +-/* +- * p1 and p2 should be directories on the same fs. +- */ +-struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ++static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2) + { + struct dentry *p; + +- if (p1 == p2) { +- inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); +- return NULL; +- } +- +- mutex_lock(&p1->d_sb->s_vfs_rename_mutex); +- + p = d_ancestor(p2, p1); + if (p) { + inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); +@@ -3012,8 +3002,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) + I_MUTEX_PARENT, I_MUTEX_PARENT2); + return NULL; + } ++ ++/* ++ * p1 and p2 should be directories on the same fs. ++ */ ++struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ++{ ++ if (p1 == p2) { ++ inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); ++ return NULL; ++ } ++ ++ mutex_lock(&p1->d_sb->s_vfs_rename_mutex); ++ return lock_two_directories(p1, p2); ++} + EXPORT_SYMBOL(lock_rename); + ++/* ++ * c1 and p2 should be on the same fs. ++ */ ++struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2) ++{ ++ if (READ_ONCE(c1->d_parent) == p2) { ++ /* ++ * hopefully won't need to touch ->s_vfs_rename_mutex at all. ++ */ ++ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); ++ /* ++ * now that p2 is locked, nobody can move in or out of it, ++ * so the test below is safe. ++ */ ++ if (likely(c1->d_parent == p2)) ++ return NULL; ++ ++ /* ++ * c1 got moved out of p2 while we'd been taking locks; ++ * unlock and fall back to slow case. ++ */ ++ inode_unlock(p2->d_inode); ++ } ++ ++ mutex_lock(&c1->d_sb->s_vfs_rename_mutex); ++ /* ++ * nobody can move out of any directories on this fs. ++ */ ++ if (likely(c1->d_parent != p2)) ++ return lock_two_directories(c1->d_parent, p2); ++ ++ /* ++ * c1 got moved into p2 while we were taking locks; ++ * we need p2 locked and ->s_vfs_rename_mutex unlocked, ++ * for consistency with lock_rename(). ++ */ ++ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); ++ mutex_unlock(&c1->d_sb->s_vfs_rename_mutex); ++ return NULL; ++} ++EXPORT_SYMBOL(lock_rename_child); ++ + void unlock_rename(struct dentry *p1, struct dentry *p2) + { + inode_unlock(p1->d_inode); +diff --git a/include/linux/namei.h b/include/linux/namei.h +index 00fee52df8423..2b66021c740dd 100644 +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -81,6 +81,7 @@ extern int follow_down(struct path *); + extern int follow_up(struct path *); + + extern struct dentry *lock_rename(struct dentry *, struct dentry *); ++extern struct dentry *lock_rename_child(struct dentry *, struct dentry *); + extern void unlock_rename(struct dentry *, struct dentry *); + + extern int __must_check nd_jump_link(const struct path *path); +-- +2.43.0 + diff --git a/queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch b/queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch new file mode 100644 index 00000000000..52365ca3fda --- /dev/null +++ b/queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch @@ -0,0 +1,176 @@ +From de2a87fa19335534b473187947b74a300733dd0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Nov 2023 13:52:58 +0100 +Subject: iio: imu: adis16475: add spi_device_id table + +From: Nuno Sa + +[ Upstream commit ee4d79055aeea27f1b8c42233cc0c90d0a8b5355 ] + +This prevents the warning message "SPI driver has no spi_device_id for..." +when registering the driver. More importantly, it makes sure that +module autoloading works as spi relies on spi: modaliases and not of. + +While at it, move the of_device_id table to it's natural place. + +Fixes: fff7352bf7a3c ("iio: imu: Add support for adis16475") +Signed-off-by: Nuno Sa +Link: https://lore.kernel.org/r/20231102125258.3284830-1-nuno.sa@analog.com +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +--- + drivers/iio/imu/adis16475.c | 117 ++++++++++++++++++++++-------------- + 1 file changed, 72 insertions(+), 45 deletions(-) + +diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c +index aec55f7e1f260..2d939773445d7 100644 +--- a/drivers/iio/imu/adis16475.c ++++ b/drivers/iio/imu/adis16475.c +@@ -1243,50 +1243,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st) + return 0; + } + +-static const struct of_device_id adis16475_of_match[] = { +- { .compatible = "adi,adis16470", +- .data = &adis16475_chip_info[ADIS16470] }, +- { .compatible = "adi,adis16475-1", +- .data = &adis16475_chip_info[ADIS16475_1] }, +- { .compatible = "adi,adis16475-2", +- .data = &adis16475_chip_info[ADIS16475_2] }, +- { .compatible = "adi,adis16475-3", +- .data = &adis16475_chip_info[ADIS16475_3] }, +- { .compatible = "adi,adis16477-1", +- .data = &adis16475_chip_info[ADIS16477_1] }, +- { .compatible = "adi,adis16477-2", +- .data = &adis16475_chip_info[ADIS16477_2] }, +- { .compatible = "adi,adis16477-3", +- .data = &adis16475_chip_info[ADIS16477_3] }, +- { .compatible = "adi,adis16465-1", +- .data = &adis16475_chip_info[ADIS16465_1] }, +- { .compatible = "adi,adis16465-2", +- .data = &adis16475_chip_info[ADIS16465_2] }, +- { .compatible = "adi,adis16465-3", +- .data = &adis16475_chip_info[ADIS16465_3] }, +- { .compatible = "adi,adis16467-1", +- .data = &adis16475_chip_info[ADIS16467_1] }, +- { .compatible = "adi,adis16467-2", +- .data = &adis16475_chip_info[ADIS16467_2] }, +- { .compatible = "adi,adis16467-3", +- .data = &adis16475_chip_info[ADIS16467_3] }, +- { .compatible = "adi,adis16500", +- .data = &adis16475_chip_info[ADIS16500] }, +- { .compatible = "adi,adis16505-1", +- .data = &adis16475_chip_info[ADIS16505_1] }, +- { .compatible = "adi,adis16505-2", +- .data = &adis16475_chip_info[ADIS16505_2] }, +- { .compatible = "adi,adis16505-3", +- .data = &adis16475_chip_info[ADIS16505_3] }, +- { .compatible = "adi,adis16507-1", +- .data = &adis16475_chip_info[ADIS16507_1] }, +- { .compatible = "adi,adis16507-2", +- .data = &adis16475_chip_info[ADIS16507_2] }, +- { .compatible = "adi,adis16507-3", +- .data = &adis16475_chip_info[ADIS16507_3] }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, adis16475_of_match); + + static int adis16475_probe(struct spi_device *spi) + { +@@ -1300,7 +1256,7 @@ static int adis16475_probe(struct spi_device *spi) + + st = iio_priv(indio_dev); + +- st->info = device_get_match_data(&spi->dev); ++ st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + +@@ -1340,12 +1296,83 @@ static int adis16475_probe(struct spi_device *spi) + return 0; + } + ++static const struct of_device_id adis16475_of_match[] = { ++ { .compatible = "adi,adis16470", ++ .data = &adis16475_chip_info[ADIS16470] }, ++ { .compatible = "adi,adis16475-1", ++ .data = &adis16475_chip_info[ADIS16475_1] }, ++ { .compatible = "adi,adis16475-2", ++ .data = &adis16475_chip_info[ADIS16475_2] }, ++ { .compatible = "adi,adis16475-3", ++ .data = &adis16475_chip_info[ADIS16475_3] }, ++ { .compatible = "adi,adis16477-1", ++ .data = &adis16475_chip_info[ADIS16477_1] }, ++ { .compatible = "adi,adis16477-2", ++ .data = &adis16475_chip_info[ADIS16477_2] }, ++ { .compatible = "adi,adis16477-3", ++ .data = &adis16475_chip_info[ADIS16477_3] }, ++ { .compatible = "adi,adis16465-1", ++ .data = &adis16475_chip_info[ADIS16465_1] }, ++ { .compatible = "adi,adis16465-2", ++ .data = &adis16475_chip_info[ADIS16465_2] }, ++ { .compatible = "adi,adis16465-3", ++ .data = &adis16475_chip_info[ADIS16465_3] }, ++ { .compatible = "adi,adis16467-1", ++ .data = &adis16475_chip_info[ADIS16467_1] }, ++ { .compatible = "adi,adis16467-2", ++ .data = &adis16475_chip_info[ADIS16467_2] }, ++ { .compatible = "adi,adis16467-3", ++ .data = &adis16475_chip_info[ADIS16467_3] }, ++ { .compatible = "adi,adis16500", ++ .data = &adis16475_chip_info[ADIS16500] }, ++ { .compatible = "adi,adis16505-1", ++ .data = &adis16475_chip_info[ADIS16505_1] }, ++ { .compatible = "adi,adis16505-2", ++ .data = &adis16475_chip_info[ADIS16505_2] }, ++ { .compatible = "adi,adis16505-3", ++ .data = &adis16475_chip_info[ADIS16505_3] }, ++ { .compatible = "adi,adis16507-1", ++ .data = &adis16475_chip_info[ADIS16507_1] }, ++ { .compatible = "adi,adis16507-2", ++ .data = &adis16475_chip_info[ADIS16507_2] }, ++ { .compatible = "adi,adis16507-3", ++ .data = &adis16475_chip_info[ADIS16507_3] }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, adis16475_of_match); ++ ++static const struct spi_device_id adis16475_ids[] = { ++ { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, ++ { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, ++ { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, ++ { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, ++ { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, ++ { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, ++ { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, ++ { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, ++ { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, ++ { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, ++ { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, ++ { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, ++ { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, ++ { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, ++ { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, ++ { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, ++ { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, ++ { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, ++ { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, ++ { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, adis16475_ids); ++ + static struct spi_driver adis16475_driver = { + .driver = { + .name = "adis16475", + .of_match_table = adis16475_of_match, + }, + .probe = adis16475_probe, ++ .id_table = adis16475_ids, + }; + module_spi_driver(adis16475_driver); + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch b/queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch new file mode 100644 index 00000000000..45c47f9b7e7 --- /dev/null +++ b/queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch @@ -0,0 +1,35 @@ +From adce1cb106c7824bd0f5afc904ec4aff9a6c3026 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:58 +0900 +Subject: ksmbd: add missing calling smb2_set_err_rsp() on error + +From: Namjae Jeon + +[ Upstream commit 0e2378eaa2b3a663726cf740d4aaa8a801e2cb31 ] + +If some error happen on smb2_sess_setup(), Need to call +smb2_set_err_rsp() to set error response. +This patch add missing calling smb2_set_err_rsp() on error. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 0fed613956f7a..b81a38803b40d 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -1904,6 +1904,7 @@ int smb2_sess_setup(struct ksmbd_work *work) + ksmbd_conn_set_need_negotiate(conn); + } + } ++ smb2_set_err_rsp(work); + } else { + unsigned int iov_len; + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch b/queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch new file mode 100644 index 00000000000..81c96306495 --- /dev/null +++ b/queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch @@ -0,0 +1,600 @@ +From 6ec47ed9ec9d4885dec6ef55dbddc216fba3f737 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:42 +0900 +Subject: ksmbd: add mnt_want_write to ksmbd vfs functions + +From: Namjae Jeon + +[ Upstream commit 40b268d384a22276dca1450549f53eed60e21deb ] + +ksmbd is doing write access using vfs helpers. There are the cases that +mnt_want_write() is not called in vfs helper. This patch add missing +mnt_want_write() to ksmbd vfs functions. + +Cc: stable@vger.kernel.org +Cc: Amir Goldstein +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 26 ++++----- + fs/smb/server/smbacl.c | 10 ++-- + fs/smb/server/vfs.c | 112 ++++++++++++++++++++++++++++++-------- + fs/smb/server/vfs.h | 17 +++--- + fs/smb/server/vfs_cache.c | 2 +- + 5 files changed, 112 insertions(+), 55 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 028b1d1055b57..f35e06ae25b3b 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2295,7 +2295,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, +- path->dentry, ++ path, + attr_name); + + if (rc < 0) { +@@ -2309,8 +2309,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { +- rc = ksmbd_vfs_setxattr(user_ns, +- path->dentry, attr_name, value, ++ rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, +@@ -2374,8 +2373,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path, + return -EBADF; + } + +- rc = ksmbd_vfs_setxattr(user_ns, path->dentry, +- xattr_stream_name, NULL, 0, 0); ++ rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +@@ -2403,7 +2401,7 @@ static int smb2_remove_smb_xattrs(const struct path *path) + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) { +- err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, ++ err = ksmbd_vfs_remove_xattr(user_ns, path, + name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", +@@ -2450,8 +2448,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), +- path->dentry, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); + } +@@ -3025,7 +3022,7 @@ int smb2_open(struct ksmbd_work *work) + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, +- inode, ++ &path, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); +@@ -3041,7 +3038,7 @@ int smb2_open(struct ksmbd_work *work) + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, +- inode); ++ &path); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { +@@ -3081,7 +3078,7 @@ int smb2_open(struct ksmbd_work *work) + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, +- path.dentry, ++ &path, + pntsd, + pntsd_size); + kfree(pntsd); +@@ -5522,7 +5519,7 @@ static int smb2_rename(struct ksmbd_work *work, + goto out; + + rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), +- fp->filp->f_path.dentry, ++ &fp->filp->f_path, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { +@@ -5687,8 +5684,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, +- filp->f_path.dentry, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); +@@ -7558,7 +7554,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, +- fp->filp->f_path.dentry, &da); ++ &fp->filp->f_path, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index 8c041e71cf156..8fe2592c5525f 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -1185,8 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + +- ksmbd_vfs_set_sd_xattr(conn, user_ns, +- path->dentry, pntsd, pntsd_size); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size); + kfree(pntsd); + } + +@@ -1406,7 +1405,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + +- ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); ++ ksmbd_vfs_remove_acl_xattrs(user_ns, path); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, +@@ -1437,9 +1436,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ +- ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); +- ksmbd_vfs_set_sd_xattr(conn, user_ns, +- path->dentry, pntsd, ntsd_len); ++ ksmbd_vfs_remove_sd_xattrs(user_ns, path); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len); + } + + out: +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 6d171f2757f15..e6218c687fa0b 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -171,6 +171,10 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) + return err; + } + ++ err = mnt_want_write(path.mnt); ++ if (err) ++ goto out_err; ++ + mode |= S_IFREG; + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode, true); +@@ -180,6 +184,9 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } ++ mnt_drop_write(path.mnt); ++ ++out_err: + done_path_create(&path, dentry); + return err; + } +@@ -210,30 +217,35 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) + return err; + } + ++ err = mnt_want_write(path.mnt); ++ if (err) ++ goto out_err2; ++ + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); +- if (err) { +- goto out; +- } else if (d_unhashed(dentry)) { ++ if (!err && d_unhashed(dentry)) { + struct dentry *d; + + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); +- goto out; ++ goto out_err1; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; +- goto out; ++ goto out_err1; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +-out: ++ ++out_err1: ++ mnt_drop_write(path.mnt); ++out_err2: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); +@@ -444,7 +456,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, +- fp->filp->f_path.dentry, ++ &fp->filp->f_path, + fp->stream.name, + (void *)stream_buf, + size, +@@ -590,6 +602,10 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + goto out_err; + } + ++ err = mnt_want_write(path->mnt); ++ if (err) ++ goto out_err; ++ + user_ns = mnt_user_ns(path->mnt); + if (S_ISDIR(d_inode(path->dentry)->i_mode)) { + err = vfs_rmdir(user_ns, d_inode(parent), path->dentry); +@@ -600,6 +616,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + if (err) + ksmbd_debug(VFS, "unlink failed, err %d\n", err); + } ++ mnt_drop_write(path->mnt); + + out_err: + ksmbd_revert_fsids(work); +@@ -645,11 +662,16 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + goto out3; + } + ++ err = mnt_want_write(newpath.mnt); ++ if (err) ++ goto out3; ++ + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); ++ mnt_drop_write(newpath.mnt); + + out3: + done_path_create(&newpath, dentry); +@@ -695,6 +717,10 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + goto out2; + } + ++ err = mnt_want_write(old_path->mnt); ++ if (err) ++ goto out2; ++ + trap = lock_rename_child(old_child, new_path.dentry); + + old_parent = dget(old_child->d_parent); +@@ -758,6 +784,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + out3: + dput(old_parent); + unlock_rename(old_parent, new_path.dentry); ++ mnt_drop_write(old_path->mnt); + out2: + path_put(&new_path); + +@@ -898,19 +925,24 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + * Return: 0 on success, otherwise error + */ + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, +- struct dentry *dentry, const char *attr_name, ++ const struct path *path, const char *attr_name, + void *attr_value, size_t attr_size, int flags) + { + int err; + ++ err = mnt_want_write(path->mnt); ++ if (err) ++ return err; ++ + err = vfs_setxattr(user_ns, +- dentry, ++ path->dentry, + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); ++ mnt_drop_write(path->mnt); + return err; + } + +@@ -1014,9 +1046,18 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + } + + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, char *attr_name) ++ const struct path *path, char *attr_name) + { +- return vfs_removexattr(user_ns, dentry, attr_name); ++ int err; ++ ++ err = mnt_want_write(path->mnt); ++ if (err) ++ return err; ++ ++ err = vfs_removexattr(user_ns, path->dentry, attr_name); ++ mnt_drop_write(path->mnt); ++ ++ return err; + } + + int ksmbd_vfs_unlink(struct file *filp) +@@ -1025,6 +1066,10 @@ int ksmbd_vfs_unlink(struct file *filp) + struct dentry *dir, *dentry = filp->f_path.dentry; + struct user_namespace *user_ns = file_mnt_user_ns(filp); + ++ err = mnt_want_write(filp->f_path.mnt); ++ if (err) ++ return err; ++ + dir = dget_parent(dentry); + err = ksmbd_vfs_lock_parent(dir, dentry); + if (err) +@@ -1042,6 +1087,7 @@ int ksmbd_vfs_unlink(struct file *filp) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + out: + dput(dir); ++ mnt_drop_write(filp->f_path.mnt); + + return err; + } +@@ -1245,13 +1291,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + } + + int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry) ++ const struct path *path) + { + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + +- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); ++ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { +@@ -1267,25 +1313,25 @@ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { +- err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); ++ err = ksmbd_vfs_remove_xattr(user_ns, path, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } ++ + out: + kvfree(xattr_list); + return err; + } + +-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry) ++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path) + { + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + +- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); ++ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { +@@ -1298,7 +1344,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { +- err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); ++ err = ksmbd_vfs_remove_xattr(user_ns, path, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } +@@ -1375,13 +1421,14 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac + + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, +- struct dentry *dentry, ++ const struct path *path, + struct smb_ntsd *pntsd, int len) + { + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; ++ struct dentry *dentry = path->dentry; + struct inode *inode = d_inode(dentry); + + acl.version = 4; +@@ -1433,7 +1480,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + goto out; + } + +- rc = ksmbd_vfs_setxattr(user_ns, dentry, ++ rc = ksmbd_vfs_setxattr(user_ns, path, + XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) +@@ -1523,7 +1570,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + } + + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, ++ const struct path *path, + struct xattr_dos_attrib *da) + { + struct ndr n; +@@ -1533,7 +1580,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + if (err) + return err; + +- err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, ++ err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); +@@ -1770,10 +1817,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) + } + + int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, +- struct inode *inode) ++ struct path *path) + { + struct posix_acl_state acl_state; + struct posix_acl *acls; ++ struct inode *inode = d_inode(path->dentry); + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) +@@ -1802,6 +1850,11 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); ++ ++ rc = mnt_want_write(path->mnt); ++ if (rc) ++ goto out_err; ++ + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", +@@ -1814,16 +1867,20 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } ++ mnt_drop_write(path->mnt); ++ ++out_err: + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; + } + + int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, +- struct inode *inode, struct inode *parent_inode) ++ struct path *path, struct inode *parent_inode) + { + struct posix_acl *acls; + struct posix_acl_entry *pace; ++ struct inode *inode = d_inode(path->dentry); + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) +@@ -1841,6 +1898,10 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + } + } + ++ rc = mnt_want_write(path->mnt); ++ if (rc) ++ goto out_err; ++ + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", +@@ -1852,6 +1913,9 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } ++ mnt_drop_write(path->mnt); ++ ++out_err: + posix_acl_release(acls); + return rc; + } +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 7660565ac2770..0a4eb1e1a79a9 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -108,12 +108,12 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, +- struct dentry *dentry, const char *attr_name, ++ const struct path *path, const char *attr_name, + void *attr_value, size_t attr_size, int flags); + int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, char *attr_name); ++ const struct path *path, char *attr_name); + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, + bool caseless); +@@ -139,26 +139,25 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); + int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); + void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); + int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry); +-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry); ++ const struct path *path); ++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path); + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, +- struct dentry *dentry, ++ const struct path *path, + struct smb_ntsd *pntsd, int len); + int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, ++ const struct path *path, + struct xattr_dos_attrib *da); + int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); + int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, +- struct inode *inode); ++ struct path *path); + int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, +- struct inode *inode, ++ struct path *path, + struct inode *parent_inode); + #endif /* __KSMBD_VFS_H__ */ +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index bafb34d4b5bbb..94ad8fa07b46e 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -251,7 +251,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), +- filp->f_path.dentry, ++ &filp->f_path, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-add-support-for-read-compound.patch b/queue-6.1/ksmbd-add-support-for-read-compound.patch new file mode 100644 index 00000000000..7216f252d2f --- /dev/null +++ b/queue-6.1/ksmbd-add-support-for-read-compound.patch @@ -0,0 +1,1945 @@ +From d1ef2a1ad19ba71f9e08268c0be9c49fa26da808 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:54 +0900 +Subject: ksmbd: add support for read compound + +From: Namjae Jeon + +[ Upstream commit e2b76ab8b5c9327ab2dae6da05d0752eb2f4771d ] + +MacOS sends a compound request including read to the server +(e.g. open-read-close). So far, ksmbd has not handled read as +a compound request. For compatibility between ksmbd and an OS that +supports SMB, This patch provides compound support for read requests. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/auth.c | 11 +- + fs/smb/server/connection.c | 55 +--- + fs/smb/server/connection.h | 2 +- + fs/smb/server/ksmbd_work.c | 91 +++++- + fs/smb/server/ksmbd_work.h | 34 ++- + fs/smb/server/oplock.c | 17 +- + fs/smb/server/server.c | 8 +- + fs/smb/server/smb2pdu.c | 510 ++++++++++++++------------------- + fs/smb/server/smb_common.c | 13 +- + fs/smb/server/transport_rdma.c | 4 +- + fs/smb/server/vfs.c | 4 +- + fs/smb/server/vfs.h | 4 +- + 12 files changed, 381 insertions(+), 372 deletions(-) + +diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c +index 15e5684e328c1..229a6527870d0 100644 +--- a/fs/smb/server/auth.c ++++ b/fs/smb/server/auth.c +@@ -1032,11 +1032,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + { + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; +- int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; ++ int i, *nr_entries, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + ++ nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL); ++ if (!nr_entries) ++ return NULL; ++ + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + +@@ -1054,8 +1058,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); +- if (!sg) ++ if (!sg) { ++ kfree(nr_entries); + return NULL; ++ } + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); +@@ -1089,6 +1095,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); ++ kfree(nr_entries); + return sg; + } + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index 00a87b4acd5c5..d1f4ed18a227f 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -125,28 +125,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) + } + } + +-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) ++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) +- return 0; ++ return; + +- if (!work->multiRsp) +- atomic_dec(&conn->req_running); +- if (!work->multiRsp) { +- spin_lock(&conn->request_lock); +- list_del_init(&work->request_entry); +- spin_unlock(&conn->request_lock); +- if (work->asynchronous) +- release_async_work(work); +- ret = 0; +- } ++ atomic_dec(&conn->req_running); ++ spin_lock(&conn->request_lock); ++ list_del_init(&work->request_entry); ++ spin_unlock(&conn->request_lock); ++ if (work->asynchronous) ++ release_async_work(work); + + wake_up_all(&conn->req_running_q); +- return ret; + } + + void ksmbd_conn_lock(struct ksmbd_conn *conn) +@@ -195,41 +189,22 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) + int ksmbd_conn_write(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- size_t len = 0; + int sent; +- struct kvec iov[3]; +- int iov_idx = 0; + + if (!work->response_buf) { + pr_err("NULL response header\n"); + return -EINVAL; + } + +- if (work->tr_buf) { +- iov[iov_idx] = (struct kvec) { work->tr_buf, +- sizeof(struct smb2_transform_hdr) + 4 }; +- len += iov[iov_idx++].iov_len; +- } +- +- if (work->aux_payload_sz) { +- iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; +- len += iov[iov_idx++].iov_len; +- iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; +- len += iov[iov_idx++].iov_len; +- } else { +- if (work->tr_buf) +- iov[iov_idx].iov_len = work->resp_hdr_sz; +- else +- iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; +- iov[iov_idx].iov_base = work->response_buf; +- len += iov[iov_idx++].iov_len; +- } ++ if (work->send_no_response) ++ return 0; + + ksmbd_conn_lock(conn); +- sent = conn->transport->ops->writev(conn->transport, &iov[0], +- iov_idx, len, +- work->need_invalidate_rkey, +- work->remote_key); ++ sent = conn->transport->ops->writev(conn->transport, work->iov, ++ work->iov_cnt, ++ get_rfc1002_len(work->iov[0].iov_base) + 4, ++ work->need_invalidate_rkey, ++ work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { +diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h +index 335fdd714d595..3c005246a32e8 100644 +--- a/fs/smb/server/connection.h ++++ b/fs/smb/server/connection.h +@@ -159,7 +159,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); + void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); ++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); + void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); + int ksmbd_conn_handler_loop(void *p); + int ksmbd_conn_transport_init(void); +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index 14b9caebf7a4f..f49c2e01ea9fc 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -27,18 +27,35 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); ++ INIT_LIST_HEAD(&work->aux_read_list); ++ work->iov_alloc_cnt = 4; ++ work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec), ++ GFP_KERNEL); ++ if (!work->iov) { ++ kmem_cache_free(work_cache, work); ++ work = NULL; ++ } + } + return work; + } + + void ksmbd_free_work_struct(struct ksmbd_work *work) + { ++ struct aux_read *ar, *tmp; ++ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); +- kvfree(work->aux_payload_buf); ++ ++ list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) { ++ kvfree(ar->buf); ++ list_del(&ar->entry); ++ kfree(ar); ++ } ++ + kfree(work->tr_buf); + kvfree(work->request_buf); ++ kfree(work->iov); + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +@@ -77,3 +94,75 @@ bool ksmbd_queue_work(struct ksmbd_work *work) + { + return queue_work(ksmbd_wq, &work->work); + } ++ ++static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib, ++ unsigned int ib_len) ++{ ++ ++ if (work->iov_alloc_cnt <= work->iov_cnt) { ++ struct kvec *new; ++ ++ work->iov_alloc_cnt += 4; ++ new = krealloc(work->iov, ++ sizeof(struct kvec) * work->iov_alloc_cnt, ++ GFP_KERNEL | __GFP_ZERO); ++ if (!new) ++ return -ENOMEM; ++ work->iov = new; ++ } ++ ++ work->iov[++work->iov_idx].iov_base = ib; ++ work->iov[work->iov_idx].iov_len = ib_len; ++ work->iov_cnt++; ++ ++ return 0; ++} ++ ++static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size) ++{ ++ /* Plus rfc_length size on first iov */ ++ if (!work->iov_idx) { ++ work->iov[work->iov_idx].iov_base = work->response_buf; ++ *(__be32 *)work->iov[0].iov_base = 0; ++ work->iov[work->iov_idx].iov_len = 4; ++ work->iov_cnt++; ++ } ++ ++ ksmbd_realloc_iov_pin(work, ib, len); ++ inc_rfc1001_len(work->iov[0].iov_base, len); ++ ++ if (aux_size) { ++ struct aux_read *ar; ++ ++ ksmbd_realloc_iov_pin(work, aux_buf, aux_size); ++ inc_rfc1001_len(work->iov[0].iov_base, aux_size); ++ ++ ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL); ++ if (!ar) ++ return -ENOMEM; ++ ++ ar->buf = aux_buf; ++ list_add(&ar->entry, &work->aux_read_list); ++ } ++ ++ return 0; ++} ++ ++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len) ++{ ++ return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0); ++} ++ ++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size) ++{ ++ return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size); ++} ++ ++void ksmbd_iov_reset(struct ksmbd_work *work) ++{ ++ work->iov_idx = 0; ++ work->iov_cnt = 0; ++ *(__be32 *)work->iov[0].iov_base = 0; ++} +diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h +index f8ae6144c0aea..255157eb26dc4 100644 +--- a/fs/smb/server/ksmbd_work.h ++++ b/fs/smb/server/ksmbd_work.h +@@ -19,6 +19,11 @@ enum { + KSMBD_WORK_CLOSED, + }; + ++struct aux_read { ++ void *buf; ++ struct list_head entry; ++}; ++ + /* one of these for every pending CIFS request at the connection */ + struct ksmbd_work { + /* Server corresponding to this mid */ +@@ -31,13 +36,19 @@ struct ksmbd_work { + /* Response buffer */ + void *response_buf; + +- /* Read data buffer */ +- void *aux_payload_buf; ++ struct list_head aux_read_list; ++ ++ struct kvec *iov; ++ int iov_alloc_cnt; ++ int iov_cnt; ++ int iov_idx; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; ++ /* Current cmd hdr in compound rsp buf*/ ++ int curr_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE +@@ -53,16 +64,11 @@ struct ksmbd_work { + unsigned int credits_granted; + + /* response smb header size */ +- unsigned int resp_hdr_sz; + unsigned int response_sz; +- /* Read data count */ +- unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; +- /* Multiple responses for one request e.g. SMB ECHO */ +- bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ +@@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; + } + ++/** ++ * ksmbd_resp_buf_curr - Get current buffer on compound response. ++ * @work: smb work containing response buffer ++ */ ++static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work) ++{ ++ return work->response_buf + work->curr_smb2_rsp_hdr_off + 4; ++} ++ + /** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer +@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void); + int ksmbd_workqueue_init(void); + void ksmbd_workqueue_destroy(void); + bool ksmbd_queue_work(struct ksmbd_work *work); +- ++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size); ++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len); ++void ksmbd_iov_reset(struct ksmbd_work *work); + #endif /* __KSMBD_WORK_H__ */ +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index c81aee9ce7ec4..4e12e3031bc53 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -639,7 +639,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + { + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); +- struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; +@@ -656,8 +655,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); +@@ -684,13 +681,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + rsp->PersistentFid = fp->persistent_id; + rsp->VolatileFid = fp->volatile_id; + +- inc_rfc1001_len(work->response_buf, 24); ++ ksmbd_fd_put(work, fp); ++ if (ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_oplock_break))) ++ goto out; + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + +- ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + + out: +@@ -751,7 +750,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; +- struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { +@@ -761,8 +759,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); +@@ -791,7 +787,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + +- inc_rfc1001_len(work->response_buf, 44); ++ if (ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_lease_break))) ++ goto out; + + ksmbd_conn_write(work); + +@@ -845,6 +843,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); ++ ksmbd_iov_reset(in_work); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index 0c0db2e614ef6..ff1514c79f162 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -163,6 +163,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + { + u16 command = 0; + int rc; ++ bool is_chained = false; + + if (conn->ops->allocate_rsp_buf(work)) + return; +@@ -229,14 +230,13 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + } + } + ++ is_chained = is_chained_smb2_message(work); ++ + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); +- } while (is_chained_smb2_message(work)); +- +- if (work->send_no_response) +- return; ++ } while (is_chained == true); + + send: + smb3_preauth_hash_rsp(work); +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 687e59cb0c8c8..0a40b793cedf4 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -144,12 +144,18 @@ void smb2_set_err_rsp(struct ksmbd_work *work) + err_rsp = smb2_get_msg(work->response_buf); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { ++ int err; ++ + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; +- inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); ++ err = ksmbd_iov_pin_rsp(work, (void *)err_rsp, ++ work->conn->vals->header_size + ++ SMB2_ERROR_STRUCTURE_SIZE2); ++ if (err) ++ work->send_no_response = 1; + } + } + +@@ -244,9 +250,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; +- +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); ++ int err; + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +@@ -285,13 +289,14 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); +- inc_rfc1001_len(work->response_buf, +- sizeof(struct smb2_negotiate_rsp) - +- sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + +- AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; ++ err = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_negotiate_rsp) - ++ sizeof(rsp->Buffer) + AUTH_GSS_LENGTH); ++ if (err) ++ return err; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(conn); +@@ -390,11 +395,12 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); +- inc_rfc1001_len(work->response_buf, +- sizeof(struct smb2_hdr) + new_len - len); ++ work->iov[work->iov_idx].iov_len += (new_len - len); ++ inc_rfc1001_len(work->response_buf, new_len - len); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; ++ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", +@@ -470,10 +476,10 @@ bool is_chained_smb2_message(struct ksmbd_work *work) + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); ++ work->iov[work->iov_idx].iov_len += len; + inc_rfc1001_len(work->response_buf, len); +- if (work->aux_payload_sz) +- work->aux_payload_sz += len; + } ++ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; + } + return false; + } +@@ -488,11 +494,8 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) + { + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); +- struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; +@@ -657,7 +660,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + struct ksmbd_conn *conn = work->conn; + int id; + +- rsp_hdr = smb2_get_msg(work->response_buf); ++ rsp_hdr = ksmbd_resp_buf_next(work); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(&conn->async_ida); +@@ -707,14 +710,12 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) + { + struct smb2_hdr *rsp_hdr; + +- rsp_hdr = smb2_get_msg(work->response_buf); ++ rsp_hdr = ksmbd_resp_buf_next(work); + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + +- work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; +- work->multiRsp = 0; + } + + static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +@@ -821,9 +822,8 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) + pneg_ctxt->Name[15] = 0x7C; + } + +-static void assemble_neg_contexts(struct ksmbd_conn *conn, +- struct smb2_negotiate_rsp *rsp, +- void *smb2_buf_len) ++static unsigned int assemble_neg_contexts(struct ksmbd_conn *conn, ++ struct smb2_negotiate_rsp *rsp) + { + char * const pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); +@@ -834,7 +834,6 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); +- inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + + if (conn->cipher_type) { +@@ -874,7 +873,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + } + + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); +- inc_rfc1001_len(smb2_buf_len, ctxt_size); ++ return ctxt_size + AUTH_GSS_PADDING; + } + + static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, +@@ -1090,7 +1089,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + int rc = 0; +- unsigned int smb2_buf_len, smb2_neg_size; ++ unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); +@@ -1183,7 +1182,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); +- assemble_neg_contexts(conn, rsp, work->response_buf); ++ neg_ctxt_len = assemble_neg_contexts(conn, rsp); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); +@@ -1233,9 +1232,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); +- inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - +- sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + +- AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + +@@ -1253,9 +1249,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + ksmbd_conn_set_need_negotiate(conn); + + err_out: ++ if (rc) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ ++ if (!rc) ++ rc = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_negotiate_rsp) - ++ sizeof(rsp->Buffer) + ++ AUTH_GSS_LENGTH + neg_ctxt_len); + if (rc < 0) + smb2_set_err_rsp(work); +- + return rc; + } + +@@ -1455,7 +1458,6 @@ static int ntlm_authenticate(struct ksmbd_work *work, + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); +- inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); + } + + user = session_user(conn, req); +@@ -1601,7 +1603,6 @@ static int krb5_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); +- inc_rfc1001_len(work->response_buf, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) +@@ -1673,7 +1674,6 @@ int smb2_sess_setup(struct ksmbd_work *work) + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; +- inc_rfc1001_len(work->response_buf, 9); + + ksmbd_conn_lock(conn); + if (!req->hdr.SessionId) { +@@ -1809,13 +1809,6 @@ int smb2_sess_setup(struct ksmbd_work *work) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; +- /* +- * Note: here total size -1 is done as an +- * adjustment for 0 size blob +- */ +- inc_rfc1001_len(work->response_buf, +- le16_to_cpu(rsp->SecurityBufferLength) - 1); +- + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work, req, rsp); + if (rc) +@@ -1900,6 +1893,17 @@ int smb2_sess_setup(struct ksmbd_work *work) + ksmbd_conn_set_need_negotiate(conn); + } + } ++ } else { ++ unsigned int iov_len; ++ ++ if (rsp->SecurityBufferLength) ++ iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) + ++ le16_to_cpu(rsp->SecurityBufferLength); ++ else ++ iov_len = sizeof(struct smb2_sess_setup_rsp); ++ rc = ksmbd_iov_pin_rsp(work, rsp, iov_len); ++ if (rc) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + } + + ksmbd_conn_unlock(conn); +@@ -1978,13 +1982,16 @@ int smb2_tree_connect(struct ksmbd_work *work) + status.tree_conn->posix_extensions = true; + + rsp->StructureSize = cpu_to_le16(16); +- inc_rfc1001_len(work->response_buf, 16); + out_err1: + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + ++ rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp)); ++ if (rc) ++ status.ret = KSMBD_TREE_CONN_STATUS_NOMEM; ++ + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) +@@ -2097,20 +2104,27 @@ int smb2_tree_disconnect(struct ksmbd_work *work) + struct smb2_tree_disconnect_req *req; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; ++ int err; + + WORK_BUFFERS(work, req, rsp); + +- rsp->StructureSize = cpu_to_le16(4); +- inc_rfc1001_len(work->response_buf, 4); +- + ksmbd_debug(SMB, "request\n"); + ++ rsp->StructureSize = cpu_to_le16(4); ++ err = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_tree_disconnect_rsp)); ++ if (err) { ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ smb2_set_err_rsp(work); ++ return err; ++ } ++ + if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); +- return 0; ++ return -ENOENT; + } + + ksmbd_close_tree_conn_fds(work); +@@ -2132,15 +2146,21 @@ int smb2_session_logoff(struct ksmbd_work *work) + struct smb2_logoff_rsp *rsp; + struct ksmbd_session *sess; + u64 sess_id; ++ int err; + + WORK_BUFFERS(work, req, rsp); + ++ ksmbd_debug(SMB, "request\n"); ++ + sess_id = le64_to_cpu(req->hdr.SessionId); + + rsp->StructureSize = cpu_to_le16(4); +- inc_rfc1001_len(work->response_buf, 4); +- +- ksmbd_debug(SMB, "request\n"); ++ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); ++ if (err) { ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ smb2_set_err_rsp(work); ++ return err; ++ } + + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); + ksmbd_close_session_fds(work); +@@ -2155,7 +2175,7 @@ int smb2_session_logoff(struct ksmbd_work *work) + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); +- return 0; ++ return -ENOENT; + } + + ksmbd_destroy_file_table(&sess->file_table); +@@ -2216,7 +2236,10 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + +- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ ++ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer)); ++ if (err) ++ goto out; ++ + kfree(name); + return 0; + +@@ -2598,6 +2621,7 @@ int smb2_open(struct ksmbd_work *work) + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; ++ int iov_len = 0; + + WORK_BUFFERS(work, req, rsp); + +@@ -3249,7 +3273,7 @@ int smb2_open(struct ksmbd_work *work) + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; +- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ ++ iov_len = offsetof(struct smb2_create_rsp, Buffer); + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { +@@ -3264,8 +3288,7 @@ int smb2_open(struct ksmbd_work *work) + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_lease_size); ++ iov_len += conn->vals->create_lease_size; + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } +@@ -3285,8 +3308,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_mxac_size); ++ iov_len += conn->vals->create_mxac_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; +@@ -3304,8 +3326,7 @@ int smb2_open(struct ksmbd_work *work) + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_disk_id_size); ++ iov_len += conn->vals->create_disk_id_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; +@@ -3319,8 +3340,7 @@ int smb2_open(struct ksmbd_work *work) + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_posix_size); ++ iov_len += conn->vals->create_posix_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } +@@ -3338,7 +3358,8 @@ int smb2_open(struct ksmbd_work *work) + } + ksmbd_revert_fsids(work); + err_out1: +- ++ if (!rc) ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; +@@ -4064,7 +4085,10 @@ int smb2_query_dir(struct ksmbd_work *work) + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; +- inc_rfc1001_len(work->response_buf, 9); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_query_directory_rsp)); ++ if (rc) ++ goto err_out; + } else { + no_buf_len: + ((struct file_directory_info *) +@@ -4076,7 +4100,11 @@ int smb2_query_dir(struct ksmbd_work *work) + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); +- inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_query_directory_rsp, Buffer) + ++ d_info.data_count); ++ if (rc) ++ goto err_out; + } + + kfree(srch_ptr); +@@ -4123,21 +4151,13 @@ int smb2_query_dir(struct ksmbd_work *work) + */ + static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, +- void *rsp_org, int infoclass_size) ++ void *rsp_org) + { + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { +- if (reqOutputBufferLength < infoclass_size) { +- pr_err("Invalid Buffer Size Requested\n"); +- rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; +- *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); +- return -EINVAL; +- } +- +- ksmbd_debug(SMB, "Buffer Overflow\n"); +- rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; +- *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + +- reqOutputBufferLength); +- rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); ++ pr_err("Invalid Buffer Size Requested\n"); ++ rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; ++ *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); ++ return -EINVAL; + } + return 0; + } +@@ -4156,7 +4176,6 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); + } + + static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, +@@ -4170,7 +4189,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); + } + + static int smb2_get_info_file_pipe(struct ksmbd_session *sess, +@@ -4196,14 +4214,12 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, rsp_org, +- FILE_STANDARD_INFORMATION_SIZE); ++ rsp, rsp_org); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, rsp_org, +- FILE_INTERNAL_INFORMATION_SIZE); ++ rsp, rsp_org); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", +@@ -4371,7 +4387,6 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); +- inc_rfc1001_len(rsp_org, rsp_data_cnt); + out: + kvfree(xattr_list); + return rc; +@@ -4386,7 +4401,6 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); + } + + static int get_file_basic_info(struct smb2_query_info_rsp *rsp, +@@ -4416,7 +4430,6 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; + } + +@@ -4441,8 +4454,6 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); +- inc_rfc1001_len(rsp_org, +- sizeof(struct smb2_file_standard_info)); + } + + static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, +@@ -4454,8 +4465,6 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); +- inc_rfc1001_len(rsp_org, +- sizeof(struct smb2_file_alignment_info)); + } + + static int get_file_all_info(struct ksmbd_work *work, +@@ -4519,7 +4528,6 @@ static int get_file_all_info(struct ksmbd_work *work, + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); +- inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; + } + +@@ -4542,7 +4550,6 @@ static void get_file_alternate_info(struct ksmbd_work *work, + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); +- inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + } + + static void get_file_stream_info(struct ksmbd_work *work, +@@ -4642,7 +4649,6 @@ static void get_file_stream_info(struct ksmbd_work *work, + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); +- inc_rfc1001_len(rsp_org, nbytes); + } + + static void get_file_internal_info(struct smb2_query_info_rsp *rsp, +@@ -4657,7 +4663,6 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); + } + + static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, +@@ -4693,7 +4698,6 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; + } + +@@ -4705,7 +4709,6 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); + } + + static void get_file_position_info(struct smb2_query_info_rsp *rsp, +@@ -4717,7 +4720,6 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); + } + + static void get_file_mode_info(struct smb2_query_info_rsp *rsp, +@@ -4729,7 +4731,6 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); + } + + static void get_file_compression_info(struct smb2_query_info_rsp *rsp, +@@ -4751,7 +4752,6 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); + } + + static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, +@@ -4770,11 +4770,10 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; + } + +-static int find_file_posix_info(struct smb2_query_info_rsp *rsp, ++static void find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) + { + struct smb311_posix_qinfo *file_info; +@@ -4812,8 +4811,6 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); + + rsp->OutputBufferLength = cpu_to_le32(out_buf_len); +- inc_rfc1001_len(rsp_org, out_buf_len); +- return out_buf_len; + } + + static int smb2_get_info_file(struct ksmbd_work *work, +@@ -4823,7 +4820,6 @@ static int smb2_get_info_file(struct ksmbd_work *work, + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; +- int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, +@@ -4856,85 +4852,69 @@ static int smb2_get_info_file(struct ksmbd_work *work, + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, work->response_buf); +- file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, work->response_buf); +- file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); +- file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { +- file_infoclass_size = find_file_posix_info(rsp, fp, +- work->response_buf); ++ find_file_posix_info(rsp, fp, work->response_buf); + } + break; + default: +@@ -4944,8 +4924,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, work->response_buf, +- file_infoclass_size); ++ rsp, work->response_buf); + ksmbd_fd_put(work, fp); + return rc; + } +@@ -4961,7 +4940,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct kstatfs stfs; + struct path path; + int rc = 0, len; +- int fs_infoclass_size = 0; + + if (!share->path) + return -EIO; +@@ -4991,8 +4969,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); +- inc_rfc1001_len(work->response_buf, 8); +- fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: +@@ -5021,8 +4997,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); +- inc_rfc1001_len(work->response_buf, sz); +- fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: +@@ -5049,8 +5023,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); +- inc_rfc1001_len(work->response_buf, sz); +- fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: +@@ -5063,8 +5035,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); +- inc_rfc1001_len(work->response_buf, 24); +- fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: +@@ -5080,8 +5050,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); +- inc_rfc1001_len(work->response_buf, 32); +- fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: +@@ -5101,8 +5069,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); +- inc_rfc1001_len(work->response_buf, 64); +- fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: +@@ -5124,8 +5090,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); +- inc_rfc1001_len(work->response_buf, 28); +- fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: +@@ -5146,8 +5110,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); +- inc_rfc1001_len(work->response_buf, 48); +- fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: +@@ -5167,8 +5129,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); +- inc_rfc1001_len(work->response_buf, 56); +- fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } +@@ -5177,8 +5137,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, work->response_buf, +- fs_infoclass_size); ++ rsp, work->response_buf); + path_put(&path); + return rc; + } +@@ -5212,7 +5171,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); +- inc_rfc1001_len(work->response_buf, secdesclen); + + return 0; + } +@@ -5257,7 +5215,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); +- inc_rfc1001_len(work->response_buf, secdesclen); + return 0; + } + +@@ -5296,6 +5253,14 @@ int smb2_query_info(struct ksmbd_work *work) + rc = -EOPNOTSUPP; + } + ++ if (!rc) { ++ rsp->StructureSize = cpu_to_le16(9); ++ rsp->OutputBufferOffset = cpu_to_le16(72); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_query_info_rsp, Buffer) + ++ le32_to_cpu(rsp->OutputBufferLength)); ++ } ++ + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; +@@ -5303,6 +5268,8 @@ int smb2_query_info(struct ksmbd_work *work) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; ++ else if (rc == -ENOMEM) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); +@@ -5311,9 +5278,6 @@ int smb2_query_info(struct ksmbd_work *work) + rc); + return rc; + } +- rsp->StructureSize = cpu_to_le16(9); +- rsp->OutputBufferOffset = cpu_to_le16(72); +- inc_rfc1001_len(work->response_buf, 8); + return 0; + } + +@@ -5344,8 +5308,9 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; +- inc_rfc1001_len(work->response_buf, 60); +- return 0; ++ ++ return ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_close_rsp)); + } + + /** +@@ -5450,15 +5415,17 @@ int smb2_close(struct ksmbd_work *work) + + err = ksmbd_close_fd(work, volatile_id); + out: ++ if (!err) ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_close_rsp)); ++ + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); +- } else { +- inc_rfc1001_len(work->response_buf, 60); + } + +- return 0; ++ return err; + } + + /** +@@ -5476,8 +5443,7 @@ int smb2_echo(struct ksmbd_work *work) + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); +- return 0; ++ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp)); + } + + static int smb2_rename(struct ksmbd_work *work, +@@ -6069,7 +6035,10 @@ int smb2_set_info(struct ksmbd_work *work) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); +- inc_rfc1001_len(work->response_buf, 2); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_set_info_rsp)); ++ if (rc) ++ goto err_out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6116,28 +6085,36 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + + id = req->VolatileFileId; + +- inc_rfc1001_len(work->response_buf, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { ++ void *aux_payload_buf; ++ + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + +- work->aux_payload_buf = ++ aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); +- if (!work->aux_payload_buf) { ++ if (!aux_payload_buf) { + err = -ENOMEM; + goto out; + } + +- memcpy(work->aux_payload_buf, rpc_resp->payload, +- rpc_resp->payload_sz); ++ memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; +- work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; +- work->aux_payload_sz = nbytes; + kvfree(rpc_resp); ++ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer), ++ aux_payload_buf, nbytes); ++ if (err) ++ goto out; ++ } else { ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer)); ++ if (err) ++ goto out; + } + + rsp->StructureSize = cpu_to_le16(17); +@@ -6146,7 +6123,6 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Flags = 0; +- inc_rfc1001_len(work->response_buf, nbytes); + return 0; + + out: +@@ -6220,13 +6196,8 @@ int smb2_read(struct ksmbd_work *work) + int err = 0; + bool is_rdma_channel = false; + unsigned int max_read_size = conn->vals->max_read_size; +- +- WORK_BUFFERS(work, req, rsp); +- if (work->next_smb2_rcv_hdr_off) { +- work->send_no_response = 1; +- err = -EOPNOTSUPP; +- goto out; +- } ++ unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ++ void *aux_payload_buf; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { +@@ -6234,6 +6205,25 @@ int smb2_read(struct ksmbd_work *work) + return smb2_read_pipe(work); + } + ++ if (work->next_smb2_rcv_hdr_off) { ++ req = ksmbd_req_buf_next(work); ++ rsp = ksmbd_resp_buf_next(work); ++ if (!has_file_id(req->VolatileFileId)) { ++ ksmbd_debug(SMB, "Compound request set FID = %llu\n", ++ work->compound_fid); ++ id = work->compound_fid; ++ pid = work->compound_pfid; ++ } ++ } else { ++ req = smb2_get_msg(work->request_buf); ++ rsp = smb2_get_msg(work->response_buf); ++ } ++ ++ if (!has_file_id(id)) { ++ id = req->VolatileFileId; ++ pid = req->PersistentFileId; ++ } ++ + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + is_rdma_channel = true; +@@ -6256,7 +6246,7 @@ int smb2_read(struct ksmbd_work *work) + goto out; + } + +- fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); ++ fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + err = -ENOENT; + goto out; +@@ -6282,21 +6272,20 @@ int smb2_read(struct ksmbd_work *work) + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + +- work->aux_payload_buf = kvzalloc(length, GFP_KERNEL); +- if (!work->aux_payload_buf) { ++ aux_payload_buf = kvzalloc(length, GFP_KERNEL); ++ if (!aux_payload_buf) { + err = -ENOMEM; + goto out; + } + +- nbytes = ksmbd_vfs_read(work, fp, length, &offset); ++ nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { +- kvfree(work->aux_payload_buf); +- work->aux_payload_buf = NULL; ++ kvfree(aux_payload_buf); + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); +@@ -6309,10 +6298,9 @@ int smb2_read(struct ksmbd_work *work) + if (is_rdma_channel == true) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, +- work->aux_payload_buf, ++ aux_payload_buf, + nbytes); +- kvfree(work->aux_payload_buf); +- work->aux_payload_buf = NULL; ++ kvfree(aux_payload_buf); + + nbytes = 0; + if (remain_bytes < 0) { +@@ -6327,10 +6315,11 @@ int smb2_read(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Flags = 0; +- inc_rfc1001_len(work->response_buf, 16); +- work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; +- work->aux_payload_sz = nbytes; +- inc_rfc1001_len(work->response_buf, nbytes); ++ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer), ++ aux_payload_buf, nbytes); ++ if (err) ++ goto out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6413,8 +6402,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; +- inc_rfc1001_len(work->response_buf, 16); +- return 0; ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_write_rsp, Buffer)); + out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; +@@ -6570,7 +6559,9 @@ int smb2_write(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; +- inc_rfc1001_len(work->response_buf, 16); ++ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); ++ if (err) ++ goto out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6617,15 +6608,11 @@ int smb2_flush(struct ksmbd_work *work) + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); +- return 0; ++ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp)); + + out: +- if (err) { +- rsp->hdr.Status = STATUS_INVALID_HANDLE; +- smb2_set_err_rsp(work); +- } +- ++ rsp->hdr.Status = STATUS_INVALID_HANDLE; ++ smb2_set_err_rsp(work); + return err; + } + +@@ -7063,6 +7050,8 @@ int smb2_lock(struct ksmbd_work *work) + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + ++ ksmbd_iov_reset(work); ++ + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); +@@ -7081,7 +7070,6 @@ int smb2_lock(struct ksmbd_work *work) + } + + init_smb2_rsp_hdr(work); +- smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); +@@ -7116,7 +7104,10 @@ int smb2_lock(struct ksmbd_work *work) + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); ++ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp)); ++ if (err) ++ goto out; ++ + ksmbd_fd_put(work, fp); + return 0; + +@@ -7912,9 +7903,9 @@ int smb2_ioctl(struct ksmbd_work *work) + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); +- inc_rfc1001_len(work->response_buf, 48 + nbytes); +- +- return 0; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes); ++ if (!ret) ++ return ret; + + out: + if (ret == -EACCES) +@@ -8049,8 +8040,9 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) + rsp->Reserved2 = 0; + rsp->VolatileFid = volatile_id; + rsp->PersistentFid = persistent_id; +- inc_rfc1001_len(work->response_buf, 24); +- return; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); ++ if (!ret) ++ return; + + err_out: + opinfo->op_state = OPLOCK_STATE_NONE; +@@ -8200,8 +8192,9 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; +- inc_rfc1001_len(work->response_buf, 36); +- return; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); ++ if (!ret) ++ return; + + err_out: + opinfo->op_state = OPLOCK_STATE_NONE; +@@ -8339,43 +8332,19 @@ int smb2_check_sign_req(struct ksmbd_work *work) + void smb2_set_sign_rsp(struct ksmbd_work *work) + { + struct smb2_hdr *hdr; +- struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; +- struct kvec iov[2]; +- size_t len; ++ struct kvec *iov; + int n_vec = 1; + +- hdr = smb2_get_msg(work->response_buf); +- if (work->next_smb2_rsp_hdr_off) +- hdr = ksmbd_resp_buf_next(work); +- +- req_hdr = ksmbd_req_buf_next(work); +- +- if (!work->next_smb2_rsp_hdr_off) { +- len = get_rfc1002_len(work->response_buf); +- if (req_hdr->NextCommand) +- len = ALIGN(len, 8); +- } else { +- len = get_rfc1002_len(work->response_buf) - +- work->next_smb2_rsp_hdr_off; +- len = ALIGN(len, 8); +- } +- +- if (req_hdr->NextCommand) +- hdr->NextCommand = cpu_to_le32(len); +- ++ hdr = ksmbd_resp_buf_curr(work); + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + +- iov[0].iov_base = (char *)&hdr->ProtocolId; +- iov[0].iov_len = len; +- +- if (work->aux_payload_sz) { +- iov[0].iov_len -= work->aux_payload_sz; +- +- iov[1].iov_base = work->aux_payload_buf; +- iov[1].iov_len = work->aux_payload_sz; ++ if (hdr->Command == SMB2_READ) { ++ iov = &work->iov[work->iov_idx - 1]; + n_vec++; ++ } else { ++ iov = &work->iov[work->iov_idx]; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, +@@ -8451,29 +8420,14 @@ int smb3_check_sign_req(struct ksmbd_work *work) + void smb3_set_sign_rsp(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- struct smb2_hdr *req_hdr, *hdr; ++ struct smb2_hdr *hdr; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; +- struct kvec iov[2]; ++ struct kvec *iov; + int n_vec = 1; +- size_t len; + char *signing_key; + +- hdr = smb2_get_msg(work->response_buf); +- if (work->next_smb2_rsp_hdr_off) +- hdr = ksmbd_resp_buf_next(work); +- +- req_hdr = ksmbd_req_buf_next(work); +- +- if (!work->next_smb2_rsp_hdr_off) { +- len = get_rfc1002_len(work->response_buf); +- if (req_hdr->NextCommand) +- len = ALIGN(len, 8); +- } else { +- len = get_rfc1002_len(work->response_buf) - +- work->next_smb2_rsp_hdr_off; +- len = ALIGN(len, 8); +- } ++ hdr = ksmbd_resp_buf_curr(work); + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { +@@ -8489,21 +8443,18 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) + if (!signing_key) + return; + +- if (req_hdr->NextCommand) +- hdr->NextCommand = cpu_to_le32(len); +- + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); +- iov[0].iov_base = (char *)&hdr->ProtocolId; +- iov[0].iov_len = len; +- if (work->aux_payload_sz) { +- iov[0].iov_len -= work->aux_payload_sz; +- iov[1].iov_base = work->aux_payload_buf; +- iov[1].iov_len = work->aux_payload_sz; ++ ++ if (hdr->Command == SMB2_READ) { ++ iov = &work->iov[work->iov_idx - 1]; + n_vec++; ++ } else { ++ iov = &work->iov[work->iov_idx]; + } + +- if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) ++ if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, ++ signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + } + +@@ -8570,45 +8521,22 @@ static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) + + int smb3_encrypt_resp(struct ksmbd_work *work) + { +- char *buf = work->response_buf; +- struct kvec iov[3]; ++ struct kvec *iov = work->iov; + int rc = -ENOMEM; +- int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); ++ void *tr_buf; + +- if (ARRAY_SIZE(iov) < rq_nvec) +- return -ENOMEM; +- +- work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); +- if (!work->tr_buf) ++ tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); ++ if (!tr_buf) + return rc; + + /* fill transform header */ +- fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); ++ fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type); + +- iov[0].iov_base = work->tr_buf; ++ iov[0].iov_base = tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; +- buf_size += iov[0].iov_len - 4; +- +- iov[1].iov_base = buf + 4; +- iov[1].iov_len = get_rfc1002_len(buf); +- if (work->aux_payload_sz) { +- iov[1].iov_len = work->resp_hdr_sz - 4; +- +- iov[2].iov_base = work->aux_payload_buf; +- iov[2].iov_len = work->aux_payload_sz; +- buf_size += iov[2].iov_len; +- } +- buf_size += iov[1].iov_len; +- work->resp_hdr_sz = iov[1].iov_len; ++ work->tr_buf = tr_buf; + +- rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); +- if (rc) +- return rc; +- +- memmove(buf, iov[1].iov_base, iov[1].iov_len); +- *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); +- +- return rc; ++ return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1); + } + + bool smb3_is_transform_hdr(void *buf) +diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c +index b6f414a2404b1..d160363c09ebc 100644 +--- a/fs/smb/server/smb_common.c ++++ b/fs/smb/server/smb_common.c +@@ -319,12 +319,6 @@ static int init_smb1_rsp_hdr(struct ksmbd_work *work) + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + +- /* +- * Remove 4 byte direct TCP header. +- */ +- *(__be32 *)work->response_buf = +- cpu_to_be32(sizeof(struct smb_hdr) - 4); +- + rsp_hdr->Command = SMB_COM_NEGOTIATE; + *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; + rsp_hdr->Flags = SMBFLG_RESPONSE; +@@ -571,10 +565,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work) + + ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); + +- /* Add 2 byte bcc and 2 byte DialectIndex. */ +- inc_rfc1001_len(work->response_buf, 4); +- neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; ++ if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp, ++ sizeof(struct smb_negotiate_rsp) - 4)) ++ return -ENOMEM; + ++ neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + neg_rsp->hdr.WordCount = 1; + neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); + neg_rsp->ByteCount = 0; +diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c +index 7578200f63b1d..3b269e1f523a1 100644 +--- a/fs/smb/server/transport_rdma.c ++++ b/fs/smb/server/transport_rdma.c +@@ -1241,14 +1241,12 @@ static int smb_direct_writev(struct ksmbd_transport *t, + + //FIXME: skip RFC1002 header.. + buflen -= 4; +- iov[0].iov_base += 4; +- iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); +- start = i = 0; ++ start = i = 1; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 1752a6c10bcc9..6f54ea1df0c58 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -368,15 +368,15 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end, + * @fid: file id of open file + * @count: read byte count + * @pos: file pos ++ * @rbuf: read data buffer + * + * Return: number of read bytes on success, otherwise error + */ + int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, +- loff_t *pos) ++ loff_t *pos, char *rbuf) + { + struct file *filp = fp->filp; + ssize_t nbytes = 0; +- char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index a7cc0aad6d576..93799ca4cc345 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -76,8 +76,8 @@ void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); + int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); + int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +-int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, +- size_t count, loff_t *pos); ++int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, ++ loff_t *pos, char *rbuf); + int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch b/queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch new file mode 100644 index 00000000000..0ddbbbd321d --- /dev/null +++ b/queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch @@ -0,0 +1,297 @@ +From 1470f82a7a7b46ab6829e55071e1cad278e5b6cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:16 +0900 +Subject: ksmbd: add support for surrogate pair conversion + +From: Namjae Jeon + +[ Upstream commit 0c180317c654a494fe429adbf7bc9b0793caf9e2 ] + +ksmbd is missing supporting to convert filename included surrogate pair +characters. It triggers a "file or folder does not exist" error in +Windows client. + +[Steps to Reproduce for bug] +1. Create surrogate pair file + touch $(echo -e '\xf0\x9d\x9f\xa3') + touch $(echo -e '\xf0\x9d\x9f\xa4') + +2. Try to open these files in ksmbd share through Windows client. + +This patch update unicode functions not to consider about surrogate pair +(and IVS). + +Reviewed-by: Marios Makassikis +Tested-by: Marios Makassikis +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/unicode.c | 187 +++++++++++++++++++++++++++++----------- + 1 file changed, 138 insertions(+), 49 deletions(-) + +diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c +index 9ae676906ed39..33fc6d45c0f38 100644 +--- a/fs/smb/server/unicode.c ++++ b/fs/smb/server/unicode.c +@@ -14,46 +14,10 @@ + #include "uniupr.h" + #include "smb_common.h" + +-/* +- * smb_utf16_bytes() - how long will a string be after conversion? +- * @from: pointer to input string +- * @maxbytes: don't go past this many bytes of input string +- * @codepage: destination codepage +- * +- * Walk a utf16le string and return the number of bytes that the string will +- * be after being converted to the given charset, not including any null +- * termination required. Don't walk past maxbytes in the source buffer. +- * +- * Return: string length after conversion +- */ +-static int smb_utf16_bytes(const __le16 *from, int maxbytes, +- const struct nls_table *codepage) +-{ +- int i; +- int charlen, outlen = 0; +- int maxwords = maxbytes / 2; +- char tmp[NLS_MAX_CHARSET_SIZE]; +- __u16 ftmp; +- +- for (i = 0; i < maxwords; i++) { +- ftmp = get_unaligned_le16(&from[i]); +- if (ftmp == 0) +- break; +- +- charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); +- if (charlen > 0) +- outlen += charlen; +- else +- outlen++; +- } +- +- return outlen; +-} +- + /* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied +- * @src_char: 2 byte host-endian source character ++ * @from: host-endian source string + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * +@@ -64,10 +28,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes, + * Return: string length after conversion + */ + static int +-cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, ++cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, + bool mapchar) + { + int len = 1; ++ __u16 src_char; ++ ++ src_char = *from; + + if (!mapchar) + goto cp_convert; +@@ -105,12 +72,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + + cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); +- if (len <= 0) { +- *target = '?'; +- len = 1; +- } ++ if (len <= 0) ++ goto surrogate_pair; + + goto out; ++ ++surrogate_pair: ++ /* convert SURROGATE_PAIR and IVS */ ++ if (strcmp(cp->charset, "utf8")) ++ goto unknown; ++ len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); ++ if (len <= 0) ++ goto unknown; ++ return len; ++ ++unknown: ++ *target = '?'; ++ len = 1; ++ goto out; ++} ++ ++/* ++ * smb_utf16_bytes() - compute converted string length ++ * @from: pointer to input string ++ * @maxbytes: input string length ++ * @codepage: destination codepage ++ * ++ * Walk a utf16le string and return the number of bytes that the string will ++ * be after being converted to the given charset, not including any null ++ * termination required. Don't walk past maxbytes in the source buffer. ++ * ++ * Return: string length after conversion ++ */ ++static int smb_utf16_bytes(const __le16 *from, int maxbytes, ++ const struct nls_table *codepage) ++{ ++ int i, j; ++ int charlen, outlen = 0; ++ int maxwords = maxbytes / 2; ++ char tmp[NLS_MAX_CHARSET_SIZE]; ++ __u16 ftmp[3]; ++ ++ for (i = 0; i < maxwords; i++) { ++ ftmp[0] = get_unaligned_le16(&from[i]); ++ if (ftmp[0] == 0) ++ break; ++ for (j = 1; j <= 2; j++) { ++ if (i + j < maxwords) ++ ftmp[j] = get_unaligned_le16(&from[i + j]); ++ else ++ ftmp[j] = 0; ++ } ++ ++ charlen = cifs_mapchar(tmp, ftmp, codepage, 0); ++ if (charlen > 0) ++ outlen += charlen; ++ else ++ outlen++; ++ } ++ ++ return outlen; + } + + /* +@@ -140,12 +161,12 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) + { +- int i, charlen, safelen; ++ int i, j, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; +- __u16 ftmp; ++ __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ + + /* + * because the chars can be of varying widths, we need to take care +@@ -156,9 +177,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { +- ftmp = get_unaligned_le16(&from[i]); +- if (ftmp == 0) ++ ftmp[0] = get_unaligned_le16(&from[i]); ++ if (ftmp[0] == 0) + break; ++ for (j = 1; j <= 2; j++) { ++ if (i + j < fromwords) ++ ftmp[j] = get_unaligned_le16(&from[i + j]); ++ else ++ ftmp[j] = 0; ++ } + + /* + * check to see if converting this character might make the +@@ -173,6 +200,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; ++ ++ /* ++ * charlen (=bytes of UTF-8 for 1 character) ++ * 4bytes UTF-8(surrogate pair) is charlen=4 ++ * (4bytes UTF-16 code) ++ * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 ++ * (2 UTF-8 pairs divided to 2 UTF-16 pairs) ++ */ ++ if (charlen == 4) ++ i++; ++ else if (charlen >= 5) ++ /* 5-6bytes UTF-8 */ ++ i += 2; + } + + /* properly null-terminate string */ +@@ -307,6 +347,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + char src_char; + __le16 dst_char; + wchar_t tmp; ++ wchar_t wchar_to[6]; /* UTF-16 */ ++ int ret; ++ unicode_t u; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); +@@ -349,11 +392,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + * if no match, use question mark, which at least in + * some cases serves as wild card + */ +- if (charlen < 1) { +- dst_char = cpu_to_le16(0x003f); +- charlen = 1; ++ if (charlen > 0) ++ goto ctoUTF16; ++ ++ /* convert SURROGATE_PAIR */ ++ if (strcmp(cp->charset, "utf8")) ++ goto unknown; ++ if (*(source + i) & 0x80) { ++ charlen = utf8_to_utf32(source + i, 6, &u); ++ if (charlen < 0) ++ goto unknown; ++ } else ++ goto unknown; ++ ret = utf8s_to_utf16s(source + i, charlen, ++ UTF16_LITTLE_ENDIAN, ++ wchar_to, 6); ++ if (ret < 0) ++ goto unknown; ++ ++ i += charlen; ++ dst_char = cpu_to_le16(*wchar_to); ++ if (charlen <= 3) ++ /* 1-3bytes UTF-8 to 2bytes UTF-16 */ ++ put_unaligned(dst_char, &target[j]); ++ else if (charlen == 4) { ++ /* ++ * 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 ++ * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 ++ * (charlen=3+4 or 4+4) ++ */ ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 1)); ++ j++; ++ put_unaligned(dst_char, &target[j]); ++ } else if (charlen >= 5) { ++ /* 5-6bytes UTF-8 to 6bytes UTF-16 */ ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 1)); ++ j++; ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 2)); ++ j++; ++ put_unaligned(dst_char, &target[j]); + } ++ continue; ++ ++unknown: ++ dst_char = cpu_to_le16(0x003f); ++ charlen = 1; + } ++ ++ctoUTF16: + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch b/queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch new file mode 100644 index 00000000000..addae6c9c1f --- /dev/null +++ b/queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch @@ -0,0 +1,110 @@ +From 4924fc112ba07a4b6bda253325d9fcae7447c09b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:34 +0900 +Subject: ksmbd: avoid duplicate negotiate ctx offset increments + +From: David Disseldorp + +[ Upstream commit a12a07a85aff72e19520328f78b1c64d2281a1ec ] + +Both pneg_ctxt and ctxt_size change in unison, with each adding the +length of the previously added context, rounded up to an eight byte +boundary. +Drop pneg_ctxt increments and instead use the ctxt_size offset when +passing output pointers to per-context helper functions. This slightly +simplifies offset tracking and shaves off a few text bytes. +Before (x86-64 gcc 7.5): + text data bss dec hex filename + 213234 8677 672 222583 36577 ksmbd.ko + +After: + text data bss dec hex filename + 213218 8677 672 222567 36567 ksmbd.ko + +Signed-off-by: David Disseldorp +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 26 ++++++++++---------------- + 1 file changed, 10 insertions(+), 16 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index c7d43c83d2335..92e6570972437 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -838,7 +838,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) + { +- char *pneg_ctxt = (char *)rsp + ++ char * const pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); + int neg_ctxt_cnt = 1; + int ctxt_size; +@@ -849,21 +849,17 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + conn->preauth_info->Preauth_HashId); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { ++ /* Round to 8 byte boundary */ + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); +- build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, ++ build_encrypt_ctxt((struct smb2_encryption_neg_context *) ++ (pneg_ctxt + ctxt_size), + conn->cipher_type); + neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; +- /* Round to 8 byte boundary */ +- pneg_ctxt += +- round_up(sizeof(struct smb2_encryption_neg_context) + 2, +- 8); + } + + if (conn->compress_algorithm) { +@@ -871,31 +867,29 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ +- build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, ++ build_compression_ctxt((struct smb2_compression_capabilities_context *) ++ (pneg_ctxt + ctxt_size), + conn->compress_algorithm); + neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, +- 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); +- build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); ++ build_posix_ctxt((struct smb2_posix_neg_context *) ++ (pneg_ctxt + ctxt_size)); + neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_posix_neg_context); +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); +- build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, ++ build_sign_cap_ctxt((struct smb2_signing_capabilities *) ++ (pneg_ctxt + ctxt_size), + conn->signing_algorithm); + neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch b/queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch new file mode 100644 index 00000000000..2b2a55db16d --- /dev/null +++ b/queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch @@ -0,0 +1,51 @@ +From d50f7b118fea7c3a07bd14b10b93b18408baa4f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:31 +0900 +Subject: ksmbd: avoid duplicate opinfo_put() call on error of + smb21_lease_break_ack() + +From: Namjae Jeon + +[ Upstream commit 658609d9a618d8881bf549b5893c0ba8fcff4526 ] + +opinfo_put() could be called twice on error of smb21_lease_break_ack(). +It will cause UAF issue if opinfo is referenced on other places. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 23e2c031cf2e5..2eea811fc8cb0 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -8219,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + le32_to_cpu(req->LeaseState)); + } + ++ if (ret < 0) { ++ rsp->hdr.Status = err; ++ goto err_out; ++ } ++ + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); +@@ -8226,11 +8231,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + +- if (ret < 0) { +- rsp->hdr.Status = err; +- goto err_out; +- } +- + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch b/queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch new file mode 100644 index 00000000000..34d6b239c5b --- /dev/null +++ b/queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch @@ -0,0 +1,62 @@ +From 0eeb3a5731237857b6fc5caa1ccdbeb9e8930c98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:40 +0900 +Subject: ksmbd: call putname after using the last component + +From: Namjae Jeon + +[ Upstream commit 6fe55c2799bc29624770c26f98ba7b06214f43e0 ] + +last component point filename struct. Currently putname is called after +vfs_path_parent_lookup(). And then last component is used for +lookup_one_qstr_excl(). name in last component is freed by previous +calling putname(). And It cause file lookup failure when testing +generic/464 test of xfstest. + +Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name") +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 5d2bb58d77e80..ebcd5a312f10d 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -87,12 +87,14 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + err = vfs_path_parent_lookup(filename, flags, + &parent_path, &last, &type, + root_share_path); +- putname(filename); +- if (err) ++ if (err) { ++ putname(filename); + return err; ++ } + + if (unlikely(type != LAST_NORM)) { + path_put(&parent_path); ++ putname(filename); + return -ENOENT; + } + +@@ -109,12 +111,14 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + path->dentry = d; + path->mnt = share_conf->vfs_path.mnt; + path_put(&parent_path); ++ putname(filename); + + return 0; + + err_out: + inode_unlock(parent_path.dentry->d_inode); + path_put(&parent_path); ++ putname(filename); + return -ENOENT; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch b/queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch new file mode 100644 index 00000000000..3fb90a08445 --- /dev/null +++ b/queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch @@ -0,0 +1,82 @@ +From ef0e9f4605537b84e4fc36e8b95da35e4a409a74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:46 +0900 +Subject: ksmbd: Change the return value of ksmbd_vfs_query_maximal_access to + void + +From: Lu Hongfei + +[ Upstream commit ccb5889af97c03c67a83fcd649602034578c0d61 ] + +The return value of ksmbd_vfs_query_maximal_access is meaningless, +it is better to modify it to void. + +Signed-off-by: Lu Hongfei +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 +--- + fs/smb/server/vfs.c | 6 +----- + fs/smb/server/vfs.h | 2 +- + 3 files changed, 3 insertions(+), 9 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index f35e06ae25b3b..e8d2c6fc3f37c 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2891,11 +2891,9 @@ int smb2_open(struct ksmbd_work *work) + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { +- rc = ksmbd_vfs_query_maximal_access(user_ns, ++ ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); +- if (rc) +- goto err_out; + already_permitted = true; + } + maximal_access = daccess; +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index d0a85774a496a..178bcd4d0b209 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -122,11 +122,9 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + return -ENOENT; + } + +-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, ++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) + { +- int ret = 0; +- + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) +@@ -143,8 +141,6 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + + if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; +- +- return ret; + } + + /** +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 0a4eb1e1a79a9..3e3c92d22e3eb 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -72,7 +72,7 @@ struct ksmbd_kstat { + }; + + int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); +-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, ++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); + int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); + int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch b/queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch new file mode 100644 index 00000000000..85c767933de --- /dev/null +++ b/queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch @@ -0,0 +1,303 @@ +From 9b66485417639519d8b34feb4ac3344deed11d67 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:52 +0900 +Subject: ksmbd: check if a mount point is crossed during path lookup + +From: Namjae Jeon + +[ Upstream commit 2b57a4322b1b14348940744fdc02f9a86cbbdbeb ] + +Since commit 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and +->d_name"), ksmbd can not lookup cross mount points. If last component is +a cross mount point during path lookup, check if it is crossed to follow it +down. And allow path lookup to cross a mount point when a crossmnt +parameter is set to 'yes' in smb.conf. + +Cc: stable@vger.kernel.org +Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name") +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_netlink.h | 3 +- + fs/smb/server/smb2pdu.c | 27 +++++++++------- + fs/smb/server/vfs.c | 58 ++++++++++++++++++++--------------- + fs/smb/server/vfs.h | 4 +-- + 4 files changed, 53 insertions(+), 39 deletions(-) + +diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h +index fb8b2d566efb6..b7521e41402e0 100644 +--- a/fs/smb/server/ksmbd_netlink.h ++++ b/fs/smb/server/ksmbd_netlink.h +@@ -352,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS { + #define KSMBD_SHARE_FLAG_STREAMS BIT(11) + #define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) + #define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +-#define KSMBD_SHARE_FLAG_UPDATE BIT(14) ++#define KSMBD_SHARE_FLAG_UPDATE BIT(14) ++#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) + + /* + * Tree connect request flags. +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 10d51256858ff..687e59cb0c8c8 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2475,8 +2475,9 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + } + } + +-static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, +- int open_flags, umode_t posix_mode, bool is_dir) ++static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, ++ struct path *path, char *name, int open_flags, ++ umode_t posix_mode, bool is_dir) + { + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; +@@ -2503,7 +2504,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + return rc; + } + +- rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0); ++ rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); +@@ -2573,7 +2574,7 @@ int smb2_open(struct ksmbd_work *work) + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; +- struct path path; ++ struct path path, parent_path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; +@@ -2794,7 +2795,8 @@ int smb2_open(struct ksmbd_work *work) + goto err_out1; + } + +- rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1); ++ rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, ++ &parent_path, &path, 1); + if (!rc) { + file_present = true; + +@@ -2914,7 +2916,8 @@ int smb2_open(struct ksmbd_work *work) + + /*create file if not present */ + if (!file_present) { +- rc = smb2_creat(work, &path, name, open_flags, posix_mode, ++ rc = smb2_creat(work, &parent_path, &path, name, open_flags, ++ posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { +@@ -3329,8 +3332,9 @@ int smb2_open(struct ksmbd_work *work) + + err_out: + if (file_present || created) { +- inode_unlock(d_inode(path.dentry->d_parent)); +- dput(path.dentry); ++ inode_unlock(d_inode(parent_path.dentry)); ++ path_put(&path); ++ path_put(&parent_path); + } + ksmbd_revert_fsids(work); + err_out1: +@@ -5553,7 +5557,7 @@ static int smb2_create_link(struct ksmbd_work *work, + struct nls_table *local_nls) + { + char *link_name = NULL, *target_name = NULL, *pathname = NULL; +- struct path path; ++ struct path path, parent_path; + bool file_present = false; + int rc; + +@@ -5583,7 +5587,7 @@ static int smb2_create_link(struct ksmbd_work *work, + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, +- &path, 0); ++ &parent_path, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; +@@ -5613,8 +5617,9 @@ static int smb2_create_link(struct ksmbd_work *work, + rc = -EINVAL; + out: + if (file_present) { +- inode_unlock(d_inode(path.dentry->d_parent)); ++ inode_unlock(d_inode(parent_path.dentry)); + path_put(&path); ++ path_put(&parent_path); + } + if (!IS_ERR(link_name)) + kfree(link_name); +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 73ce3fb6e4054..1752a6c10bcc9 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -64,13 +64,13 @@ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) + + static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + char *pathname, unsigned int flags, ++ struct path *parent_path, + struct path *path) + { + struct qstr last; + struct filename *filename; + struct path *root_share_path = &share_conf->vfs_path; + int err, type; +- struct path parent_path; + struct dentry *d; + + if (pathname[0] == '\0') { +@@ -85,7 +85,7 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + return PTR_ERR(filename); + + err = vfs_path_parent_lookup(filename, flags, +- &parent_path, &last, &type, ++ parent_path, &last, &type, + root_share_path); + if (err) { + putname(filename); +@@ -93,13 +93,13 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + } + + if (unlikely(type != LAST_NORM)) { +- path_put(&parent_path); ++ path_put(parent_path); + putname(filename); + return -ENOENT; + } + +- inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); +- d = lookup_one_qstr_excl(&last, parent_path.dentry, 0); ++ inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); ++ d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); + if (IS_ERR(d)) + goto err_out; + +@@ -109,15 +109,22 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + } + + path->dentry = d; +- path->mnt = share_conf->vfs_path.mnt; +- path_put(&parent_path); +- putname(filename); ++ path->mnt = mntget(parent_path->mnt); ++ ++ if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) { ++ err = follow_down(path); ++ if (err < 0) { ++ path_put(path); ++ goto err_out; ++ } ++ } + ++ putname(filename); + return 0; + + err_out: +- inode_unlock(parent_path.dentry->d_inode); +- path_put(&parent_path); ++ inode_unlock(d_inode(parent_path->dentry)); ++ path_put(parent_path); + putname(filename); + return -ENOENT; + } +@@ -1196,14 +1203,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + * Return: 0 on success, otherwise error + */ + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, +- unsigned int flags, struct path *path, +- bool caseless) ++ unsigned int flags, struct path *parent_path, ++ struct path *path, bool caseless) + { + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; +- struct path parent_path; + +- err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path); ++ err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path, ++ path); + if (!err) + return 0; + +@@ -1218,10 +1225,10 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + path_len = strlen(filepath); + remain_len = path_len; + +- parent_path = share_conf->vfs_path; +- path_get(&parent_path); ++ *parent_path = share_conf->vfs_path; ++ path_get(parent_path); + +- while (d_can_lookup(parent_path.dentry)) { ++ while (d_can_lookup(parent_path->dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; +@@ -1230,7 +1237,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + if (filename_len == 0) + break; + +- err = ksmbd_vfs_lookup_in_dir(&parent_path, filename, ++ err = ksmbd_vfs_lookup_in_dir(parent_path, filename, + filename_len, + work->conn->um); + if (err) +@@ -1247,8 +1254,8 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + goto out2; + else if (is_last) + goto out1; +- path_put(&parent_path); +- parent_path = *path; ++ path_put(parent_path); ++ *parent_path = *path; + + next[0] = '/'; + remain_len -= filename_len + 1; +@@ -1256,16 +1263,17 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + + err = -EINVAL; + out2: +- path_put(&parent_path); ++ path_put(parent_path); + out1: + kfree(filepath); + } + + if (!err) { +- err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry); +- if (err) +- dput(path->dentry); +- path_put(&parent_path); ++ err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); ++ if (err) { ++ path_put(path); ++ path_put(parent_path); ++ } + } + return err; + } +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 3e3c92d22e3eb..a7cc0aad6d576 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -115,8 +115,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + const struct path *path, char *attr_name); + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, +- unsigned int flags, struct path *path, +- bool caseless); ++ unsigned int flags, struct path *parent_path, ++ struct path *path, bool caseless); + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch b/queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch new file mode 100644 index 00000000000..0026107eb32 --- /dev/null +++ b/queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch @@ -0,0 +1,38 @@ +From 85ea5bd41f9df019d6e25cefc8a9c69670d6ee9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:03 +0900 +Subject: ksmbd: check iov vector index in ksmbd_conn_write() + +From: Namjae Jeon + +[ Upstream commit 73f949ea87c7d697210653501ca21efe57295327 ] + +If ->iov_idx is zero, This means that the iov vector for the response +was not added during the request process. In other words, it means that +there is a problem in generating a response, So this patch return as +an error to avoid NULL pointer dereferencing problem. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/connection.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index d1f4ed18a227f..4b38c3a285f60 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -199,6 +199,9 @@ int ksmbd_conn_write(struct ksmbd_work *work) + if (work->send_no_response) + return 0; + ++ if (!work->iov_idx) ++ return -EINVAL; ++ + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, work->iov, + work->iov_cnt, +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch b/queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch new file mode 100644 index 00000000000..26e7fe4a823 --- /dev/null +++ b/queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch @@ -0,0 +1,67 @@ +From 343c46a61f2feaf71ed606c915931fa11c370a7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:24 +0900 +Subject: ksmbd: Convert to use sysfs_emit()/sysfs_emit_at() APIs + +From: ye xingchen + +[ Upstream commit 72ee45fd46d0d3578c4e6046f66fae3218543ce3 ] + +Follow the advice of the Documentation/filesystems/sysfs.rst and show() +should only use sysfs_emit() or sysfs_emit_at() when formatting the +value to be returned to user space. + +Signed-off-by: ye xingchen +Reviewed-by: Sergey Senozhatsky +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/server.c | 20 ++++++-------------- + 1 file changed, 6 insertions(+), 14 deletions(-) + +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index 9804cabe72a84..0c0db2e614ef6 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -442,11 +442,9 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr, + "reset", + "shutdown" + }; +- +- ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, +- state[server_conf.state], server_conf.tcp_port, +- server_conf.ipc_last_active / HZ); +- return sz; ++ return sysfs_emit(buf, "%d %s %d %lu\n", stats_version, ++ state[server_conf.state], server_conf.tcp_port, ++ server_conf.ipc_last_active / HZ); + } + + static ssize_t kill_server_store(struct class *class, +@@ -478,19 +476,13 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr, + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { +- pos = scnprintf(buf + sz, +- PAGE_SIZE - sz, +- "[%s] ", +- debug_type_strings[i]); ++ pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]); + } else { +- pos = scnprintf(buf + sz, +- PAGE_SIZE - sz, +- "%s ", +- debug_type_strings[i]); ++ pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]); + } + sz += pos; + } +- sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); ++ sz += sysfs_emit_at(buf, sz, "\n"); + return sz; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch b/queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch new file mode 100644 index 00000000000..983a301a4fd --- /dev/null +++ b/queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch @@ -0,0 +1,172 @@ +From c818867348319fc17476d7cf610f4ea3e3bfd84e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:32 +0900 +Subject: ksmbd: delete asynchronous work from list + +From: Namjae Jeon + +[ Upstream commit 3a9b557f44ea8f216aab515a7db20e23f0eb51b9 ] + +When smb2_lock request is canceled by smb2_cancel or smb2_close(), +ksmbd is missing deleting async_request_entry async_requests list. +Because calling init_smb2_rsp_hdr() in smb2_lock() mark ->synchronous +as true and then it will not be deleted in +ksmbd_conn_try_dequeue_request(). This patch add release_async_work() to +release the ones allocated for async work. + +Cc: stable@vger.kernel.org +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/connection.c | 12 +++++------- + fs/smb/server/ksmbd_work.h | 2 +- + fs/smb/server/smb2pdu.c | 33 +++++++++++++++++++++------------ + fs/smb/server/smb2pdu.h | 1 + + 4 files changed, 28 insertions(+), 20 deletions(-) + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index ffbf14d024191..00a87b4acd5c5 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -114,10 +114,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; + +- if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { ++ if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) + requests_queue = &conn->requests; +- work->synchronous = true; +- } + + if (requests_queue) { + atomic_inc(&conn->req_running); +@@ -138,14 +136,14 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) + + if (!work->multiRsp) + atomic_dec(&conn->req_running); +- spin_lock(&conn->request_lock); + if (!work->multiRsp) { ++ spin_lock(&conn->request_lock); + list_del_init(&work->request_entry); +- if (!work->synchronous) +- list_del_init(&work->async_request_entry); ++ spin_unlock(&conn->request_lock); ++ if (work->asynchronous) ++ release_async_work(work); + ret = 0; + } +- spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h +index 3234f2cf6327c..f8ae6144c0aea 100644 +--- a/fs/smb/server/ksmbd_work.h ++++ b/fs/smb/server/ksmbd_work.h +@@ -68,7 +68,7 @@ struct ksmbd_work { + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ +- bool synchronous:1; ++ bool asynchronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index d3939fd481497..ee128f5d38c35 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -508,12 +508,6 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + +- work->synchronous = true; +- if (work->async_id) { +- ksmbd_release_id(&conn->async_ida, work->async_id); +- work->async_id = 0; +- } +- + return 0; + } + +@@ -671,7 +665,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + pr_err("Failed to alloc async message id\n"); + return id; + } +- work->synchronous = false; ++ work->asynchronous = true; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + +@@ -691,6 +685,24 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + return 0; + } + ++void release_async_work(struct ksmbd_work *work) ++{ ++ struct ksmbd_conn *conn = work->conn; ++ ++ spin_lock(&conn->request_lock); ++ list_del_init(&work->async_request_entry); ++ spin_unlock(&conn->request_lock); ++ ++ work->asynchronous = 0; ++ work->cancel_fn = NULL; ++ kfree(work->cancel_argv); ++ work->cancel_argv = NULL; ++ if (work->async_id) { ++ ksmbd_release_id(&conn->async_ida, work->async_id); ++ work->async_id = 0; ++ } ++} ++ + void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) + { + struct smb2_hdr *rsp_hdr; +@@ -7153,13 +7165,9 @@ int smb2_lock(struct ksmbd_work *work) + + ksmbd_vfs_posix_lock_wait(flock); + +- spin_lock(&work->conn->request_lock); + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); +- work->cancel_fn = NULL; +- kfree(argv); + spin_unlock(&fp->f_lock); +- spin_unlock(&work->conn->request_lock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); +@@ -7177,6 +7185,7 @@ int smb2_lock(struct ksmbd_work *work) + work->send_no_response = 1; + goto out; + } ++ + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = +@@ -7189,7 +7198,7 @@ int smb2_lock(struct ksmbd_work *work) + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); +- ++ release_async_work(work); + goto retry; + } else if (!rc) { + spin_lock(&work->conn->llist_lock); +diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h +index f13bd65993ccc..59e3de95961c1 100644 +--- a/fs/smb/server/smb2pdu.h ++++ b/fs/smb/server/smb2pdu.h +@@ -488,6 +488,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + struct file_lock *smb_flock_init(struct file *f); + int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); ++void release_async_work(struct ksmbd_work *work); + void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); + struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch b/queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch new file mode 100644 index 00000000000..bfab86093c0 --- /dev/null +++ b/queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch @@ -0,0 +1,35 @@ +From d136a842f4b76e9488834b12514f75c38df74606 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:25 +0900 +Subject: ksmbd: don't update ->op_state as OPLOCK_STATE_NONE on error + +From: Namjae Jeon + +[ Upstream commit cd80ce7e68f1624ac29cd0a6b057789d1236641e ] + +ksmbd set ->op_state as OPLOCK_STATE_NONE on lease break ack error. +op_state of lease should not be updated because client can send lease +break ack again. This patch fix smb2.lease.breaking2 test failure. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 2ba5e685dd3fe..6a698a6036bb2 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -8235,7 +8235,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + return; + + err_out: +- opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch b/queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch new file mode 100644 index 00000000000..b7dfc8320c1 --- /dev/null +++ b/queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch @@ -0,0 +1,94 @@ +From 60c2dfbc1c3e2325b8c8605ac0349453ff22411f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:28 +0900 +Subject: ksmbd: downgrade RWH lease caching state to RH for directory + +From: Namjae Jeon + +[ Upstream commit eb547407f3572d2110cb1194ecd8865b3371a7a4 ] + +RWH(Read + Write + Handle) caching state is not supported for directory. +ksmbd downgrade it to RH for directory if client send RWH caching lease +state. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/oplock.c | 9 +++++++-- + fs/smb/server/oplock.h | 2 +- + fs/smb/server/smb2pdu.c | 8 ++++---- + 3 files changed, 12 insertions(+), 7 deletions(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 7346cbfbff6b0..f8ac539b2164b 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -1398,10 +1398,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) + /** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request ++ * @is_dir: whether leasing file is directory + * + * Return: oplock state, -ENOENT if create lease context not found + */ +-struct lease_ctx_info *parse_lease_state(void *open_req) ++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) + { + struct create_context *cc; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; +@@ -1419,7 +1420,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req) + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); +- lreq->req_state = lc->lcontext.LeaseState; ++ if (is_dir) ++ lreq->req_state = lc->lcontext.LeaseState & ++ ~SMB2_LEASE_WRITE_CACHING_LE; ++ else ++ lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->epoch = lc->lcontext.Epoch; + lreq->duration = lc->lcontext.LeaseDuration; +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index ad31439c61fef..672127318c750 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -109,7 +109,7 @@ void opinfo_put(struct oplock_info *opinfo); + + /* Lease related functions */ + void create_lease_buf(u8 *rbuf, struct lease *lease); +-struct lease_ctx_info *parse_lease_state(void *open_req); ++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir); + __u8 smb2_map_lease_to_oplock(__le32 lease_state); + int lease_read_to_write(struct oplock_info *opinfo); + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 6a698a6036bb2..d7235c5e2e4d8 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2732,10 +2732,6 @@ int smb2_open(struct ksmbd_work *work) + } + } + +- req_op_level = req->RequestedOplockLevel; +- if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) +- lc = parse_lease_state(req); +- + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); +@@ -3215,6 +3211,10 @@ int smb2_open(struct ksmbd_work *work) + need_truncate = 1; + } + ++ req_op_level = req->RequestedOplockLevel; ++ if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) ++ lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode)); ++ + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch b/queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch new file mode 100644 index 00000000000..132fd3a878c --- /dev/null +++ b/queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch @@ -0,0 +1,72 @@ +From 07e92566adb80f5ca96e2e6266a58d5f7dd475ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:56 +0900 +Subject: ksmbd: fix `force create mode' and `force directory mode' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Atte Heikkilä + +[ Upstream commit 65656f5242e500dcfeffa6a0a1519eae14724f86 ] + +`force create mode' and `force directory mode' should be bitwise ORed +with the perms after `create mask' and `directory mask' have been +applied, respectively. + +Signed-off-by: Atte Heikkilä +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/mgmt/share_config.h | 29 +++++++++++------------------ + 1 file changed, 11 insertions(+), 18 deletions(-) + +diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h +index 3fd3382939421..5f591751b9236 100644 +--- a/fs/smb/server/mgmt/share_config.h ++++ b/fs/smb/server/mgmt/share_config.h +@@ -34,29 +34,22 @@ struct ksmbd_share_config { + #define KSMBD_SHARE_INVALID_UID ((__u16)-1) + #define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +-static inline int share_config_create_mode(struct ksmbd_share_config *share, +- umode_t posix_mode) ++static inline umode_t ++share_config_create_mode(struct ksmbd_share_config *share, ++ umode_t posix_mode) + { +- if (!share->force_create_mode) { +- if (!posix_mode) +- return share->create_mask; +- else +- return posix_mode & share->create_mask; +- } +- return share->force_create_mode & share->create_mask; ++ umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask; ++ ++ return mode | share->force_create_mode; + } + +-static inline int share_config_directory_mode(struct ksmbd_share_config *share, +- umode_t posix_mode) ++static inline umode_t ++share_config_directory_mode(struct ksmbd_share_config *share, ++ umode_t posix_mode) + { +- if (!share->force_directory_mode) { +- if (!posix_mode) +- return share->directory_mask; +- else +- return posix_mode & share->directory_mask; +- } ++ umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask; + +- return share->force_directory_mode & share->directory_mask; ++ return mode | share->force_directory_mode; + } + + static inline int test_share_config_flag(struct ksmbd_share_config *share, +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch new file mode 100644 index 00000000000..ab0eff1d03c --- /dev/null +++ b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch @@ -0,0 +1,44 @@ +From 1545888153b0930dd1e9fa78d6bdc1b1ba06d61a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:18 +0900 +Subject: ksmbd: fix kernel-doc comment of ksmbd_vfs_kern_path_locked() + +From: Namjae Jeon + +[ Upstream commit f6049712e520287ad695e9d4f1572ab76807fa0c ] + +Fix argument list that the kdoc format and script verified in +ksmbd_vfs_kern_path_locked(). + +fs/smb/server/vfs.c:1207: warning: Function parameter or member 'parent_path' +not described in 'ksmbd_vfs_kern_path_locked' + +Reported-by: kernel test robot +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index e2e454eba4095..d4298a751d4a2 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -1178,9 +1178,10 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + + /** + * ksmbd_vfs_kern_path_locked() - lookup a file and get path info +- * @name: file path that is relative to share +- * @flags: lookup flags +- * @path: if lookup succeed, return path info ++ * @name: file path that is relative to share ++ * @flags: lookup flags ++ * @parent_path: if lookup succeed, return parent_path info ++ * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch new file mode 100644 index 00000000000..fc44511b699 --- /dev/null +++ b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch @@ -0,0 +1,39 @@ +From 30497f8971c380596dadb0733b268a0c7bbb495f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:13 +0900 +Subject: ksmbd: fix kernel-doc comment of ksmbd_vfs_setxattr() + +From: Namjae Jeon + +[ Upstream commit 3354db668808d5b6d7c5e0cb19ff4c9da4bb5e58 ] + +Fix argument list that the kdoc format and script verified in +ksmbd_vfs_setxattr(). + +fs/smb/server/vfs.c:929: warning: Function parameter or member 'path' +not described in 'ksmbd_vfs_setxattr' + +Reported-by: kernel test robot +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 6f54ea1df0c58..071c344dd0333 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -920,7 +920,7 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + /** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace +- * @dentry: dentry to set XATTR at ++ * @path: path of dentry to set XATTR at + * @attr_name: xattr name for setxattr + * @attr_value: xattr value to set + * @attr_size: size of xattr value +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch b/queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch new file mode 100644 index 00000000000..f941aaa6d73 --- /dev/null +++ b/queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch @@ -0,0 +1,89 @@ +From c879a4a24fa9a5f7d1fcc9e48ff9bfc83fcabd2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:15 +0900 +Subject: ksmbd: fix missing RDMA-capable flag for IPoIB device in + ksmbd_rdma_capable_netdev() + +From: Kangjing Huang + +[ Upstream commit ecce70cf17d91c3dd87a0c4ea00b2d1387729701 ] + +Physical ib_device does not have an underlying net_device, thus its +association with IPoIB net_device cannot be retrieved via +ops.get_netdev() or ib_device_get_by_netdev(). ksmbd reads physical +ib_device port GUID from the lower 16 bytes of the hardware addresses on +IPoIB net_device and match its underlying ib_device using ib_find_gid() + +Signed-off-by: Kangjing Huang +Acked-by: Namjae Jeon +Reviewed-by: Tom Talpey +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/transport_rdma.c | 40 +++++++++++++++++++++++++--------- + 1 file changed, 30 insertions(+), 10 deletions(-) + +diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c +index 3b269e1f523a1..c5629a68c8b73 100644 +--- a/fs/smb/server/transport_rdma.c ++++ b/fs/smb/server/transport_rdma.c +@@ -2140,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) + if (ib_dev->node_type != RDMA_NODE_IB_CA) + smb_direct_port = SMB_DIRECT_PORT_IWARP; + +- if (!ib_dev->ops.get_netdev || +- !rdma_frwr_is_supported(&ib_dev->attrs)) ++ if (!rdma_frwr_is_supported(&ib_dev->attrs)) + return 0; + + smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); +@@ -2241,17 +2240,38 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) + for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { + struct net_device *ndev; + +- ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, +- i + 1); +- if (!ndev) +- continue; ++ if (smb_dev->ib_dev->ops.get_netdev) { ++ ndev = smb_dev->ib_dev->ops.get_netdev( ++ smb_dev->ib_dev, i + 1); ++ if (!ndev) ++ continue; + +- if (ndev == netdev) { ++ if (ndev == netdev) { ++ dev_put(ndev); ++ rdma_capable = true; ++ goto out; ++ } + dev_put(ndev); +- rdma_capable = true; +- goto out; ++ /* if ib_dev does not implement ops.get_netdev ++ * check for matching infiniband GUID in hw_addr ++ */ ++ } else if (netdev->type == ARPHRD_INFINIBAND) { ++ struct netdev_hw_addr *ha; ++ union ib_gid gid; ++ u32 port_num; ++ int ret; ++ ++ netdev_hw_addr_list_for_each( ++ ha, &netdev->dev_addrs) { ++ memcpy(&gid, ha->addr + 4, sizeof(gid)); ++ ret = ib_find_gid(smb_dev->ib_dev, &gid, ++ &port_num, NULL); ++ if (!ret) { ++ rdma_capable = true; ++ goto out; ++ } ++ } + } +- dev_put(ndev); + } + } + out: +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch b/queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch new file mode 100644 index 00000000000..11d7a4698ed --- /dev/null +++ b/queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch @@ -0,0 +1,56 @@ +From 402b062ba5000a9682a8d3c575f2284158a9c37f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:09 +0900 +Subject: ksmbd: fix Null pointer dereferences in ksmbd_update_fstate() + +From: Namjae Jeon + +[ Upstream commit 414849040fcf11d45025b8ae26c9fd91da1465da ] + +Coverity Scan report the following one. This report is a false alarm. +Because fp is never NULL when rc is zero. This patch add null check for fp +in ksmbd_update_fstate to make alarm silence. + +*** CID 1568583: Null pointer dereferences (FORWARD_NULL) +/fs/smb/server/smb2pdu.c: 3408 in smb2_open() +3402 path_put(&path); +3403 path_put(&parent_path); +3404 } +3405 ksmbd_revert_fsids(work); +3406 err_out1: +3407 if (!rc) { +>>> CID 1568583: Null pointer dereferences (FORWARD_NULL) +>>> Passing null pointer "fp" to "ksmbd_update_fstate", which dereferences it. +3408 ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); +3409 rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); +3410 } +3411 if (rc) { +3412 if (rc == -EINVAL) +3413 rsp->hdr.Status = STATUS_INVALID_PARAMETER; + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Reported-by: Coverity Scan +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index f600279b0a9ee..38f414e803adb 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -602,6 +602,9 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) + void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state) + { ++ if (!fp) ++ return; ++ + write_lock(&ft->lock); + fp->f_state = state; + write_unlock(&ft->lock); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch b/queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch new file mode 100644 index 00000000000..e4e4fb6c790 --- /dev/null +++ b/queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch @@ -0,0 +1,35 @@ +From 86227b9f68b1e6b4fe5bf8ccfe5bb8f41a3f17a2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:57 +0900 +Subject: ksmbd: Fix one kernel-doc comment + +From: Yang Li + +[ Upstream commit bf26f1b4e0918f017775edfeacf6d867204b680b ] + +Fix one kernel-doc comment to silence the warning: +fs/smb/server/smb2pdu.c:4160: warning: Excess function parameter 'infoclass_size' description in 'buffer_check_err' + +Signed-off-by: Yang Li +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index dfb4fd4cb42f6..0fed613956f7a 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -4156,7 +4156,6 @@ int smb2_query_dir(struct ksmbd_work *work) + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @rsp_org: base response buffer pointer in case of chained response +- * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch b/queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch new file mode 100644 index 00000000000..161780094e3 --- /dev/null +++ b/queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch @@ -0,0 +1,42 @@ +From e67aecfb9b539c1f4db68ad2cd47fd29c618e5d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:30 +0900 +Subject: ksmbd: Fix parameter name and comment mismatch + +From: Jiapeng Chong + +[ Upstream commit 63f09a9986eb58578ed6ad0e27a6e2c54e49f797 ] + +fs/ksmbd/vfs.c:965: warning: Function parameter or member 'attr_value' not described in 'ksmbd_vfs_setxattr'. + +Reported-by: Abaci Robot +Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3946 +Signed-off-by: Jiapeng Chong +Acked-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 36914db8b6616..187d31d984946 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -951,9 +951,9 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace + * @dentry: dentry to set XATTR at +- * @name: xattr name for setxattr +- * @value: xattr value to set +- * @size: size of xattr value ++ * @attr_name: xattr name for setxattr ++ * @attr_value: xattr value to set ++ * @attr_size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch b/queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch new file mode 100644 index 00000000000..5003585706e --- /dev/null +++ b/queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch @@ -0,0 +1,43 @@ +From af50bb43ac3da04dad814a1f4b9c5348a59938d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:01 +0900 +Subject: ksmbd: fix passing freed memory 'aux_payload_buf' + +From: Namjae Jeon + +[ Upstream commit 59d8d24f4610333560cf2e8fe3f44cafe30322eb ] + +The patch e2b76ab8b5c9: "ksmbd: add support for read compound" leads +to the following Smatch static checker warning: + + fs/smb/server/smb2pdu.c:6329 smb2_read() + warn: passing freed memory 'aux_payload_buf' + +It doesn't matter that we're passing a freed variable because nbytes is +zero. This patch set "aux_payload_buf = NULL" to make smatch silence. + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Reported-by: Dan Carpenter +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index b81a38803b40d..42697ea86d47b 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -6312,7 +6312,7 @@ int smb2_read(struct ksmbd_work *work) + aux_payload_buf, + nbytes); + kvfree(aux_payload_buf); +- ++ aux_payload_buf = NULL; + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch b/queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch new file mode 100644 index 00000000000..e7e4a73c571 --- /dev/null +++ b/queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch @@ -0,0 +1,79 @@ +From e7b95998e08d4ba7a04c83e7aee93aa1c699ddbc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:41 +0900 +Subject: ksmbd: fix posix_acls and acls dereferencing possible ERR_PTR() + +From: Namjae Jeon + +[ Upstream commit 25933573ef48f3586f559c2cac6c436c62dcf63f ] + +Dan reported the following error message: + +fs/smb/server/smbacl.c:1296 smb_check_perm_dacl() + error: 'posix_acls' dereferencing possible ERR_PTR() +fs/smb/server/vfs.c:1323 ksmbd_vfs_make_xattr_posix_acl() + error: 'posix_acls' dereferencing possible ERR_PTR() +fs/smb/server/vfs.c:1830 ksmbd_vfs_inherit_posix_acl() + error: 'acls' dereferencing possible ERR_PTR() + +__get_acl() returns a mix of error pointers and NULL. This change it +with IS_ERR_OR_NULL(). + +Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") +Cc: stable@vger.kernel.org +Reported-by: Dan Carpenter +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smbacl.c | 4 ++-- + fs/smb/server/vfs.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index c24df86eb112b..8c041e71cf156 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -1313,7 +1313,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); +- if (posix_acls && !found) { ++ if (!IS_ERR_OR_NULL(posix_acls) && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; +@@ -1337,7 +1337,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + } + } + } +- if (posix_acls) ++ if (!IS_ERR_OR_NULL(posix_acls)) + posix_acl_release(posix_acls); + } + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index ebcd5a312f10d..6d171f2757f15 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -1322,7 +1322,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac + return NULL; + + posix_acls = get_acl(inode, acl_type); +- if (!posix_acls) ++ if (IS_ERR_OR_NULL(posix_acls)) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + +@@ -1830,7 +1830,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + return -EOPNOTSUPP; + + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); +- if (!acls) ++ if (IS_ERR_OR_NULL(acls)) + return -ENOENT; + pace = acls->a_entries; + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch b/queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch new file mode 100644 index 00000000000..60357516d21 --- /dev/null +++ b/queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch @@ -0,0 +1,488 @@ +From 7539c7c5112a3946d637236fe20993d4cbf2cb99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:20 +0900 +Subject: ksmbd: fix possible deadlock in smb2_open + +From: Namjae Jeon + +[ Upstream commit 864fb5d3716303a045c3ffb397f651bfd37bfb36 ] + +[ 8743.393379] ====================================================== +[ 8743.393385] WARNING: possible circular locking dependency detected +[ 8743.393391] 6.4.0-rc1+ #11 Tainted: G OE +[ 8743.393397] ------------------------------------------------------ +[ 8743.393402] kworker/0:2/12921 is trying to acquire lock: +[ 8743.393408] ffff888127a14460 (sb_writers#8){.+.+}-{0:0}, at: ksmbd_vfs_setxattr+0x3d/0xd0 [ksmbd] +[ 8743.393510] + but task is already holding lock: +[ 8743.393515] ffff8880360d97f0 (&type->i_mutex_dir_key#6/1){+.+.}-{3:3}, at: ksmbd_vfs_kern_path_locked+0x181/0x670 [ksmbd] +[ 8743.393618] + which lock already depends on the new lock. + +[ 8743.393623] + the existing dependency chain (in reverse order) is: +[ 8743.393628] + -> #1 (&type->i_mutex_dir_key#6/1){+.+.}-{3:3}: +[ 8743.393648] down_write_nested+0x9a/0x1b0 +[ 8743.393660] filename_create+0x128/0x270 +[ 8743.393670] do_mkdirat+0xab/0x1f0 +[ 8743.393680] __x64_sys_mkdir+0x47/0x60 +[ 8743.393690] do_syscall_64+0x5d/0x90 +[ 8743.393701] entry_SYSCALL_64_after_hwframe+0x72/0xdc +[ 8743.393711] + -> #0 (sb_writers#8){.+.+}-{0:0}: +[ 8743.393728] __lock_acquire+0x2201/0x3b80 +[ 8743.393737] lock_acquire+0x18f/0x440 +[ 8743.393746] mnt_want_write+0x5f/0x240 +[ 8743.393755] ksmbd_vfs_setxattr+0x3d/0xd0 [ksmbd] +[ 8743.393839] ksmbd_vfs_set_dos_attrib_xattr+0xcc/0x110 [ksmbd] +[ 8743.393924] compat_ksmbd_vfs_set_dos_attrib_xattr+0x39/0x50 [ksmbd] +[ 8743.394010] smb2_open+0x3432/0x3cc0 [ksmbd] +[ 8743.394099] handle_ksmbd_work+0x2c9/0x7b0 [ksmbd] +[ 8743.394187] process_one_work+0x65a/0xb30 +[ 8743.394198] worker_thread+0x2cf/0x700 +[ 8743.394209] kthread+0x1ad/0x1f0 +[ 8743.394218] ret_from_fork+0x29/0x50 + +This patch add mnt_want_write() above parent inode lock and remove +nested mnt_want_write calls in smb2_open(). + +Fixes: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions") +Cc: stable@vger.kernel.org +Reported-by: Marios Makassikis +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 47 +++++++++++++--------------- + fs/smb/server/smbacl.c | 7 +++-- + fs/smb/server/smbacl.h | 2 +- + fs/smb/server/vfs.c | 68 +++++++++++++++++++++++++---------------- + fs/smb/server/vfs.h | 10 ++++-- + 5 files changed, 75 insertions(+), 59 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index a89a69d752a3b..b8340f1382bef 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2380,7 +2380,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value, +- le16_to_cpu(eabuf->EaValueLength), 0); ++ le16_to_cpu(eabuf->EaValueLength), ++ 0, true); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", +@@ -2443,7 +2444,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path, + return -EBADF; + } + +- rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0); ++ rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0, false); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +@@ -2518,7 +2519,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, false); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); + } +@@ -2608,7 +2609,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, +- le32_to_cpu(sd_buf->ccontext.DataLength), true); ++ le32_to_cpu(sd_buf->ccontext.DataLength), true, false); + } + + static void ksmbd_acls_fattr(struct smb_fattr *fattr, +@@ -3152,7 +3153,8 @@ int smb2_open(struct ksmbd_work *work) + user_ns, + &path, + pntsd, +- pntsd_size); ++ pntsd_size, ++ false); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", +@@ -3228,12 +3230,6 @@ int smb2_open(struct ksmbd_work *work) + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + +- if (need_truncate) { +- rc = smb2_create_truncate(&path); +- if (rc) +- goto err_out; +- } +- + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + +@@ -3398,11 +3394,12 @@ int smb2_open(struct ksmbd_work *work) + } + + err_out: +- if (file_present || created) { +- inode_unlock(d_inode(parent_path.dentry)); +- path_put(&path); +- path_put(&parent_path); +- } ++ if (file_present || created) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ ++ if (fp && need_truncate) ++ rc = smb2_create_truncate(&fp->filp->f_path); ++ + ksmbd_revert_fsids(work); + err_out1: + if (!rc) { +@@ -5537,7 +5534,7 @@ static int smb2_rename(struct ksmbd_work *work, + rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), + &fp->filp->f_path, + xattr_stream_name, +- NULL, 0, 0); ++ NULL, 0, 0, true); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); +@@ -5630,11 +5627,9 @@ static int smb2_create_link(struct ksmbd_work *work, + if (rc) + rc = -EINVAL; + out: +- if (file_present) { +- inode_unlock(d_inode(parent_path.dentry)); +- path_put(&path); +- path_put(&parent_path); +- } ++ if (file_present) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); +@@ -5701,7 +5696,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da, ++ true); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); +@@ -6013,7 +6009,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, +- buf_len, false); ++ buf_len, false, true); + } + + /** +@@ -7583,7 +7579,8 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, +- &fp->filp->f_path, &da); ++ &fp->filp->f_path, ++ &da, true); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index 7a42728d8047c..d9bbd2eb89c35 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -1185,7 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + +- ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size, false); + kfree(pntsd); + } + +@@ -1377,7 +1377,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + + int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, +- bool type_check) ++ bool type_check, bool get_write) + { + int rc; + struct smb_fattr fattr = {{0}}; +@@ -1437,7 +1437,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(user_ns, path); +- ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len, ++ get_write); + } + + out: +diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h +index 618f2e0236b31..9651a25518881 100644 +--- a/fs/smb/server/smbacl.h ++++ b/fs/smb/server/smbacl.h +@@ -207,7 +207,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid); + int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, +- bool type_check); ++ bool type_check, bool get_write); + void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); + void ksmbd_init_domain(u32 *sub_auth); + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index d4298a751d4a2..08f3f66e4b38e 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -98,6 +98,13 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + return -ENOENT; + } + ++ err = mnt_want_write(parent_path->mnt); ++ if (err) { ++ path_put(parent_path); ++ putname(filename); ++ return -ENOENT; ++ } ++ + inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); + d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); + if (IS_ERR(d)) +@@ -124,6 +131,7 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, + + err_out: + inode_unlock(d_inode(parent_path->dentry)); ++ mnt_drop_write(parent_path->mnt); + path_put(parent_path); + putname(filename); + return -ENOENT; +@@ -452,7 +460,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + fp->stream.name, + (void *)stream_buf, + size, +- 0); ++ 0, ++ true); + if (err < 0) + goto out; + +@@ -594,10 +603,6 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + goto out_err; + } + +- err = mnt_want_write(path->mnt); +- if (err) +- goto out_err; +- + user_ns = mnt_user_ns(path->mnt); + if (S_ISDIR(d_inode(path->dentry)->i_mode)) { + err = vfs_rmdir(user_ns, d_inode(parent), path->dentry); +@@ -608,7 +613,6 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + if (err) + ksmbd_debug(VFS, "unlink failed, err %d\n", err); + } +- mnt_drop_write(path->mnt); + + out_err: + ksmbd_revert_fsids(work); +@@ -908,18 +912,22 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + * @attr_value: xattr value to set + * @attr_size: size of xattr value + * @flags: destination buffer length ++ * @get_write: get write access to a mount + * + * Return: 0 on success, otherwise error + */ + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + const struct path *path, const char *attr_name, +- void *attr_value, size_t attr_size, int flags) ++ void *attr_value, size_t attr_size, int flags, ++ bool get_write) + { + int err; + +- err = mnt_want_write(path->mnt); +- if (err) +- return err; ++ if (get_write == true) { ++ err = mnt_want_write(path->mnt); ++ if (err) ++ return err; ++ } + + err = vfs_setxattr(user_ns, + path->dentry, +@@ -929,7 +937,8 @@ int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); +- mnt_drop_write(path->mnt); ++ if (get_write == true) ++ mnt_drop_write(path->mnt); + return err; + } + +@@ -1253,6 +1262,13 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + } + + if (!err) { ++ err = mnt_want_write(parent_path->mnt); ++ if (err) { ++ path_put(path); ++ path_put(parent_path); ++ return err; ++ } ++ + err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); + if (err) { + path_put(path); +@@ -1262,6 +1278,14 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + return err; + } + ++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path) ++{ ++ inode_unlock(d_inode(parent_path->dentry)); ++ mnt_drop_write(parent_path->mnt); ++ path_put(path); ++ path_put(parent_path); ++} ++ + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +@@ -1411,7 +1435,8 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + const struct path *path, +- struct smb_ntsd *pntsd, int len) ++ struct smb_ntsd *pntsd, int len, ++ bool get_write) + { + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; +@@ -1471,7 +1496,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + + rc = ksmbd_vfs_setxattr(user_ns, path, + XATTR_NAME_SD, sd_ndr.data, +- sd_ndr.offset, 0); ++ sd_ndr.offset, 0, get_write); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + +@@ -1560,7 +1585,8 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + const struct path *path, +- struct xattr_dos_attrib *da) ++ struct xattr_dos_attrib *da, ++ bool get_write) + { + struct ndr n; + int err; +@@ -1570,7 +1596,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + return err; + + err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE, +- (void *)n.data, n.offset, 0); ++ (void *)n.data, n.offset, 0, get_write); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); +@@ -1840,10 +1866,6 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + } + posix_state_to_acl(&acl_state, acls->a_entries); + +- rc = mnt_want_write(path->mnt); +- if (rc) +- goto out_err; +- + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", +@@ -1856,9 +1878,7 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } +- mnt_drop_write(path->mnt); + +-out_err: + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +@@ -1887,10 +1907,6 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + } + } + +- rc = mnt_want_write(path->mnt); +- if (rc) +- goto out_err; +- + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", +@@ -1902,9 +1918,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } +- mnt_drop_write(path->mnt); + +-out_err: + posix_acl_release(acls); + return rc; + } +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 93799ca4cc345..e761dde2443e2 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -109,7 +109,8 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + int attr_name_len); + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + const struct path *path, const char *attr_name, +- void *attr_value, size_t attr_size, int flags); ++ void *attr_value, size_t attr_size, int flags, ++ bool get_write); + int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, +@@ -117,6 +118,7 @@ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *parent_path, + struct path *path, bool caseless); ++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path); + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +@@ -144,14 +146,16 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + const struct path *path, +- struct smb_ntsd *pntsd, int len); ++ struct smb_ntsd *pntsd, int len, ++ bool get_write); + int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + const struct path *path, +- struct xattr_dos_attrib *da); ++ struct xattr_dos_attrib *da, ++ bool get_write); + int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch b/queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch new file mode 100644 index 00000000000..e884e7aa421 --- /dev/null +++ b/queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch @@ -0,0 +1,43 @@ +From 095cce1d119cb8e983f458775c02fa4083b9603b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:10 +0900 +Subject: ksmbd: fix potential double free on smb2_read_pipe() error path + +From: Namjae Jeon + +[ Upstream commit 1903e6d0578118e9aab1ee23f4a9de55737d1d05 ] + +Fix new smatch warnings: +fs/smb/server/smb2pdu.c:6131 smb2_read_pipe() error: double free of 'rpc_resp' + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index c773272cd3ac2..a89a69d752a3b 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -6152,12 +6152,12 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; +- kvfree(rpc_resp); + err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, + offsetof(struct smb2_read_rsp, Buffer), + aux_payload_buf, nbytes); + if (err) + goto out; ++ kvfree(rpc_resp); + } else { + err = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_read_rsp, Buffer)); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch b/queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch new file mode 100644 index 00000000000..ac8f94eac1c --- /dev/null +++ b/queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch @@ -0,0 +1,280 @@ +From 67710341be33231c229f95062e6fc7dc194e81cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:07 +0900 +Subject: ksmbd: fix race condition between tree conn lookup and disconnect + +From: Namjae Jeon + +[ Upstream commit 33b235a6e6ebe0f05f3586a71e8d281d00f71e2e ] + +if thread A in smb2_write is using work-tcon, other thread B use +smb2_tree_disconnect free the tcon, then thread A will use free'd tcon. + + Time + + + Thread A | Thread A + smb2_write | smb2_tree_disconnect + | + | + | kfree(tree_conn) + | + // UAF! | + work->tcon->share_conf | + + + +This patch add state, reference count and lock for tree conn to fix race +condition issue. + +Reported-by: luosili +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/mgmt/tree_connect.c | 42 ++++++++++++++++++++++++-- + fs/smb/server/mgmt/tree_connect.h | 11 +++++-- + fs/smb/server/mgmt/user_session.c | 1 + + fs/smb/server/mgmt/user_session.h | 1 + + fs/smb/server/server.c | 2 ++ + fs/smb/server/smb2pdu.c | 50 +++++++++++++++++++++++-------- + 6 files changed, 90 insertions(+), 17 deletions(-) + +diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c +index 408cddf2f094a..d2c81a8a11dda 100644 +--- a/fs/smb/server/mgmt/tree_connect.c ++++ b/fs/smb/server/mgmt/tree_connect.c +@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + + tree_conn->user = sess->user; + tree_conn->share_conf = sc; ++ tree_conn->t_state = TREE_NEW; + status.tree_conn = tree_conn; ++ atomic_set(&tree_conn->refcount, 1); ++ init_waitqueue_head(&tree_conn->refcount_q); + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); +@@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + return status; + } + ++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) ++{ ++ /* ++ * Checking waitqueue to releasing tree connect on ++ * tree disconnect. waitqueue_active is safe because it ++ * uses atomic operation for condition. ++ */ ++ if (!atomic_dec_return(&tcon->refcount) && ++ waitqueue_active(&tcon->refcount_q)) ++ wake_up(&tcon->refcount_q); ++} ++ + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) + { + int ret; + ++ write_lock(&sess->tree_conns_lock); ++ xa_erase(&sess->tree_conns, tree_conn->id); ++ write_unlock(&sess->tree_conns_lock); ++ ++ if (!atomic_dec_and_test(&tree_conn->refcount)) ++ wait_event(tree_conn->refcount_q, ++ atomic_read(&tree_conn->refcount) == 0); ++ + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); +- xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +@@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + { + struct ksmbd_tree_connect *tcon; + ++ read_lock(&sess->tree_conns_lock); + tcon = xa_load(&sess->tree_conns, id); + if (tcon) { +- if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) ++ if (tcon->t_state != TREE_CONNECTED) ++ tcon = NULL; ++ else if (!atomic_inc_not_zero(&tcon->refcount)) + tcon = NULL; + } ++ read_unlock(&sess->tree_conns_lock); + + return tcon; + } +@@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) + if (!sess) + return -EINVAL; + +- xa_for_each(&sess->tree_conns, id, tc) ++ xa_for_each(&sess->tree_conns, id, tc) { ++ write_lock(&sess->tree_conns_lock); ++ if (tc->t_state == TREE_DISCONNECTED) { ++ write_unlock(&sess->tree_conns_lock); ++ ret = -ENOENT; ++ continue; ++ } ++ tc->t_state = TREE_DISCONNECTED; ++ write_unlock(&sess->tree_conns_lock); ++ + ret |= ksmbd_tree_conn_disconnect(sess, tc); ++ } + xa_destroy(&sess->tree_conns); + return ret; + } +diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h +index 562d647ad9fad..6377a70b811c8 100644 +--- a/fs/smb/server/mgmt/tree_connect.h ++++ b/fs/smb/server/mgmt/tree_connect.h +@@ -14,7 +14,11 @@ struct ksmbd_share_config; + struct ksmbd_user; + struct ksmbd_conn; + +-#define TREE_CONN_EXPIRE 1 ++enum { ++ TREE_NEW = 0, ++ TREE_CONNECTED, ++ TREE_DISCONNECTED ++}; + + struct ksmbd_tree_connect { + int id; +@@ -27,7 +31,9 @@ struct ksmbd_tree_connect { + + int maximal_access; + bool posix_extensions; +- unsigned long status; ++ atomic_t refcount; ++ wait_queue_head_t refcount_q; ++ unsigned int t_state; + }; + + struct ksmbd_tree_conn_status { +@@ -46,6 +52,7 @@ struct ksmbd_session; + struct ksmbd_tree_conn_status + ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name); ++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); + + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); +diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c +index b8be14a96cf66..15f68ee050894 100644 +--- a/fs/smb/server/mgmt/user_session.c ++++ b/fs/smb/server/mgmt/user_session.c +@@ -355,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol) + xa_init(&sess->ksmbd_chann_list); + xa_init(&sess->rpc_handle_list); + sess->sequence_number = 1; ++ rwlock_init(&sess->tree_conns_lock); + + ret = __init_smb2_session(sess); + if (ret) +diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h +index f99d475b28db4..63cb08fffde84 100644 +--- a/fs/smb/server/mgmt/user_session.h ++++ b/fs/smb/server/mgmt/user_session.h +@@ -60,6 +60,7 @@ struct ksmbd_session { + + struct ksmbd_file_table file_table; + unsigned long last_active; ++ rwlock_t tree_conns_lock; + }; + + static inline int test_session_flag(struct ksmbd_session *sess, int bit) +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index 633383e55723c..11b201e6ee44b 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -241,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + } while (is_chained == true); + + send: ++ if (work->tcon) ++ ksmbd_tree_connect_put(work->tcon); + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index a76529512acfb..aad08866746c8 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work) + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + ++ write_lock(&sess->tree_conns_lock); ++ status.tree_conn->t_state = TREE_CONNECTED; ++ write_unlock(&sess->tree_conns_lock); + rsp->StructureSize = cpu_to_le16(16); + out_err1: + rsp->Capabilities = 0; +@@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work) + + ksmbd_debug(SMB, "request\n"); + ++ if (!tcon) { ++ ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); ++ ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ err = -ENOENT; ++ goto err_out; ++ } ++ ++ ksmbd_close_tree_conn_fds(work); ++ ++ write_lock(&sess->tree_conns_lock); ++ if (tcon->t_state == TREE_DISCONNECTED) { ++ write_unlock(&sess->tree_conns_lock); ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ err = -ENOENT; ++ goto err_out; ++ } ++ ++ WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); ++ tcon->t_state = TREE_DISCONNECTED; ++ write_unlock(&sess->tree_conns_lock); ++ ++ err = ksmbd_tree_conn_disconnect(sess, tcon); ++ if (err) { ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ goto err_out; ++ } ++ ++ work->tcon = NULL; ++ + rsp->StructureSize = cpu_to_le16(4); + err = ksmbd_iov_pin_rsp(work, rsp, + sizeof(struct smb2_tree_disconnect_rsp)); + if (err) { + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; +- smb2_set_err_rsp(work); +- return err; ++ goto err_out; + } + +- if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { +- ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); ++ return 0; + +- rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; +- smb2_set_err_rsp(work); +- return -ENOENT; +- } ++err_out: ++ smb2_set_err_rsp(work); ++ return err; + +- ksmbd_close_tree_conn_fds(work); +- ksmbd_tree_conn_disconnect(sess, tcon); +- work->tcon = NULL; +- return 0; + } + + /** +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch new file mode 100644 index 00000000000..8da6cbb008c --- /dev/null +++ b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch @@ -0,0 +1,85 @@ +From f27d88cd8c7a4cd964f92f4bd2d661a92ef5a153 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:06 +0900 +Subject: ksmbd: fix race condition from parallel smb2 lock requests + +From: Namjae Jeon + +[ Upstream commit 75ac9a3dd65f7eab4d12b0a0f744234b5300a491 ] + +There is a race condition issue between parallel smb2 lock request. + + Time + + +Thread A | Thread A +smb2_lock | smb2_lock + | + insert smb_lock to lock_list | + spin_unlock(&work->conn->llist_lock) | + | + | spin_lock(&conn->llist_lock); + | kfree(cmp_lock); + | + // UAF! | + list_add(&smb_lock->llist, &rollback_list) + + +This patch swaps the line for adding the smb lock to the rollback list and +adding the lock list of connection to fix the race issue. + +Reported-by: luosili +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index e8c779fa354ca..a76529512acfb 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -7038,10 +7038,6 @@ int smb2_lock(struct ksmbd_work *work) + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); +- spin_lock(&work->conn->llist_lock); +- list_add_tail(&smb_lock->clist, +- &work->conn->lock_list); +- spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); +@@ -7073,9 +7069,6 @@ int smb2_lock(struct ksmbd_work *work) + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); +- spin_lock(&work->conn->llist_lock); +- list_del(&smb_lock->clist); +- spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { +@@ -7095,19 +7088,16 @@ int smb2_lock(struct ksmbd_work *work) + } + + list_del(&smb_lock->llist); +- spin_lock(&work->conn->llist_lock); +- list_del(&smb_lock->clist); +- spin_unlock(&work->conn->llist_lock); + release_async_work(work); + goto retry; + } else if (!rc) { ++ list_add(&smb_lock->llist, &rollback_list); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); +- list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch new file mode 100644 index 00000000000..5f700984185 --- /dev/null +++ b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch @@ -0,0 +1,71 @@ +From 5547b324bb52d3478a4a7ad73d054861ed035aca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:05 +0900 +Subject: ksmbd: fix race condition from parallel smb2 logoff requests + +From: Namjae Jeon + +[ Upstream commit 7ca9da7d873ee8024e9548d3366101c2b6843eab ] + +If parallel smb2 logoff requests come in before closing door, running +request count becomes more than 1 even though connection status is set to +KSMBD_SESS_NEED_RECONNECT. It can't get condition true, and sleep forever. +This patch fix race condition problem by returning error if connection +status was already set to KSMBD_SESS_NEED_RECONNECT. + +Reported-by: luosili +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index d5bf1f480700a..e8c779fa354ca 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2164,17 +2164,17 @@ int smb2_session_logoff(struct ksmbd_work *work) + + ksmbd_debug(SMB, "request\n"); + +- sess_id = le64_to_cpu(req->hdr.SessionId); +- +- rsp->StructureSize = cpu_to_le16(4); +- err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); +- if (err) { +- rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ ksmbd_conn_lock(conn); ++ if (!ksmbd_conn_good(conn)) { ++ ksmbd_conn_unlock(conn); ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); +- return err; ++ return -ENOENT; + } +- ++ sess_id = le64_to_cpu(req->hdr.SessionId); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); ++ ksmbd_conn_unlock(conn); ++ + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn, sess_id); + +@@ -2196,6 +2196,14 @@ int smb2_session_logoff(struct ksmbd_work *work) + ksmbd_free_user(sess->user); + sess->user = NULL; + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); ++ ++ rsp->StructureSize = cpu_to_le16(4); ++ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); ++ if (err) { ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ smb2_set_err_rsp(work); ++ return err; ++ } + return 0; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-race-condition-with-fp.patch b/queue-6.1/ksmbd-fix-race-condition-with-fp.patch new file mode 100644 index 00000000000..8f24d029b36 --- /dev/null +++ b/queue-6.1/ksmbd-fix-race-condition-with-fp.patch @@ -0,0 +1,152 @@ +From b57845f40f0bba8a4b103274bc0d12a861038b48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:04 +0900 +Subject: ksmbd: fix race condition with fp + +From: Namjae Jeon + +[ Upstream commit 5a7ee91d1154f35418367a6eaae74046fd06ed89 ] + +fp can used in each command. If smb2_close command is coming at the +same time, UAF issue can happen by race condition. + + Time + + +Thread A | Thread B1 B2 .... B5 +smb2_open | smb2_close + | + __open_id | + insert fp to file_table | + | + | atomic_dec_and_test(&fp->refcount) + | if fp->refcount == 0, free fp by kfree. + // UAF! | + use fp | + + +This patch add f_state not to use freed fp is used and not to free fp in +use. + +Reported-by: luosili +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 +++- + fs/smb/server/vfs_cache.c | 23 ++++++++++++++++++++--- + fs/smb/server/vfs_cache.h | 9 +++++++++ + 3 files changed, 32 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 42697ea86d47b..d5bf1f480700a 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3370,8 +3370,10 @@ int smb2_open(struct ksmbd_work *work) + } + ksmbd_revert_fsids(work); + err_out1: +- if (!rc) ++ if (!rc) { ++ ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); ++ } + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 94ad8fa07b46e..f600279b0a9ee 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -332,6 +332,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) + + static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) + { ++ if (fp->f_state != FP_INITED) ++ return NULL; ++ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +@@ -381,15 +384,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + return 0; + + ft = &work->sess->file_table; +- read_lock(&ft->lock); ++ write_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + +- if (!atomic_dec_and_test(&fp->refcount)) ++ if (fp->f_state != FP_INITED) + fp = NULL; ++ else { ++ fp->f_state = FP_CLOSED; ++ if (!atomic_dec_and_test(&fp->refcount)) ++ fp = NULL; ++ } + } +- read_unlock(&ft->lock); ++ write_unlock(&ft->lock); + + if (!fp) + return -EINVAL; +@@ -569,6 +577,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; ++ fp->f_state = FP_NEW; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { +@@ -590,6 +599,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) + return ERR_PTR(ret); + } + ++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, ++ unsigned int state) ++{ ++ write_lock(&ft->lock); ++ fp->f_state = state; ++ write_unlock(&ft->lock); ++} ++ + static int + __close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index fcb13413fa8d9..03d0bf941216f 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -60,6 +60,12 @@ struct ksmbd_inode { + __le32 m_fattr; + }; + ++enum { ++ FP_NEW = 0, ++ FP_INITED, ++ FP_CLOSED ++}; ++ + struct ksmbd_file { + struct file *filp; + u64 persistent_id; +@@ -98,6 +104,7 @@ struct ksmbd_file { + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; ++ unsigned int f_state; + }; + + static inline void set_ctx_actor(struct dir_context *ctx, +@@ -142,6 +149,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); + int ksmbd_init_global_file_table(void); + void ksmbd_free_global_file_table(void); + void ksmbd_set_fd_limit(unsigned long limit); ++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, ++ unsigned int state); + + /* + * INODE hash +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch b/queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch new file mode 100644 index 00000000000..4367d71e6b1 --- /dev/null +++ b/queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch @@ -0,0 +1,1193 @@ +From 998937d36cb39e921a2def28ee189b78bb236fad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:37 +0900 +Subject: ksmbd: fix racy issue from using ->d_parent and ->d_name + +From: Namjae Jeon + +[ Upstream commit 74d7970febf7e9005375aeda0df821d2edffc9f7 ] + +Al pointed out that ksmbd has racy issue from using ->d_parent and ->d_name +in ksmbd_vfs_unlink and smb2_vfs_rename(). and use new lock_rename_child() +to lock stable parent while underlying rename racy. +Introduce vfs_path_parent_lookup helper to avoid out of share access and +export vfs functions like the following ones to use +vfs_path_parent_lookup(). + - rename __lookup_hash() to lookup_one_qstr_excl(). + - export lookup_one_qstr_excl(). + - export getname_kernel() and putname(). + +vfs_path_parent_lookup() is used for parent lookup of destination file +using absolute pathname given from FILE_RENAME_INFORMATION request. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/namei.c | 57 +++-- + fs/smb/server/smb2pdu.c | 147 +++---------- + fs/smb/server/vfs.c | 435 ++++++++++++++++---------------------- + fs/smb/server/vfs.h | 19 +- + fs/smb/server/vfs_cache.c | 5 +- + include/linux/namei.h | 6 + + 6 files changed, 283 insertions(+), 386 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 6daaf84567195..b5578f4ce5d6e 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -253,6 +253,7 @@ getname_kernel(const char * filename) + + return result; + } ++EXPORT_SYMBOL(getname_kernel); + + void putname(struct filename *name) + { +@@ -271,6 +272,7 @@ void putname(struct filename *name) + } else + __putname(name); + } ++EXPORT_SYMBOL(putname); + + /** + * check_acl - perform ACL permission checking +@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name, + * when directory is guaranteed to have no in-lookup children + * at all. + */ +-static struct dentry *__lookup_hash(const struct qstr *name, +- struct dentry *base, unsigned int flags) ++struct dentry *lookup_one_qstr_excl(const struct qstr *name, ++ struct dentry *base, ++ unsigned int flags) + { + struct dentry *dentry = lookup_dcache(name, base, flags); + struct dentry *old; +@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name, + } + return dentry; + } ++EXPORT_SYMBOL(lookup_one_qstr_excl); + + static struct dentry *lookup_fast(struct nameidata *nd) + { +@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags, + } + + /* Note: this does not consume "name" */ +-static int filename_parentat(int dfd, struct filename *name, +- unsigned int flags, struct path *parent, +- struct qstr *last, int *type) ++static int __filename_parentat(int dfd, struct filename *name, ++ unsigned int flags, struct path *parent, ++ struct qstr *last, int *type, ++ const struct path *root) + { + int retval; + struct nameidata nd; + + if (IS_ERR(name)) + return PTR_ERR(name); +- set_nameidata(&nd, dfd, name, NULL); ++ set_nameidata(&nd, dfd, name, root); + retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); + if (unlikely(retval == -ECHILD)) + retval = path_parentat(&nd, flags, parent); +@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name, + return retval; + } + ++static int filename_parentat(int dfd, struct filename *name, ++ unsigned int flags, struct path *parent, ++ struct qstr *last, int *type) ++{ ++ return __filename_parentat(dfd, name, flags, parent, last, type, NULL); ++} ++ + /* does lookup, returns the object with parent locked */ + static struct dentry *__kern_path_locked(struct filename *name, struct path *path) + { +@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat + return ERR_PTR(-EINVAL); + } + inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); +- d = __lookup_hash(&last, path->dentry, 0); ++ d = lookup_one_qstr_excl(&last, path->dentry, 0); + if (IS_ERR(d)) { + inode_unlock(path->dentry->d_inode); + path_put(path); +@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path) + } + EXPORT_SYMBOL(kern_path); + ++/** ++ * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair ++ * @filename: filename structure ++ * @flags: lookup flags ++ * @parent: pointer to struct path to fill ++ * @last: last component ++ * @type: type of the last component ++ * @root: pointer to struct path of the base directory ++ */ ++int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, ++ struct path *parent, struct qstr *last, int *type, ++ const struct path *root) ++{ ++ return __filename_parentat(AT_FDCWD, filename, flags, parent, last, ++ type, root); ++} ++EXPORT_SYMBOL(vfs_path_parent_lookup); ++ + /** + * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair + * @dentry: pointer to dentry of the base directory +@@ -3852,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, + if (last.name[last.len] && !want_dir) + create_flags = 0; + inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags); ++ dentry = lookup_one_qstr_excl(&last, path->dentry, ++ reval_flag | create_flags); + if (IS_ERR(dentry)) + goto unlock; + +@@ -4214,7 +4245,7 @@ int do_rmdir(int dfd, struct filename *name) + goto exit2; + + inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path.dentry, lookup_flags); ++ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit3; +@@ -4348,7 +4379,7 @@ int do_unlinkat(int dfd, struct filename *name) + goto exit2; + retry_deleg: + inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path.dentry, lookup_flags); ++ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct user_namespace *mnt_userns; +@@ -4922,7 +4953,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, + retry_deleg: + trap = lock_rename(new_path.dentry, old_path.dentry); + +- old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); ++ old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry, ++ lookup_flags); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit3; +@@ -4930,7 +4962,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, + error = -ENOENT; + if (d_is_negative(old_dentry)) + goto exit4; +- new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); ++ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, ++ lookup_flags | target_flags); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit4; +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index ca57e85abf915..fe10c75f6f2b9 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2506,7 +2506,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + return rc; + } + +- rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); ++ rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); +@@ -2797,8 +2797,10 @@ int smb2_open(struct ksmbd_work *work) + goto err_out1; + } + +- rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); ++ rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { ++ file_present = true; ++ + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access +@@ -2807,7 +2809,6 @@ int smb2_open(struct ksmbd_work *work) + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; +- path_put(&path); + goto err_out; + } + +@@ -2815,26 +2816,23 @@ int smb2_open(struct ksmbd_work *work) + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; +- path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; +- path_put(&path); + goto err_out; + } +- } + +- if (rc) { ++ file_present = true; ++ user_ns = mnt_user_ns(path.mnt); ++ } else { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; +- } else { +- file_present = true; +- user_ns = mnt_user_ns(path.mnt); + } ++ + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { +@@ -2962,8 +2960,9 @@ int smb2_open(struct ksmbd_work *work) + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { +- rc = ksmbd_vfs_may_delete(user_ns, +- path.dentry); ++ rc = inode_permission(user_ns, ++ d_inode(path.dentry->d_parent), ++ MAY_EXEC | MAY_WRITE); + if (rc) + goto err_out; + } +@@ -3334,10 +3333,13 @@ int smb2_open(struct ksmbd_work *work) + } + + err_out: +- if (file_present || created) +- path_put(&path); ++ if (file_present || created) { ++ inode_unlock(d_inode(path.dentry->d_parent)); ++ dput(path.dentry); ++ } + ksmbd_revert_fsids(work); + err_out1: ++ + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; +@@ -5481,44 +5483,19 @@ int smb2_echo(struct ksmbd_work *work) + + static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, +- struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) + { + struct ksmbd_share_config *share = fp->tcon->share_conf; +- char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; +- char *pathname = NULL; +- struct path path; +- bool file_present = true; +- int rc; ++ char *new_name = NULL; ++ int rc, flags = 0; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); +- pathname = kmalloc(PATH_MAX, GFP_KERNEL); +- if (!pathname) +- return -ENOMEM; +- +- abs_oldname = file_path(fp->filp, pathname, PATH_MAX); +- if (IS_ERR(abs_oldname)) { +- rc = -EINVAL; +- goto out; +- } +- old_name = strrchr(abs_oldname, '/'); +- if (old_name && old_name[1] != '\0') { +- old_name++; +- } else { +- ksmbd_debug(SMB, "can't get last component in path %s\n", +- abs_oldname); +- rc = -ENOENT; +- goto out; +- } +- + new_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); +- if (IS_ERR(new_name)) { +- rc = PTR_ERR(new_name); +- goto out; +- } ++ if (IS_ERR(new_name)) ++ return PTR_ERR(new_name); + + if (strchr(new_name, ':')) { + int s_type; +@@ -5544,7 +5521,7 @@ static int smb2_rename(struct ksmbd_work *work, + if (rc) + goto out; + +- rc = ksmbd_vfs_setxattr(user_ns, ++ rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); +@@ -5559,47 +5536,18 @@ static int smb2_rename(struct ksmbd_work *work, + } + + ksmbd_debug(SMB, "new name %s\n", new_name); +- rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); +- if (rc) { +- if (rc != -ENOENT) +- goto out; +- file_present = false; +- } else { +- path_put(&path); +- } +- + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + +- if (file_info->ReplaceIfExists) { +- if (file_present) { +- rc = ksmbd_vfs_remove_file(work, new_name); +- if (rc) { +- if (rc != -ENOTEMPTY) +- rc = -EINVAL; +- ksmbd_debug(SMB, "cannot delete %s, rc %d\n", +- new_name, rc); +- goto out; +- } +- } +- } else { +- if (file_present && +- strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { +- rc = -EEXIST; +- ksmbd_debug(SMB, +- "cannot rename already existing file\n"); +- goto out; +- } +- } ++ if (!file_info->ReplaceIfExists) ++ flags = RENAME_NOREPLACE; + +- rc = ksmbd_vfs_fp_rename(work, fp, new_name); ++ rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags); + out: +- kfree(pathname); +- if (!IS_ERR(new_name)) +- kfree(new_name); ++ kfree(new_name); + return rc; + } + +@@ -5639,18 +5587,17 @@ static int smb2_create_link(struct ksmbd_work *work, + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); +- rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); ++ rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, ++ &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; +- } else { +- path_put(&path); + } + + if (file_info->ReplaceIfExists) { + if (file_present) { +- rc = ksmbd_vfs_remove_file(work, link_name); ++ rc = ksmbd_vfs_remove_file(work, &path); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", +@@ -5670,6 +5617,10 @@ static int smb2_create_link(struct ksmbd_work *work, + if (rc) + rc = -EINVAL; + out: ++ if (file_present) { ++ inode_unlock(d_inode(path.dentry->d_parent)); ++ path_put(&path); ++ } + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); +@@ -5847,12 +5798,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) + { +- struct user_namespace *user_ns; +- struct ksmbd_file *parent_fp; +- struct dentry *parent; +- struct dentry *dentry = fp->filp->f_path.dentry; +- int ret; +- + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; +@@ -5862,32 +5807,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + +- user_ns = file_mnt_user_ns(fp->filp); +- if (ksmbd_stream_fd(fp)) +- goto next; +- +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; +- } +- +- parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); +- inode_unlock(d_inode(parent)); +- dput(parent); ++ if (!le32_to_cpu(rename_info->FileNameLength)) ++ return -EINVAL; + +- if (parent_fp) { +- if (parent_fp->daccess & FILE_DELETE_LE) { +- pr_err("parent dir is opened with delete access\n"); +- ksmbd_fd_put(work, parent_fp); +- return -ESHARE; +- } +- ksmbd_fd_put(work, parent_fp); +- } +-next: +- return smb2_rename(work, fp, user_ns, rename_info, +- work->conn->local_nls); ++ return smb2_rename(work, fp, rename_info, work->conn->local_nls); + } + + static int set_file_disposition_info(struct ksmbd_file *fp, +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 187d31d984946..58a6665f1c3ab 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include "../../internal.h" /* for vfs_path_lookup */ + +@@ -36,19 +37,6 @@ + #include "mgmt/user_session.h" + #include "mgmt/user_config.h" + +-static char *extract_last_component(char *path) +-{ +- char *p = strrchr(path, '/'); +- +- if (p && p[1] != '\0') { +- *p = '\0'; +- p++; +- } else { +- p = NULL; +- } +- return p; +-} +- + static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +@@ -62,65 +50,77 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + + /** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable +- * +- * the parent dentry got by dget_parent or @parent could be +- * unstable, we try to lock a parent inode and lookup the +- * child dentry again. +- * +- * the reference count of @parent isn't incremented. + */ +-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, +- struct dentry *child) ++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) + { +- struct dentry *dentry; +- int ret = 0; +- + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); +- dentry = lookup_one(user_ns, child->d_name.name, parent, +- child->d_name.len); +- if (IS_ERR(dentry)) { +- ret = PTR_ERR(dentry); +- goto out_err; +- } +- +- if (dentry != child) { +- ret = -ESTALE; +- dput(dentry); +- goto out_err; ++ if (child->d_parent != parent) { ++ inode_unlock(d_inode(parent)); ++ return -ENOENT; + } + +- dput(dentry); + return 0; +-out_err: +- inode_unlock(d_inode(parent)); +- return ret; + } + +-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, +- struct dentry *dentry) ++static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, ++ char *pathname, unsigned int flags, ++ struct path *path) + { +- struct dentry *parent; +- int ret; ++ struct qstr last; ++ struct filename *filename; ++ struct path *root_share_path = &share_conf->vfs_path; ++ int err, type; ++ struct path parent_path; ++ struct dentry *d; ++ ++ if (pathname[0] == '\0') { ++ pathname = share_conf->path; ++ root_share_path = NULL; ++ } else { ++ flags |= LOOKUP_BENEATH; ++ } + +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; ++ filename = getname_kernel(pathname); ++ if (IS_ERR(filename)) ++ return PTR_ERR(filename); ++ ++ err = vfs_path_parent_lookup(filename, flags, ++ &parent_path, &last, &type, ++ root_share_path); ++ putname(filename); ++ if (err) ++ return err; ++ ++ if (unlikely(type != LAST_NORM)) { ++ path_put(&parent_path); ++ return -ENOENT; + } + +- ret = inode_permission(user_ns, d_inode(parent), +- MAY_EXEC | MAY_WRITE); ++ inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); ++ d = lookup_one_qstr_excl(&last, parent_path.dentry, 0); ++ if (IS_ERR(d)) ++ goto err_out; + +- inode_unlock(d_inode(parent)); +- dput(parent); +- return ret; ++ if (d_is_negative(d)) { ++ dput(d); ++ goto err_out; ++ } ++ ++ path->dentry = d; ++ path->mnt = share_conf->vfs_path.mnt; ++ path_put(&parent_path); ++ ++ return 0; ++ ++err_out: ++ inode_unlock(parent_path.dentry->d_inode); ++ path_put(&parent_path); ++ return -ENOENT; + } + + int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) + { +- struct dentry *parent; + int ret = 0; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); +@@ -137,18 +137,9 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + *daccess |= FILE_EXECUTE_LE; + +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; +- } +- +- if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) ++ if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; + +- inode_unlock(d_inode(parent)); +- dput(parent); + return ret; + } + +@@ -581,54 +572,32 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) + * + * Return: 0 on success, otherwise error + */ +-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) ++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + { + struct user_namespace *user_ns; +- struct path path; +- struct dentry *parent; ++ struct dentry *parent = path->dentry->d_parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + +- err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); +- if (err) { +- ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); +- ksmbd_revert_fsids(work); +- return err; +- } +- +- user_ns = mnt_user_ns(path.mnt); +- parent = dget_parent(path.dentry); +- err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); +- if (err) { +- dput(parent); +- path_put(&path); +- ksmbd_revert_fsids(work); +- return err; +- } +- +- if (!d_inode(path.dentry)->i_nlink) { ++ if (!d_inode(path->dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + +- if (S_ISDIR(d_inode(path.dentry)->i_mode)) { +- err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); ++ user_ns = mnt_user_ns(path->mnt); ++ if (S_ISDIR(d_inode(path->dentry)->i_mode)) { ++ err = vfs_rmdir(user_ns, d_inode(parent), path->dentry); + if (err && err != -ENOTEMPTY) +- ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, +- err); ++ ksmbd_debug(VFS, "rmdir failed, err %d\n", err); + } else { +- err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); ++ err = vfs_unlink(user_ns, d_inode(parent), path->dentry, NULL); + if (err) +- ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, +- err); ++ ksmbd_debug(VFS, "unlink failed, err %d\n", err); + } + + out_err: +- inode_unlock(d_inode(parent)); +- dput(parent); +- path_put(&path); + ksmbd_revert_fsids(work); + return err; + } +@@ -687,149 +656,114 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + return err; + } + +-static int ksmbd_validate_entry_in_use(struct dentry *src_dent) ++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, ++ char *newname, int flags) + { +- struct dentry *dst_dent; +- +- spin_lock(&src_dent->d_lock); +- list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { +- struct ksmbd_file *child_fp; ++ struct dentry *old_parent, *new_dentry, *trap; ++ struct dentry *old_child = old_path->dentry; ++ struct path new_path; ++ struct qstr new_last; ++ struct renamedata rd; ++ struct filename *to; ++ struct ksmbd_share_config *share_conf = work->tcon->share_conf; ++ struct ksmbd_file *parent_fp; ++ int new_type; ++ int err, lookup_flags = LOOKUP_NO_SYMLINKS; + +- if (d_really_is_negative(dst_dent)) +- continue; ++ if (ksmbd_override_fsids(work)) ++ return -ENOMEM; + +- child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); +- if (child_fp) { +- spin_unlock(&src_dent->d_lock); +- ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); +- return -EACCES; +- } ++ to = getname_kernel(newname); ++ if (IS_ERR(to)) { ++ err = PTR_ERR(to); ++ goto revert_fsids; + } +- spin_unlock(&src_dent->d_lock); + +- return 0; +-} ++retry: ++ err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, ++ &new_path, &new_last, &new_type, ++ &share_conf->vfs_path); ++ if (err) ++ goto out1; + +-static int __ksmbd_vfs_rename(struct ksmbd_work *work, +- struct user_namespace *src_user_ns, +- struct dentry *src_dent_parent, +- struct dentry *src_dent, +- struct user_namespace *dst_user_ns, +- struct dentry *dst_dent_parent, +- struct dentry *trap_dent, +- char *dst_name) +-{ +- struct dentry *dst_dent; +- int err; ++ if (old_path->mnt != new_path.mnt) { ++ err = -EXDEV; ++ goto out2; ++ } + +- if (!work->tcon->posix_extensions) { +- err = ksmbd_validate_entry_in_use(src_dent); +- if (err) +- return err; ++ trap = lock_rename_child(old_child, new_path.dentry); ++ ++ old_parent = dget(old_child->d_parent); ++ if (d_unhashed(old_child)) { ++ err = -EINVAL; ++ goto out3; + } + +- if (d_really_is_negative(src_dent_parent)) +- return -ENOENT; +- if (d_really_is_negative(dst_dent_parent)) +- return -ENOENT; +- if (d_really_is_negative(src_dent)) +- return -ENOENT; +- if (src_dent == trap_dent) +- return -EINVAL; ++ parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent)); ++ if (parent_fp) { ++ if (parent_fp->daccess & FILE_DELETE_LE) { ++ pr_err("parent dir is opened with delete access\n"); ++ err = -ESHARE; ++ ksmbd_fd_put(work, parent_fp); ++ goto out3; ++ } ++ ksmbd_fd_put(work, parent_fp); ++ } + +- if (ksmbd_override_fsids(work)) +- return -ENOMEM; ++ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, ++ lookup_flags | LOOKUP_RENAME_TARGET); ++ if (IS_ERR(new_dentry)) { ++ err = PTR_ERR(new_dentry); ++ goto out3; ++ } + +- dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, +- strlen(dst_name)); +- err = PTR_ERR(dst_dent); +- if (IS_ERR(dst_dent)) { +- pr_err("lookup failed %s [%d]\n", dst_name, err); +- goto out; ++ if (d_is_symlink(new_dentry)) { ++ err = -EACCES; ++ goto out4; + } + +- err = -ENOTEMPTY; +- if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { +- struct renamedata rd = { +- .old_mnt_userns = src_user_ns, +- .old_dir = d_inode(src_dent_parent), +- .old_dentry = src_dent, +- .new_mnt_userns = dst_user_ns, +- .new_dir = d_inode(dst_dent_parent), +- .new_dentry = dst_dent, +- }; +- err = vfs_rename(&rd); ++ if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { ++ err = -EEXIST; ++ goto out4; + } +- if (err) +- pr_err("vfs_rename failed err %d\n", err); +- if (dst_dent) +- dput(dst_dent); +-out: +- ksmbd_revert_fsids(work); +- return err; +-} + +-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, +- char *newname) +-{ +- struct user_namespace *user_ns; +- struct path dst_path; +- struct dentry *src_dent_parent, *dst_dent_parent; +- struct dentry *src_dent, *trap_dent, *src_child; +- char *dst_name; +- int err; ++ if (old_child == trap) { ++ err = -EINVAL; ++ goto out4; ++ } + +- dst_name = extract_last_component(newname); +- if (!dst_name) { +- dst_name = newname; +- newname = ""; ++ if (new_dentry == trap) { ++ err = -ENOTEMPTY; ++ goto out4; + } + +- src_dent_parent = dget_parent(fp->filp->f_path.dentry); +- src_dent = fp->filp->f_path.dentry; ++ rd.old_mnt_userns = mnt_user_ns(old_path->mnt), ++ rd.old_dir = d_inode(old_parent), ++ rd.old_dentry = old_child, ++ rd.new_mnt_userns = mnt_user_ns(new_path.mnt), ++ rd.new_dir = new_path.dentry->d_inode, ++ rd.new_dentry = new_dentry, ++ rd.flags = flags, ++ err = vfs_rename(&rd); ++ if (err) ++ ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); + +- err = ksmbd_vfs_kern_path(work, newname, +- LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, +- &dst_path, false); +- if (err) { +- ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); +- goto out; ++out4: ++ dput(new_dentry); ++out3: ++ dput(old_parent); ++ unlock_rename(old_parent, new_path.dentry); ++out2: ++ path_put(&new_path); ++ ++ if (retry_estale(err, lookup_flags)) { ++ lookup_flags |= LOOKUP_REVAL; ++ goto retry; + } +- dst_dent_parent = dst_path.dentry; +- +- trap_dent = lock_rename(src_dent_parent, dst_dent_parent); +- dget(src_dent); +- dget(dst_dent_parent); +- user_ns = file_mnt_user_ns(fp->filp); +- src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, +- src_dent->d_name.len); +- if (IS_ERR(src_child)) { +- err = PTR_ERR(src_child); +- goto out_lock; +- } +- +- if (src_child != src_dent) { +- err = -ESTALE; +- dput(src_child); +- goto out_lock; +- } +- dput(src_child); +- +- err = __ksmbd_vfs_rename(work, +- user_ns, +- src_dent_parent, +- src_dent, +- mnt_user_ns(dst_path.mnt), +- dst_dent_parent, +- trap_dent, +- dst_name); +-out_lock: +- dput(src_dent); +- dput(dst_dent_parent); +- unlock_rename(src_dent_parent, dst_dent_parent); +- path_put(&dst_path); +-out: +- dput(src_dent_parent); ++out1: ++ putname(to); ++revert_fsids: ++ ksmbd_revert_fsids(work); + return err; + } + +@@ -1080,14 +1014,16 @@ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + return vfs_removexattr(user_ns, dentry, attr_name); + } + +-int ksmbd_vfs_unlink(struct user_namespace *user_ns, +- struct dentry *dir, struct dentry *dentry) ++int ksmbd_vfs_unlink(struct file *filp) + { + int err = 0; ++ struct dentry *dir, *dentry = filp->f_path.dentry; ++ struct user_namespace *user_ns = file_mnt_user_ns(filp); + +- err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); ++ dir = dget_parent(dentry); ++ err = ksmbd_vfs_lock_parent(dir, dentry); + if (err) +- return err; ++ goto out; + dget(dentry); + + if (S_ISDIR(d_inode(dentry)->i_mode)) +@@ -1099,6 +1035,8 @@ int ksmbd_vfs_unlink(struct user_namespace *user_ns, + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); ++out: ++ dput(dir); + + return err; + } +@@ -1201,7 +1139,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + } + + /** +- * ksmbd_vfs_kern_path() - lookup a file and get path info ++ * ksmbd_vfs_kern_path_locked() - lookup a file and get path info + * @name: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info +@@ -1209,24 +1147,20 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + * + * Return: 0 on success, otherwise error + */ +-int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, +- unsigned int flags, struct path *path, bool caseless) ++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, ++ unsigned int flags, struct path *path, ++ bool caseless) + { + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; ++ struct path parent_path; + +- flags |= LOOKUP_BENEATH; +- err = vfs_path_lookup(share_conf->vfs_path.dentry, +- share_conf->vfs_path.mnt, +- name, +- flags, +- path); ++ err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path); + if (!err) +- return 0; ++ return err; + + if (caseless) { + char *filepath; +- struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); +@@ -1236,10 +1170,10 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + path_len = strlen(filepath); + remain_len = path_len; + +- parent = share_conf->vfs_path; +- path_get(&parent); ++ parent_path = share_conf->vfs_path; ++ path_get(&parent_path); + +- while (d_can_lookup(parent.dentry)) { ++ while (d_can_lookup(parent_path.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; +@@ -1248,12 +1182,11 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + if (filename_len == 0) + break; + +- err = ksmbd_vfs_lookup_in_dir(&parent, filename, ++ err = ksmbd_vfs_lookup_in_dir(&parent_path, filename, + filename_len, + work->conn->um); +- path_put(&parent); + if (err) +- goto out; ++ goto out2; + + next[0] = '\0'; + +@@ -1261,23 +1194,31 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + share_conf->vfs_path.mnt, + filepath, + flags, +- &parent); ++ path); + if (err) +- goto out; +- else if (is_last) { +- *path = parent; +- goto out; +- } ++ goto out2; ++ else if (is_last) ++ goto out1; ++ path_put(&parent_path); ++ parent_path = *path; + + next[0] = '/'; + remain_len -= filename_len + 1; + } + +- path_put(&parent); + err = -EINVAL; +-out: ++out2: ++ path_put(&parent_path); ++out1: + kfree(filepath); + } ++ ++ if (!err) { ++ err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry); ++ if (err) ++ dput(path->dentry); ++ path_put(&parent_path); ++ } + return err; + } + +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 593059ca85112..7660565ac2770 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -71,9 +71,7 @@ struct ksmbd_kstat { + __le32 file_attributes; + }; + +-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, +- struct dentry *child); +-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); ++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); + int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); + int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +@@ -84,12 +82,12 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); + int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); ++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path); + int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); + int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); +-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, +- char *newname); ++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, ++ char *newname, int flags); + int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); + struct srv_copychunk; +@@ -116,9 +114,9 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); +-int ksmbd_vfs_kern_path(struct ksmbd_work *work, +- char *name, unsigned int flags, struct path *path, +- bool caseless); ++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, ++ unsigned int flags, struct path *path, ++ bool caseless); + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +@@ -131,8 +129,7 @@ struct file_allocated_range_buffer; + int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +-int ksmbd_vfs_unlink(struct user_namespace *user_ns, +- struct dentry *dir, struct dentry *dentry); ++int ksmbd_vfs_unlink(struct file *filp); + void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); + int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 6ec6c129465d3..bafb34d4b5bbb 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -243,7 +243,6 @@ void ksmbd_release_inode_hash(void) + + static void __ksmbd_inode_close(struct ksmbd_file *fp) + { +- struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; +@@ -262,11 +261,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { +- dentry = filp->f_path.dentry; +- dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); +- ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); ++ ksmbd_vfs_unlink(filp); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); +diff --git a/include/linux/namei.h b/include/linux/namei.h +index 2b66021c740dd..5c0149603dc3d 100644 +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags, + return user_path_at_empty(dfd, name, flags, path, NULL); + } + ++struct dentry *lookup_one_qstr_excl(const struct qstr *name, ++ struct dentry *base, ++ unsigned int flags); + extern int kern_path(const char *, unsigned, struct path *); + + extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int); + extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); + extern void done_path_create(struct path *, struct dentry *); + extern struct dentry *kern_path_locked(const char *, struct path *); ++int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, ++ struct path *parent, struct qstr *last, int *type, ++ const struct path *root); + + extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch b/queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch new file mode 100644 index 00000000000..d2efb534462 --- /dev/null +++ b/queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch @@ -0,0 +1,145 @@ +From 92a6fffdf64337ca6e0860aac8b64eedafa8bfc5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:14 +0900 +Subject: ksmbd: fix recursive locking in vfs helpers + +From: Marios Makassikis + +[ Upstream commit 807252f028c59b9a3bac4d62ad84761548c10f11 ] + +Running smb2.rename test from Samba smbtorture suite against a kernel built +with lockdep triggers a "possible recursive locking detected" warning. + +This is because mnt_want_write() is called twice with no mnt_drop_write() +in between: + -> ksmbd_vfs_mkdir() + -> ksmbd_vfs_kern_path_create() + -> kern_path_create() + -> filename_create() + -> mnt_want_write() + -> mnt_want_write() + +Fix this by removing the mnt_want_write/mnt_drop_write calls from vfs +helpers that call kern_path_create(). + +Full lockdep trace below: + +============================================ +WARNING: possible recursive locking detected +6.6.0-rc5 #775 Not tainted +-------------------------------------------- +kworker/1:1/32 is trying to acquire lock: +ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: ksmbd_vfs_mkdir+0xe1/0x410 + +but task is already holding lock: +ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: filename_create+0xb6/0x260 + +other info that might help us debug this: + Possible unsafe locking scenario: + + CPU0 + ---- + lock(sb_writers#5); + lock(sb_writers#5); + + *** DEADLOCK *** + + May be due to missing lock nesting notation + +4 locks held by kworker/1:1/32: + #0: ffff8880064e4138 ((wq_completion)ksmbd-io){+.+.}-{0:0}, at: process_one_work+0x40e/0x980 + #1: ffff888005b0fdd0 ((work_completion)(&work->work)){+.+.}-{0:0}, at: process_one_work+0x40e/0x980 + #2: ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: filename_create+0xb6/0x260 + #3: ffff8880057ce760 (&type->i_mutex_dir_key#3/1){+.+.}-{3:3}, at: filename_create+0x123/0x260 + +Cc: stable@vger.kernel.org +Fixes: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions") +Signed-off-by: Marios Makassikis +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 071c344dd0333..e2e454eba4095 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -174,10 +174,6 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) + return err; + } + +- err = mnt_want_write(path.mnt); +- if (err) +- goto out_err; +- + mode |= S_IFREG; + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode, true); +@@ -187,9 +183,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } +- mnt_drop_write(path.mnt); + +-out_err: + done_path_create(&path, dentry); + return err; + } +@@ -220,10 +214,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) + return err; + } + +- err = mnt_want_write(path.mnt); +- if (err) +- goto out_err2; +- + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); +@@ -234,21 +224,19 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); +- goto out_err1; ++ goto out_err; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; +- goto out_err1; ++ goto out_err; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } + +-out_err1: +- mnt_drop_write(path.mnt); +-out_err2: ++out_err: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); +@@ -666,16 +654,11 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + goto out3; + } + +- err = mnt_want_write(newpath.mnt); +- if (err) +- goto out3; +- + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); +- mnt_drop_write(newpath.mnt); + + out3: + done_path_create(&newpath, dentry); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch b/queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch new file mode 100644 index 00000000000..1d5984710f2 --- /dev/null +++ b/queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch @@ -0,0 +1,59 @@ +From 0d51a9c716ba0144b2ec5bb72e19beba8ed0f3b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:23 +0900 +Subject: ksmbd: Fix resource leak in smb2_lock() + +From: Marios Makassikis + +[ Upstream commit 01f6c61bae3d658058ee6322af77acea26a5ee3a ] + +"flock" is leaked if an error happens before smb2_lock_init(), as the +lock is not added to the lock_list to be cleaned up. + +Signed-off-by: Marios Makassikis +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 554214fca5b78..21d0416f11012 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -6951,6 +6951,7 @@ int smb2_lock(struct ksmbd_work *work) + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; ++ locks_free_lock(flock); + goto out; + } + +@@ -6970,6 +6971,7 @@ int smb2_lock(struct ksmbd_work *work) + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; ++ locks_free_lock(flock); + goto out; + } + +@@ -6981,6 +6983,7 @@ int smb2_lock(struct ksmbd_work *work) + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; ++ locks_free_lock(flock); + goto out; + } + } +@@ -6989,6 +6992,7 @@ int smb2_lock(struct ksmbd_work *work) + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; ++ locks_free_lock(flock); + goto out; + } + } +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch b/queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch new file mode 100644 index 00000000000..636d7d0bc43 --- /dev/null +++ b/queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch @@ -0,0 +1,35 @@ +From 375933363fd663716b9e4e37444780b3b504da95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:29 +0900 +Subject: ksmbd: Fix spelling mistake "excceed" -> "exceeded" + +From: Colin Ian King + +[ Upstream commit 7a17c61ee3b2683c40090179c273f4701fca9677 ] + +There is a spelling mistake in an error message. Fix it. + +Signed-off-by: Colin Ian King +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/connection.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index e885e0eb0dc35..ffbf14d024191 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -345,7 +345,7 @@ int ksmbd_conn_handler_loop(void *p) + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { +- pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", ++ pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + READ_ONCE(conn->status)); + break; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch b/queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch new file mode 100644 index 00000000000..3e8ba408e44 --- /dev/null +++ b/queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch @@ -0,0 +1,82 @@ +From 5a79311e5a7449d9097f927d811aaeb3598513ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:26 +0900 +Subject: ksmbd: fix typo, syncronous->synchronous + +From: Dawei Li + +[ Upstream commit f8d6e7442aa716a233c7eba99dec628f8885e00b ] + +syncronous->synchronous + +Signed-off-by: Dawei Li +Acked-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/connection.c | 4 ++-- + fs/smb/server/ksmbd_work.h | 2 +- + fs/smb/server/smb2pdu.c | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index ff97cad8d5b45..e885e0eb0dc35 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -116,7 +116,7 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) + + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; +- work->syncronous = true; ++ work->synchronous = true; + } + + if (requests_queue) { +@@ -141,7 +141,7 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); +- if (work->syncronous == false) ++ if (!work->synchronous) + list_del_init(&work->async_request_entry); + ret = 0; + } +diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h +index 5ece58e40c979..3234f2cf6327c 100644 +--- a/fs/smb/server/ksmbd_work.h ++++ b/fs/smb/server/ksmbd_work.h +@@ -68,7 +68,7 @@ struct ksmbd_work { + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ +- bool syncronous:1; ++ bool synchronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 21d0416f11012..d3939fd481497 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -508,7 +508,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + +- work->syncronous = true; ++ work->synchronous = true; + if (work->async_id) { + ksmbd_release_id(&conn->async_ida, work->async_id); + work->async_id = 0; +@@ -671,7 +671,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + pr_err("Failed to alloc async message id\n"); + return id; + } +- work->syncronous = false; ++ work->synchronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch new file mode 100644 index 00000000000..e09c41e4641 --- /dev/null +++ b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch @@ -0,0 +1,37 @@ +From 748bcda2d871dff1709fc4b62b4aed517aee25e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:38 +0900 +Subject: ksmbd: fix uninitialized pointer read in ksmbd_vfs_rename() + +From: Namjae Jeon + +[ Upstream commit 48b47f0caaa8a9f05ed803cb4f335fa3a7bfc622 ] + +Uninitialized rd.delegated_inode can be used in vfs_rename(). +Fix this by setting rd.delegated_inode to NULL to avoid the uninitialized +read. + +Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name") +Reported-by: Coverity Scan +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 58a6665f1c3ab..5d2bb58d77e80 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -744,6 +744,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + rd.new_dir = new_path.dentry->d_inode, + rd.new_dentry = new_dentry, + rd.flags = flags, ++ rd.delegated_inode = NULL, + err = vfs_rename(&rd); + if (err) + ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch new file mode 100644 index 00000000000..da18a33bde8 --- /dev/null +++ b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch @@ -0,0 +1,49 @@ +From 5337945c1c1c20b0a848d6cf07ec45ee874e30b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:39 +0900 +Subject: ksmbd: fix uninitialized pointer read in smb2_create_link() + +From: Namjae Jeon + +[ Upstream commit df14afeed2e6c1bbadef7d2f9c46887bbd6d8d94 ] + +There is a case that file_present is true and path is uninitialized. +This patch change file_present is set to false by default and set to +true when patch is initialized. + +Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name") +Reported-by: Coverity Scan +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index fe10c75f6f2b9..028b1d1055b57 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -5559,7 +5559,7 @@ static int smb2_create_link(struct ksmbd_work *work, + { + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; +- bool file_present = true; ++ bool file_present = false; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + +@@ -5592,8 +5592,8 @@ static int smb2_create_link(struct ksmbd_work *work, + if (rc) { + if (rc != -ENOENT) + goto out; +- file_present = false; +- } ++ } else ++ file_present = true; + + if (file_info->ReplaceIfExists) { + if (file_present) { +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch b/queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch new file mode 100644 index 00000000000..41070834155 --- /dev/null +++ b/queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch @@ -0,0 +1,52 @@ +From 819f47cefce3f9a56997ba9f6c05882744bb281b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:51 +0900 +Subject: ksmbd: Fix unsigned expression compared with zero + +From: Wang Ming + +[ Upstream commit 0266a2f791294e0b4ba36f4a1d89b8615ea3cac0 ] + +The return value of the ksmbd_vfs_getcasexattr() is signed. +However, the return value is being assigned to an unsigned +variable and subsequently recasted, causing warnings. Use +a signed type. + +Signed-off-by: Wang Ming +Acked-by: Tom Talpey +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index d05d2d1274b04..73ce3fb6e4054 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -413,7 +413,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + { + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); +- size_t size, v_len; ++ size_t size; ++ ssize_t v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", +@@ -430,9 +431,9 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + fp->stream.name, + fp->stream.size, + &stream_buf); +- if ((int)v_len < 0) { ++ if (v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); +- err = (int)v_len; ++ err = v_len; + goto out; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch b/queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch new file mode 100644 index 00000000000..a94b3f29151 --- /dev/null +++ b/queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch @@ -0,0 +1,84 @@ +From 052fe9f3cfc02eafa35479e5fae9d50579c0b8c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:32 +0900 +Subject: ksmbd: fix wrong allocation size update in smb2_open() + +From: Namjae Jeon + +[ Upstream commit a9f106c765c12d2f58aa33431bd8ce8e9d8a404a ] + +When client send SMB2_CREATE_ALLOCATION_SIZE create context, ksmbd update +old size to ->AllocationSize in smb2 create response. ksmbd_vfs_getattr() +should be called after it to get updated stat result. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 36 ++++++++++++++++++------------------ + 1 file changed, 18 insertions(+), 18 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 2eea811fc8cb0..ea48dd06d4da3 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2516,7 +2516,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, false); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, true); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); + } +@@ -3185,23 +3185,6 @@ int smb2_open(struct ksmbd_work *work) + goto err_out; + } + +- rc = ksmbd_vfs_getattr(&path, &stat); +- if (rc) +- goto err_out; +- +- if (stat.result_mask & STATX_BTIME) +- fp->create_time = ksmbd_UnixTimeToNT(stat.btime); +- else +- fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); +- if (req->FileAttributes || fp->f_ci->m_fattr == 0) +- fp->f_ci->m_fattr = +- cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); +- +- if (!created) +- smb2_update_xattrs(tcon, &path, fp); +- else +- smb2_new_xattrs(tcon, &path, fp); +- + if (file_present || created) + ksmbd_vfs_kern_path_unlock(&parent_path, &path); + +@@ -3302,6 +3285,23 @@ int smb2_open(struct ksmbd_work *work) + } + } + ++ rc = ksmbd_vfs_getattr(&path, &stat); ++ if (rc) ++ goto err_out1; ++ ++ if (stat.result_mask & STATX_BTIME) ++ fp->create_time = ksmbd_UnixTimeToNT(stat.btime); ++ else ++ fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); ++ if (req->FileAttributes || fp->f_ci->m_fattr == 0) ++ fp->f_ci->m_fattr = ++ cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); ++ ++ if (!created) ++ smb2_update_xattrs(tcon, &path, fp); ++ else ++ smb2_new_xattrs(tcon, &path, fp); ++ + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + rsp->StructureSize = cpu_to_le16(89); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch b/queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch new file mode 100644 index 00000000000..367f963a2ef --- /dev/null +++ b/queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch @@ -0,0 +1,45 @@ +From 36f0753185071ab62a7d2fc061c9020f746e9ffe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:08 +0900 +Subject: ksmbd: fix wrong error response status by using set_smb2_rsp_status() + +From: Namjae Jeon + +[ Upstream commit be0f89d4419dc5413a1cf06db3671c9949be0d52 ] + +set_smb2_rsp_status() after __process_request() sets the wrong error +status. This patch resets all iov vectors and sets the error status +on clean one. + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index aad08866746c8..c773272cd3ac2 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -230,11 +230,12 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) + { + struct smb2_hdr *rsp_hdr; + +- if (work->next_smb2_rcv_hdr_off) +- rsp_hdr = ksmbd_resp_buf_next(work); +- else +- rsp_hdr = smb2_get_msg(work->response_buf); ++ rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Status = err; ++ ++ work->iov_idx = 0; ++ work->iov_cnt = 0; ++ work->next_smb2_rcv_hdr_off = 0; + smb2_set_err_rsp(work); + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch b/queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch new file mode 100644 index 00000000000..b3e08fbb2cf --- /dev/null +++ b/queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch @@ -0,0 +1,164 @@ +From 421ed7f8cb1bb0c39afe592b314be35df3c8d833 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:55 +0900 +Subject: ksmbd: fix wrong interim response on compound + +From: Namjae Jeon + +[ Upstream commit 041bba4414cda37d00063952c9bff9c3d5812a19 ] + +If smb2_lock or smb2_open request is compound, ksmbd could send wrong +interim response to client. ksmbd allocate new interim buffer instead of +using resonse buffer to support compound request. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_work.c | 10 ++++++---- + fs/smb/server/ksmbd_work.h | 2 +- + fs/smb/server/oplock.c | 14 ++------------ + fs/smb/server/smb2pdu.c | 26 +++++++++++++++++--------- + 4 files changed, 26 insertions(+), 26 deletions(-) + +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index f49c2e01ea9fc..51def3ca74c01 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -160,9 +160,11 @@ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, + return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size); + } + +-void ksmbd_iov_reset(struct ksmbd_work *work) ++int allocate_interim_rsp_buf(struct ksmbd_work *work) + { +- work->iov_idx = 0; +- work->iov_cnt = 0; +- *(__be32 *)work->iov[0].iov_base = 0; ++ work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); ++ if (!work->response_buf) ++ return -ENOMEM; ++ work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; ++ return 0; + } +diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h +index 255157eb26dc4..8ca2c813246e6 100644 +--- a/fs/smb/server/ksmbd_work.h ++++ b/fs/smb/server/ksmbd_work.h +@@ -131,5 +131,5 @@ bool ksmbd_queue_work(struct ksmbd_work *work); + int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, + void *aux_buf, unsigned int aux_size); + int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len); +-void ksmbd_iov_reset(struct ksmbd_work *work); ++int allocate_interim_rsp_buf(struct ksmbd_work *work); + #endif /* __KSMBD_WORK_H__ */ +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 4e12e3031bc53..90a035c27130f 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -616,15 +616,6 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) + return 0; + } + +-static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +-{ +- work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); +- if (!work->response_buf) +- return -ENOMEM; +- work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; +- return 0; +-} +- + /** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client +@@ -647,7 +638,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + if (!fp) + goto out; + +- if (allocate_oplock_break_buf(work)) { ++ if (allocate_interim_rsp_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + ksmbd_fd_put(work, fp); + goto out; +@@ -752,7 +743,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + struct lease_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + +- if (allocate_oplock_break_buf(work)) { ++ if (allocate_interim_rsp_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + goto out; + } +@@ -843,7 +834,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); +- ksmbd_iov_reset(in_work); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 0a40b793cedf4..dfb4fd4cb42f6 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -152,8 +152,8 @@ void smb2_set_err_rsp(struct ksmbd_work *work) + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + err = ksmbd_iov_pin_rsp(work, (void *)err_rsp, +- work->conn->vals->header_size + +- SMB2_ERROR_STRUCTURE_SIZE2); ++ __SMB2_HEADER_STRUCTURE_SIZE + ++ SMB2_ERROR_STRUCTURE_SIZE2); + if (err) + work->send_no_response = 1; + } +@@ -709,13 +709,24 @@ void release_async_work(struct ksmbd_work *work) + void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) + { + struct smb2_hdr *rsp_hdr; ++ struct ksmbd_work *in_work = ksmbd_alloc_work_struct(); + +- rsp_hdr = ksmbd_resp_buf_next(work); +- smb2_set_err_rsp(work); ++ if (allocate_interim_rsp_buf(in_work)) { ++ pr_err("smb_allocate_rsp_buf failed!\n"); ++ ksmbd_free_work_struct(in_work); ++ return; ++ } ++ ++ in_work->conn = work->conn; ++ memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work), ++ __SMB2_HEADER_STRUCTURE_SIZE); ++ ++ rsp_hdr = smb2_get_msg(in_work->response_buf); ++ smb2_set_err_rsp(in_work); + rsp_hdr->Status = status; + +- ksmbd_conn_write(work); +- rsp_hdr->Status = 0; ++ ksmbd_conn_write(in_work); ++ ksmbd_free_work_struct(in_work); + } + + static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +@@ -7050,8 +7061,6 @@ int smb2_lock(struct ksmbd_work *work) + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + +- ksmbd_iov_reset(work); +- + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); +@@ -7069,7 +7078,6 @@ int smb2_lock(struct ksmbd_work *work) + goto out; + } + +- init_smb2_rsp_hdr(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch b/queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch new file mode 100644 index 00000000000..da2b087b79d --- /dev/null +++ b/queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch @@ -0,0 +1,142 @@ +From 4ccf30b4bd0fc93d2a68ae3ee2111d3e9445c7ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:25 +0900 +Subject: ksmbd: Implements sess->rpc_handle_list as xarray + +From: Dawei Li + +[ Upstream commit b685757c7b08d5073046fb379be965fd6c06aafc ] + +For some ops on rpc handle: +1. ksmbd_session_rpc_method(), possibly on high frequency. +2. ksmbd_session_rpc_close(). + +id is used as indexing key to lookup channel, in that case, +linear search based on list may suffer a bit for performance. + +Implements sess->rpc_handle_list as xarray. + +Signed-off-by: Dawei Li +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/mgmt/user_session.c | 37 ++++++++++++------------------- + fs/smb/server/mgmt/user_session.h | 2 +- + 2 files changed, 15 insertions(+), 24 deletions(-) + +diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c +index cf6621e21ba36..b8be14a96cf66 100644 +--- a/fs/smb/server/mgmt/user_session.c ++++ b/fs/smb/server/mgmt/user_session.c +@@ -25,7 +25,6 @@ static DECLARE_RWSEM(sessions_table_lock); + struct ksmbd_session_rpc { + int id; + unsigned int method; +- struct list_head list; + }; + + static void free_channel_list(struct ksmbd_session *sess) +@@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess, + static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) + { + struct ksmbd_session_rpc *entry; ++ long index; + +- while (!list_empty(&sess->rpc_handle_list)) { +- entry = list_entry(sess->rpc_handle_list.next, +- struct ksmbd_session_rpc, +- list); +- +- list_del(&entry->list); ++ xa_for_each(&sess->rpc_handle_list, index, entry) { ++ xa_erase(&sess->rpc_handle_list, index); + __session_rpc_close(sess, entry); + } ++ ++ xa_destroy(&sess->rpc_handle_list); + } + + static int __rpc_method(char *rpc_name) +@@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) +- return -EINVAL; ++ return -ENOMEM; + +- list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto free_entry; ++ xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL); + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) +@@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) + kvfree(resp); + return entry->id; + free_id: ++ xa_erase(&sess->rpc_handle_list, entry->id); + ksmbd_rpc_id_free(entry->id); + free_entry: +- list_del(&entry->list); + kfree(entry); + return -EINVAL; + } +@@ -128,24 +126,17 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) + { + struct ksmbd_session_rpc *entry; + +- list_for_each_entry(entry, &sess->rpc_handle_list, list) { +- if (entry->id == id) { +- list_del(&entry->list); +- __session_rpc_close(sess, entry); +- break; +- } +- } ++ entry = xa_erase(&sess->rpc_handle_list, id); ++ if (entry) ++ __session_rpc_close(sess, entry); + } + + int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) + { + struct ksmbd_session_rpc *entry; + +- list_for_each_entry(entry, &sess->rpc_handle_list, list) { +- if (entry->id == id) +- return entry->method; +- } +- return 0; ++ entry = xa_load(&sess->rpc_handle_list, id); ++ return entry ? entry->method : 0; + } + + void ksmbd_session_destroy(struct ksmbd_session *sess) +@@ -362,7 +353,7 @@ static struct ksmbd_session *__session_create(int protocol) + set_session_flag(sess, protocol); + xa_init(&sess->tree_conns); + xa_init(&sess->ksmbd_chann_list); +- INIT_LIST_HEAD(&sess->rpc_handle_list); ++ xa_init(&sess->rpc_handle_list); + sess->sequence_number = 1; + + ret = __init_smb2_session(sess); +diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h +index 51f38e5b61abb..f99d475b28db4 100644 +--- a/fs/smb/server/mgmt/user_session.h ++++ b/fs/smb/server/mgmt/user_session.h +@@ -52,7 +52,7 @@ struct ksmbd_session { + struct xarray ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; +- struct list_head rpc_handle_list; ++ struct xarray rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch b/queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch new file mode 100644 index 00000000000..24e9f51e198 --- /dev/null +++ b/queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch @@ -0,0 +1,124 @@ +From c9626e2457663b84804fd5d00bf57452cd96ad2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:30 +0900 +Subject: ksmbd: lazy v2 lease break on smb2_write() + +From: Namjae Jeon + +[ Upstream commit c2a721eead71202a0d8ddd9b56ec8dce652c71d1 ] + +Don't immediately send directory lease break notification on smb2_write(). +Instead, It postpones it until smb2_close(). + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/oplock.c | 45 +++++++++++++++++++++++++++++++++++++-- + fs/smb/server/oplock.h | 1 + + fs/smb/server/vfs.c | 3 +++ + fs/smb/server/vfs_cache.h | 1 + + 4 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 4a8745b3e8304..af0f6914eca45 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -396,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp) + { + struct oplock_info *opinfo; + +- if (S_ISDIR(file_inode(fp->filp)->i_mode)) +- return; ++ if (fp->reserve_lease_break) ++ smb_lazy_parent_lease_break_close(fp); + + opinfo = opinfo_get(fp); + if (!opinfo) +@@ -1127,6 +1127,47 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + ksmbd_inode_put(p_ci); + } + ++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) ++{ ++ struct oplock_info *opinfo; ++ struct ksmbd_inode *p_ci = NULL; ++ ++ rcu_read_lock(); ++ opinfo = rcu_dereference(fp->f_opinfo); ++ rcu_read_unlock(); ++ ++ if (!opinfo->is_lease || opinfo->o_lease->version != 2) ++ return; ++ ++ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); ++ if (!p_ci) ++ return; ++ ++ read_lock(&p_ci->m_lock); ++ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { ++ if (!opinfo->is_lease) ++ continue; ++ ++ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { ++ if (!atomic_inc_not_zero(&opinfo->refcount)) ++ continue; ++ ++ atomic_inc(&opinfo->conn->r_count); ++ if (ksmbd_conn_releasing(opinfo->conn)) { ++ atomic_dec(&opinfo->conn->r_count); ++ continue; ++ } ++ read_unlock(&p_ci->m_lock); ++ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); ++ opinfo_conn_put(opinfo); ++ read_lock(&p_ci->m_lock); ++ } ++ } ++ read_unlock(&p_ci->m_lock); ++ ++ ksmbd_inode_put(p_ci); ++} ++ + /** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index b64d1536882a1..5b93ea9196c01 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -129,4 +129,5 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + void destroy_lease_table(struct ksmbd_conn *conn); + void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + struct lease_ctx_info *lctx); ++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp); + #endif /* __KSMBD_OPLOCK_H */ +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 42f270ee399c7..fe2c80ea2e47e 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -518,6 +518,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + } + } + ++ /* Reserve lease break for parent dir at closing time */ ++ fp->reserve_lease_break = true; ++ + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index 4d4938d6029b6..a528f0cc775ae 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -105,6 +105,7 @@ struct ksmbd_file { + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; + unsigned int f_state; ++ bool reserve_lease_break; + }; + + static inline void set_ctx_actor(struct dir_context *ctx, +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch b/queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch new file mode 100644 index 00000000000..b0d2bf1452e --- /dev/null +++ b/queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch @@ -0,0 +1,363 @@ +From 66128cdefdbca0874d099a9f97ddb8fd57537576 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:22 +0900 +Subject: ksmbd: move oplock handling after unlock parent dir + +From: Namjae Jeon + +[ Upstream commit 2e450920d58b4991a436c8cecf3484bcacd8e535 ] + +ksmbd should process secound parallel smb2 create request during waiting +oplock break ack. parent lock range that is too large in smb2_open() causes +smb2_open() to be serialized. Move the oplock handling to the bottom of +smb2_open() and make it called after parent unlock. This fixes the failure +of smb2.lease.breaking1 testcase. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 121 +++++++++++++++++++++------------------- + 1 file changed, 65 insertions(+), 56 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index ff4cc39b85c72..3d965be412751 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2691,7 +2691,7 @@ int smb2_open(struct ksmbd_work *work) + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + name = smb2_get_name(req->Buffer, +@@ -2702,7 +2702,7 @@ int smb2_open(struct ksmbd_work *work) + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; +- goto err_out1; ++ goto err_out2; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); +@@ -2710,28 +2710,28 @@ int smb2_open(struct ksmbd_work *work) + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; +- goto err_out1; ++ goto err_out2; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) +- goto err_out1; ++ goto err_out2; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) +- goto err_out1; ++ goto err_out2; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); +- goto err_out1; ++ goto err_out2; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; +- goto err_out1; ++ goto err_out2; + } + } + +@@ -2744,14 +2744,14 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) +@@ -2761,13 +2761,13 @@ int smb2_open(struct ksmbd_work *work) + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } +@@ -2779,21 +2779,21 @@ int smb2_open(struct ksmbd_work *work) + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; +- goto err_out1; ++ goto err_out2; + } + + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateContextsOffset) { +@@ -2801,19 +2801,19 @@ int smb2_open(struct ksmbd_work *work) + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; +- goto err_out1; ++ goto err_out2; + } + } + +@@ -2821,7 +2821,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); +@@ -2832,11 +2832,11 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_TIMEWARP_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; +- goto err_out1; ++ goto err_out2; + } + + if (tcon->posix_extensions) { +@@ -2844,7 +2844,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; +@@ -2852,7 +2852,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + ksmbd_debug(SMB, "get posix context\n"); + +@@ -2864,7 +2864,7 @@ int smb2_open(struct ksmbd_work *work) + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; +- goto err_out1; ++ goto err_out2; + } + + rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, +@@ -3177,11 +3177,6 @@ int smb2_open(struct ksmbd_work *work) + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); +- if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && +- !fp->attrib_only && !stream_name) { +- smb_break_all_oplock(work, fp); +- need_truncate = 1; +- } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are +@@ -3197,13 +3192,39 @@ int smb2_open(struct ksmbd_work *work) + goto err_out; + } + ++ rc = ksmbd_vfs_getattr(&path, &stat); ++ if (rc) ++ goto err_out; ++ ++ if (stat.result_mask & STATX_BTIME) ++ fp->create_time = ksmbd_UnixTimeToNT(stat.btime); ++ else ++ fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); ++ if (req->FileAttributes || fp->f_ci->m_fattr == 0) ++ fp->f_ci->m_fattr = ++ cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); ++ ++ if (!created) ++ smb2_update_xattrs(tcon, &path, fp); ++ else ++ smb2_new_xattrs(tcon, &path, fp); ++ ++ if (file_present || created) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ ++ if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && ++ !fp->attrib_only && !stream_name) { ++ smb_break_all_oplock(work, fp); ++ need_truncate = 1; ++ } ++ + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; +- goto err_out; ++ goto err_out1; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { +@@ -3213,7 +3234,7 @@ int smb2_open(struct ksmbd_work *work) + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) +- goto err_out; ++ goto err_out1; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) +@@ -3224,12 +3245,18 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) +- goto err_out; ++ goto err_out1; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + ++ if (need_truncate) { ++ rc = smb2_create_truncate(&fp->filp->f_path); ++ if (rc) ++ goto err_out1; ++ } ++ + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + +@@ -3237,7 +3264,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_ALLOCATION_SIZE, 4); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); +- goto err_out; ++ goto err_out1; + } else if (az_req) { + loff_t alloc_size; + int err; +@@ -3246,7 +3273,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; +- goto err_out; ++ goto err_out1; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, +@@ -3264,30 +3291,13 @@ int smb2_open(struct ksmbd_work *work) + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out; ++ goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + +- rc = ksmbd_vfs_getattr(&path, &stat); +- if (rc) +- goto err_out; +- +- if (stat.result_mask & STATX_BTIME) +- fp->create_time = ksmbd_UnixTimeToNT(stat.btime); +- else +- fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); +- if (req->FileAttributes || fp->f_ci->m_fattr == 0) +- fp->f_ci->m_fattr = +- cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); +- +- if (!created) +- smb2_update_xattrs(tcon, &path, fp); +- else +- smb2_new_xattrs(tcon, &path, fp); +- + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + rsp->StructureSize = cpu_to_le16(89); +@@ -3394,14 +3404,13 @@ int smb2_open(struct ksmbd_work *work) + } + + err_out: +- if (file_present || created) ++ if (rc && (file_present || created)) + ksmbd_vfs_kern_path_unlock(&parent_path, &path); + +- if (fp && need_truncate) +- rc = smb2_create_truncate(&fp->filp->f_path); +- +- ksmbd_revert_fsids(work); + err_out1: ++ ksmbd_revert_fsids(work); ++ ++err_out2: + if (!rc) { + ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch b/queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch new file mode 100644 index 00000000000..5debc702bd7 --- /dev/null +++ b/queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch @@ -0,0 +1,57 @@ +From df39b2661a4d2e0ef3bea3e02d74abeb3b934abf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:24 +0900 +Subject: ksmbd: move setting SMB2_FLAGS_ASYNC_COMMAND and AsyncId + +From: Namjae Jeon + +[ Upstream commit 9ac45ac7cf65b0623ceeab9b28b307a08efa22dc ] + +Directly set SMB2_FLAGS_ASYNC_COMMAND flags and AsyncId in smb2 header of +interim response instead of current response header. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 3d965be412751..2ba5e685dd3fe 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -657,13 +657,9 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) + + int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + { +- struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + +- rsp_hdr = ksmbd_resp_buf_next(work); +- rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; +- + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); +@@ -671,7 +667,6 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + } + work->asynchronous = true; + work->async_id = id; +- rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", +@@ -723,6 +718,8 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) + __SMB2_HEADER_STRUCTURE_SIZE); + + rsp_hdr = smb2_get_msg(in_work->response_buf); ++ rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; ++ rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id); + smb2_set_err_rsp(in_work); + rsp_hdr->Status = status; + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch b/queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch new file mode 100644 index 00000000000..4c77b584e76 --- /dev/null +++ b/queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch @@ -0,0 +1,51 @@ +From 20dc27d41bf97e602b86ab48845254ca5bc47636 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:17 +0900 +Subject: ksmbd: no need to wait for binded connection termination at logoff + +From: Namjae Jeon + +[ Upstream commit 67797da8a4b82446d42c52b6ee1419a3100d78ff ] + +The connection could be binded to the existing session for Multichannel. +session will be destroyed when binded connections are released. +So no need to wait for that's connection at logoff. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/connection.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index 4b38c3a285f60..b6fa1e285c401 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -167,23 +167,7 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status) + + void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) + { +- struct ksmbd_conn *bind_conn; +- + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +- +- down_read(&conn_list_lock); +- list_for_each_entry(bind_conn, &conn_list, conns_list) { +- if (bind_conn == conn) +- continue; +- +- if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && +- !ksmbd_conn_releasing(bind_conn) && +- atomic_read(&bind_conn->req_running)) { +- wait_event(bind_conn->req_running_q, +- atomic_read(&bind_conn->req_running) == 0); +- } +- } +- up_read(&conn_list_lock); + } + + int ksmbd_conn_write(struct ksmbd_work *work) +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch b/queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch new file mode 100644 index 00000000000..f38b6fed4d2 --- /dev/null +++ b/queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch @@ -0,0 +1,53 @@ +From 4d4fef19e7b78ea56780f540e72fe63eb01e8e39 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:19 +0900 +Subject: ksmbd: prevent memory leak on error return + +From: Zongmin Zhou + +[ Upstream commit 90044481e7cca6cb3125b3906544954a25f1309f ] + +When allocated memory for 'new' failed,just return +will cause memory leak of 'ar'. + +Fixes: 1819a9042999 ("ksmbd: reorganize ksmbd_iov_pin_rsp()") +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/r/202311031837.H3yo7JVl-lkp@intel.com/ +Signed-off-by: Zongmin Zhou +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_work.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index a2ed441e837ae..2510b9f3c8c14 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -106,7 +106,7 @@ static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib, + static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, + void *aux_buf, unsigned int aux_size) + { +- struct aux_read *ar; ++ struct aux_read *ar = NULL; + int need_iov_cnt = 1; + + if (aux_size) { +@@ -123,8 +123,11 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, + new = krealloc(work->iov, + sizeof(struct kvec) * work->iov_alloc_cnt, + GFP_KERNEL | __GFP_ZERO); +- if (!new) ++ if (!new) { ++ kfree(ar); ++ work->iov_alloc_cnt -= 4; + return -ENOMEM; ++ } + work->iov = new; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch b/queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch new file mode 100644 index 00000000000..eb31a72a3dc --- /dev/null +++ b/queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch @@ -0,0 +1,51 @@ +From d1d9117edcf8001b839d58cbf03b220bd0a2055b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:23 +0900 +Subject: ksmbd: release interim response after sending status pending response + +From: Namjae Jeon + +[ Upstream commit 2a3f7857ec742e212d6cee7fbbf7b0e2ae7f5161 ] + +Add missing release async id and delete interim response entry after +sending status pending response. This only cause when smb2 lease is enable. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_work.c | 3 +++ + fs/smb/server/oplock.c | 3 ++- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index 2510b9f3c8c14..d7c676c151e20 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -56,6 +56,9 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) + kfree(work->tr_buf); + kvfree(work->request_buf); + kfree(work->iov); ++ if (!list_empty(&work->interim_entry)) ++ list_del(&work->interim_entry); ++ + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 90a035c27130f..4c74e8ea9649a 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -833,7 +833,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); +- list_del(&in_work->interim_entry); ++ list_del_init(&in_work->interim_entry); ++ release_async_work(in_work); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-duplicated-codes.patch b/queue-6.1/ksmbd-remove-duplicated-codes.patch new file mode 100644 index 00000000000..3fafc464db0 --- /dev/null +++ b/queue-6.1/ksmbd-remove-duplicated-codes.patch @@ -0,0 +1,66 @@ +From 816c5c3bfab4296c64a13822757bc2905ef378ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:27 +0900 +Subject: ksmbd: Remove duplicated codes + +From: Dawei Li + +[ Upstream commit 7010357004096e54c884813e702d71147dc081f8 ] + +ksmbd_neg_token_init_mech_token() and ksmbd_neg_token_targ_resp_token() +share same implementation, unify them. + +Signed-off-by: Dawei Li +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/asn1.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c +index c03eba0903682..cc6384f796759 100644 +--- a/fs/smb/server/asn1.c ++++ b/fs/smb/server/asn1.c +@@ -208,9 +208,9 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + return 0; + } + +-int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, +- unsigned char tag, const void *value, +- size_t vlen) ++static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, ++ unsigned char tag, const void *value, ++ size_t vlen) + { + struct ksmbd_conn *conn = context; + +@@ -223,17 +223,16 @@ int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + return 0; + } + +-int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, ++int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) + { +- struct ksmbd_conn *conn = context; +- +- conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); +- if (!conn->mechToken) +- return -ENOMEM; ++ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); ++} + +- memcpy(conn->mechToken, value, vlen); +- conn->mechToken[vlen] = '\0'; +- return 0; ++int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, ++ unsigned char tag, const void *value, ++ size_t vlen) ++{ ++ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); + } +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-experimental-warning.patch b/queue-6.1/ksmbd-remove-experimental-warning.patch new file mode 100644 index 00000000000..cad3f23b0ad --- /dev/null +++ b/queue-6.1/ksmbd-remove-experimental-warning.patch @@ -0,0 +1,48 @@ +From 2fc81539421dde8e7b4cd9a20f0d9263736bb600 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:59 +0900 +Subject: ksmbd: remove experimental warning + +From: Steve French + +[ Upstream commit f5069159f32c8c943e047f22731317463c8e9b84 ] + +ksmbd has made significant improvements over the past two +years and is regularly tested and used. Remove the experimental +warning. + +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/Kconfig | 2 +- + fs/smb/server/server.c | 2 -- + 2 files changed, 1 insertion(+), 3 deletions(-) + +diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig +index 7055cb5d28800..d036ab80fec35 100644 +--- a/fs/smb/server/Kconfig ++++ b/fs/smb/server/Kconfig +@@ -1,5 +1,5 @@ + config SMB_SERVER +- tristate "SMB3 server support (EXPERIMENTAL)" ++ tristate "SMB3 server support" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index ff1514c79f162..f5d8e405cf6fd 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -591,8 +591,6 @@ static int __init ksmbd_server_init(void) + if (ret) + goto err_crypto_destroy; + +- pr_warn_once("The ksmbd server is experimental\n"); +- + return 0; + + err_crypto_destroy: +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch b/queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch new file mode 100644 index 00000000000..4f76f30e158 --- /dev/null +++ b/queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch @@ -0,0 +1,34 @@ +From de09696f841811974662d4f4c783d7a84cf78027 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:00 +0900 +Subject: ksmbd: remove unneeded mark_inode_dirty in set_info_sec() + +From: Namjae Jeon + +[ Upstream commit e4e14095cc68a2efefba6f77d95efe1137e751d4 ] + +mark_inode_dirty will be called in notify_change(). +This patch remove unneeded mark_inode_dirty in set_info_sec(). + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smbacl.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index 03f19d3de2a17..7a42728d8047c 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -1443,7 +1443,6 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); +- mark_inode_dirty(inode); + return rc; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch b/queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch new file mode 100644 index 00000000000..e4a5b32f40c --- /dev/null +++ b/queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch @@ -0,0 +1,80 @@ +From 795025229aa428ad34e549dda0e7365b5aabd641 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:35 +0900 +Subject: ksmbd: remove unused compression negotiate ctx packing + +From: David Disseldorp + +[ Upstream commit af36c51e0e111de4e908328d49cba49de758f66e ] + +build_compression_ctxt() is currently unreachable due to +conn.compress_algorithm remaining zero (SMB3_COMPRESS_NONE). + +It appears to have been broken in a couple of subtle ways over the +years: +- prior to d6c9ad23b421 ("ksmbd: use the common definitions for + NEGOTIATE_PROTOCOL") smb2_compression_ctx.DataLength was set to 8, + which didn't account for the single CompressionAlgorithms flexible + array member. +- post d6c9ad23b421 smb2_compression_capabilities_context + CompressionAlgorithms is a three member array, while + CompressionAlgorithmCount is set to indicate only one member. + assemble_neg_contexts() ctxt_size is also incorrectly incremented by + sizeof(struct smb2_compression_capabilities_context) + 2, which + assumes one flexible array member. + +Signed-off-by: David Disseldorp +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 26 ++------------------------ + 1 file changed, 2 insertions(+), 24 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 92e6570972437..ca57e85abf915 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -786,19 +786,6 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + pneg_ctxt->Ciphers[0] = cipher_type; + } + +-static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, +- __le16 comp_algo) +-{ +- pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; +- pneg_ctxt->DataLength = +- cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) +- - sizeof(struct smb2_neg_context)); +- pneg_ctxt->Reserved = cpu_to_le32(0); +- pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); +- pneg_ctxt->Flags = cpu_to_le32(0); +- pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +-} +- + static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) + { +@@ -862,17 +849,8 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + } + +- if (conn->compress_algorithm) { +- ctxt_size = round_up(ctxt_size, 8); +- ksmbd_debug(SMB, +- "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); +- /* Temporarily set to SMB3_COMPRESS_NONE */ +- build_compression_ctxt((struct smb2_compression_capabilities_context *) +- (pneg_ctxt + ctxt_size), +- conn->compress_algorithm); +- neg_ctxt_cnt++; +- ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; +- } ++ /* compression context not yet supported */ ++ WARN_ON(conn->compress_algorithm != SMB3_COMPRESS_NONE); + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch b/queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch new file mode 100644 index 00000000000..f7efcade93b --- /dev/null +++ b/queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch @@ -0,0 +1,34 @@ +From 04fd95999012d5bf530ef2744ddd065a5aa1e180 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:11 +0900 +Subject: ksmbd: Remove unused field in ksmbd_user struct + +From: Cheng-Han Wu + +[ Upstream commit eacc655e18d1dec9b50660d16a1ddeeb4d6c48f2 ] + +fs/smb/server/mgmt/user_config.h:21: Remove the unused field 'failed_login_count' from the ksmbd_user struct. + +Signed-off-by: Cheng-Han Wu +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/mgmt/user_config.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h +index 6a44109617f14..e068a19fd9049 100644 +--- a/fs/smb/server/mgmt/user_config.h ++++ b/fs/smb/server/mgmt/user_config.h +@@ -18,7 +18,6 @@ struct ksmbd_user { + + size_t passkey_sz; + char *passkey; +- unsigned int failed_login_count; + }; + + static inline bool user_guest(struct ksmbd_user *user) +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch b/queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch new file mode 100644 index 00000000000..865ac2712d3 --- /dev/null +++ b/queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch @@ -0,0 +1,57 @@ +From b618eadd3d5203cd53b8b49cb0b03b4d62be8e6c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:31 +0900 +Subject: ksmbd: remove unused is_char_allowed function + +From: Tom Rix + +[ Upstream commit 2824861773eb512b37547516d81ef78108032cb2 ] + +clang with W=1 reports +fs/ksmbd/unicode.c:122:19: error: unused function + 'is_char_allowed' [-Werror,-Wunused-function] +static inline int is_char_allowed(char *ch) + ^ +This function is not used so remove it. + +Signed-off-by: Tom Rix +Reviewed-by: Sergey Senozhatsky +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/unicode.c | 18 ------------------ + 1 file changed, 18 deletions(-) + +diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c +index a0db699ddafda..9ae676906ed39 100644 +--- a/fs/smb/server/unicode.c ++++ b/fs/smb/server/unicode.c +@@ -113,24 +113,6 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + goto out; + } + +-/* +- * is_char_allowed() - check for valid character +- * @ch: input character to be checked +- * +- * Return: 1 if char is allowed, otherwise 0 +- */ +-static inline int is_char_allowed(char *ch) +-{ +- /* check for control chars, wildcards etc. */ +- if (!(*ch & 0x80) && +- (*ch <= 0x1f || +- *ch == '?' || *ch == '"' || *ch == '<' || +- *ch == '>' || *ch == '|')) +- return 0; +- +- return 1; +-} +- + /* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch b/queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch new file mode 100644 index 00000000000..5c7fe8abcac --- /dev/null +++ b/queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch @@ -0,0 +1,59 @@ +From ecf9c38c087394c798e945c1fc796e5d100e2774 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:43 +0900 +Subject: ksmbd: remove unused ksmbd_tree_conn_share function + +From: Namjae Jeon + +[ Upstream commit 7bd9f0876fdef00f4e155be35e6b304981a53f80 ] + +Remove unused ksmbd_tree_conn_share function. + +Signed-off-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/mgmt/tree_connect.c | 11 ----------- + fs/smb/server/mgmt/tree_connect.h | 3 --- + 2 files changed, 14 deletions(-) + +diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c +index f07a05f376513..408cddf2f094a 100644 +--- a/fs/smb/server/mgmt/tree_connect.c ++++ b/fs/smb/server/mgmt/tree_connect.c +@@ -120,17 +120,6 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + return tcon; + } + +-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, +- unsigned int id) +-{ +- struct ksmbd_tree_connect *tc; +- +- tc = ksmbd_tree_conn_lookup(sess, id); +- if (tc) +- return tc->share_conf; +- return NULL; +-} +- + int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) + { + int ret = 0; +diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h +index 700df36cf3e30..562d647ad9fad 100644 +--- a/fs/smb/server/mgmt/tree_connect.h ++++ b/fs/smb/server/mgmt/tree_connect.h +@@ -53,9 +53,6 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, +- unsigned int id); +- + int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + + #endif /* __TREE_CONNECT_MANAGEMENT_H__ */ +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch b/queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch new file mode 100644 index 00000000000..6e55c3ef620 --- /dev/null +++ b/queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch @@ -0,0 +1,99 @@ +From c0f0dd47e274b5a231b783cf7f3eef3e4e0e1c07 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:12 +0900 +Subject: ksmbd: reorganize ksmbd_iov_pin_rsp() + +From: Namjae Jeon + +[ Upstream commit 1819a904299942b309f687cc0f08b123500aa178 ] + +If ksmbd_iov_pin_rsp fail, io vertor should be rollback. +This patch moves memory allocations to before setting the io vector +to avoid rollbacks. + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_work.c | 43 +++++++++++++++++++------------------- + 1 file changed, 22 insertions(+), 21 deletions(-) + +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index 51def3ca74c01..a2ed441e837ae 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -95,11 +95,28 @@ bool ksmbd_queue_work(struct ksmbd_work *work) + return queue_work(ksmbd_wq, &work->work); + } + +-static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib, +- unsigned int ib_len) ++static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib, ++ unsigned int ib_len) + { ++ work->iov[++work->iov_idx].iov_base = ib; ++ work->iov[work->iov_idx].iov_len = ib_len; ++ work->iov_cnt++; ++} ++ ++static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size) ++{ ++ struct aux_read *ar; ++ int need_iov_cnt = 1; + +- if (work->iov_alloc_cnt <= work->iov_cnt) { ++ if (aux_size) { ++ need_iov_cnt++; ++ ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL); ++ if (!ar) ++ return -ENOMEM; ++ } ++ ++ if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) { + struct kvec *new; + + work->iov_alloc_cnt += 4; +@@ -111,16 +128,6 @@ static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib, + work->iov = new; + } + +- work->iov[++work->iov_idx].iov_base = ib; +- work->iov[work->iov_idx].iov_len = ib_len; +- work->iov_cnt++; +- +- return 0; +-} +- +-static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, +- void *aux_buf, unsigned int aux_size) +-{ + /* Plus rfc_length size on first iov */ + if (!work->iov_idx) { + work->iov[work->iov_idx].iov_base = work->response_buf; +@@ -129,19 +136,13 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, + work->iov_cnt++; + } + +- ksmbd_realloc_iov_pin(work, ib, len); ++ __ksmbd_iov_pin(work, ib, len); + inc_rfc1001_len(work->iov[0].iov_base, len); + + if (aux_size) { +- struct aux_read *ar; +- +- ksmbd_realloc_iov_pin(work, aux_buf, aux_size); ++ __ksmbd_iov_pin(work, aux_buf, aux_size); + inc_rfc1001_len(work->iov[0].iov_base, aux_size); + +- ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL); +- if (!ar) +- return -ENOMEM; +- + ar->buf = aux_buf; + list_add(&ar->entry, &work->aux_read_list); + } +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch b/queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch new file mode 100644 index 00000000000..082364df708 --- /dev/null +++ b/queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch @@ -0,0 +1,43 @@ +From 2a99a4d488fd9df0dbaf78bf2ed3bf77e583f6b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:50 +0900 +Subject: ksmbd: Replace one-element array with flexible-array member + +From: Gustavo A. R. Silva + +[ Upstream commit 11d5e2061e973a8d4ff2b95a114b4b8ef8652633 ] + +One-element arrays are deprecated, and we are replacing them with flexible +array members instead. So, replace one-element array with flexible-array +member in struct smb_negotiate_req. + +This results in no differences in binary output. + +Link: https://github.com/KSPP/linux/issues/79 +Link: https://github.com/KSPP/linux/issues/317 +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Kees Cook +Acked-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb_common.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h +index f0134d16067fb..f1092519c0c28 100644 +--- a/fs/smb/server/smb_common.h ++++ b/fs/smb/server/smb_common.h +@@ -200,7 +200,7 @@ struct smb_hdr { + struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; +- unsigned char DialectsArray[1]; ++ unsigned char DialectsArray[]; + } __packed; + + struct smb_negotiate_rsp { +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch b/queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch new file mode 100644 index 00000000000..e2b0601a43c --- /dev/null +++ b/queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch @@ -0,0 +1,132 @@ +From e5d9c15451d11295687fcfa387bc90c3305f76af Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:20 +0900 +Subject: ksmbd: replace one-element arrays with flexible-array members + +From: Gustavo A. R. Silva + +[ Upstream commit d272e01fa0a2f15c5c331a37cd99c6875c7b7186 ] + +One-element arrays are deprecated, and we are replacing them with flexible +array members instead. So, replace one-element arrays with flexible-array +members in multiple structs in fs/ksmbd/smb_common.h and one in +fs/ksmbd/smb2pdu.h. + +Important to mention is that doing a build before/after this patch results +in no binary output differences. + +This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines +on memcpy() and help us make progress towards globally enabling +-fstrict-flex-arrays=3 [1]. + +Link: https://github.com/KSPP/linux/issues/242 +Link: https://github.com/KSPP/linux/issues/79 +Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Sergey Senozhatsky +Acked-by: Namjae Jeon +Reviewed-by: Kees Cook +Signed-off-by: Kees Cook +Link: https://lore.kernel.org/r/Y3OxronfaPYv9qGP@work +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 ++-- + fs/smb/server/smb2pdu.h | 2 +- + fs/smb/server/smb_common.h | 12 ++++++------ + 3 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 1598ad6155fef..3f4f6b0385655 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3525,7 +3525,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + goto free_conv_name; + } + +- struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; ++ struct_sz = readdir_info_level_struct_sz(info_level) + conv_len; + next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); + d_info->last_entry_off_align = next_entry_offset - struct_sz; + +@@ -3777,7 +3777,7 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; +- next_entry_offset = ALIGN(struct_sz - 1 + conv_len, ++ next_entry_offset = ALIGN(struct_sz + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { +diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h +index 665a837378540..f13bd65993ccc 100644 +--- a/fs/smb/server/smb2pdu.h ++++ b/fs/smb/server/smb2pdu.h +@@ -446,7 +446,7 @@ struct smb2_posix_info { + /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ + u8 SidBuffer[32]; + __le32 name_len; +- u8 name[1]; ++ u8 name[]; + /* + * var sized owner SID + * var sized group SID +diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h +index 1cbb492cdefec..f0134d16067fb 100644 +--- a/fs/smb/server/smb_common.h ++++ b/fs/smb/server/smb_common.h +@@ -263,14 +263,14 @@ struct file_directory_info { + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x101 FF resp data */ + + struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0xc FF resp data */ + + struct file_full_directory_info { +@@ -285,7 +285,7 @@ struct file_full_directory_info { + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x102 FF resp */ + + struct file_both_directory_info { +@@ -303,7 +303,7 @@ struct file_both_directory_info { + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x104 FFrsp data */ + + struct file_id_both_directory_info { +@@ -323,7 +323,7 @@ struct file_id_both_directory_info { + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; +- char FileName[1]; ++ char FileName[]; + } __packed; + + struct file_id_full_dir_info { +@@ -340,7 +340,7 @@ struct file_id_full_dir_info { + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x105 FF rsp data */ + + struct smb_version_values { +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch b/queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch new file mode 100644 index 00000000000..1f79bdf362d --- /dev/null +++ b/queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch @@ -0,0 +1,36 @@ +From c88a7d9cf464e00cfea6380e905e5c4e7f75d313 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:48 +0900 +Subject: ksmbd: Replace the ternary conditional operator with min() + +From: Lu Hongfei + +[ Upstream commit f65fadb0422537d73f9a6472861852dc2f7a6a5b ] + +It would be better to replace the traditional ternary conditional +operator with min() in compare_sids. + +Signed-off-by: Lu Hongfei +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smbacl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index 8fe2592c5525f..03f19d3de2a17 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -97,7 +97,7 @@ int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; +- num_subauth = num_sat < num_saw ? num_sat : num_saw; ++ num_subauth = min(num_sat, num_saw); + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch b/queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch new file mode 100644 index 00000000000..18d77fcef3f --- /dev/null +++ b/queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch @@ -0,0 +1,36 @@ +From 76d96abe4c6f3273699618028e277420ecdad103 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:45 +0900 +Subject: ksmbd: return a literal instead of 'err' in + ksmbd_vfs_kern_path_locked() + +From: Namjae Jeon + +[ Upstream commit cf5e7f734f445588a30350591360bca2f6bf016f ] + +Return a literal instead of 'err' in ksmbd_vfs_kern_path_locked(). + +Reported-by: Dan Carpenter +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index e6218c687fa0b..d0a85774a496a 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -1208,7 +1208,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, + + err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path); + if (!err) +- return err; ++ return 0; + + if (caseless) { + char *filepath; +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch b/queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch new file mode 100644 index 00000000000..1dd23f1f62e --- /dev/null +++ b/queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch @@ -0,0 +1,103 @@ +From 19d93b0f235c5b18469c13a09b8d78cdc62b92ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:02 +0900 +Subject: ksmbd: return invalid parameter error response if smb2 request is + invalid + +From: Namjae Jeon + +[ Upstream commit f2f11fca5d7112e2f91c4854cddd68a059fdaa4a ] + +If smb2 request from client is invalid, The following kernel oops could +happen. The patch e2b76ab8b5c9: "ksmbd: add support for read compound" +leads this issue. When request is invalid, It doesn't set anything in +the response buffer. This patch add missing set invalid parameter error +response. + +[ 673.085542] ksmbd: cli req too short, len 184 not 142. cmd:5 mid:109 +[ 673.085580] BUG: kernel NULL pointer dereference, address: 0000000000000000 +[ 673.085591] #PF: supervisor read access in kernel mode +[ 673.085600] #PF: error_code(0x0000) - not-present page +[ 673.085608] PGD 0 P4D 0 +[ 673.085620] Oops: 0000 [#1] PREEMPT SMP NOPTI +[ 673.085631] CPU: 3 PID: 1039 Comm: kworker/3:0 Not tainted 6.6.0-rc2-tmt #16 +[ 673.085643] Hardware name: AZW U59/U59, BIOS JTKT001 05/05/2022 +[ 673.085651] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd] +[ 673.085719] RIP: 0010:ksmbd_conn_write+0x68/0xc0 [ksmbd] +[ 673.085808] RAX: 0000000000000000 RBX: ffff88811ade4f00 RCX: 0000000000000000 +[ 673.085817] RDX: 0000000000000000 RSI: ffff88810c2a9780 RDI: ffff88810c2a9ac0 +[ 673.085826] RBP: ffffc900005e3e00 R08: 0000000000000000 R09: 0000000000000000 +[ 673.085834] R10: ffffffffa3168160 R11: 63203a64626d736b R12: ffff8881057c8800 +[ 673.085842] R13: ffff8881057c8820 R14: ffff8882781b2380 R15: ffff8881057c8800 +[ 673.085852] FS: 0000000000000000(0000) GS:ffff888278180000(0000) knlGS:0000000000000000 +[ 673.085864] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 673.085872] CR2: 0000000000000000 CR3: 000000015b63c000 CR4: 0000000000350ee0 +[ 673.085883] Call Trace: +[ 673.085890] +[ 673.085900] ? show_regs+0x6a/0x80 +[ 673.085916] ? __die+0x25/0x70 +[ 673.085926] ? page_fault_oops+0x154/0x4b0 +[ 673.085938] ? tick_nohz_tick_stopped+0x18/0x50 +[ 673.085954] ? __irq_work_queue_local+0xba/0x140 +[ 673.085967] ? do_user_addr_fault+0x30f/0x6c0 +[ 673.085979] ? exc_page_fault+0x79/0x180 +[ 673.085992] ? asm_exc_page_fault+0x27/0x30 +[ 673.086009] ? ksmbd_conn_write+0x68/0xc0 [ksmbd] +[ 673.086067] ? ksmbd_conn_write+0x46/0xc0 [ksmbd] +[ 673.086123] handle_ksmbd_work+0x28d/0x4b0 [ksmbd] +[ 673.086177] process_one_work+0x178/0x350 +[ 673.086193] ? __pfx_worker_thread+0x10/0x10 +[ 673.086202] worker_thread+0x2f3/0x420 +[ 673.086210] ? _raw_spin_unlock_irqrestore+0x27/0x50 +[ 673.086222] ? __pfx_worker_thread+0x10/0x10 +[ 673.086230] kthread+0x103/0x140 +[ 673.086242] ? __pfx_kthread+0x10/0x10 +[ 673.086253] ret_from_fork+0x39/0x60 +[ 673.086263] ? __pfx_kthread+0x10/0x10 +[ 673.086274] ret_from_fork_asm+0x1b/0x30 + +Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") +Reported-by: Tom Talpey +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/server.c | 4 +++- + fs/smb/server/smb2misc.c | 4 +--- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index f5d8e405cf6fd..633383e55723c 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + +- if (ksmbd_verify_smb_message(work)) ++ if (ksmbd_verify_smb_message(work)) { ++ conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_ABORT; ++ } + + command = conn->ops->get_cmd_val(work); + *cmd = command; +diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c +index e881df1d10cbd..23bd3d1209dfa 100644 +--- a/fs/smb/server/smb2misc.c ++++ b/fs/smb/server/smb2misc.c +@@ -440,10 +440,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) + + validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && +- smb2_validate_credit_charge(work->conn, hdr)) { +- work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); ++ smb2_validate_credit_charge(work->conn, hdr)) + return 1; +- } + + return 0; + } +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch b/queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch new file mode 100644 index 00000000000..8167aae7a1e --- /dev/null +++ b/queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch @@ -0,0 +1,230 @@ +From 8097f1559f0fd3800bf60d72bee5091714a47224 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:29 +0900 +Subject: ksmbd: send v2 lease break notification for directory + +From: Namjae Jeon + +[ Upstream commit d47d9886aeef79feba7adac701a510d65f3682b5 ] + +If client send different parent key, different client guid, or there is +no parent lease key flags in create context v2 lease, ksmbd send lease +break to client. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/common/smb2pdu.h | 1 + + fs/smb/server/oplock.c | 56 +++++++++++++++++++++++++++++++++++---- + fs/smb/server/oplock.h | 4 +++ + fs/smb/server/smb2pdu.c | 7 +++++ + fs/smb/server/vfs_cache.c | 13 ++++++++- + fs/smb/server/vfs_cache.h | 2 ++ + 6 files changed, 77 insertions(+), 6 deletions(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index c8a4014f9d395..07549957b3099 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -1196,6 +1196,7 @@ struct create_posix { + #define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + + #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) ++#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04) + + #define SMB2_LEASE_KEY_SIZE 16 + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index f8ac539b2164b..4a8745b3e8304 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -102,6 +102,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; ++ lease->is_dir = lctx->is_dir; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = le16_to_cpu(lctx->epoch); +@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { +- if (lease->state == +- (lctx->req_state & lease->state)) { ++ if (lease->state != SMB2_LEASE_NONE_LE && ++ lease->state == (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); ++ + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { +@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { +- if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) ++ if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE && ++ !lease->is_dir) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else +@@ -1082,6 +1085,48 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, + } + } + ++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx) ++{ ++ struct oplock_info *opinfo; ++ struct ksmbd_inode *p_ci = NULL; ++ ++ if (lctx->version != 2) ++ return; ++ ++ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); ++ if (!p_ci) ++ return; ++ ++ read_lock(&p_ci->m_lock); ++ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { ++ if (!opinfo->is_lease) ++ continue; ++ ++ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && ++ (!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) || ++ !compare_guid_key(opinfo, fp->conn->ClientGUID, ++ lctx->parent_lease_key))) { ++ if (!atomic_inc_not_zero(&opinfo->refcount)) ++ continue; ++ ++ atomic_inc(&opinfo->conn->r_count); ++ if (ksmbd_conn_releasing(opinfo->conn)) { ++ atomic_dec(&opinfo->conn->r_count); ++ continue; ++ } ++ ++ read_unlock(&p_ci->m_lock); ++ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); ++ opinfo_conn_put(opinfo); ++ read_lock(&p_ci->m_lock); ++ } ++ } ++ read_unlock(&p_ci->m_lock); ++ ++ ksmbd_inode_put(p_ci); ++} ++ + /** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work +@@ -1420,10 +1465,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); +- if (is_dir) ++ if (is_dir) { + lreq->req_state = lc->lcontext.LeaseState & + ~SMB2_LEASE_WRITE_CACHING_LE; +- else ++ lreq->is_dir = true; ++ } else + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->epoch = lc->lcontext.Epoch; +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index 672127318c750..b64d1536882a1 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -36,6 +36,7 @@ struct lease_ctx_info { + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + __le16 epoch; + int version; ++ bool is_dir; + }; + + struct lease_table { +@@ -54,6 +55,7 @@ struct lease { + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; ++ bool is_dir; + struct lease_table *l_lb; + }; + +@@ -125,4 +127,6 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); + void destroy_lease_table(struct ksmbd_conn *conn); ++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx); + #endif /* __KSMBD_OPLOCK_H */ +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index d7235c5e2e4d8..23e2c031cf2e5 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3225,6 +3225,13 @@ int smb2_open(struct ksmbd_work *work) + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { ++ /* ++ * Compare parent lease using parent key. If there is no ++ * a lease that has same parent key, Send lease break ++ * notification. ++ */ ++ smb_send_parent_lease_break_noti(fp, lc); ++ + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 774a387fccced..2528ce8aeebbe 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -86,6 +86,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) + return __ksmbd_inode_lookup(fp->filp->f_path.dentry); + } + ++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d) ++{ ++ struct ksmbd_inode *ci; ++ ++ read_lock(&inode_hash_lock); ++ ci = __ksmbd_inode_lookup(d); ++ read_unlock(&inode_hash_lock); ++ ++ return ci; ++} ++ + int ksmbd_query_inode_status(struct dentry *dentry) + { + struct ksmbd_inode *ci; +@@ -198,7 +209,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci) + kfree(ci); + } + +-static void ksmbd_inode_put(struct ksmbd_inode *ci) ++void ksmbd_inode_put(struct ksmbd_inode *ci) + { + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index 8325cf4527c46..4d4938d6029b6 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -138,6 +138,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); + struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); + void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); ++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); ++void ksmbd_inode_put(struct ksmbd_inode *ci); + struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); + struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch b/queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch new file mode 100644 index 00000000000..fa367d0c32b --- /dev/null +++ b/queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch @@ -0,0 +1,181 @@ +From 9bfc222a581bac716680de0c035f8099d06449f0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:21 +0900 +Subject: ksmbd: separately allocate ci per dentry + +From: Namjae Jeon + +[ Upstream commit 4274a9dc6aeb9fea66bffba15697a35ae8983b6a ] + +xfstests generic/002 test fail when enabling smb2 leases feature. +This test create hard link file, but removeal failed. +ci has a file open count to count file open through the smb client, +but in the case of hard link files, The allocation of ci per inode +cause incorrectly open count for file deletion. This patch allocate +ci per dentry to counts open counts for hard link. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 2 +- + fs/smb/server/vfs.c | 2 +- + fs/smb/server/vfs_cache.c | 33 +++++++++++++-------------------- + fs/smb/server/vfs_cache.h | 6 +++--- + 4 files changed, 18 insertions(+), 25 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index b8340f1382bef..ff4cc39b85c72 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3039,7 +3039,7 @@ int smb2_open(struct ksmbd_work *work) + } + } + +- rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); ++ rc = ksmbd_query_inode_status(path.dentry->d_parent); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 08f3f66e4b38e..42f270ee399c7 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -720,7 +720,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + goto out3; + } + +- parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent)); ++ parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + pr_err("parent dir is opened with delete access\n"); +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 38f414e803adb..774a387fccced 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -65,14 +65,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) + return tmp & inode_hash_mask; + } + +-static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) ++static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de) + { + struct hlist_head *head = inode_hashtable + +- inode_hash(inode->i_sb, inode->i_ino); ++ inode_hash(d_inode(de)->i_sb, (unsigned long)de); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { +- if (ci->m_inode == inode) { ++ if (ci->m_de == de) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; +@@ -83,26 +83,16 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) + + static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) + { +- return __ksmbd_inode_lookup(file_inode(fp->filp)); ++ return __ksmbd_inode_lookup(fp->filp->f_path.dentry); + } + +-static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +-{ +- struct ksmbd_inode *ci; +- +- read_lock(&inode_hash_lock); +- ci = __ksmbd_inode_lookup(inode); +- read_unlock(&inode_hash_lock); +- return ci; +-} +- +-int ksmbd_query_inode_status(struct inode *inode) ++int ksmbd_query_inode_status(struct dentry *dentry) + { + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); +- ci = __ksmbd_inode_lookup(inode); ++ ci = __ksmbd_inode_lookup(dentry); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)) +@@ -142,7 +132,7 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + static void ksmbd_inode_hash(struct ksmbd_inode *ci) + { + struct hlist_head *b = inode_hashtable + +- inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); ++ inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de); + + hlist_add_head(&ci->m_hash, b); + } +@@ -156,7 +146,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci) + + static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) + { +- ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); +@@ -165,6 +154,7 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); ++ ci->m_de = fp->filp->f_path.dentry; + return 0; + } + +@@ -487,12 +477,15 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) + return fp; + } + +-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) ++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) + { + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; ++ struct inode *inode = d_inode(dentry); + +- ci = ksmbd_inode_lookup_by_vfsinode(inode); ++ read_lock(&inode_hash_lock); ++ ci = __ksmbd_inode_lookup(dentry); ++ read_unlock(&inode_hash_lock); + if (!ci) + return NULL; + +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index 03d0bf941216f..8325cf4527c46 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -51,7 +51,7 @@ struct ksmbd_inode { + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; +- struct inode *m_inode; ++ struct dentry *m_de; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; +@@ -140,7 +140,7 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); + struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); ++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); + unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); + struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); + void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +@@ -164,7 +164,7 @@ enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_PENDING_DELETE, + }; + +-int ksmbd_query_inode_status(struct inode *inode); ++int ksmbd_query_inode_status(struct dentry *dentry); + bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch b/queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch new file mode 100644 index 00000000000..16af7eea08d --- /dev/null +++ b/queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch @@ -0,0 +1,72 @@ +From e7a5c4b228eee1bfd4f893d7dbe116d62f83007f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:26 +0900 +Subject: ksmbd: set epoch in create context v2 lease + +From: Namjae Jeon + +[ Upstream commit d045850b628aaf931fc776c90feaf824dca5a1cf ] + +To support v2 lease(directory lease), ksmbd set epoch in create context +v2 lease response. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/oplock.c | 5 ++++- + fs/smb/server/oplock.h | 1 + + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 4c74e8ea9649a..037316c78506e 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -104,7 +104,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) + lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; +- lease->epoch = 0; ++ lease->epoch = le16_to_cpu(lctx->epoch); + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + +@@ -1032,6 +1032,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; ++ lease2->epoch = lease1->epoch++; + } + + static int add_lease_global_list(struct oplock_info *opinfo) +@@ -1364,6 +1365,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; ++ buf->lcontext.Epoch = cpu_to_le16(++lease->epoch); + buf->lcontext.LeaseState = lease->state; + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); +@@ -1423,6 +1425,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; ++ lreq->epoch = lc->lcontext.Epoch; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index 4b0fe6da76940..ad31439c61fef 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -34,6 +34,7 @@ struct lease_ctx_info { + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; ++ __le16 epoch; + int version; + }; + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch b/queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch new file mode 100644 index 00000000000..cf1420f1a15 --- /dev/null +++ b/queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch @@ -0,0 +1,75 @@ +From b1d31d12751738d75ce2c554aaad1ed4d00d7227 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:33 +0900 +Subject: ksmbd: set NegotiateContextCount once instead of every inc + +From: David Disseldorp + +[ Upstream commit 34e8ccf9ce24b6b2e275bbe35cd392e18fbbd369 ] + +There are no early returns, so marshalling the incremented +NegotiateContextCount with every context is unnecessary. + +Signed-off-by: David Disseldorp +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index ee128f5d38c35..c7d43c83d2335 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -847,7 +847,6 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); +- rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ +@@ -859,7 +858,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += +@@ -874,7 +873,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, + conn->compress_algorithm); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, +@@ -886,7 +885,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); +@@ -898,10 +897,11 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + ++ rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(smb2_buf_len, ctxt_size); + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch b/queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch new file mode 100644 index 00000000000..7c04f6f2bf6 --- /dev/null +++ b/queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch @@ -0,0 +1,103 @@ +From 5a01e0a08c272f0d6e9802e4a9aade5f18bef742 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:21 +0900 +Subject: ksmbd: set SMB2_SESSION_FLAG_ENCRYPT_DATA when enforcing data + encryption for this share + +From: Namjae Jeon + +[ Upstream commit 37ba7b005a7a4454046bd8659c7a9c5330552396 ] + +Currently, SMB2_SESSION_FLAG_ENCRYPT_DATA is always set session setup +response. Since this forces data encryption from the client, there is a +problem that data is always encrypted regardless of the use of the cifs +seal mount option. SMB2_SESSION_FLAG_ENCRYPT_DATA should be set according +to KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION flags, and in case of +KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF, encryption mode is turned off for +all connections. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/ksmbd_netlink.h | 1 + + fs/smb/server/smb2ops.c | 10 ++++++++-- + fs/smb/server/smb2pdu.c | 8 +++++--- + 3 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h +index ce866ff159bfe..fb8b2d566efb6 100644 +--- a/fs/smb/server/ksmbd_netlink.h ++++ b/fs/smb/server/ksmbd_netlink.h +@@ -74,6 +74,7 @@ struct ksmbd_heartbeat { + #define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) + #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) + #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) ++#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) + + /* + * IPC request for ksmbd server startup +diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c +index ab23da2120b94..e401302478c36 100644 +--- a/fs/smb/server/smb2ops.c ++++ b/fs/smb/server/smb2ops.c +@@ -247,8 +247,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn) + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + +- if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && +- conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || ++ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && ++ conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) +@@ -271,6 +272,11 @@ int init_smb3_11_server(struct ksmbd_conn *conn) + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || ++ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && ++ conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; ++ + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 3f4f6b0385655..f5a46b6831636 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -935,7 +935,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + return; + } + +- if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) + return; + + for (i = 0; i < cph_cnt; i++) { +@@ -1544,7 +1544,8 @@ static int ntlm_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + sess->enc = true; +- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) ++ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session +@@ -1630,7 +1631,8 @@ static int krb5_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + sess->enc = true; +- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) ++ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-set-v2-lease-capability.patch b/queue-6.1/ksmbd-set-v2-lease-capability.patch new file mode 100644 index 00000000000..c6757a4ea62 --- /dev/null +++ b/queue-6.1/ksmbd-set-v2-lease-capability.patch @@ -0,0 +1,72 @@ +From 8e531b27cdcfd6b2fd690d5a6fc684b84386320f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:13:27 +0900 +Subject: ksmbd: set v2 lease capability + +From: Namjae Jeon + +[ Upstream commit 18dd1c367c31d0a060f737d48345747662369b64 ] + +Set SMB2_GLOBAL_CAP_DIRECTORY_LEASING to ->capabilities to inform server +support directory lease to client. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/oplock.c | 4 ---- + fs/smb/server/smb2ops.c | 9 ++++++--- + 2 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 037316c78506e..7346cbfbff6b0 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -1105,10 +1105,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + bool prev_op_has_lease; + __le32 prev_op_state = 0; + +- /* not support directory lease */ +- if (S_ISDIR(file_inode(fp->filp)->i_mode)) +- return 0; +- + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; +diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c +index e401302478c36..535402629655e 100644 +--- a/fs/smb/server/smb2ops.c ++++ b/fs/smb/server/smb2ops.c +@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) +@@ -245,7 +246,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || + (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && +@@ -270,7 +272,8 @@ int init_smb3_11_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || + (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch b/queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch new file mode 100644 index 00000000000..7823acff387 --- /dev/null +++ b/queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch @@ -0,0 +1,41 @@ +From 61a8df80b1a0ba0280d148df81a0403dba06f67e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:53 +0900 +Subject: ksmbd: switch to use kmemdup_nul() helper + +From: Yang Yingliang + +[ Upstream commit 084ba46fc41c21ba827fd92e61f78def7a6e52ea ] + +Use kmemdup_nul() helper instead of open-coding to +simplify the code. + +Acked-by: Namjae Jeon +Signed-off-by: Yang Yingliang +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/asn1.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c +index cc6384f796759..4a4b2b03ff33d 100644 +--- a/fs/smb/server/asn1.c ++++ b/fs/smb/server/asn1.c +@@ -214,12 +214,10 @@ static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, + { + struct ksmbd_conn *conn = context; + +- conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); ++ conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + +- memcpy(conn->mechToken, value, vlen); +- conn->mechToken[vlen] = '\0'; + return 0; + } + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch b/queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch new file mode 100644 index 00000000000..8da305fe1f2 --- /dev/null +++ b/queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch @@ -0,0 +1,58 @@ +From a2cca2e2827e921994f32e7c939f2cc57a7a458a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:28 +0900 +Subject: ksmbd: update Kconfig to note Kerberos support and fix indentation + +From: Steve French + +[ Upstream commit d280a958f8b2b62610c280ecdf35d780e7922620 ] + +Fix indentation of server config options, and also since +support for very old, less secure, NTLM authentication was removed +(and quite a while ago), remove the mention of that in Kconfig, but +do note Kerberos (not just NTLMv2) which are supported and much +more secure. + +Acked-by: Namjae Jeon +Acked-by: David Howells +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/Kconfig | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig +index e1fe17747ed69..7055cb5d28800 100644 +--- a/fs/smb/server/Kconfig ++++ b/fs/smb/server/Kconfig +@@ -33,14 +33,16 @@ config SMB_SERVER + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is +- available via README file ++ available via the README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session +- establishment via NTLM or NTLMv2. ++ establishment via Kerberos or NTLMv2. ++ ++if SMB_SERVER + + config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" +@@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + ++endif ++ + config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch b/queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch new file mode 100644 index 00000000000..04723e7dd27 --- /dev/null +++ b/queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch @@ -0,0 +1,50 @@ +From 86edb53671ff99396167aea58c33fcc3f2519fb9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:22 +0900 +Subject: ksmbd: use F_SETLK when unlocking a file + +From: Jeff Layton + +[ Upstream commit 7ecbe92696bb7fe32c80b6cf64736a0d157717a9 ] + +ksmbd seems to be trying to use a cmd value of 0 when unlocking a file. +That activity requires a type of F_UNLCK with a cmd of F_SETLK. For +local POSIX locking, it doesn't matter much since vfs_lock_file ignores +@cmd, but filesystems that define their own ->lock operation expect to +see it set sanely. + +Cc: David Howells +Signed-off-by: Jeff Layton +Reviewed-by: David Howells +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index f5a46b6831636..554214fca5b78 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -6845,7 +6845,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; +- cmd = 0; ++ cmd = F_SETLK; + break; + } + +@@ -7228,7 +7228,7 @@ int smb2_lock(struct ksmbd_work *work) + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + +- rc = vfs_lock_file(filp, 0, rlock, NULL); ++ rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch b/queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch new file mode 100644 index 00000000000..06601370803 --- /dev/null +++ b/queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch @@ -0,0 +1,108 @@ +From ee0517dd0376763ba9e0b6432ac94fa691904670 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:47 +0900 +Subject: ksmbd: use kvzalloc instead of kvmalloc + +From: Namjae Jeon + +[ Upstream commit 81a94b27847f7d2e499415db14dd9dc7c22b19b0 ] + +Use kvzalloc instead of kvmalloc. + +Reported-by: kernel test robot +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 8 ++++---- + fs/smb/server/transport_ipc.c | 4 ++-- + fs/smb/server/vfs.c | 4 ++-- + 3 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index e8d2c6fc3f37c..10d51256858ff 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -543,7 +543,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + +- work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); ++ work->response_buf = kvzalloc(sz, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + +@@ -6120,7 +6120,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + } + + work->aux_payload_buf = +- kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); ++ kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; +@@ -6277,7 +6277,7 @@ int smb2_read(struct ksmbd_work *work) + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + +- work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); ++ work->aux_payload_buf = kvzalloc(length, GFP_KERNEL); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; +@@ -6428,7 +6428,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + int ret; + ssize_t nbytes; + +- data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); ++ data_buf = kvzalloc(length, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + +diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c +index 40c721f9227e4..b49d47bdafc94 100644 +--- a/fs/smb/server/transport_ipc.c ++++ b/fs/smb/server/transport_ipc.c +@@ -229,7 +229,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + +- msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); ++ msg = kvzalloc(msg_sz, GFP_KERNEL); + if (msg) + msg->sz = sz; + return msg; +@@ -268,7 +268,7 @@ static int handle_response(int type, void *payload, size_t sz) + entry->type + 1, type); + } + +- entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); ++ entry->response = kvzalloc(sz, GFP_KERNEL); + if (!entry->response) { + ret = -ENOMEM; + break; +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 178bcd4d0b209..d05d2d1274b04 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -437,7 +437,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + } + + if (v_len < size) { +- wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); ++ wbuf = kvzalloc(size, GFP_KERNEL); + if (!wbuf) { + err = -ENOMEM; + goto out; +@@ -854,7 +854,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) + if (size <= 0) + return size; + +- vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); ++ vlist = kvzalloc(size, GFP_KERNEL); + if (!vlist) + return -ENOMEM; + +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch b/queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch new file mode 100644 index 00000000000..45f450b1e7b --- /dev/null +++ b/queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch @@ -0,0 +1,38 @@ +From c97f5127fb0e6a274a550e2be9ce17a1ba1938dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:44 +0900 +Subject: ksmbd: use kzalloc() instead of __GFP_ZERO + +From: Namjae Jeon + +[ Upstream commit f87d4f85f43f0d4b12ef64b015478d8053e1a33e ] + +Use kzalloc() instead of __GFP_ZERO. + +Reported-by: Dan Carpenter +Signed-off-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb_common.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c +index adc41b57b84c6..62c33d3357fe1 100644 +--- a/fs/smb/server/smb_common.c ++++ b/fs/smb/server/smb_common.c +@@ -359,8 +359,8 @@ static int smb1_check_user_session(struct ksmbd_work *work) + */ + static int smb1_allocate_rsp_buf(struct ksmbd_work *work) + { +- work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, +- GFP_KERNEL | __GFP_ZERO); ++ work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, ++ GFP_KERNEL); + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (!work->response_buf) { +-- +2.43.0 + diff --git a/queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch b/queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch new file mode 100644 index 00000000000..5656e2d2e88 --- /dev/null +++ b/queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch @@ -0,0 +1,38 @@ +From 9de1541d1c548ff46e8285362f587e02c4afc3ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 31 Dec 2023 16:12:49 +0900 +Subject: ksmbd: Use struct_size() helper in ksmbd_negotiate_smb_dialect() + +From: Gustavo A. R. Silva + +[ Upstream commit 5211cc8727ed9701b04976ab47602955e5641bda ] + +Prefer struct_size() over open-coded versions. + +Link: https://github.com/KSPP/linux/issues/160 +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Kees Cook +Acked-by: Namjae Jeon +Reviewed-by: Sergey Senozhatsky +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb_common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c +index 62c33d3357fe1..b6f414a2404b1 100644 +--- a/fs/smb/server/smb_common.c ++++ b/fs/smb/server/smb_common.c +@@ -266,7 +266,7 @@ static int ksmbd_negotiate_smb_dialect(void *buf) + if (smb2_neg_size > smb_buf_length) + goto err_out; + +- if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > ++ if (struct_size(req, Dialects, le16_to_cpu(req->DialectCount)) > + smb_buf_length) + goto err_out; + +-- +2.43.0 + diff --git a/queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch b/queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch new file mode 100644 index 00000000000..617ac8ccd28 --- /dev/null +++ b/queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch @@ -0,0 +1,35 @@ +From f5606fbe0adf2adc4a9c9edec8e29d4e0e17f7ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 Dec 2023 11:36:03 +0100 +Subject: linux/export: Ensure natural alignment of kcrctab array + +From: Helge Deller + +[ Upstream commit 753547de0daecbdbd1af3618987ddade325d9aaa ] + +The ___kcrctab section holds an array of 32-bit CRC values. +Add a .balign 4 to tell the linker the correct memory alignment. + +Fixes: f3304ecd7f06 ("linux/export: use inline assembler to populate symbol CRCs") +Signed-off-by: Helge Deller +Signed-off-by: Masahiro Yamada +Signed-off-by: Sasha Levin +--- + include/linux/export-internal.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h +index fe7e6ba918f10..29de29af9546c 100644 +--- a/include/linux/export-internal.h ++++ b/include/linux/export-internal.h +@@ -12,6 +12,7 @@ + + #define SYMBOL_CRC(sym, crc, sec) \ + asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \ ++ ".balign 4" "\n" \ + "__crc_" #sym ":" "\n" \ + ".long " #crc "\n" \ + ".previous" "\n") +-- +2.43.0 + diff --git a/queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch b/queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch new file mode 100644 index 00000000000..32fde615f55 --- /dev/null +++ b/queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch @@ -0,0 +1,83 @@ +From 1ef488e4b54c2f2168f1219e1319bd08e7bb5e9f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 Dec 2023 11:56:31 +1100 +Subject: nfsd: call nfsd_last_thread() before final nfsd_put() + +From: NeilBrown + +[ Upstream commit 2a501f55cd641eb4d3c16a2eab0d678693fac663 ] + +If write_ports_addfd or write_ports_addxprt fail, they call nfsd_put() +without calling nfsd_last_thread(). This leaves nn->nfsd_serv pointing +to a structure that has been freed. + +So remove 'static' from nfsd_last_thread() and call it when the +nfsd_serv is about to be destroyed. + +Fixes: ec52361df99b ("SUNRPC: stop using ->sv_nrthreads as a refcount") +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Cc: +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfsctl.c | 9 +++++++-- + fs/nfsd/nfsd.h | 1 + + fs/nfsd/nfssvc.c | 2 +- + 3 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c +index 573de0d49e172..b3b4542e31ed5 100644 +--- a/fs/nfsd/nfsctl.c ++++ b/fs/nfsd/nfsctl.c +@@ -716,8 +716,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred + + err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); + +- if (err >= 0 && +- !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) ++ if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active) ++ nfsd_last_thread(net); ++ else if (err >= 0 && ++ !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) + svc_get(nn->nfsd_serv); + + nfsd_put(net); +@@ -767,6 +769,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr + svc_xprt_put(xprt); + } + out_err: ++ if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active) ++ nfsd_last_thread(net); ++ + nfsd_put(net); + return err; + } +diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h +index fddd70372e4cb..53166cce7062c 100644 +--- a/fs/nfsd/nfsd.h ++++ b/fs/nfsd/nfsd.h +@@ -139,6 +139,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); + int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change); + void nfsd_reset_versions(struct nfsd_net *nn); + int nfsd_create_serv(struct net *net); ++void nfsd_last_thread(struct net *net); + + extern int nfsd_max_blksize; + +diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c +index 6ac18399fed2b..d8662bdca5706 100644 +--- a/fs/nfsd/nfssvc.c ++++ b/fs/nfsd/nfssvc.c +@@ -523,7 +523,7 @@ static struct notifier_block nfsd_inet6addr_notifier = { + /* Only used under nfsd_mutex, so this atomic may be overkill: */ + static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); + +-static void nfsd_last_thread(struct net *net) ++void nfsd_last_thread(struct net *net) + { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct svc_serv *serv = nn->nfsd_serv; +-- +2.43.0 + diff --git a/queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch b/queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch new file mode 100644 index 00000000000..da09796916d --- /dev/null +++ b/queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch @@ -0,0 +1,182 @@ +From 08172072a5a43c346fab7d1dd36176cb949282f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 31 Jul 2023 16:48:32 +1000 +Subject: nfsd: separate nfsd_last_thread() from nfsd_put() + +From: NeilBrown + +[ Upstream commit 9f28a971ee9fdf1bf8ce8c88b103f483be610277 ] + +Now that the last nfsd thread is stopped by an explicit act of calling +svc_set_num_threads() with a count of zero, we only have a limited +number of places that can happen, and don't need to call +nfsd_last_thread() in nfsd_put() + +So separate that out and call it at the two places where the number of +threads is set to zero. + +Move the clearing of ->nfsd_serv and the call to svc_xprt_destroy_all() +into nfsd_last_thread(), as they are really part of the same action. + +nfsd_put() is now a thin wrapper around svc_put(), so make it a static +inline. + +nfsd_put() cannot be called after nfsd_last_thread(), so in a couple of +places we have to use svc_put() instead. + +Signed-off-by: NeilBrown +Signed-off-by: Chuck Lever +Stable-dep-of: 2a501f55cd64 ("nfsd: call nfsd_last_thread() before final nfsd_put()") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfsd.h | 7 ++++++- + fs/nfsd/nfssvc.c | 52 ++++++++++++++++++------------------------------ + 2 files changed, 25 insertions(+), 34 deletions(-) + +diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h +index 09726c5b9a317..fddd70372e4cb 100644 +--- a/fs/nfsd/nfsd.h ++++ b/fs/nfsd/nfsd.h +@@ -97,7 +97,12 @@ int nfsd_pool_stats_open(struct inode *, struct file *); + int nfsd_pool_stats_release(struct inode *, struct file *); + void nfsd_shutdown_threads(struct net *net); + +-void nfsd_put(struct net *net); ++static inline void nfsd_put(struct net *net) ++{ ++ struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ ++ svc_put(nn->nfsd_serv); ++} + + bool i_am_nfsd(void); + +diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c +index f6cc99af81925..6ac18399fed2b 100644 +--- a/fs/nfsd/nfssvc.c ++++ b/fs/nfsd/nfssvc.c +@@ -523,9 +523,14 @@ static struct notifier_block nfsd_inet6addr_notifier = { + /* Only used under nfsd_mutex, so this atomic may be overkill: */ + static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); + +-static void nfsd_last_thread(struct svc_serv *serv, struct net *net) ++static void nfsd_last_thread(struct net *net) + { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ struct svc_serv *serv = nn->nfsd_serv; ++ ++ spin_lock(&nfsd_notifier_lock); ++ nn->nfsd_serv = NULL; ++ spin_unlock(&nfsd_notifier_lock); + + /* check if the notifier still has clients */ + if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { +@@ -535,6 +540,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) + #endif + } + ++ svc_xprt_destroy_all(serv, net); ++ + /* + * write_ports can create the server without actually starting + * any threads--if we get shut down before any threads are +@@ -625,7 +632,8 @@ void nfsd_shutdown_threads(struct net *net) + svc_get(serv); + /* Kill outstanding nfsd threads */ + svc_set_num_threads(serv, NULL, 0); +- nfsd_put(net); ++ nfsd_last_thread(net); ++ svc_put(serv); + mutex_unlock(&nfsd_mutex); + } + +@@ -655,9 +663,6 @@ int nfsd_create_serv(struct net *net) + serv->sv_maxconn = nn->max_connections; + error = svc_bind(serv, net); + if (error < 0) { +- /* NOT nfsd_put() as notifiers (see below) haven't +- * been set up yet. +- */ + svc_put(serv); + return error; + } +@@ -700,29 +705,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) + return 0; + } + +-/* This is the callback for kref_put() below. +- * There is no code here as the first thing to be done is +- * call svc_shutdown_net(), but we cannot get the 'net' from +- * the kref. So do all the work when kref_put returns true. +- */ +-static void nfsd_noop(struct kref *ref) +-{ +-} +- +-void nfsd_put(struct net *net) +-{ +- struct nfsd_net *nn = net_generic(net, nfsd_net_id); +- +- if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) { +- svc_xprt_destroy_all(nn->nfsd_serv, net); +- nfsd_last_thread(nn->nfsd_serv, net); +- svc_destroy(&nn->nfsd_serv->sv_refcnt); +- spin_lock(&nfsd_notifier_lock); +- nn->nfsd_serv = NULL; +- spin_unlock(&nfsd_notifier_lock); +- } +-} +- + int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + { + int i = 0; +@@ -773,7 +755,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + if (err) + break; + } +- nfsd_put(net); ++ svc_put(nn->nfsd_serv); + return err; + } + +@@ -788,6 +770,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + int error; + bool nfsd_up_before; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ struct svc_serv *serv; + + mutex_lock(&nfsd_mutex); + dprintk("nfsd: creating service\n"); +@@ -807,22 +790,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + goto out; + + nfsd_up_before = nn->nfsd_net_up; ++ serv = nn->nfsd_serv; + + error = nfsd_startup_net(net, cred); + if (error) + goto out_put; +- error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs); ++ error = svc_set_num_threads(serv, NULL, nrservs); + if (error) + goto out_shutdown; +- error = nn->nfsd_serv->sv_nrthreads; ++ error = serv->sv_nrthreads; ++ if (error == 0) ++ nfsd_last_thread(net); + out_shutdown: + if (error < 0 && !nfsd_up_before) + nfsd_shutdown_net(net); + out_put: + /* Threads now hold service active */ + if (xchg(&nn->keep_active, 0)) +- nfsd_put(net); +- nfsd_put(net); ++ svc_put(serv); ++ svc_put(serv); + out: + mutex_unlock(&nfsd_mutex); + return error; +-- +2.43.0 + diff --git a/queue-6.1/series b/queue-6.1/series new file mode 100644 index 00000000000..d550eab06ce --- /dev/null +++ b/queue-6.1/series @@ -0,0 +1,84 @@ +ksmbd-replace-one-element-arrays-with-flexible-array.patch +ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch +ksmbd-use-f_setlk-when-unlocking-a-file.patch +ksmbd-fix-resource-leak-in-smb2_lock.patch +ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch +ksmbd-implements-sess-rpc_handle_list-as-xarray.patch +ksmbd-fix-typo-syncronous-synchronous.patch +ksmbd-remove-duplicated-codes.patch +ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch +ksmbd-fix-spelling-mistake-excceed-exceeded.patch +ksmbd-fix-parameter-name-and-comment-mismatch.patch +ksmbd-remove-unused-is_char_allowed-function.patch +ksmbd-delete-asynchronous-work-from-list.patch +ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch +ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch +ksmbd-remove-unused-compression-negotiate-ctx-packin.patch +fs-introduce-lock_rename_child-helper.patch +ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch +ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch +ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch +ksmbd-call-putname-after-using-the-last-component.patch +ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch +ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch +ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch +ksmbd-use-kzalloc-instead-of-__gfp_zero.patch +ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch +ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch +ksmbd-use-kvzalloc-instead-of-kvmalloc.patch +ksmbd-replace-the-ternary-conditional-operator-with-.patch +ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch +ksmbd-replace-one-element-array-with-flexible-array-.patch +ksmbd-fix-unsigned-expression-compared-with-zero.patch +ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch +ksmbd-switch-to-use-kmemdup_nul-helper.patch +ksmbd-add-support-for-read-compound.patch +ksmbd-fix-wrong-interim-response-on-compound.patch +ksmbd-fix-force-create-mode-and-force-directory-mode.patch +ksmbd-fix-one-kernel-doc-comment.patch +ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch +ksmbd-remove-experimental-warning.patch +ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch +ksmbd-fix-passing-freed-memory-aux_payload_buf.patch +ksmbd-return-invalid-parameter-error-response-if-smb.patch +ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch +ksmbd-fix-race-condition-with-fp.patch +ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch +ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch +ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch +ksmbd-fix-wrong-error-response-status-by-using-set_s.patch +ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch +ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch +ksmbd-remove-unused-field-in-ksmbd_user-struct.patch +ksmbd-reorganize-ksmbd_iov_pin_rsp.patch +ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch +ksmbd-fix-recursive-locking-in-vfs-helpers.patch +ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch +ksmbd-add-support-for-surrogate-pair-conversion.patch +ksmbd-no-need-to-wait-for-binded-connection-terminat.patch +ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch +ksmbd-prevent-memory-leak-on-error-return.patch +ksmbd-fix-possible-deadlock-in-smb2_open.patch +ksmbd-separately-allocate-ci-per-dentry.patch +ksmbd-move-oplock-handling-after-unlock-parent-dir.patch +ksmbd-release-interim-response-after-sending-status-.patch +ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch +ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch +ksmbd-set-epoch-in-create-context-v2-lease.patch +ksmbd-set-v2-lease-capability.patch +ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch +ksmbd-send-v2-lease-break-notification-for-directory.patch +ksmbd-lazy-v2-lease-break-on-smb2_write.patch +ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch +ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch +arm-dts-fix-occasional-boot-hang-for-am3-usb.patch +usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch +spi-introduce-spi_get_device_match_data-helper.patch +iio-imu-adis16475-add-spi_device_id-table.patch +nfsd-separate-nfsd_last_thread-from-nfsd_put.patch +nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch +linux-export-ensure-natural-alignment-of-kcrctab-arr.patch +spi-reintroduce-spi_set_cs_timing.patch +spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch +spi-atmel-fix-clock-issue-when-using-devices-with-di.patch +block-renumber-queue_flag_hw_wc.patch diff --git a/queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch b/queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch new file mode 100644 index 00000000000..2dc5d702d75 --- /dev/null +++ b/queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch @@ -0,0 +1,236 @@ +From 394837184fdb35417c97e669b04b1d4c96988f37 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jan 2023 00:23:30 +0530 +Subject: spi: Add APIs in spi core to set/get spi->chip_select and + spi->cs_gpiod + +From: Amit Kumar Mahapatra + +[ Upstream commit 303feb3cc06ac0665d0ee9c1414941200e60e8a3 ] + +Supporting multi-cs in spi core and spi controller drivers would require +the chip_select & cs_gpiod members of struct spi_device to be an array. +But changing the type of these members to array would break the spi driver +functionality. To make the transition smoother introduced four new APIs to +get/set the spi->chip_select & spi->cs_gpiod and replaced all +spi->chip_select and spi->cs_gpiod references in spi core with the API +calls. +While adding multi-cs support in further patches the chip_select & cs_gpiod +members of the spi_device structure would be converted to arrays & the +"idx" parameter of the APIs would be used as array index i.e., +spi->chip_select[idx] & spi->cs_gpiod[idx] respectively. + +Suggested-by: Lars-Peter Clausen +Signed-off-by: Amit Kumar Mahapatra +Reviewed-by: Michal Simek +Link: https://lore.kernel.org/r/20230119185342.2093323-2-amit.kumar-mahapatra@amd.com +Signed-off-by: Mark Brown +Stable-dep-of: fc70d643a2f6 ("spi: atmel: Fix clock issue when using devices with different polarities") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi.c | 45 ++++++++++++++++++++--------------------- + include/linux/spi/spi.h | 20 ++++++++++++++++++ + 2 files changed, 42 insertions(+), 23 deletions(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index f1ed2863a183e..22d227878bc44 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -604,7 +604,7 @@ static void spi_dev_set_name(struct spi_device *spi) + } + + dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev), +- spi->chip_select); ++ spi_get_chipselect(spi, 0)); + } + + static int spi_dev_check(struct device *dev, void *data) +@@ -613,7 +613,7 @@ static int spi_dev_check(struct device *dev, void *data) + struct spi_device *new_spi = data; + + if (spi->controller == new_spi->controller && +- spi->chip_select == new_spi->chip_select) ++ spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0)) + return -EBUSY; + return 0; + } +@@ -638,7 +638,7 @@ static int __spi_add_device(struct spi_device *spi) + status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); + if (status) { + dev_err(dev, "chipselect %d already in use\n", +- spi->chip_select); ++ spi_get_chipselect(spi, 0)); + return status; + } + +@@ -649,7 +649,7 @@ static int __spi_add_device(struct spi_device *spi) + } + + if (ctlr->cs_gpiods) +- spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select]; ++ spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]); + + /* + * Drivers may modify this initial i/o setup, but will +@@ -692,8 +692,8 @@ int spi_add_device(struct spi_device *spi) + int status; + + /* Chipselects are numbered 0..max; validate. */ +- if (spi->chip_select >= ctlr->num_chipselect) { +- dev_err(dev, "cs%d >= max %d\n", spi->chip_select, ++ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) { ++ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0), + ctlr->num_chipselect); + return -EINVAL; + } +@@ -714,8 +714,8 @@ static int spi_add_device_locked(struct spi_device *spi) + struct device *dev = ctlr->dev.parent; + + /* Chipselects are numbered 0..max; validate. */ +- if (spi->chip_select >= ctlr->num_chipselect) { +- dev_err(dev, "cs%d >= max %d\n", spi->chip_select, ++ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) { ++ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0), + ctlr->num_chipselect); + return -EINVAL; + } +@@ -761,7 +761,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr, + + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); + +- proxy->chip_select = chip->chip_select; ++ spi_set_chipselect(proxy, 0, chip->chip_select); + proxy->max_speed_hz = chip->max_speed_hz; + proxy->mode = chip->mode; + proxy->irq = chip->irq; +@@ -970,24 +970,23 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + * Avoid calling into the driver (or doing delays) if the chip select + * isn't actually changing from the last time this was called. + */ +- if (!force && ((enable && spi->controller->last_cs == spi->chip_select) || +- (!enable && spi->controller->last_cs != spi->chip_select)) && ++ if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) || ++ (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) && + (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH))) + return; + + trace_spi_set_cs(spi, activate); + +- spi->controller->last_cs = enable ? spi->chip_select : -1; ++ spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1; + spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; + +- if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) { ++ if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate) + spi_delay_exec(&spi->cs_hold, NULL); +- } + + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + +- if (spi->cs_gpiod) { ++ if (spi_get_csgpiod(spi, 0)) { + if (!(spi->mode & SPI_NO_CS)) { + /* + * Historically ACPI has no means of the GPIO polarity and +@@ -1000,10 +999,10 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + * into account. + */ + if (has_acpi_companion(&spi->dev)) +- gpiod_set_value_cansleep(spi->cs_gpiod, !enable); ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable); + else + /* Polarity handled by GPIO library */ +- gpiod_set_value_cansleep(spi->cs_gpiod, activate); ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate); + } + /* Some SPI masters need both GPIO CS & slave_select */ + if ((spi->controller->flags & SPI_MASTER_GPIO_SS) && +@@ -1013,7 +1012,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + spi->controller->set_cs(spi, !enable); + } + +- if (spi->cs_gpiod || !spi->controller->set_cs_timing) { ++ if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) { + if (activate) + spi_delay_exec(&spi->cs_setup, NULL); + else +@@ -2303,7 +2302,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + nc, rc); + return rc; + } +- spi->chip_select = value; ++ spi_set_chipselect(spi, 0, value); + + /* Device speed */ + if (!of_property_read_u32(nc, "spi-max-frequency", &value)) +@@ -2417,7 +2416,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi, + strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias)); + + /* Use provided chip-select for ancillary device */ +- ancillary->chip_select = chip_select; ++ spi_set_chipselect(ancillary, 0, chip_select); + + /* Take over SPI mode/speed from SPI main device */ + ancillary->max_speed_hz = spi->max_speed_hz; +@@ -2664,7 +2663,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, + spi->mode |= lookup.mode; + spi->irq = lookup.irq; + spi->bits_per_word = lookup.bits_per_word; +- spi->chip_select = lookup.chip_select; ++ spi_set_chipselect(spi, 0, lookup.chip_select); + + return spi; + } +@@ -3634,7 +3633,7 @@ static int spi_set_cs_timing(struct spi_device *spi) + struct device *parent = spi->controller->dev.parent; + int status = 0; + +- if (spi->controller->set_cs_timing && !spi->cs_gpiod) { ++ if (spi->controller->set_cs_timing && !spi_get_csgpiod(spi, 0)) { + if (spi->controller->auto_runtime_pm) { + status = pm_runtime_get_sync(parent); + if (status < 0) { +@@ -3839,7 +3838,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) + * cs_change is set for each transfer. + */ + if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) || +- spi->cs_gpiod)) { ++ spi_get_csgpiod(spi, 0))) { + size_t maxsize; + int ret; + +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index 635a05c30283c..a87afac9742cd 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -263,6 +263,26 @@ static inline void *spi_get_drvdata(struct spi_device *spi) + return dev_get_drvdata(&spi->dev); + } + ++static inline u8 spi_get_chipselect(struct spi_device *spi, u8 idx) ++{ ++ return spi->chip_select; ++} ++ ++static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect) ++{ ++ spi->chip_select = chipselect; ++} ++ ++static inline struct gpio_desc *spi_get_csgpiod(struct spi_device *spi, u8 idx) ++{ ++ return spi->cs_gpiod; ++} ++ ++static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod) ++{ ++ spi->cs_gpiod = csgpiod; ++} ++ + struct spi_message; + + /** +-- +2.43.0 + diff --git a/queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch b/queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch new file mode 100644 index 00000000000..7ddf931f0d3 --- /dev/null +++ b/queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch @@ -0,0 +1,247 @@ +From 7f454679fa857e2f9b8c680fd804cf24aa5c1f2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 Dec 2023 16:49:03 +0100 +Subject: spi: atmel: Fix clock issue when using devices with different + polarities + +From: Louis Chauvet + +[ Upstream commit fc70d643a2f6678cbe0f5c86433c1aeb4d613fcc ] + +The current Atmel SPI controller driver (v2) behaves incorrectly when +using two SPI devices with different clock polarities and GPIO CS. + +When switching from one device to another, the controller driver first +enables the CS and then applies whatever configuration suits the targeted +device (typically, the polarities). The side effect of such order is the +apparition of a spurious clock edge after enabling the CS when the clock +polarity needs to be inverted wrt. the previous configuration of the +controller. + +This parasitic clock edge is problematic when the SPI device uses that edge +for internal processing, which is perfectly legitimate given that its CS +was asserted. Indeed, devices such as HVS8080 driven by driver gpio-sr in +the kernel are shift registers and will process this first clock edge to +perform a first register shift. In this case, the first bit gets lost and +the whole data block that will later be read by the kernel is all shifted +by one. + + Current behavior: + The actual switching of the clock polarity only occurs after the CS + when the controller sends the first message: + + CLK ------------\ /-\ /-\ + | | | | | . . . + \---/ \-/ \ + CS -----\ + | + \------------------ + + ^ ^ ^ + | | | + | | Actual clock of the message sent + | | + | Change of clock polarity, which occurs with the first + | write to the bus. This edge occurs when the CS is + | already asserted, and can be interpreted as + | the first clock edge by the receiver. + | + GPIO CS toggle + +This issue is specific to this controller because while the SPI core +performs the operations in the right order, the controller however does +not. In practice, the controller only applies the clock configuration right +before the first transmission. + +So this is not a problem when using the controller's dedicated CS, as the +controller does things correctly, but it becomes a problem when you need to +change the clock polarity and use an external GPIO for the CS. + +One possible approach to solve this problem is to send a dummy message +before actually activating the CS, so that the controller applies the clock +polarity beforehand. + +New behavior: + +CLK ------\ /-\ /-\ /-\ /-\ + | | | ... | | | | ... | | + \------/ \- -/ \------/ \- -/ \------ + +CS -\/-----------------------\ + || | + \/ \--------------------- + ^ ^ ^ ^ ^ + | | | | | + | | | | Expected clock cycles when + | | | | sending the message + | | | | + | | | Actual GPIO CS activation, occurs inside + | | | the driver + | | | + | | Dummy message, to trigger clock polarity + | | reconfiguration. This message is not received and + | | processed by the device because CS is low. + | | + | Change of clock polarity, forced by the dummy message. This + | time, the edge is not detected by the receiver. + | + This small spike in CS activation is due to the fact that the + spi-core activates the CS gpio before calling the driver's + set_cs callback, which deactivates this gpio again until the + clock polarity is correct. + +To avoid having to systematically send a dummy packet, the driver keeps +track of the clock's current polarity. In this way, it only sends the dummy +packet when necessary, ensuring that the clock will have the correct +polarity when the CS is toggled. + +There could be two hardware problems with this patch: +1- Maybe the small CS activation peak can confuse SPI devices +2- If on a design, a single wire is used to select two devices depending +on its state, the dummy message may disturb them. + +Fixes: 5ee36c989831 ("spi: atmel_spi update chipselect handling") +Cc: +Signed-off-by: Louis Chauvet +Link: https://msgid.link/r/20231204154903.11607-1-louis.chauvet@bootlin.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-atmel.c | 82 ++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 81 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c +index c4f22d50dba58..78daf2b2143c5 100644 +--- a/drivers/spi/spi-atmel.c ++++ b/drivers/spi/spi-atmel.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + + /* SPI register offsets */ +@@ -278,6 +279,7 @@ struct atmel_spi { + bool keep_cs; + + u32 fifo_size; ++ bool last_polarity; + u8 native_cs_free; + u8 native_cs_for_gpio; + }; +@@ -290,6 +292,22 @@ struct atmel_spi_device { + #define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */ + #define INVALID_DMA_ADDRESS 0xffffffff + ++/* ++ * This frequency can be anything supported by the controller, but to avoid ++ * unnecessary delay, the highest possible frequency is chosen. ++ * ++ * This frequency is the highest possible which is not interfering with other ++ * chip select registers (see Note for Serial Clock Bit Rate configuration in ++ * Atmel-11121F-ATARM-SAMA5D3-Series-Datasheet_02-Feb-16, page 1283) ++ */ ++#define DUMMY_MSG_FREQUENCY 0x02 ++/* ++ * 8 bits is the minimum data the controller is capable of sending. ++ * ++ * This message can be anything as it should not be treated by any SPI device. ++ */ ++#define DUMMY_MSG 0xAA ++ + /* + * Version 2 of the SPI controller has + * - CR.LASTXFER +@@ -303,6 +321,43 @@ static bool atmel_spi_is_v2(struct atmel_spi *as) + return as->caps.is_spi2; + } + ++/* ++ * Send a dummy message. ++ * ++ * This is sometimes needed when using a CS GPIO to force clock transition when ++ * switching between devices with different polarities. ++ */ ++static void atmel_spi_send_dummy(struct atmel_spi *as, struct spi_device *spi, int chip_select) ++{ ++ u32 status; ++ u32 csr; ++ ++ /* ++ * Set a clock frequency to allow sending message on SPI bus. ++ * The frequency here can be anything, but is needed for ++ * the controller to send the data. ++ */ ++ csr = spi_readl(as, CSR0 + 4 * chip_select); ++ csr = SPI_BFINS(SCBR, DUMMY_MSG_FREQUENCY, csr); ++ spi_writel(as, CSR0 + 4 * chip_select, csr); ++ ++ /* ++ * Read all data coming from SPI bus, needed to be able to send ++ * the message. ++ */ ++ spi_readl(as, RDR); ++ while (spi_readl(as, SR) & SPI_BIT(RDRF)) { ++ spi_readl(as, RDR); ++ cpu_relax(); ++ } ++ ++ spi_writel(as, TDR, DUMMY_MSG); ++ ++ readl_poll_timeout_atomic(as->regs + SPI_SR, status, ++ (status & SPI_BIT(TXEMPTY)), 1, 1000); ++} ++ ++ + /* + * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby + * they assume that spi slave device state will not change on deselect, so +@@ -319,11 +374,17 @@ static bool atmel_spi_is_v2(struct atmel_spi *as) + * Master on Chip Select 0.") No workaround exists for that ... so for + * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, + * and (c) will trigger that first erratum in some cases. ++ * ++ * When changing the clock polarity, the SPI controller waits for the next ++ * transmission to enforce the default clock state. This may be an issue when ++ * using a GPIO as Chip Select: the clock level is applied only when the first ++ * packet is sent, once the CS has already been asserted. The workaround is to ++ * avoid this by sending a first (dummy) message before toggling the CS state. + */ +- + static void cs_activate(struct atmel_spi *as, struct spi_device *spi) + { + struct atmel_spi_device *asd = spi->controller_state; ++ bool new_polarity; + int chip_select; + u32 mr; + +@@ -352,6 +413,25 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) + } + + mr = spi_readl(as, MR); ++ ++ /* ++ * Ensures the clock polarity is valid before we actually ++ * assert the CS to avoid spurious clock edges to be ++ * processed by the spi devices. ++ */ ++ if (spi_get_csgpiod(spi, 0)) { ++ new_polarity = (asd->csr & SPI_BIT(CPOL)) != 0; ++ if (new_polarity != as->last_polarity) { ++ /* ++ * Need to disable the GPIO before sending the dummy ++ * message because it is already set by the spi core. ++ */ ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 0); ++ atmel_spi_send_dummy(as, spi, chip_select); ++ as->last_polarity = new_polarity; ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 1); ++ } ++ } + } else { + u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0; + int i; +-- +2.43.0 + diff --git a/queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch b/queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch new file mode 100644 index 00000000000..fa95714210b --- /dev/null +++ b/queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch @@ -0,0 +1,65 @@ +From 8611fe53a8a4ed513bfe83195bed931835cbebae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 20 Oct 2022 22:54:21 +0300 +Subject: spi: Introduce spi_get_device_match_data() helper + +From: Andy Shevchenko + +[ Upstream commit aea672d054a21782ed8450c75febb6ba3c208ca4 ] + +The proposed spi_get_device_match_data() helper is for retrieving +a driver data associated with the ID in an ID table. First, it tries +to get driver data of the device enumerated by firmware interface +(usually Device Tree or ACPI). If none is found it falls back to +the SPI ID table matching. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20221020195421.10482-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Mark Brown +Stable-dep-of: ee4d79055aee ("iio: imu: adis16475: add spi_device_id table") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi.c | 12 ++++++++++++ + include/linux/spi/spi.h | 3 +++ + 2 files changed, 15 insertions(+) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 5d046be8b2dd5..dfce0f7d4c640 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) + } + EXPORT_SYMBOL_GPL(spi_get_device_id); + ++const void *spi_get_device_match_data(const struct spi_device *sdev) ++{ ++ const void *match; ++ ++ match = device_get_match_data(&sdev->dev); ++ if (match) ++ return match; ++ ++ return (const void *)spi_get_device_id(sdev)->driver_data; ++} ++EXPORT_SYMBOL_GPL(spi_get_device_match_data); ++ + static int spi_match_device(struct device *dev, struct device_driver *drv) + { + const struct spi_device *spi = to_spi_device(dev); +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index 877395e075afe..635a05c30283c 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -1515,6 +1515,9 @@ extern void spi_unregister_device(struct spi_device *spi); + extern const struct spi_device_id * + spi_get_device_id(const struct spi_device *sdev); + ++extern const void * ++spi_get_device_match_data(const struct spi_device *sdev); ++ + static inline bool + spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer) + { +-- +2.43.0 + diff --git a/queue-6.1/spi-reintroduce-spi_set_cs_timing.patch b/queue-6.1/spi-reintroduce-spi_set_cs_timing.patch new file mode 100644 index 00000000000..708c519e6bd --- /dev/null +++ b/queue-6.1/spi-reintroduce-spi_set_cs_timing.patch @@ -0,0 +1,105 @@ +From 3009ab5427304d3514cdb9c6b147c5dbdb158c47 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 17 Nov 2022 12:52:44 +0200 +Subject: spi: Reintroduce spi_set_cs_timing() + +From: Tudor Ambarus + +[ Upstream commit 684a47847ae639689e7b823251975348a8e5434f ] + +commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"), removed the +method as noboby used it. Nobody used it probably because some SPI +controllers use some default large cs-setup time that covers the usual +cs-setup time required by the spi devices. There are though SPI controllers +that have a smaller granularity for the cs-setup time and their default +value can't fulfill the spi device requirements. That's the case for the +at91 QSPI IPs where the default cs-setup time is half of the QSPI clock +period. This was observed when using an sst26vf064b SPI NOR flash which +needs a spi-cs-setup-ns = <7>; in order to be operated close to its maximum +104 MHz frequency. + +Call spi_set_cs_timing() in spi_setup() just before calling spi_set_cs(), +as the latter needs the CS timings already set. +If spi->controller->set_cs_timing is not set, the method will return 0. +There's no functional impact expected for the existing drivers. Even if the +spi-mt65xx.c and spi-tegra114.c drivers set the set_cs_timing method, +there's no user for them as of now. The only tested user of this support +will be a SPI NOR flash that comunicates with the Atmel QSPI controller for +which the support follows in the next patches. + +One will notice that this support is a bit different from the one that was +removed in commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"), +because this patch adapts to the changes done after the removal: the move +of the cs delays to the spi device, the retirement of the lelgacy GPIO +handling. The mutex handling was removed from spi_set_cs_timing() because +we now always call spi_set_cs_timing() in spi_setup(), which already +handles the spi->controller->io_mutex, so use the mutex handling from +spi_setup(). + +Signed-off-by: Tudor Ambarus +Link: https://lore.kernel.org/r/20221117105249.115649-4-tudor.ambarus@microchip.com +Signed-off-by: Mark Brown +Stable-dep-of: fc70d643a2f6 ("spi: atmel: Fix clock issue when using devices with different polarities") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi.c | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index dfce0f7d4c640..f1ed2863a183e 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -3623,6 +3623,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr, + return 0; + } + ++/** ++ * spi_set_cs_timing - configure CS setup, hold, and inactive delays ++ * @spi: the device that requires specific CS timing configuration ++ * ++ * Return: zero on success, else a negative error code. ++ */ ++static int spi_set_cs_timing(struct spi_device *spi) ++{ ++ struct device *parent = spi->controller->dev.parent; ++ int status = 0; ++ ++ if (spi->controller->set_cs_timing && !spi->cs_gpiod) { ++ if (spi->controller->auto_runtime_pm) { ++ status = pm_runtime_get_sync(parent); ++ if (status < 0) { ++ pm_runtime_put_noidle(parent); ++ dev_err(&spi->controller->dev, "Failed to power device: %d\n", ++ status); ++ return status; ++ } ++ ++ status = spi->controller->set_cs_timing(spi); ++ pm_runtime_mark_last_busy(parent); ++ pm_runtime_put_autosuspend(parent); ++ } else { ++ status = spi->controller->set_cs_timing(spi); ++ } ++ } ++ return status; ++} ++ + /** + * spi_setup - setup SPI mode and clock rate + * @spi: the device whose settings are being modified +@@ -3719,6 +3750,12 @@ int spi_setup(struct spi_device *spi) + } + } + ++ status = spi_set_cs_timing(spi); ++ if (status) { ++ mutex_unlock(&spi->controller->io_mutex); ++ return status; ++ } ++ + if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { + status = pm_runtime_resume_and_get(spi->controller->dev.parent); + if (status < 0) { +-- +2.43.0 + diff --git a/queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch b/queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch new file mode 100644 index 00000000000..7c70bb8864d --- /dev/null +++ b/queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch @@ -0,0 +1,63 @@ +From 88318aeb5673f00c0c7be6c91186ab9b539f69dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 Dec 2023 16:22:43 +0300 +Subject: usb: fotg210-hcd: delete an incorrect bounds test + +From: Dan Carpenter + +[ Upstream commit 7fbcd195e2b8cc952e4aeaeb50867b798040314c ] + +Here "temp" is the number of characters that we have written and "size" +is the size of the buffer. The intent was clearly to say that if we have +written to the end of the buffer then stop. + +However, for that to work the comparison should have been done on the +original "size" value instead of the "size -= temp" value. Not only +will that not trigger when we want to, but there is a small chance that +it will trigger incorrectly before we want it to and we break from the +loop slightly earlier than intended. + +This code was recently changed from using snprintf() to scnprintf(). With +snprintf() we likely would have continued looping and passed a negative +size parameter to snprintf(). This would have triggered an annoying +WARN(). Now that we have converted to scnprintf() "size" will never +drop below 1 and there is no real need for this test. We could change +the condition to "if (temp <= 1) goto done;" but just deleting the test +is cleanest. + +Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver") +Cc: stable +Signed-off-by: Dan Carpenter +Reviewed-by: Linus Walleij +Reviewed-by: Lee Jones +Link: https://lore.kernel.org/r/ZXmwIwHe35wGfgzu@suswa +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/host/fotg210-hcd.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c +index c4c1fbc12b4cd..dc968960769e1 100644 +--- a/drivers/usb/host/fotg210-hcd.c ++++ b/drivers/usb/host/fotg210-hcd.c +@@ -429,8 +429,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + temp = size; + size -= temp; + next += temp; +- if (temp == size) +- goto done; + } + + temp = snprintf(next, size, "\n"); +@@ -440,7 +438,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + size -= temp; + next += temp; + +-done: + *sizep = size; + *nextp = next; + } +-- +2.43.0 +