execve(/bin/sh, 0, 0); в трубе

У меня есть следующий пример программы:

#include <stdio.h>

int
main(int argc, char ** argv){
    char buf[100];

    printf("Please enter your name: ");
    fflush(stdout);
    gets(buf);
    printf("Hello \"%s\"\n", buf);

    execve("/bin/sh", 0, 0);
}

I и когда я запускаю без какой-либо трубы, он работает как надо и возвращает приглашение sh:

bash$ ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
testName
Hello "testName"
$ exit
bash$

Но это не работает в трубе, я думаю, что знаю, почему это так, но я не могу найти решение. Пример ниже.

bash$ echo -e "testName\npwd" | ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
Hello "testName"
bash$

Я полагаю, что это как-то связано с тем фактом, что gets очищает stdin таким образом, что /bin/sh получает EOF и быстро завершает работу без сообщения об ошибке.

Но как мне обойти это (без изменения программы, если возможно, и не удаляя gets, если нет), чтобы я получал подсказку, даже если я ввожу ввод через канал?

P.S. Я запускаю это на машине FreeBSD (4.8) D.S.


person A. Nilsson    schedule 14.12.2011    source источник
comment
НИКОГДА не используйте gets. Он всегда открывает брешь в защите от переполнения буфера.   -  person ThiefMaster    schedule 14.12.2011
comment
Я знаю ;) ... это часть попытки переполнения буфера на компьютере в лаборатории безопасности в моем университете. Чисто академический. = Д   -  person A. Nilsson    schedule 14.12.2011


Ответы (3)


Вы можете запустить свою программу без каких-либо модификаций следующим образом:

(echo -e 'testName\n'; cat ) | ./a.out

Таким образом вы гарантируете, что стандартный ввод вашей программы не заканчивается после того, что выводит echo. Вместо этого cat продолжает вводить входные данные для вашей программы. Источником этого последующего ввода является ваш терминал, так как именно оттуда cat читает.

Вот пример сеанса:

bash-3.2$ cc stdin_shell.c 
bash-3.2$ (echo -e 'testName\n'; cat ) | ./a.out 
Please enter your name: warning: this program uses gets(), which is unsafe.
Hello "testName"
pwd
/home/user/stackoverflow/stdin_shell_question
ls -l
total 32
-rwxr-xr-x  1 user  group  9024 Dec 14 18:53 a.out
-rw-r--r--  1 user  group   216 Dec 14 18:52 stdin_shell.c
ps -p $$
  PID TTY           TIME CMD
93759 ttys000    0:00.01 (sh)
exit

bash-3.2$

Обратите внимание, что поскольку стандартный ввод оболочки не подключен к терминалу, sh считает, что он не выполняется в интерактивном режиме, и, следовательно, не отображает приглашение. Однако вы можете вводить свои команды как обычно.

person Adam Zalcman    schedule 14.12.2011
comment
Ах ... да, вы правы (на самом деле мне удалось найти это решение до того, как я увидел ваш пост, но спасибо, что разместили его здесь). - person A. Nilsson; 14.12.2011

Использование execve("/bin/sh", 0, 0); является жестоким и необычным наказанием для оболочки. Он вообще не дает ему никаких аргументов или окружения — ни собственного имени программы, ни даже таких обязательных переменных окружения, как PATH или HOME.

person Jonathan Leffler    schedule 14.12.2011
comment
ну... да, я полностью согласен, но это просто используется в качестве эксперимента. Я бы никогда не использовал это в чем-то похожем на полезную программу. - person A. Nilsson; 14.12.2011

Не на 100% уверен в этом (используемая конкретная оболочка и ОС могут немного отбрасывать эти ответы; я полагаю, что FreeBSD использует GNU bash по умолчанию как /bin/sh?), но

  • sh может обнаружить, что его ввод не является tty.

or

  • Ваша версия sh может перейти в неинтерактивный режим, например, если она вызывается как sh, ожидая, что login добавит - к argv[0] для этого. Настройка execve ("/bin/sh", { "-sh", NULL}, NULL) может убедить его, что он запускается как оболочка входа в систему.
person BRPocock    schedule 14.12.2011