C Bash在简单程序上重新打开tty

C Bash在简单程序上重新打开tty,c,bash,shell,zsh,bash4,C,Bash,Shell,Zsh,Bash4,我想知道: 为什么第一个示例不起作用 什么命令可以使程序在不改变源代码的情况下运行 我的问题是shell和操作系统不可知论,但我希望它至少能在Bash4上工作 编辑: 在测试答案时,我发现这是可行的 # This does not work - it prints nothing (echo ; echo ls) | ./a.out # This does work if you type ls manually (echo ; cat) | ./a.out 使用strace: (pyt

我想知道:

  • 为什么第一个示例不起作用
  • 什么命令可以使程序在不改变源代码的情况下运行
我的问题是shell和操作系统不可知论,但我希望它至少能在Bash4上工作

编辑:

在测试答案时,我发现这是可行的

# This does not work - it prints nothing
(echo ; echo ls) | ./a.out

# This does work if you type ls manually
(echo ; cat) | ./a.out
使用strace:

(python -c "print ''" ; echo ls) | ./a.out
这也适用于:

$ (python -c "print ''" ; echo ls) | strace ./a.out
...
read(0, "\n", 4096)
...

似乎缓冲被忽略了。这是由于比赛条件造成的吗?

strace
显示了发生的情况:

(echo ; sleep 0.1; echo ls) | ./a.out
换句话说,在运行shell之前,您的程序将读取整个4096字节的缓冲区,其中包括两行。交互方式很好,因为在读取发生时,管道缓冲区中只有一行,因此不可能过度读取

相反,您需要在第一个
\n
之后停止读取,唯一的方法是逐字节读取,直到命中为止。我对libc不太了解,不知道是否支持这种功能,但这里有
read
直接:

$ ( echo; echo ls; ) | strace ./foo
[...]
read(0, "\nls\n", 4096)                 = 4
[...]
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffdefc88b9c) = 9680
#包括
#包括
int main()
{
char-buf[1];
while((读(0,buf,1))==1&&buf[0]!='\n');
系统(“/bin/sh”);
}

问题是stdio缓冲了它的输入。尽管
fgets()
只返回一行,但它已将标准输入的其余部分放入其内部输入缓冲区,并且不再可供
/bin/sh
读取。有关如何禁用输入缓冲,请参阅。有关如何使用stdioNice catch针对缓冲和竞争条件禁用缓冲,请参阅。你能看看我的编辑吗?@b这仍然是一个比赛条件,你只是操纵比赛
(python-c“print'”;echo ls)|(sleep 1;/a.out)
使其再次失败。如果您将
1
调低到系统特定的值,可能
0.01
左右,您可以重复运行它,看到它有时工作,有时失败。因为它是用于缓冲区溢出攻击的,所以它可以做到这一点。现在我明白发生了什么。非常感谢。
$ ( echo; echo ls; ) | strace ./foo
[...]
read(0, "\nls\n", 4096)                 = 4
[...]
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffdefc88b9c) = 9680
#include <unistd.h>
#include <stdlib.h>

int main()
{
    char buf[1];
    while((read(0, buf, 1)) == 1 && buf[0] != '\n');
    system("/bin/sh");
}