* [PATCH 0/1] Fix to allow more correct isatty() @ 2024-11-21 11:12 Gil Pedersen 2024-11-21 11:12 ` [PATCH 1/1] tty: respond to TIOCGWINSZ when hung Gil Pedersen 2024-11-22 23:54 ` [PATCH 0/1] Fix to allow more correct isatty() nerdopolis 0 siblings, 2 replies; 6+ messages in thread From: Gil Pedersen @ 2024-11-21 11:12 UTC (permalink / raw) To: linux-kernel, linux-serial; +Cc: Greg Kroah-Hartman, Jiri Slaby, Gil Pedersen This patch comes from an issue discovered in systemd, where it can fail to restore a text TTY session after a GUI session is stopped when compiled with musl libc. The specific systemd integration issue is currently resolved in the musl master branch by closer aligning the implementation with glibc, but the underlying isatty() implementation is still flawed since it can return 0 (false) for something that is definitely a TTY. Essentially both musl and glibc give up correctly handling this case on Linux due to inadequate/buggy kernel APIs. Thus I am proposing this patch as a solution to fix the kernel APIs. An alternative fix could be to add a new IOCTL to specifically handle this, but that seems overkill. Link: https://github.com/systemd/systemd/pull/34039 Link: https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/70711 Link: https://www.openwall.com/lists/musl/2024/08/20/2 Link: https://git.musl-libc.org/cgit/musl/commit/src/unistd/isatty.c?id=c94a0c16f08894ce3be6dafb0fe80baa77a6ff2a Link: https://sourceware.org/bugzilla/show_bug.cgi?id=32103 Gil Pedersen (1): tty: respond to TIOCGWINSZ when hung drivers/tty/tty_io.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) -- 2.45.2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/1] tty: respond to TIOCGWINSZ when hung 2024-11-21 11:12 [PATCH 0/1] Fix to allow more correct isatty() Gil Pedersen @ 2024-11-21 11:12 ` Gil Pedersen 2024-12-23 17:56 ` Greg Kroah-Hartman 2024-11-22 23:54 ` [PATCH 0/1] Fix to allow more correct isatty() nerdopolis 1 sibling, 1 reply; 6+ messages in thread From: Gil Pedersen @ 2024-11-21 11:12 UTC (permalink / raw) To: linux-kernel, linux-serial; +Cc: Greg Kroah-Hartman, Jiri Slaby, Gil Pedersen Userspace libc implementations of the isatty() POSIX system interface are currently unable to reliably determine if a fd is really a tty when it is hung. Specifically glibc libc returns the success status of a TCGETS ioctl. This will return an incorrect result when the TTY is hung, since an EIO is unconditionally returned. Ie. an isatty() will return 0, wrongly indicating that something that definitely is a TTY, is not a TTY. Userspace implementations could potentially remap EIO errors to a success to work around this. This will likely work in 99.99% of cases, but there is no guarantee that a TCGETS ioctl on a non-TTY fd will not also return EIO, making the isatty() call return a false positive! This commit enables a specific non-driver, non-ldisc, ioctl to continue working after the TTY is hung. The TIOCGWINSZ ioctl was chosen since it is readonly, and only access tty_struct.winsize (and its mutex), and is already used for the isatty() implementation in musl. The glibc implementation will need to be updated to use the TIOCGWINSZ ioctl, either as a direct replacement, or more conservatively, as a fallback test when the TCGETS ioctl fails with EIO. Note that TCGETS is not available to use for this, since it is implemented at the ldisc level, which can not be called into once the TTY is hung. Signed-off-by: Gil Pedersen <gpdev@gpost.dk> --- drivers/tty/tty_io.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9771072da177..678fcc9b8264 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -157,6 +157,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, static int __tty_fasync(int fd, struct file *filp, int on); static int tty_fasync(int fd, struct file *filp, int on); static void release_tty(struct tty_struct *tty, int idx); +static long hung_up_tty_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); /** * free_tty_struct - free a disused tty @@ -433,16 +435,10 @@ static __poll_t hung_up_tty_poll(struct file *filp, poll_table *wait) return EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | EPOLLWRNORM; } -static long hung_up_tty_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return cmd == TIOCSPGRP ? -ENOTTY : -EIO; -} - static long hung_up_tty_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return cmd == TIOCSPGRP ? -ENOTTY : -EIO; + return hung_up_tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } static int hung_up_tty_fasync(int fd, struct file *file, int on) @@ -2817,6 +2813,25 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return retval; } +static long hung_up_tty_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct tty_struct *tty = file_tty(file); + struct tty_struct *real_tty; + void __user *p = (void __user *)arg; + + real_tty = tty_pair_get_tty(tty); + + switch (cmd) { + case TIOCGWINSZ: + return tiocgwinsz(real_tty, p); + case TIOCSPGRP: + return -ENOTTY; + } + + return -EIO; +} + #ifdef CONFIG_COMPAT struct serial_struct32 { -- 2.45.2 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1] tty: respond to TIOCGWINSZ when hung 2024-11-21 11:12 ` [PATCH 1/1] tty: respond to TIOCGWINSZ when hung Gil Pedersen @ 2024-12-23 17:56 ` Greg Kroah-Hartman 2025-01-07 11:44 ` Gil Pedersen 0 siblings, 1 reply; 6+ messages in thread From: Greg Kroah-Hartman @ 2024-12-23 17:56 UTC (permalink / raw) To: Gil Pedersen; +Cc: linux-kernel, linux-serial, Jiri Slaby On Thu, Nov 21, 2024 at 12:12:54PM +0100, Gil Pedersen wrote: > Userspace libc implementations of the isatty() POSIX system interface > are currently unable to reliably determine if a fd is really a tty when > it is hung. > > Specifically glibc libc returns the success status of a TCGETS ioctl. > This will return an incorrect result when the TTY is hung, since an EIO > is unconditionally returned. Ie. an isatty() will return 0, wrongly > indicating that something that definitely is a TTY, is not a TTY. > > Userspace implementations could potentially remap EIO errors to a > success to work around this. This will likely work in 99.99% of cases, > but there is no guarantee that a TCGETS ioctl on a non-TTY fd will not > also return EIO, making the isatty() call return a false positive! > > This commit enables a specific non-driver, non-ldisc, ioctl to continue > working after the TTY is hung. The TIOCGWINSZ ioctl was chosen since it > is readonly, and only access tty_struct.winsize (and its mutex), and is > already used for the isatty() implementation in musl. The glibc > implementation will need to be updated to use the TIOCGWINSZ ioctl, > either as a direct replacement, or more conservatively, as a fallback > test when the TCGETS ioctl fails with EIO. This is a fun "hack", yes, but now you are encoding an odd "side affect" into the system that everyone is going to rely on, well, eventually rely on. What code needs to be changed in userspace to determine this? Why not just have a new ioctl that tells you if the tty really is hung or not? Why does isatty() need to know this, does POSIX require it? And if it does, what does it say the ioctl command should be? thanks, greg k-h ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1] tty: respond to TIOCGWINSZ when hung 2024-12-23 17:56 ` Greg Kroah-Hartman @ 2025-01-07 11:44 ` Gil Pedersen 2025-01-10 14:52 ` Greg Kroah-Hartman 0 siblings, 1 reply; 6+ messages in thread From: Gil Pedersen @ 2025-01-07 11:44 UTC (permalink / raw) To: Greg Kroah-Hartman; +Cc: linux-kernel, linux-serial, Jiri Slaby > On 23 Dec 2024, at 18.56, Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote: > > On Thu, Nov 21, 2024 at 12:12:54PM +0100, Gil Pedersen wrote: >> Userspace libc implementations of the isatty() POSIX system interface >> are currently unable to reliably determine if a fd is really a tty when >> it is hung. >> >> Specifically glibc libc returns the success status of a TCGETS ioctl. >> This will return an incorrect result when the TTY is hung, since an EIO >> is unconditionally returned. Ie. an isatty() will return 0, wrongly >> indicating that something that definitely is a TTY, is not a TTY. >> >> Userspace implementations could potentially remap EIO errors to a >> success to work around this. This will likely work in 99.99% of cases, >> but there is no guarantee that a TCGETS ioctl on a non-TTY fd will not >> also return EIO, making the isatty() call return a false positive! >> >> This commit enables a specific non-driver, non-ldisc, ioctl to continue >> working after the TTY is hung. The TIOCGWINSZ ioctl was chosen since it >> is readonly, and only access tty_struct.winsize (and its mutex), and is >> already used for the isatty() implementation in musl. The glibc >> implementation will need to be updated to use the TIOCGWINSZ ioctl, >> either as a direct replacement, or more conservatively, as a fallback >> test when the TCGETS ioctl fails with EIO. > > This is a fun "hack", yes, but now you are encoding an odd "side affect" > into the system that everyone is going to rely on, well, eventually rely > on. What code needs to be changed in userspace to determine this? The patch can definitely be considered a hack, but viewed with another lens: a bugfix. There is no specific reason that the call should return an EIO on hung terminals, so making it always return the current value could be considered more correct. POSIX tcgetwinsize(), which this ioctl maps to, does not consider hung terminals, and expects it to return suitable values whenever possible. Userspace implementations will have to reconsider their handling of an EIO error, as the isatty() call could still return an EIO if calling into a non-TTY device. Unconditionally mapping it to a success, like isatty_safe() in systemd, would be an error. Supporting both versions would require a runtime check to determine which variant is used, where the legacy version would accept the risk of a "wrong" EIO, while the new version would treat it as a proper error. > Why not just have a new ioctl that tells you if the tty really is hung > or not? Why does isatty() need to know this, does POSIX require it? > And if it does, what does it say the ioctl command should be? isatty() should not need to know if the TTY is hung, and besides cannot safely call any ioctl to check this before it knows that it is indeed a TTY. POSIX does not seem to include the concept of hung terminals. A case could be made for introducing a new ioctl though, but it would need a more generic approach, like the BSD FIODTYPE ioctl that exposes a d_type property on chardev & block driver interfaces. If implemented before calling into the VFS layer, it could make the isatty() call 100% safe (on kernels that support the ioctl). Additionally, this would mean that it can never return EIO, which makes userspace adaptions simpler, since it can know that any returned EIO means that it is running on an unpatched/legacy kernel and/or libc. /Gil Link: https://pubs.opengroup.org/onlinepubs/9799919799/ Link: https://github.com/systemd/systemd/blob/83c0b95f63417a36e67305fe9ad16a89ed53ef52/src/basic/terminal-util.c#L63-L79 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1] tty: respond to TIOCGWINSZ when hung 2025-01-07 11:44 ` Gil Pedersen @ 2025-01-10 14:52 ` Greg Kroah-Hartman 0 siblings, 0 replies; 6+ messages in thread From: Greg Kroah-Hartman @ 2025-01-10 14:52 UTC (permalink / raw) To: Gil Pedersen; +Cc: linux-kernel, linux-serial, Jiri Slaby On Tue, Jan 07, 2025 at 12:44:28PM +0100, Gil Pedersen wrote: > > On 23 Dec 2024, at 18.56, Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote: > > > > On Thu, Nov 21, 2024 at 12:12:54PM +0100, Gil Pedersen wrote: > >> Userspace libc implementations of the isatty() POSIX system interface > >> are currently unable to reliably determine if a fd is really a tty when > >> it is hung. > >> > >> Specifically glibc libc returns the success status of a TCGETS ioctl. > >> This will return an incorrect result when the TTY is hung, since an EIO > >> is unconditionally returned. Ie. an isatty() will return 0, wrongly > >> indicating that something that definitely is a TTY, is not a TTY. > >> > >> Userspace implementations could potentially remap EIO errors to a > >> success to work around this. This will likely work in 99.99% of cases, > >> but there is no guarantee that a TCGETS ioctl on a non-TTY fd will not > >> also return EIO, making the isatty() call return a false positive! > >> > >> This commit enables a specific non-driver, non-ldisc, ioctl to continue > >> working after the TTY is hung. The TIOCGWINSZ ioctl was chosen since it > >> is readonly, and only access tty_struct.winsize (and its mutex), and is > >> already used for the isatty() implementation in musl. The glibc > >> implementation will need to be updated to use the TIOCGWINSZ ioctl, > >> either as a direct replacement, or more conservatively, as a fallback > >> test when the TCGETS ioctl fails with EIO. > > > > This is a fun "hack", yes, but now you are encoding an odd "side affect" > > into the system that everyone is going to rely on, well, eventually rely > > on. What code needs to be changed in userspace to determine this? > > The patch can definitely be considered a hack, but viewed with another > lens: a bugfix. All hacks could be viewed that way :) > There is no specific reason that the call should return an EIO on hung > terminals, so making it always return the current value could be > considered more correct. POSIX tcgetwinsize(), which this ioctl maps > to, does not consider hung terminals, and expects it to return suitable > values whenever possible. There's no specific reason, but we are stuck with what we have today as that is how things work. I'm more worried about making this change and then nothing ever changes in userspace. And userspace would never "know" if it could or could not rely on this change, as some necro-enterprise-systems never update their kernel. > Userspace implementations will have to reconsider their handling of an > EIO error, as the isatty() call could still return an EIO if calling > into a non-TTY device. Unconditionally mapping it to a success, like > isatty_safe() in systemd, would be an error. Supporting both versions > would require a runtime check to determine which variant is used, where > the legacy version would accept the risk of a "wrong" EIO, while the > new version would treat it as a proper error. How would such a runtime check work? Do you have working patches for existing userspace programs that want to know this that shows how this all works? We can't take api changes without a working userspace user, you know that... > > Why not just have a new ioctl that tells you if the tty really is hung > > or not? Why does isatty() need to know this, does POSIX require it? > > And if it does, what does it say the ioctl command should be? > > isatty() should not need to know if the TTY is hung, and besides cannot > safely call any ioctl to check this before it knows that it is indeed a > TTY. POSIX does not seem to include the concept of hung terminals. > > A case could be made for introducing a new ioctl though, but it would > need a more generic approach, like the BSD FIODTYPE ioctl that exposes > a d_type property on chardev & block driver interfaces. If implemented > before calling into the VFS layer, it could make the isatty() call 100% > safe (on kernels that support the ioctl). Additionally, this would mean > that it can never return EIO, which makes userspace adaptions simpler, > since it can know that any returned EIO means that it is running on an > unpatched/legacy kernel and/or libc. Yes, that's why I suggested a new ioctl. thanks, greg k-h ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 0/1] Fix to allow more correct isatty() 2024-11-21 11:12 [PATCH 0/1] Fix to allow more correct isatty() Gil Pedersen 2024-11-21 11:12 ` [PATCH 1/1] tty: respond to TIOCGWINSZ when hung Gil Pedersen @ 2024-11-22 23:54 ` nerdopolis 1 sibling, 0 replies; 6+ messages in thread From: nerdopolis @ 2024-11-22 23:54 UTC (permalink / raw) To: linux-kernel, linux-serial, Gil Pedersen; +Cc: Greg Kroah-Hartman, Jiri Slaby On Thursday, November 21, 2024 6:12:53 AM EST Gil Pedersen wrote: > This patch comes from an issue discovered in systemd, where it can fail to > restore a text TTY session after a GUI session is stopped when compiled > with musl libc. > > The specific systemd integration issue is currently resolved in the musl > master branch by closer aligning the implementation with glibc, but the > underlying isatty() implementation is still flawed since it can return 0 > (false) for something that is definitely a TTY. Essentially both musl > and glibc give up correctly handling this case on Linux due to > inadequate/buggy kernel APIs. > > Thus I am proposing this patch as a solution to fix the kernel APIs. An > alternative fix could be to add a new IOCTL to specifically handle this, > but that seems overkill. > This is pretty cool as it fixes the /dev/console-is-a-disconnected-/dev/ttyS0 issue I found this year https://github.com/systemd/systemd/pull/33690 and https://lore.kernel.org/all/2669238.7s5MMGUR32@nerdopolis2/ namely on vt-less kernels that now have /dev/console as /dev/ttyS0 instead of /dev/tty0 or a virtual device. So yeah, when you have hardware and you've been using vt-enabled kernels on it for years (where your /dev/ttyS0 doesn't even have an rs232 port, let alone connected to anything), and then when you upgrade to a vt-less kernel, sometime in the future, because of systemd's isatty() check failing, and refusing to write to /dev/console, Plymouth's new log display facility in turn gets far fewer logs from systemd, because of the ioctl failure in isatty() MY idea for this solution was that kernels turning off CONFIG_VT_CONSOLE could then turn on a CONFIG_NULL_TTY_CONSOLE that would get selected at the same time the VT console would have, so that if distros choose to do so, the virtual /dev/ttynull device to always work for User Space. (that way kernels that never had CONFIG_VT_CONSOLE turned on won't have a behavior change as well) (the one benefit to my idea is that Plymouth goes into text mode logging when the console device is /dev/ttyS0, so Desktop distros would need to add a new "plymouth.graphical" to their grub commands to get the splash to work again, however when the console device is /dev/ttynull it automatically assumes that the user wants a splash screen.) This is more specific to Plymouth though However testing this patch and it also works, and this won't need a new configuration change. > Link: https://github.com/systemd/systemd/pull/34039 > Link: https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/70711 > Link: https://www.openwall.com/lists/musl/2024/08/20/2 > Link: https://git.musl-libc.org/cgit/musl/commit/src/unistd/isatty.c?id=c94a0c16f08894ce3be6dafb0fe80baa77a6ff2a > Link: https://sourceware.org/bugzilla/show_bug.cgi?id=32103 > > Gil Pedersen (1): > tty: respond to TIOCGWINSZ when hung > > drivers/tty/tty_io.c | 29 ++++++++++++++++++++++------- > 1 file changed, 22 insertions(+), 7 deletions(-) > > ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-01-10 14:52 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-11-21 11:12 [PATCH 0/1] Fix to allow more correct isatty() Gil Pedersen 2024-11-21 11:12 ` [PATCH 1/1] tty: respond to TIOCGWINSZ when hung Gil Pedersen 2024-12-23 17:56 ` Greg Kroah-Hartman 2025-01-07 11:44 ` Gil Pedersen 2025-01-10 14:52 ` Greg Kroah-Hartman 2024-11-22 23:54 ` [PATCH 0/1] Fix to allow more correct isatty() nerdopolis
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox