bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

incorrect lseek() when processing script ending in unterminated line


From: Stephane Chazelas
Subject: incorrect lseek() when processing script ending in unterminated line
Date: Wed, 21 Dec 2016 14:35:44 +0000
User-agent: Mutt/1.5.21 (2010-09-15)

Hello.

That was discovered at
http://unix.stackexchange.com/a/331884

Consider this script that modifies itself (and happens not to
end in a newline character):

$ printf %s 'printf "\necho %s\n" {1..10} >> $0' > script.sh; bash -x 
./script.sh
+ printf '\necho %s\n' 1 2 3 4 5 6 7 8 9 10
+ echo 1
1
+ echo 2
2
+ echo 3
3
+ echo 4
4
+ echo 5
5
+ echo 6
6
+ echo 7
7
+ echo 8
8
+ echo 9
9
+ echo 10
10

That's fine so far. Now, if I run an external command instead of
printf (like /usr/bin/printf):

$ printf %s '/usr/bin/printf "\necho %s\n" {1..10} >> $0' > script.sh; bash -x 
./script.sh
+ /usr/bin/printf '\necho %s\n' 1 2 3 4 5 6 7 8 9 10
+ ho 6
./script.sh: line 2: ho: command not found
+ echo 7
7
+ echo 8
8
+ echo 9
9
+ echo 10
10

Running bash under strace, we see:

read(255, "/usr/bin/printf \"\\necho %s\\n\" {1"..., 43) = 43
read(255, "", 43)                       = 0
brk(0x12c5000)                          = 0x12c5000
write(2, "+ /usr/bin/printf '\\necho %s\\n' "..., 53+ /usr/bin/printf '\necho 
%s\n' 1 2 3 4 5 6 7 8 9 10
) = 53
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, 43, SEEK_CUR)                = 86

Note that it's a SEEK_CUR, not SEEK_SET above, so we seek 43
bytes past the end of the file.

In gdb breaking on that lseek():

(gdb) bt
#0  lseek64 () at ../sysdeps/unix/syscall-template.S:84
#1  0x00005555555c808f in sync_buffered_stream (bfd=<optimized out>) at 
input.c:554
#2  0x00005555555aeff8 in make_child (command=command@entry=0x555555925808 
"/usr/bin/printf \"\\necho %s\\n\" {1..10} >> $0", async_p=async_p@entry=0)
    at jobs.c:1910
#3  0x0000555555599fcd in execute_disk_command 
(words=words@entry=0x555555928d88, redirects=0x555555928b08,
    command_line=command_line@entry=0x55555592c5c8 "/usr/bin/printf \"\\necho 
%s\\n\" {1..10} >> $0", pipe_in=pipe_in@entry=-1, pipe_out=pipe_out@entry=-1,
    async=async@entry=0, fds_to_close=0x555555928aa8, cmdflags=0) at 
execute_cmd.c:5232
#4  0x000055555559ac52 in execute_simple_command 
(simple_command=0x555555928a48, pipe_in=pipe_in@entry=-1, 
pipe_out=pipe_out@entry=-1, async=async@entry=0,
    fds_to_close=fds_to_close@entry=0x555555928aa8) at execute_cmd.c:4429
#5  0x000055555559bcea in execute_command_internal 
(command=command@entry=0x555555928a08, asynchronous=asynchronous@entry=0, 
pipe_in=pipe_in@entry=-1,
    pipe_out=pipe_out@entry=-1, fds_to_close=fds_to_close@entry=0x555555928aa8) 
at execute_cmd.c:806
#6  0x000055555559dfa2 in execute_command (command=0x555555928a08) at 
execute_cmd.c:405
#7  0x0000555555585e30 in reader_loop () at eval.c:180
#8  0x00005555555848fa in main (argc=3, argv=0x7fffffffdb78, 
env=0x7fffffffdb98) at shell.c:792
(gdb) frame 1
#1  0x00005555555c808f in sync_buffered_stream (bfd=<optimized out>) at 
input.c:554
554         lseek (bp->b_fd, -chars_left, SEEK_CUR);
(gdb) p *bp
$1 = {b_fd = 255, b_buffer = 0x555555927f08 "", b_size = 43, b_used = 0, b_flag 
= 1, b_inputp = 43}

Here b_used < b_inputp which as far as I understand is not meant to happen.

That sync_buffered_stream is meant to seek back to where we're
meant to resume reading the script when we've read more than
needed, but here b_inputp > b_used would suggest we've processed
code that is passed what we've read. Or more likely b_used has
incorrectly been set to 0.

I stopped looking at that point.

-- 
Stephane



reply via email to

[Prev in Thread] Current Thread [Next in Thread]