tty: avoid using vfs_iocb_iter_write() for redirected console writes

It turns out that the vfs_iocb_iter_{read,write}() functions are
entirely broken, and don't actually use the passed-in file pointer for
IO - only for the preparatory work (permission checking and for the
write_iter function lookup).

That worked fine for overlayfs, which always builds the new iocb with
the same file pointer that it passes in, but in the general case it ends
up doing nonsensical things (and could cause an iterator call that
doesn't even match the passed-in file pointer).

This subtly broke the tty conversion to write_iter in commit
9bb48c82ac ("tty: implement write_iter"), because the console
redirection didn't actually end up redirecting anything, since the
passed-in file pointer was basically ignored, and the actual write was
done with the original non-redirected console tty after all.

The main visible effect of this is that the console messages were no
longer logged to /var/log/boot.log during graphical boot.

Fix the issue by simply not using the vfs write "helper" function at
all, and just redirecting the write entirely internally to the tty
layer.  Do the target writability permission checks when actually
registering the target tty with TIOCCONS instead of at write time.

Fixes: 9bb48c82ac ("tty: implement write_iter")
Reported-and-tested-by: Hans de Goede <hdegoede@redhat.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2021-01-29 12:28:20 -08:00
parent bec4c2968f
commit a9cbbb80e3

View File

@ -1026,9 +1026,8 @@ void tty_write_message(struct tty_struct *tty, char *msg)
* write method will not be invoked in parallel for each device.
*/
static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
static ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
@ -1051,6 +1050,11 @@ static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
return ret;
}
static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
{
return file_tty_write(iocb->ki_filp, iocb, from);
}
ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *p = NULL;
@ -1060,9 +1064,13 @@ ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
p = get_file(redirect);
spin_unlock(&redirect_lock);
/*
* We know the redirected tty is just another tty, we can can
* call file_tty_write() directly with that file pointer.
*/
if (p) {
ssize_t res;
res = vfs_iocb_iter_write(p, iocb, iter);
res = file_tty_write(p, iocb, iter);
fput(p);
return res;
}
@ -2308,6 +2316,12 @@ static int tioccons(struct file *file)
fput(f);
return 0;
}
if (file->f_op->write_iter != tty_write)
return -ENOTTY;
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL;
spin_lock(&redirect_lock);
if (redirect) {
spin_unlock(&redirect_lock);