drivers/tty/vt/vc_screen.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
If vcs_vc() returns NULL in vcs_read(), break if partial read,
else if no reads have been done, go to unlock_out and return ENXIO.
Fixes: 226fae124b2d ("vc_screen: move load of struct vc_data pointer in vcs_read() to avoid UAF")
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: George Kennedy <george.kennedy@oracle.com>
---
drivers/tty/vt/vc_screen.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index f566eb1839dc..29288401cf9e 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -403,10 +403,13 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
unsigned int this_round, skip = 0;
int size;
- ret = -ENXIO;
vc = vcs_vc(inode, &viewed);
- if (!vc)
+ if (!vc) {
+ if (read)
+ break;
+ ret = -ENXIO;
goto unlock_out;
+ }
/* Check whether we are above size each round,
* as copy_to_user at the end of this loop
--
2.31.1
On Mon, Feb 6, 2023 at 9:34 AM George Kennedy <george.kennedy@oracle.com> wrote: > > > - ret = -ENXIO; > vc = vcs_vc(inode, &viewed); > - if (!vc) > + if (!vc) { > + if (read) > + break; > + ret = -ENXIO; > goto unlock_out; > + } That works, but the whole "if (read)" thing is already done after the loop, so instead of essentially duplicating that logic, I really think the patch should be just a plain vc = vcs_vc(inode, &viewed); if (!vc) - goto unlock_out; + break; and nothing else. And yes, the pre-existing vcs_size() error handling has that same ugly pattern. It might be worth cleaning up too, although right now that size = vcs_size(vc, attr, uni_mode); if (size < 0) { if (read) break; pattern means that if we 'break' there, 'read' is non-zero, so 'ret' doesn't matter. Which is also ugly, but works. I *think* it could all be rewritten to just use 'break' everywhere in the loop, and make 'ret' handling be saner. Something like the attached patch, but while I tried to think about it, I didn't spend a lot of effort on it, and I certainly didn't test it. So I'm sending this out as a "Hmm. This _looks_ better to me, but whatever" patch. Linus
On 2/6/2023 1:12 PM, Linus Torvalds wrote: > On Mon, Feb 6, 2023 at 9:34 AM George Kennedy <george.kennedy@oracle.com> wrote: >> >> - ret = -ENXIO; >> vc = vcs_vc(inode, &viewed); >> - if (!vc) >> + if (!vc) { >> + if (read) >> + break; >> + ret = -ENXIO; >> goto unlock_out; >> + } > That works, but the whole "if (read)" thing is already done after the > loop, so instead of essentially duplicating that logic, I really think > the patch should be just a plain > > vc = vcs_vc(inode, &viewed); > if (!vc) > - goto unlock_out; > + break; > > and nothing else. > > And yes, the pre-existing vcs_size() error handling has that same ugly pattern. > > It might be worth cleaning up too, although right now that > > size = vcs_size(vc, attr, uni_mode); > if (size < 0) { > if (read) > break; > > pattern means that if we 'break' there, 'read' is non-zero, so 'ret' > doesn't matter. Which is also ugly, but works. > > I *think* it could all be rewritten to just use 'break' everywhere in > the loop, and make 'ret' handling be saner. > > Something like the attached patch, but while I tried to think about > it, I didn't spend a lot of effort on it, and I certainly didn't test > it. So I'm sending this out as a "Hmm. This _looks_ better to me, but > whatever" patch. Thank you Linus, Will start with your suggested patch and will test it. George > > Linus
On Mon, Feb 06, 2023 at 01:20:28PM -0500, George Kennedy wrote: > > > On 2/6/2023 1:12 PM, Linus Torvalds wrote: > > On Mon, Feb 6, 2023 at 9:34 AM George Kennedy <george.kennedy@oracle.com> wrote: > > > > > > - ret = -ENXIO; > > > vc = vcs_vc(inode, &viewed); > > > - if (!vc) > > > + if (!vc) { > > > + if (read) > > > + break; > > > + ret = -ENXIO; > > > goto unlock_out; > > > + } > > That works, but the whole "if (read)" thing is already done after the > > loop, so instead of essentially duplicating that logic, I really think > > the patch should be just a plain > > > > vc = vcs_vc(inode, &viewed); > > if (!vc) > > - goto unlock_out; > > + break; > > > > and nothing else. > > > > And yes, the pre-existing vcs_size() error handling has that same ugly pattern. > > > > It might be worth cleaning up too, although right now that > > > > size = vcs_size(vc, attr, uni_mode); > > if (size < 0) { > > if (read) > > break; > > > > pattern means that if we 'break' there, 'read' is non-zero, so 'ret' > > doesn't matter. Which is also ugly, but works. > > > > I *think* it could all be rewritten to just use 'break' everywhere in > > the loop, and make 'ret' handling be saner. > > > > Something like the attached patch, but while I tried to think about > > it, I didn't spend a lot of effort on it, and I certainly didn't test > > it. So I'm sending this out as a "Hmm. This _looks_ better to me, but > > whatever" patch. > > Thank you Linus, > > Will start with your suggested patch and will test it. And I'll go drop your patch from my tree before the 0-day bots pick it up :) thanks, greg k-h
© 2016 - 2025 Red Hat, Inc.