Message ID | 9cd9d3eb-418f-44cc-afcf-7283d51252d6@I-love.SAKURA.ne.jp |
---|---|
State | New |
Headers | show |
Series | tty: vt: check for atomic context in con_write() | expand |
On 2024/01/22 15:48, Jiri Slaby wrote: > On 20. 01. 24, 11:34, Tetsuo Handa wrote: >> syzbot is reporting sleep in atomic context, for gsmld_write() is calling >> con_write() with spinlock held and IRQs disabled. > > gsm should never be bound to a console in the first place. > > Noone has sent a patch to deny that yet. > > Follow: > https://lore.kernel.org/all/49453ebd-b321-4f34-a1a5-d828d8881010@kernel.org/ > > And feel free to patch that ;). > > thanks, OK. Here is a deny-listing based filter using device number of sysfs entry. (We don't want to compare with the function address of con_write(). Thus, this patch is comparing with device major/minor numbers.) ---------- diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4036566febcb..6f9730dce5aa 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -3630,6 +3630,10 @@ static int gsmld_open(struct tty_struct *tty) if (tty->ops->write == NULL) return -EINVAL; + /* Can't be attached to virtual consoles. */ + if (tty->dev && MAJOR(tty->dev->devt) == 4 && MINOR(tty->dev->devt) < 64) + return -EINVAL; + /* Attach our ldisc data */ gsm = gsm_alloc_mux(); if (gsm == NULL) ---------- Is it possible to use allow-listing based filtering? (Attaching on /dev/tty (major=5, minor=0) causes current ssh session to be closed. Unexpectedly loosing connection might be a problem for fuzz testing...) ---------- #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/tty.h> int main(int argc, char *argv[]) { int ldisc = N_GSM0710; return ioctl(open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY), TIOCSETD, &ldisc) == 0; } ----------
On Wed, Jan 24, 2024 at 07:06:04PM +0900, Tetsuo Handa wrote: > syzbot is reporting sleep in atomic context, for gsmld_write() is calling > con_write() with spinlock held and IRQs disabled. > > Since n_gsm is designed to be used for serial port, reject attaching to > virtual consoles and PTY devices, by checking tty's device major/minor > numbers at gsmld_open(). > > Link: https://www.kernel.org/doc/html/v6.7/driver-api/tty/n_gsm.html > Reported-by: syzbot+06fa1063cca8163ea541@syzkaller.appspotmail.com > Closes: https://syzkaller.appspot.com/bug?extid=06fa1063cca8163ea541 > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> > --- > drivers/tty/n_gsm.c | 12 ++++++++++++ > 1 file changed, 12 insertions(+) > > diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c > index 4036566febcb..14581483af78 100644 > --- a/drivers/tty/n_gsm.c > +++ b/drivers/tty/n_gsm.c > @@ -3623,6 +3623,7 @@ static void gsmld_close(struct tty_struct *tty) > static int gsmld_open(struct tty_struct *tty) > { > struct gsm_mux *gsm; > + int major; > > if (!capable(CAP_NET_ADMIN)) > return -EPERM; > @@ -3630,6 +3631,17 @@ static int gsmld_open(struct tty_struct *tty) > if (tty->ops->write == NULL) > return -EINVAL; > > + major = tty->driver->major; > + /* Reject Virtual consoles */ > + if (major == 4 && tty->driver->minor_start == 1) > + return -EINVAL; > + /* Reject Unix98 PTY masters/slaves */ > + if (major >= 128 && major <= 143) > + return -EINVAL; > + /* Reject BSD PTY masters/slaves */ > + if (major >= 2 && major <= 3) > + return -EINVAL; That is a lot of hard-coded magic numbers, why aren't these defined anywhere? But really, this is only for fuzz testers, why can't they just not ever bind this to a console? Why do we have to have these checks in the kernel to prevent userspace from doing dumb things that no real userspace will ever do? thanks, greg k-h
On 2024/01/24 22:14, Greg Kroah-Hartman wrote: >> @@ -3630,6 +3631,17 @@ static int gsmld_open(struct tty_struct *tty) >> if (tty->ops->write == NULL) >> return -EINVAL; >> >> + major = tty->driver->major; >> + /* Reject Virtual consoles */ >> + if (major == 4 && tty->driver->minor_start == 1) >> + return -EINVAL; >> + /* Reject Unix98 PTY masters/slaves */ >> + if (major >= 128 && major <= 143) >> + return -EINVAL; >> + /* Reject BSD PTY masters/slaves */ >> + if (major >= 2 && major <= 3) >> + return -EINVAL; > > That is a lot of hard-coded magic numbers, why aren't these defined > anywhere? Well, include/uapi/linux/major.h defines #define TTY_MAJOR 4 #define UNIX98_PTY_MASTER_MAJOR 128 #define UNIX98_PTY_MAJOR_COUNT 8 #define UNIX98_PTY_SLAVE_MAJOR (UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT) #define PTY_MASTER_MAJOR 2 #define PTY_SLAVE_MAJOR 3 but does not define end of UNIX98_PTY_SLAVE_MAJOR range, and no file defines start of minor number for virtual consoles. Does fixing this bug worth updating include/uapi/linux/major.h and adding include/uapi/linux/minor.h ? Since these numbers won't change, I feel that a line of comment is sufficient. > > But really, this is only for fuzz testers, why can't they just not ever > bind this to a console? Why do we have to have these checks in the > kernel to prevent userspace from doing dumb things that no real > userspace will ever do? Fuzz testing is for testing unexpected arguments. This bug is nothing but missing validation that should be fixed on the kernel side rather than trying to tune fuzzers. > > thanks, > > greg k-h
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 156efda7c80d..0d3d602ae147 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2856,7 +2856,7 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count) struct vt_notifier_param param; bool rescan; - if (in_interrupt()) + if (in_interrupt() || irqs_disabled()) return count; console_lock(); @@ -3314,7 +3314,7 @@ static void con_flush_chars(struct tty_struct *tty) { struct vc_data *vc; - if (in_interrupt()) /* from flush_to_ldisc */ + if (in_interrupt() || irqs_disabled()) /* from flush_to_ldisc */ return; /* if we race with con_close(), vt may be null */
syzbot is reporting sleep in atomic context, for gsmld_write() is calling con_write() with spinlock held and IRQs disabled. Since include/linux/tty_ldisc.h says that "struct tty_ldisc_ops"->write (e.g. gsmld_write()) is allowed to sleep and include/linux/tty_driver.h says that "struct tty_operations"->write (e.g. con_write()) is not allowed to sleep, we should handle this problem on the con_write() side. It seems that "Andrew Morton: console locking merge" in 2.4.10-pre11 added in_interrupt() check to do_con_write()/con_put_char()/con_flush_chars() in order to handle exceptional caller. Since include/linux/preempt.h says that in_atomic() cannot know about held spinlocks in non-preemptible kernels, but gsmld_write() is calling con_write() with IRQs disabled, we can add irqs_disabled() check to do_con_write()/con_flush_chars() in order to handle this case. Though, I'm not sure whether returning the bytes to write is appropriate behavior when do_con_write() can't work... Reported-by: syzbot+06fa1063cca8163ea541@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=06fa1063cca8163ea541 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> --- drivers/tty/vt/vt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)