a repeatable 'more' crash and question about wide char and 'get_line' safety
Hi,
I've got a repeatable (for me!) crash in 'more' in git
( 5d3da4a24d0f952eb3709ba62c74aaa04729e08f ) that's also in at least 2.23.1 - and I don't know how much older.
I believe the problem relates to the way the Line buffer is managed in get_line; but more of the diagnosis below.
The problem:
------------
*** Error in `/usr/bin/more': free(): invalid pointer: 0x000000000060f850 ***
This was generated on OpenSuse 'factory' with their 2.23.1-4.3.x86_64 package, but then verified it on the current util-linux git. Here is the bug I filed in their system:
https://bugzilla.novell.com/show_bug.cgi?id=829720
With the following test file:
https://bugzilla.novell.com/attachment.cgi?id=548234
(It's a fragment of /var/lib/rpm/Packages that I was stupid enough to more).
The seg triggers in an 86x25 terminal for me just with
more tstfile2
It needs a UTF-8 LANG, I'm running with en_GB.UTF-8 but en_US.UTF-8 also triggers it.
My original test triggered an invalid free check, but with the help of valgrind I narrowed it down to:
==22488== Invalid write of size 1
==22488== at 0x4037A2: get_line (more.c:1043)
if (colflg && eatnl && Wrap) {
*p++ = '\n'; /* simulate normal wrap */
}==22488== by 0x4066C3: screen (more.c:660)
==22488== by 0x4025E4: main (more.c:503)
==22488== Address 0x542c318 is 0 bytes after a block of size 344 alloc'd
==22488== at 0x4C297AB: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-lin
Answers/questions
-----------------
I've put a load of debug in and have some answers/questions:
1) The loop in get_line() is:
while (p < &Line[LineLen - 1]) {
if I understand correctly that would seem to imply that if the loop always wrote no more than 1 character per iteration there should be 1 space left in Line when you hit the bottom; of the loop, one for the \n in the code above that writes the '\n' sometimes.... but then it always writes a \0 on the end as well - so whenever we tack on a \n I think we overflow with the \0 if we're right at the end.
2) unfortunately there are cases where the loop writes more than 1 char per iteration; looking at:
http://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/text-utils/more.c#n874
we see that the wide character code can take it's (size_t)-1 case, write a character and then it GOTO's (*) process_mbc: and hopefully lands in the default case and writes some characters in it's else clause; (in this test case 1) - but that loop worries me that it's possible to write more than one.
So hmm, what's the fix?
a) I think that loop limit needs to be at least LineLen - 2 just to cope with the \n and \0 case; and there is also at least one inner loop (for tabs) that also has that loop limit coded in it that needs to match
b) If we knew what the worst case was for the number of characters the WIDECHAR stuff could write then we could make the loop termination condition would then leave enoguh space for an entire widechar.
c) Do we also need to change the allocation code in prepare_line_buffer?
It has:
size_t nsz = Mcol * 4;
Now that magic 4 isn't commented, but I'm currently guessing as it being the answer of (b) above - ie the size of a widechar? If so then I think it needs at least a +2 for the \n and \0
Dave (who doesn't know multi byte/wide stuff)
* Please hand Dijkstra a muffin
Signature · 4 lines
-----Open up your eyes, open up your mind, open up your code ------- / Dr. David Alan Gilbert | Running GNU/Linux | Happy \ \ gro.gilbert @ treblig.org | | In Hex / \ _________________________|_____ http://www.treblig.org |_______/
PATCH 1/1 get_line fixes for wide characters and overflows
Hi,
This is a fix for the bug I reported with 'more' crashing:
http://marc.info/?l=util-linux-ng&m=137401887913346&w=2
It seems to work for me, but I'd appreciate another pair of eyes on it, especially a pair with a better understanding of wide characters.
I could also see that it might be nice to add my failing case from the bug as a regression test, but is there a good way to get those tests to run with a given idea of terminal width/character encoding?
Dave
commit 96c2d8eaed137cbb9d62f0f50bdf689aa8a0f2e3
Author: Dr. David Alan Gilbert <[email protected]> Date: Fri Jul 19 23:19:39 2013 +0100
Ensure there is space at the end of the buffer for the termination characters, and that there is space for a full wide character.
text-utils/more.c +8 -3
@@ -43,6 +43,7 @@* modified mem allocation handling for util-linux */ +#include <assert.h> #include <stdio.h> #include <string.h> #include <unistd.h>
@@ -167,6 +168,9 @@ int shellp; /* A previous shell command exists */sigjmp_buf restore;char *Line;/* Line buffer */ size_t LineLen;/* size of Line buffer */ +const unsigned int line_end_space =2 ;/* For the nl and \0 at the end */ +const unsigned int wchar_max_bytes =4 ;/* I don't see a portable way to +determine this */ int Lpp =LINES_PER_PAGE ;/* lines per page */ char *Clear;/* clear screen */ char *eraseln;/* erase line */
@@ -827,7 +831,7 @@ static void prompt(char *filename)void prepare_line_buffer (void ) {char *nline; -size_t nsz = Mcol *4 ; +size_t nsz = Mcol * wchar_max_bytes + line_end_space;if (LineLen >= nsz)return ;
@@ -872,7 +876,7 @@ int get_line(register FILE *f, int *length) Currline++; c =Getc (f); } -while (p < &Line[LineLen -1 ]) { +while (p <= &Line[LineLen - (line_end_space + wchar_max_bytes)]) {#ifdef HAVE_WIDECHAR if (fold_opt && use_mbc_buffer_flag &&MB_CUR_MAX >1 ) { use_mbc_buffer_flag =0 ;
@@ -961,7 +965,7 @@ int get_line(register FILE *f, int *length)my_putstring (eraseln); promptlen =0 ; }else { -for (--p; p < &Line[LineLen -1 ];) { +for (--p; p <= &Line[LineLen - (1 + line_end_space)];) { *p++ =' ' ;if ((++column &7 ) ==0 )break ;
@@ -1039,6 +1043,7 @@ int get_line(register FILE *f, int *length)if (colflg && eatnl && Wrap) { *p++ ='\n' ;/* simulate normal wrap */ } +assert (p < &Line[LineLen]); *length = p - Line; *p =0 ;return (column);
Karel Zak
maintainerRe: [PATCH] get_line fixes for wide characters and overflows
Quoted 2 lines · Dr. David Alan Gilbert
This is a fix for the bug I reported with 'more' crashing:
http://marc.info/?l=util-linux-ng&m=137401887913346&w=2
It seems that bug has been introduced 4 years ago by my commit 1ac300932deab8dea2c43050921bbbdb36d62ff1.
The original code used static buffer Line[LINSIZ+2] -- yes, +2 for \n\0.
I have applied the patch below. Please, test it (I'm not able to reproduce the problem with the file from Suse bugzilla).
Thanks!
Karel
Quoted 1 line
From
1ef2db5a5672e09fa1337099b7d9d6ab61c19bdcMon Sep 17 00:00:00 2001
From: Karel Zak <[email protected]>
Date: Thu, 1 Aug 2013 12:58:22 +0200
Subject: [PATCH] more: fix buffer overflow
The bug has been probably introduced by commit1ac300932deab8dea2c43050921bbbdb36d62ff1.
References: https://bugzilla.novell.com/show_bug.cgi?id=829720
--- text-utils/more.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
text-utils/more.c +2 -1
@@ -835,7 +835,8 @@ void prepare_line_buffer(void)if (nsz <LINSIZ ) nsz =LINSIZ ; - nline =xrealloc (Line, nsz); +/* alloc nsz and extra space for \n\0 */ + nline =xrealloc (Line, nsz +2 ); Line = nline; LineLen = nsz; }
Signature · 1 line
1.8.1.4
Re: [PATCH] get_line fixes for wide characters and overflows
Quoted 11 lines · * Karel Zak ([email protected]) wrote:
Quoted 2 lines · Dr. David Alan Gilbert
This is a fix for the bug I reported with 'more' crashing:
http://marc.info/?l=util-linux-ng&m=137401887913346&w=2It seems that bug has been introduced 4 years ago by my commit
1ac300932deab8dea2c43050921bbbdb36d62ff1.The original code used static buffer Line[LINSIZ+2] -- yes, +2 for \n\0.
I have applied the patch below. Please, test it (I'm not able to reproduce the problem with the file from Suse bugzilla).
Hi Karel,
Thanks for the reply. I'll give it a go over the weekend,
but I don't think it can handle the wchar problems I described and fixed in my follow up patch. If a 4byte wchar hapens to land at the end of the buffer how do you guarantee the space?
Dave
Quoted 37 lines
Thanks!
Karel
Quoted 1 line
From
1ef2db5a5672e09fa1337099b7d9d6ab61c19bdcMon Sep 17 00:00:00 2001From: Karel Zak <[email protected]>
Date: Thu, 1 Aug 2013 12:58:22 +0200
Subject: [PATCH] more: fix buffer overflowThe bug has been probably introduced by commit
1ac300932deab8dea2c43050921bbbdb36d62ff1.Reported-by "Dr. David Alan Gilbert" <[email protected]>References: https://bugzilla.novell.com/show_bug.cgi?id=829720
Signed-off-by Karel Zak <[email protected]>--- text-utils/more.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)text-utils/more.c +2
-1@@ -835,7 +835,8 @@ void prepare_line_buffer(void)if (nsz <LINSIZ ) nsz =LINSIZ ; - nline =xrealloc (Line, nsz); +/* alloc nsz and extra space for \n\0 */ + nline =xrealloc (Line, nsz +2 ); Line = nline; LineLen = nsz; }Signature · 1 line
1.8.1.4
Signature · 4 lines
-----Open up your eyes, open up your mind, open up your code ------- / Dr. David Alan Gilbert | Running GNU/Linux | Happy \ \ gro.gilbert @ treblig.org | | In Hex / \ _________________________|_____ http://www.treblig.org |_______/
Karel Zak
maintainerRe: [PATCH] get_line fixes for wide characters and overflows
Quoted 8 lines · Dr. David Alan Gilbert
e to
Quoted 1 line
reproduce the problem with the file from Suse bugzilla).
Hi Karel,
Thanks for the reply. I'll give it a go over the weekend,
but I don't think it can handle the wchar problems I described and fixed in my follow up patch. If a 4byte wchar hapens to land at the end of the buffer how do you guarantee the space?
Ah, I probably see what you mean. So I have added p < &Line[LineLen - 1] check to the for() where the code copies the multibyte buffer to line buffer. See git tree for more details.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Re: [PATCH] get_line fixes for wide characters and overflows
Quoted 10 lines · * Karel Zak ([email protected]) wrote:
Quoted 8 lines · Dr. David Alan Gilbert
e to
Quoted 1 line
reproduce the problem with the file from Suse bugzilla).
Hi Karel,
Thanks for the reply. I'll give it a go over the weekend,
but I don't think it can handle the wchar problems I described and fixed in my follow up patch. If a 4byte wchar hapens to land at the end of the buffer how do you guarantee the space?
Quoted 3 lines
Ah, I probably see what you mean. So I have added p < &Line[LineLen - 1] check to the
for()where the code copies the multibyte buffer to line buffer. See git tree for more details.
OK; so I think with your ...ea608842 change then it should work (not tried to push it); but with that change hopefully the extra check in ..623bc74f should never trigger; which is good because if it does then I think you'll end up outputing part of a wide character which would get messy.
One thing I tried to do in my change was remove the magic '4's that pop up in a few places because of the wide characters.
(Any particular reason you prefered this over my fix?)
Dave
Quoted 8 lines
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Signature · 4 lines
-----Open up your eyes, open up your mind, open up your code ------- / Dr. David Alan Gilbert | Running GNU/Linux | Happy \ \ gro.gilbert @ treblig.org | | In Hex / \ _________________________|_____ http://www.treblig.org |_______/
Karel Zak
maintainerRe: [PATCH] get_line fixes for wide characters and overflows
Quoted 11 lines · Dr. David Alan Gilbert
u mean. So I have added p < &Line[LineLen - 1]
Quoted 2 lines
check to the
for()where the code copies the multibyte buffer to line buffer. See git tree for more details.OK; so I think with your
...ea608842change then it should work (not tried to push it); but with that change hopefully the extra check in..623bc74fshould never trigger; which is good because if it does then I think you'll end up outputing part of a wide character which would get messy.One thing I tried to do in my change was remove the magic '4's that pop up in a few places because of the wide characters.
then it would be better to use #define ...
Anyway, the more.c code is so horrible that it does not make sense to waste time to try to make it perfect. It would be better to do complete refactoring (some control struct, small functions, no global variables, etc.)
Quoted 1 line
(Any particular reason you prefered this over my fix?)
Unfortunately I somehow missed the widechar issue in my first commit, so your original patch was no bad (except the global const variables).
Thanks, look forward to see more patches from you :-)
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com