2

Suppose interactive process A reads input from the terminal and occasionally spawns process B which also reads input from the terminal. For the sake of example, A reads a number and passes control to B which reads whatever.

Edit: A and B are not running concurrently, in the particular scenario A invokes B using system().

Is it possible to redirect stdin such that A and B read from stdin?

Experiments show that a naive

echo -e '123\nxyzzy' | A 

where 123 is supposed to be read by A and xyzzy by B does not work.

2
  • How is A reading data? Because a simple test program written in bash shows no problem (sorry for the formatting; that's comments..): $ cat a #!/bin/bash read x echo In a: $x read, calling b ./b $ cat b #!/bin/bash read x echo In b: $x read $ echo -e '123\nxyzzy' | ./a In a: 123 read, calling b In b: xyzzy read Commented Sep 3, 2016 at 23:49
  • In the particular scenario A is a C program using scanf and B is /bin/sh. Commented Sep 5, 2016 at 7:13

2 Answers 2

3

It's possible to feed input to multiple processes. When multiple processes are reading from the same pipe or terminal, each byte goes to one of the processes, whichever happens to read that particular byte first. When only one process is actively reading, it gets the input. When multiple processes are actively reading at the same time, which one gets the input is unpredictable.

You are running afoul of buffering. Most programs, evidently including A, read input a whole buffer at a time — typically a few hundred bytes or a few kilobytes — and then store it in their own memory until they get around to processing it. This is a lot faster than reading one byte at a time. But in this scenario, it means that A reads more than the part that it will process before calling B, so the input meant for B is already consumed by A when B starts.

If you can make B read from a different source, that's of course a solution.

If you have control over how A is executed, try stdbuf from GNU coreutils. It hooks into library calls to cause the process to read one byte at a time. This works with most programs, but not all: it doesn't work with statically linked executables and it doesn't work if the program uses a buffering method other than the standard library (stdio).

… | stdbuf -i 1 A 

Alternatively, try reading from a regular file. When A has read input from a pipe or terminal, it can't put it back. But when it's read from a regular file, it can rewind the reading position before calling B. This is how the read shell builtin behaves, for example. There's no guarantee that the particular program A does it, in fact it isn't very common behavior, but if it does, that's a simple solution.

If that doesn't work, or if you have no control over how A is executed, you'll need to arrange the timing of the input so that the part intended for B is not present until B starts. How to do that depends on how you can detect that B has started. A possible solution, but a fragile one, is to put a delay:

{ echo 123; sleep 1; echo xyzzy; } | A 

This only works if A calls B within 1 second, which is fragile. A more reliable solution is to detect output produced by A (or B). This is the kind of problem that expect is designed to solve. For example, if B displays some kind of prompt like B>:

#!/usr/bin/expect -f spawn A send "123\r" expect "B>" send "xyzzy\r" 
2
  • In the specific situation the source of input cannot be altered. It was a hacking exercise where B is /bin/sh and A can only by exploited by providing 8-bit-input. Expect sounds like a nice alternative. Commented Sep 3, 2016 at 23:39
  • @countermode In this scenario, a timeout is probably the easiest method. It isn't robust, but for an exploit it should be good enough. Commented Sep 5, 2016 at 7:48
0

It is a buffering issue. Process A, using stdio, reads quite a chunk from stdin, so that the part intended for B is already gone when B receives control. A possible solution is to pad the input like this:

python -c 'print("123" + "\n"*4093 + "xyzzy\n")' | A 

works as expected (the actual buffer size - here 4096 - may need adjustment).

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.