[SOLVED] Ash segfaults on second backspace 64-current
SlackwareThis Forum is for the discussion of Slackware Linux.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I've tried rebuilding the existing version against the currently installed libraries just in case it needs a rebuild after the recent library changes, but it still doesn't work.
A strace of the sequence 1 2 3 backspace 4 backspace, produces this:
But actually, it has not been recompiled since Bluewhite64 11.0. So I guess this problem has something to do with recent versions of gcc/glibc, or the patches added in slackware64-13.0 to make ash compile.
I've never been able to build a package of ash that would not have this bug on my Slackware64 box, though...
I've rebuilt ash and left the debugging symbols in it just out of curiosity so I could run it through gdb
Code:
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-slackware-linux"...
(gdb) watch parsenextc
Hardware watchpoint 1: parsenextc
(gdb) run
Starting program: /bin/ash
$ Hardware watchpoint 1: parsenextc
Old value = 0x0
New value = 0x61f020 ""
0x0000000000409b2a in preadbuffer () at input.c:193
193 parsenextc = buf;
(gdb) c
Continuing.
123 *I press backspace here*
Hardware watchpoint 1: parsenextc
Old value = 0x61f020 "12"
New value = 0x61f0f0 ""
0x00007f90d573586a in memmove () from /lib64/libc.so.6
(gdb) c
Continuing.
3 *and another backspace*
Hardware watchpoint 1: parsenextc
Old value = 0x61f0f0 ""
New value = 0xff000000000061f0 <Address 0xff000000000061f0 out of bounds>
0x00007f90d57374a1 in _wordcopy_fwd_dest_aligned () from /lib64/libc.so.6
(gdb) bt
#0 0x00007f90d57374a1 in _wordcopy_fwd_dest_aligned () from /lib64/libc.so.6
#1 0x00007f90d573583e in memmove () from /lib64/libc.so.6
#2 0x0000000000416013 in input_backspace (cursor=0x7fff13552a24, len=0x7fff13552a28) at hetio.c:147
#3 0x000000000041662b in hetio_read_input (fd=0) at hetio.c:214
#4 0x0000000000409b5b in preadbuffer () at input.c:211
#5 0x0000000000410505 in xxreadtoken () at parser.c:770
#6 0x0000000000410803 in readtoken () at parser.c:687
#7 0x0000000000410957 in parsecmd (interact=<value optimized out>) at parser.c:138
#8 0x000000000040bc60 in cmdloop (top=1) at main.c:269
#9 0x000000000040c08a in main (argc=<value optimized out>, argv=<value optimized out>) at main.c:229
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000416031 in input_backspace (cursor=0x7fff13552a24, len=0x7fff13552a28) at hetio.c:151
151 if (!*(parsenextc + j))
If I'm reading this right it looks like somethings going wrong within the libc memmove() call, corrupting the parsenextc pointer which then causes the segfault later when it's used in input_backspace().
Anyway, per gnashley's advice (thanks matey), I've built a static dash and put that on my system to try. However, I'm wondering if there's an underlying libc issue here. I'm on the limit of my skills here, so I could be misunderstanding things.
Thanks gnashley. Changing bufsize to a size_t certainly fixes a compile time warning, but doesn't seem to change the symptoms of the crash. gdb still looks to be pointing to parsenextc being set out of bounds in _wordcopy_fwd_dest_aligned () within the memmove() libc call.
I've written this workaround, which seems to prove my hypothesis:
Now, whether this is a compiler bug, library bug, or just a bad assumption in the ash code that memmove() won't corrupt the pointer arguments passed to it, I don't know.
Yep, I've already discovered the '-O0'. My first attempt to fix it was to change the offending code to:
Code:
if (*cursor > 0) {
out1str("\b \b");
--*cursor;
to = parsenextc + *cursor ;
from = to + 1 ;
memmove(to, from,
BUFSIZ - *cursor + 1);
... but the damn silly compiler optimised the new 'to' and 'from' pointers out making no difference to what was there before. '-O0' fixed that, but it's still acting strange. The really strange part, is when I use that snippet above, the memmove() still changes the value of the parsenextc pointer (+56 bytes), even though it's not passed as a parameter to the memmove() function at all.
I wasn't aware of the -fno-inline, so thanks for that tip. I'll have to check it out.
Anyway, My patch in the post above already prevents the segfault in ash. I was more concerned by whether there's a underlying libc/toolchain issue going on here in current64. I'm pretty confident now that the pointer is being corrupted within that memmove() libc call in input_backspace(), What I don't understand is why.
Thanks for your "stab in the dark" patch, but the memmove() args are both (void *) so if I'm correct in my thinking then I don't see how it'll help as type shouldn't matter.
I think I understand the 'what', it's just the why that's eluding me.
(As I said at the beginning, I'm at the very edge of my skill level with this type of debugging. and libc internals are a bit beyond my competence level)
Now, whether this is a compiler bug, library bug, or just a bad assumption in the ash code that memmove() won't corrupt the pointer arguments passed to it, I don't know.
This is C. memmove() should not be able to corrupt parsenextc itself, since all function parameters are call-by-value. For memmove() to change the value of parsenextc, you would have to pass in the address of parsenextc itself (by &parsenextc).
Just for a data point, the current 32 bit ash code does not have this problem. I just tried out your test case on my Slackware 13.0 box and there's no segfault there. (It fails on my Slackware64 13.0 box as you've documented above.)
Thanks. I've done some digging since the last post on here and I sent a patch to Pat back at the beginning of the month, but I've not heard anything from him about it and it didn't show up in the latest set of updates.
As you've taken an interest in this I'll post it here too for completeness...
Code:
# Patch file for use against Slackware64 13.0/current(2010-03-08)
# .../slackware64/source/ap/ash/patches/ash-hetio.patch.gz
#
# May also be applicable to 32bit, but I haven't checked.
#
# Instructions
# Apply with:
# cd .../slackware64/source/ap/ash/patches
# gunzip ash-hetio.patch.gz
# patch < this_file
# gzip ash-hetio.patch
# and rebuild package.
#
# What this fixes:
# Incorrect size calculation on the memmove() in hetio.c:input_backspace()
# results in parsenextc's storage location being overwritten.
#
# Pressing backspace once will result in silent corruption of the
# parsenextc pointer. Pressing backspace a second time will take
# parsenextc out of bounds and result in a segfault.
#
--- ash-hetio.patch 2010-03-08 13:47:44.982364101 +0000
+++ ash-hetio.patch 2010-03-08 13:54:08.443871155 +0000
@@ -177,7 +177,7 @@
+ out1str("\b \b");
+ --*cursor;
+ memmove(parsenextc + *cursor, parsenextc + *cursor + 1,
-+ BUFSIZ - *cursor + 1);
++ BUFSIZ - (*cursor + 1) ) ;
+
+ for (j = *cursor; j < (BUFSIZ - 1); j++) {
+ if (!*(parsenextc + j))
The underlying problem is that memmove() is being asked to move 2 bytes too many due to the above error. On 64bit, the parsenextc pointer is stored in the memory location directly after the buffer that parsenextc is manipulating. The upshot is that the second byte of parsenextc is being moved over the first by the call to memmove().
As for 32bit, my guess is that the parsenextc pointer is being stored at a slightly different memory location and some other less vital variable is being overwritten by the move.
Good stuff eh? I learned quite a bit while trying to diagnose that one.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.