LINUX.ORG.RU
ФорумTalks

Задачка


0

1

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

Пусть есть программа на C следующего содержания:

#include <stdio.h>

int main()
{
	printf ("Hello! I am cute program.\n");

	return 0;
}
Мы это программу скомпилим и положим в /usr/bin:
$ gcc my_c_program.c -o my_c_program
$ sudo mv my_c_program /usr/bin
А теперь играемся, и видим странное:
$ my_c_program
I AM SCARY VIRUS AHAHAHAHA!!!
$ which my_c_program
/usr/bin/my_c_program
$ /usr/bin/my_c_program
Hello! I am cute program.

Все происходит в баше; баш, gcc и which непатченные; alias незадействован; новые функции в баше не определены.

Вопрос: как такое может быть? :)

Кто отгадает (знает), тому - респект.

Примечание: в моем решении нет злого умысла, только одна ошибка, и одно небольшое совпадение. Инфернальность вирусного сообщения не должна смущать: там могло быть что-то вроде «Hello !I amc ute prgoram.»

Респекты выражаются anon_666, Cancellor и Tark за правильные ответы, но не совпадающие с моим (более неожиданным!). Главный респект еще не разыгран!



Последнее исправление: zzo (всего исправлений: 3)

Ответ на: комментарий от anon_666

Мда, виноват. Конечно, алиасов нет.

zzo
() автор топика
Ответ на: комментарий от zzo

Написать маленькую библиотечку, в которой лежит своя реализация printf(). При вызове, она проверяет какие-то критерии (имя процесса, например). Если совпадает - выводим весёленькую строчку, если нет - передаём вызов настоящей printf()

Cancellor ★★★★☆
()
Ответ на: комментарий от Zodd

Но ведь всеравно вызовется та, которая в `which my_c_program` находится.

cvb
()

> Все происходит в баше; баш, gcc и which непатченные; alias незадействован.

а sudo патченный!

а как выросший на мультиюзерных линуксах, я предположу что это над нами с соседнего терминала издеваются :-) на первом-втором курсе сам так развлекался...

gods-little-toy ★★★
()
Ответ на: комментарий от Cancellor

Похоже, так пойдет. :) Но это не мое настоящее решение. Мое решение хорошо тем, что любой из присутствующих может его воспроизвести за 5 минут.

Вот как работает непатченный strace:

$ strace my_c_program
execve("/usr/bin/my_c_program", ["my_c_program"], [/* 39 vars */]) = 0
brk(0)                                  = 0xa82000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa42dabe000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=113264, ...}) = 0
mmap(NULL, 113264, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa42daa2000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\355\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1432968, ...}) = 0
mmap(NULL, 3541032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa42d542000
mprotect(0x7fa42d69a000, 2093056, PROT_NONE) = 0
mmap(0x7fa42d899000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157000) = 0x7fa42d899000
mmap(0x7fa42d89e000, 18472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa42d89e000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa42daa1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa42daa0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa42da9f000
arch_prctl(ARCH_SET_FS, 0x7fa42daa0700) = 0
mprotect(0x7fa42d899000, 16384, PROT_READ) = 0
mprotect(0x7fa42dac0000, 4096, PROT_READ) = 0
munmap(0x7fa42daa2000, 113264)          = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa42dabd000
write(1, "Hello! I am cute program.\n", 26Hello! I am cute program.
) = 26
exit_group(0)                           = ?

Будет ли он так работать при вашем варианте, я не знаю.

zzo
() автор топика
Ответ на: комментарий от Tark

> function my_c_program { echo 'I AM SCARY VIRUS AHAHAHAHA!!!'; }

Да, это правильный ответ. Но это все же не мой случай. Тут не сильно неожиданное поведение: этож надо было функцию догадаться в интерактивный баш загнать! Спасибо за вариант, я подправлю условие :)

zzo
() автор топика
Ответ на: комментарий от zzo

Может это связано с тем, что если пихаешь 2 файла с одинаковым именем в /usr/bin и /usr/local/bin, то вызывается первый, а по which выходит второй?

Tark ★★
()
Ответ на: комментарий от Tark
$ which -a wc
/usr/local/bin/wc
/usr/bin/wc
/usr/bin/X11/wc

но вызывался из /usr/bin (в $PATH объявлен раньше /usr/local/bin)

Ja-Ja-Hey-Ho ★★★★
()
Ответ на: комментарий от Tark

В обоих случаях будет тот, кто раньше в $PATH, разве нет?

zzo
() автор топика
Ответ на: комментарий от Ja-Ja-Hey-Ho

> вызывался из /usr/bin (в $PATH объявлен раньше /usr/local/bin)

Это надо парсить так: в $PATH /usr/bin объявлен раньше, чем /usr/local/bin, да?

zzo
() автор топика
Ответ на: комментарий от vertexua

> Допиши в условие задачи вывод echo $PATH

Нет, лучше ты напиши его в ответ :)

