summaryrefslogtreecommitdiffstats
path: root/fs/aio.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2020-05-14 16:44:24 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-06-22 09:05:01 +0200
commite4df4140aceb68ac9fd0258e5a03300e0f35a246 (patch)
tree51d9baa47a4a0f0712111774762779382bc67c51 /fs/aio.c
parentcdfd1ec69045872a8deb873cc22fb49cbbf264a3 (diff)
downloadlinux-stable-e4df4140aceb68ac9fd0258e5a03300e0f35a246.tar.gz
linux-stable-e4df4140aceb68ac9fd0258e5a03300e0f35a246.tar.bz2
linux-stable-e4df4140aceb68ac9fd0258e5a03300e0f35a246.zip
aio: fix async fsync creds
commit 530f32fc370fd1431ea9802dbc53ab5601dfccdb upstream. Avi Kivity reports that on fuse filesystems running in a user namespace asyncronous fsync fails with EOVERFLOW. The reason is that f_ops->fsync() is called with the creds of the kthread performing aio work instead of the creds of the process originally submitting IOCB_CMD_FSYNC. Fuse sends the creds of the caller in the request header and it needs to translate the uid and gid into the server's user namespace. Since the kthread is running in init_user_ns, the translation will fail and the operation returns an error. It can be argued that fsync doesn't actually need any creds, but just zeroing out those fields in the header (as with requests that currently don't take creds) is a backward compatibility risk. Instead of working around this issue in fuse, solve the core of the problem by calling the filesystem with the proper creds. Reported-by: Avi Kivity <avi@scylladb.com> Tested-by: Giuseppe Scrivano <gscrivan@redhat.com> Fixes: c9582eb0ff7d ("fuse: Fail all requests with invalid uids or gids") Cc: stable@vger.kernel.org # 4.18+ Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/aio.c')
-rw-r--r--fs/aio.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/fs/aio.c b/fs/aio.c
index b5fbf2061868..413ec289bfa1 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -169,6 +169,7 @@ struct fsync_iocb {
struct file *file;
struct work_struct work;
bool datasync;
+ struct cred *creds;
};
struct poll_iocb {
@@ -1579,8 +1580,11 @@ static ssize_t aio_write(struct kiocb *req, const struct iocb *iocb,
static void aio_fsync_work(struct work_struct *work)
{
struct aio_kiocb *iocb = container_of(work, struct aio_kiocb, fsync.work);
+ const struct cred *old_cred = override_creds(iocb->fsync.creds);
iocb->ki_res.res = vfs_fsync(iocb->fsync.file, iocb->fsync.datasync);
+ revert_creds(old_cred);
+ put_cred(iocb->fsync.creds);
iocb_put(iocb);
}
@@ -1594,6 +1598,10 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb,
if (unlikely(!req->file->f_op->fsync))
return -EINVAL;
+ req->creds = prepare_creds();
+ if (!req->creds)
+ return -ENOMEM;
+
req->datasync = datasync;
INIT_WORK(&req->work, aio_fsync_work);
schedule_work(&req->work);