ash while loops and variable scope
Denys Vlasenko
vda.linux at googlemail.com
Sun Sep 5 13:29:28 UTC 2010
On Sun, Sep 5, 2010 at 5:33 AM, Harald Becker <ralda at gmx.de> wrote:
> Hallo Denys!
>> Newer bash seems to use /proc/PID/fd/NNN. Need to investigate hot it does that.
> That don't need to be investigated and isn't difficult to do:
>
> - use open() or creat() to create a file or maybe pipe()/fork() to run a
> child process
> - in case a file is created temporarily do an unlink() while still
> holding the file open
> - use sprintf( "/proc/%u/fd/%d", getpid(), your_open_fd )
This requires writable filesystem. I don't like it when some features
of the shell depend in whether I have a writable filesystem somewhere,
> That's it. The file still exists on file system until all references to
> it's file descriptor got closed and is automatically removed afterwards.
But this leaks a file descriptor.
The code itself is not that hard. bash does something like this:
int main() {
char buf[64];
int pipefd[2];
pipe(pipefd);
if (fork() == 0) {
/* child */
close(pipefd[0]);
if (pipefd[1] != 63) {
dup2(pipefd[1], 63);
close(pipefd[1]);
}
/* bash would exec here, passing "/proc/self/fd/63" to
the child,
* child will open it and write into it. */
write(open("/proc/self/fd/63", /*O_WRONLY:*/ 1), "hello\n", 6);
return 0;
}
close(pipefd[1]);
write(1, buf, read(pipefd[0], buf, sizeof(buf)));
return 0;
}
The strace:
9567 pipe([3, 4]) = 0
9567 clone(child_stack=0,
flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
child_tidptr=0x7fc00c3e19d0) = 9568
9567 close(4) = 0
9567 read(3, <unfinished ...>
9568 close(3) = 0
9568 dup2(4, 63) = 63
9568 close(4) = 0
9568 open("/proc/self/fd/63", O_WRONLY) = 3
9568 write(3, "hello\n", 6 <unfinished ...>
9567 <... read resumed> "hello\n", 64) = 6
9567 write(1, "hello\n", 6) = 6
9567 exit_group(0) = ?
9568 <... write resumed> ) = 6
9568 exit_group(0) = ?
See? fd 63 in the child LEAKS. Child doesn't realize it has it open.
If child will decide to close the "file" it opened,
fd 63 will still be open, and the reading process will not see the EOF!
Modify the program like this and you'll see:
...
...
/* bash would exec here, passing "/proc/self/fd/63" to
the child,
* child will open it and write into it. */
int fd = open("/proc/self/fd/63", /*O_WRONLY:*/ 1);
if (fd != 1) {
dup2(fd, 1);
close(fd);
}
write(1, "hello\n", 6);
close(1); /* send EOF */
sleep(3);
return 0;
}
close(pipefd[1]);
write(1, buf, read(pipefd[0], buf, sizeof(buf)));
write(1, buf, read(pipefd[0], buf, sizeof(buf)));
return 0;
}
The second read() in the parent will not return 0 (EOF) at once,
it will wait 3 seconds for child to exit.
This is wrong.
Here's the strace:
9604 15:25:01.302013 pipe([3, 4]) = 0
9604 15:25:01.302143 clone(child_stack=0,
flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
child_tidptr=0x7f94a12d59d0) = 9605
9604 15:25:01.302413 close(4) = 0
9604 15:25:01.302615 read(3, <unfinished ...>
9605 15:25:01.302851 close(3) = 0
9605 15:25:01.303017 dup2(4, 63) = 63
9605 15:25:01.303166 close(4) = 0
9605 15:25:01.303314 open("/proc/self/fd/63", O_WRONLY) = 3
9605 15:25:01.303504 dup2(3, 1) = 1
9605 15:25:01.303665 close(3) = 0
9605 15:25:01.303833 write(1, "hello\n", 6 <unfinished ...>
9604 15:25:01.303949 <... read resumed> "hello\n", 64) = 6
9604 15:25:01.304018 write(1, "hello\n", 6) = 6
9604 15:25:01.304228 read(3, <unfinished ...>
9605 15:25:01.304339 <... write resumed> ) = 6
9605 15:25:01.304395 close(1) = 0
--- parent will not see EOF despite us closing our output ---
9605 15:25:01.304506 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
9605 15:25:01.304632 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
9605 15:25:01.304764 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
9605 15:25:01.304931 nanosleep({3, 0}, 0x7fff48f8d0b0) = 0
--- note 3 second gap here ---
9605 15:25:04.305315 exit_group(0) = ?
9604 15:25:04.305527 <... read resumed> "", 64) = 0
9604 15:25:04.305653 --- SIGCHLD (Child exited) @ 0 (0) ---
9604 15:25:04.305741 write(1, "", 0) = 0
9604 15:25:04.305930 exit_group(0) = ?
--
vda
More information about the busybox
mailing list