zzo
() автор топика

Кэш баша же.
Имхо он знает уже, что прога с таким именем лежит где-то в /usr/local/bin, и заново не ищет по PATH. А which честно сканит PATH каждый раз.

Пруф:
bash-3.2$ echo 'echo AA' > o
bash-3.2$ chmod +x o
bash-3.2$ sudo mv o /usr/local/bin/
bash-3.2$ o
AA
bash-3.2$ echo 'echo BB' > o
bash-3.2$ chmod +x o
bash-3.2$ sudo mv o /usr/bin/
bash-3.2$ o
AA
bash-3.2$ which o
/usr/bin/o
bash-3.2$ /usr/bin/o
BB
bash-3.2$ bash
bash-3.2$ o
BB

Tayler ★★
()
Ответ на: комментарий от Tayler

Да, работает. Но опять не мой ответ: в моем поведение сохранялось даже после перезагрузки линукса! Подправлю условие. Вам респект, но задача не решена.

zzo
() автор топика
Ответ на: комментарий от zzo

Хм, не дают подправить. В общем, после запуска нового баша тоже должно работать.

zzo
() автор топика
Ответ на: комментарий от Alan_Steel

А which разве тогда покажет путь к бинарику из /usr/bin/?
Он же бинарик из текущей директории по логике и должен показать.

Tayler ★★
()
Ответ на: комментарий от Tayler
[sergey@niels-nb]: export PATH=./:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

┌─[~/eiger_2.0.2]
└─[sergey@niels-nb]: echo $PATH
./:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

┌─[~/eiger_2.0.2]
└─[sergey@niels-nb]: which awk
/usr/bin/awk

┌─[~/eiger_2.0.2]
└─[sergey@niels-nb]: cp /usr/bin/awk ./

┌─[~/eiger_2.0.2]
└─[sergey@niels-nb]: which awk
.//awk

┌─[~/eiger_2.0.2]
└─[sergey@niels-nb]: 
Alan_Steel ★★
()
Ответ на: комментарий от Alan_Steel

Which укажет на тот экземпляр, который будет вызван. На то он и which.

Даю ЛОРу последний шанс получить удовольствие, завтра расскажу ответ.

zzo
() автор топика

Возможно, это связано с тем, что which не следует по символическим ссылкам.

Igron ★★★★★
()
Ответ на: комментарий от Igron

> Возможно, это связано с тем, что which не следует по символическим ссылкам.

Нет.

zzo
() автор топика
Ответ на: комментарий от guilder

Внимание ответ

В моем случае проблема была в следующем:

$ echo $PATH
~/bin:/usr/local/bin:/usr/bin
Я храню всякие скрипты в моей домашней директории, и меня угораздило записать ее с помощью тильды.

В результате сам баш интерпретирует тильду, и зовет программу из домашней директории, а which и strace не интерпретируют, и зовут из /usr/local.

