| From 13d5354208acd27d7f1d7b06b9049b06ea19d5ed Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 17 Dec 2018 09:35:27 -0800 |
| Subject: xfs: Fix bulkstat compat ioctls on x32 userspace. |
| |
| From: Nick Bowler <nbowler@draconx.ca> |
| |
| [ Upstream commit 7ca860e3c1a74ad6bd8949364073ef1044cad758 ] |
| |
| The bulkstat family of ioctls are problematic on x32, because there is |
| a mixup of native 32-bit and 64-bit conventions. The xfs_fsop_bulkreq |
| struct contains pointers and 32-bit integers so that matches the native |
| 32-bit layout, and that means the ioctl implementation goes into the |
| regular compat path on x32. |
| |
| However, the 'ubuffer' member of that struct in turn refers to either |
| struct xfs_inogrp or xfs_bstat (or an array of these). On x32, those |
| structures match the native 64-bit layout. The compat implementation |
| writes out the 32-bit version of these structures. This is not the |
| expected format for x32 userspace, causing problems. |
| |
| Fortunately the functions which actually output these xfs_inogrp and |
| xfs_bstat structures have an easy way to select which output format |
| is required, so we just need a little tweak to select the right format |
| on x32. |
| |
| Signed-off-by: Nick Bowler <nbowler@draconx.ca> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/xfs/xfs_ioctl32.c | 34 ++++++++++++++++++++++++++++++---- |
| 1 file changed, 30 insertions(+), 4 deletions(-) |
| |
| diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c |
| index 8f18756ee405e..6b7ed221726db 100644 |
| --- a/fs/xfs/xfs_ioctl32.c |
| +++ b/fs/xfs/xfs_ioctl32.c |
| @@ -251,6 +251,32 @@ xfs_compat_ioc_bulkstat( |
| int done; |
| int error; |
| |
| + /* |
| + * Output structure handling functions. Depending on the command, |
| + * either the xfs_bstat and xfs_inogrp structures are written out |
| + * to userpace memory via bulkreq.ubuffer. Normally the compat |
| + * functions and structure size are the correct ones to use ... |
| + */ |
| + inumbers_fmt_pf inumbers_func = xfs_inumbers_fmt_compat; |
| + bulkstat_one_pf bs_one_func = xfs_bulkstat_one_compat; |
| + size_t bs_one_size = sizeof(struct compat_xfs_bstat); |
| + |
| +#ifdef CONFIG_X86_X32 |
| + if (in_x32_syscall()) { |
| + /* |
| + * ... but on x32 the input xfs_fsop_bulkreq has pointers |
| + * which must be handled in the "compat" (32-bit) way, while |
| + * the xfs_bstat and xfs_inogrp structures follow native 64- |
| + * bit layout convention. So adjust accordingly, otherwise |
| + * the data written out in compat layout will not match what |
| + * x32 userspace expects. |
| + */ |
| + inumbers_func = xfs_inumbers_fmt; |
| + bs_one_func = xfs_bulkstat_one; |
| + bs_one_size = sizeof(struct xfs_bstat); |
| + } |
| +#endif |
| + |
| /* done = 1 if there are more stats to get and if bulkstat */ |
| /* should be called again (unused here, but used in dmapi) */ |
| |
| @@ -282,15 +308,15 @@ xfs_compat_ioc_bulkstat( |
| |
| if (cmd == XFS_IOC_FSINUMBERS_32) { |
| error = xfs_inumbers(mp, &inlast, &count, |
| - bulkreq.ubuffer, xfs_inumbers_fmt_compat); |
| + bulkreq.ubuffer, inumbers_func); |
| } else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) { |
| int res; |
| |
| - error = xfs_bulkstat_one_compat(mp, inlast, bulkreq.ubuffer, |
| - sizeof(compat_xfs_bstat_t), NULL, &res); |
| + error = bs_one_func(mp, inlast, bulkreq.ubuffer, |
| + bs_one_size, NULL, &res); |
| } else if (cmd == XFS_IOC_FSBULKSTAT_32) { |
| error = xfs_bulkstat(mp, &inlast, &count, |
| - xfs_bulkstat_one_compat, sizeof(compat_xfs_bstat_t), |
| + bs_one_func, bs_one_size, |
| bulkreq.ubuffer, &done); |
| } else |
| error = -EINVAL; |
| -- |
| 2.20.1 |
| |