Так-то)

zzo
() автор топика
Ответ на: Внимание ответ от zzo

УМВР (или у вас PATH не в bashrc прописан?):

bash-3.2$ cat /home/tayler/.bashrc | grep PATH
PATH=~:/usr/local/bin:/usr/bin
bash-3.2$ bash
bash-3.2$ echo $PATH
/home/tayler:/usr/local/bin:/usr/bin
bash-3.2$ echo 'echo AA' > ./awk
bash-3.2$ chmod +x awk
bash-3.2$ awk
AA
bash-3.2$ which awk
/home/tayler/awk

Tayler ★★
()
Ответ на: комментарий от Tayler

> PATH=~:/usr/local/bin:/usr/bin

Надо в ковычках, чтобы баш не разыменовал тильду при присвоении:

PATH=«~:/usr/local/bin:/usr/bin»

zzo
() автор топика
Ответ на: Внимание ответ от zzo

>В результате сам баш интерпретирует тильду, и зовет программу из домашней директории, а which и strace не интерпретируют, и зовут из /usr/local.

Красиво. Кстати, я бы сказал, что ошибка в bash и можно накатать отчёт в багзиллу.

JackYF ★★★★
()
Ответ на: комментарий от zzo

Да блин, уже из-за спортивного интереса хочу воспроизвести, ))
но не выходит, ЧЯДНТ?

bash-3.2$ cat ./.bashrc | grep PATH
PATH=«~/bin:/bin:/usr/bin:/usr/local/bin»
bash-3.2$ echo $PATH
~/bin:/bin:/usr/bin:/usr/local/bin
bash-3.2$ awk
AA
bash-3.2$ which awk
/home/tayler/bin/awk


strace тоже находит.

Tayler ★★
()
Ответ на: комментарий от Tayler

> strace тоже находит

Ну это жесть какая-то :). Я посмотрел код strace, он руками парсит PATH, и делает stat(), пока файл не найдется.

Запустите это:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
	struct stat statbuf;
  	char *path = getenv ("PATH");
	printf ("PATH: %s\n", path, path);
	int r = stat ("~/bin/awk", &statbuf);
	printf ("stat: %d\n", r);
	return 0;
}
Этот код у меня пишет -1, (то есть не нашел, как и должно быть по условию). Если у вас он пишет 0, значит у вас какие-то сумасшедшие библиотеки; если -1, то по-другому работает strace, или опыт не чист.

zzo
() автор топика
Ответ на: комментарий от JackYF

> Кстати, я бы сказал, что ошибка в bash и можно накатать отчёт в багзиллу.

То есть баш не должен работать с тильдой, как не работают остальные? Но вон у Тайлера все работает! :)

zzo
() автор топика
Ответ на: комментарий от zzo

Пардон, strace ничего не находит, это я скосячил.
И ваш код соответственно -1 возвращает.
А вот which работает:

bash-3.2$ which awk
/home/tayler/bin/awk
bash-3.2$ which --skip-tilde awk
/bin/awk
bash-3.2$ which -v
GNU which v2.20, Copyright (C) 1999 - 2008 Carlo Wood


Из мана:
--skip-tilde
Пропускает все каталоги из переменной окружения PATH, имена которых начинаются с
символа тильда (~), а также все исполняемые файлы, которые расположены в
каталоге, указанном в переменной HOME.

Соответственно из мана можно предположить, что which (тот, что у меня установлен по крайне мере =) ) с тильдой умеет справляться.

Tayler ★★
()
Ответ на: комментарий от Tayler

> which --skip-tilde awk

О да. Это крутой, прокачанный which. У меня какой-то паленый дебиановский, и другого вроде и нет.

Значит заключаем, что тильда в пути - это известная фича баша и вича, но сомнительная, ибо недоступна олд-скул программам типа вот strace и gdb, например.

zzo
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.