Por que a fonte do Bash não precisa do bit de execução?

57

Com o Bash's source, é possível executar um script sem um bit de execução definido. Este é um comportamento documentado e esperado, mas não é contra o uso de um bit de execução?

Eu sei, isso sourcenão cria um subshell.

Motte001
fonte
2
O fato de chmodpermitir que você defina permissões (incluindo `x) com um número octal dá uma pista de que época ele vem. Eu não ficaria surpreso se ele começasse como um indicador rápido e sujo "este é um arquivo binário que você pode executar", desde os dias anteriores à
criação do
9
Por outro lado, quando o SUID entra em ação, diferentes níveis de proteção para diferentes categorias de usuários se tornam mais importantes. Vá em frente e leia meu programa SUID, se quiser. Mas se você executá-lo através da simples leitura, os poderes do SUID não o acompanham
inserida em 22/06/16
@infixed O carregador de programa do Linux nem olha para o shebang, a menos que o bit de execução esteja definido. (Para toot meu próprio chifre um pouco: veja aqui .)
Kyle Strand
Você também pode ter + xr, mas isso é um pouco estranho, porque geralmente é possível ler o binário injetando código no processo em execução #
Niklas B.
@KyleStrand por "se você executá-lo através da simples leitura, os poderes do SUID não o acompanham" Eu estava imaginando algo do tipocp /sbin/suidexecutable /tmp/mycopy; /tmp/mycopy
inserido em

Respostas:

36

Bash é um intérprete; aceita entrada e faz o que quiser. Não precisa atender ao bit executável. De fato, o Bash é portátil e pode ser executado em sistemas operacionais e sistemas de arquivos que não possuem nenhum conceito de bit executável.

O que importa com o bit executável é o kernel do sistema operacional. Quando o kernel do Linux executa exec, por exemplo, verifica se o sistema de arquivos não está montado com uma noexecopção, verifica o bit executável do arquivo do programa e aplica todos os requisitos impostos pelos módulos de segurança (como SELinux ou AppArmor).

Observe que o bit executável é um tipo de controle bastante discricionário. Em um sistema Linux x86-64, por exemplo, você pode ignorar a verificação do bit executável do kernel invocando explicitamente /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2como intérprete :

cp /bin/ls /tmp/
chmod -x /tmp/ls
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /tmp/ls

Isso é um pouco análogo ao fornecimento do código-fonte do Bash no Bash, exceto que esse ld.soé o intérprete e o código que ele executa é o código da máquina no formato ELF.

200_success
fonte
3
Usar o carregador como um "intérprete" de "bytecode" que por acaso é um binário executável real ... incrível. Obrigado por isso.
Kyle Strand
@KyleStrand pode haver vários níveis de indireção (a diferença entre o programa armazenado e o processo carregado e como a memória é mapeada, vários supervisores, VMs etc.), mas, em princípio, o código da máquina pode ser executado diretamente pela CPU (sem microcódigo, etc) e, portanto, o código da máquina não é interpretado: o hardware entende como é - é a língua nativa - não são necessários intérpretes.
jfs
2
@JFSebastian Sim, sabemos que é um código nativo, portanto, "intérprete" está entre aspas. O trabalho de ld.soé fazer a ligação dinâmica.
200_success
@JFSebastian O que 200_success disse. Além disso, é isso que eu quis dizer com "binário executável real" e também coloquei "bytecode" entre aspas (já que o AFAIK "bytecode" normalmente não é usado para se referir ao código binário executável nativamente compilado real). Além disso, bom nome de usuário.
Kyle Strand
@JFSebastian Eu acredito que as modernas CPUs do tipo x86 são realmente RISC internamente, com o conjunto de instruções CISC à moda antiga basicamente conduzindo a execução de microcódigo das instruções RISC correspondentes, onde uma instrução CISC pode causar a execução de muitas instruções RISC. Então, de certa forma, chamar o que a CPU faz com o código de máquina RISC "interpretar" é, se não completamente correto do ponto de vista técnico, pelo menos um modelo mental válido. O AES-NI é provavelmente um dos exemplos mais extremos: uma instrução CISC por rodada do AES!
a CVn
57

sourceou o ponto. equivalente, mas padrão , não executa o script, mas os comandos do arquivo de script e, em seguida, executa-os, linha por linha, no ambiente atual do shell.

Não há nada contra o uso de bit de execução, porque o shell precisa apenas ler permissão para ler o conteúdo do arquivo.

O bit de execução é necessário apenas quando você executa o script. Aqui, o shell fará um fork()novo processo, usando a execve()função para criar uma nova imagem de processo a partir do script, que é necessário para ser um arquivo executável regular.

cuonglm
fonte
5
@alexis: Não sei se realmente entendo. Se o fizer bash < script, obtém essencialmente o mesmo resultado que source script. Que proteção fornece uma verificação de bits de execução?
Compilador conspícuo
27
@alexis, não é tarefa de um intérprete verificar as permissões dos scripts que ele interpreta. Nada faz isso - nem Python, nem Ruby, nem a máquina virtual Java, nem outros shells aleatórios. O bit execute controla se a execvfamília de syscalls OS * pode ser usada com um executável, não se um intérprete o executará. Por que confundir as pessoas (e interromper a capacidade de avaliar código transmitido de fontes que não são arquivos) quebrando convenções?
Charles Duffy
14
@alexis, ... além disso, há um significado valioso em ter uma biblioteca shell que não é executável: Diz "Eu sou uma biblioteca, não um comando - me forneça fontes, não me execute". Caso contrário, você precisará escrever um código para detectar e corrigir o uso indevido do código destinado apenas a ser originado, mas, ao não ter a permissão + x, você pode garantir que os usuários não possam (não) usar mal uma biblioteca dessa maneira.
Charles Duffy
6
@alexis "foi uma decisão dos autores" Somente no sentido de que qualquer outro código possível que não estivesse incluído era uma decisão. O shell abre o arquivo para leitura, para ler os comandos dele. Eu não esperaria que ele verifique a permissão de leitura, ele apenas tenta abrir o arquivo e falha se não puder. Da mesma forma, não verifica a permissão de gravação ou execução, pois essas verificações não são relevantes para a leitura do arquivo.
Randy Orrison
2
@alexis Isso é usado na prática, por exemplo, com o virtualenv do python, o script para ativá-lo deve ser originado e não executado e, portanto bin/activate, não possui um bit executável. Os scripts podem ser totalmente bibliotecas ou outras coisas assim. Eu acho que ter .sh pode ser um sinal bem, mas ter um mínimo de pé-armas é bom: é bom que não é possível executar ./bin/activatepor acidente em vez de. bin/activate
daboross
20

O bit executável (ao contrário do resto) nos arquivos não-setetid e não-setetidid não é um mecanismo de segurança. Qualquer coisa que você possa ler, poderá executar indiretamente, e o Linux permitirá que você leia indiretamente qualquer coisa que possa executar, mas não possa ler diretamente (isso deve ser suficiente para abrir um buraco no conceito de que o xid não definido (g) é um medida de segurança).

É mais uma coisa conveniente: deixe o sistema executá-lo diretamente para mim se o bit estiver definido, caso contrário, eu preciso fazê-lo indiretamente ( bash the_script;ou alguns hackers para obter a imagem de memória de um executável sem permissão de leitura ).

Você pode configurá-lo por conveniência, se você pretende tanto na fonte quanto executar seu insourcable.

Aparentemente, no entanto, muitos implementadores de bibliotecas compartilhadas compartilham seu pensamento e, consequentemente, muitos sistemas exigem que as bibliotecas compartilhadas, que são essencialmente o equivalente nativo dos insourcables do shell, sejam marcadas como executáveis ​​para serem utilizáveis. Consulte Por que as bibliotecas compartilhadas são executáveis? .

PSkocik
fonte
3
@ Motte001 Eles podem executar esses anexos se realmente quiserem. É uma conveniência.
PSKocik
2
O bit executável não é por segurança: se eu possuo um arquivo, sou livre para chmodexecutá-lo. É para classificar dados de programas, então a pergunta do OP é razoável.
22416 Alexis
11
@ Motte001 Esse é mais um recurso de segurança do navegador de arquivos GUI / aplicativo de email - ele pode facilmente decidir que clicar duas vezes em qualquer arquivo cujo nome termine ".sh" seja executado em um terminal (estilo Windows) ou, inversamente, em clicar em qualquer arquivo no diretório "anexos baixados" invocará um aplicativo de visualização em caixa de areia embutido. O xbit é apenas um lugar extra, ele pode ler / escrever uma dica do que fazer.
IMSOP
3
Uma razão pela qual precisamos do bit executável é executar programas setuid, particularmente aqueles pertencentes ao root. Embora você possa executá-los por conta própria, é necessário que o sistema operacional os execute para que eles tenham as permissões necessárias. Em alguns outros casos, é realmente apenas uma conveniência.
precisa
2
"O Linux permitirá que você leia qualquer coisa que você possa executar via / proc" - Bem, meu kernel Debian /tmp$ cp /bin/cat ./cat ; chmod a-rw ./cat ; ./cat & cp /proc/$!/exe /tmp/cat2cp: cannot stat ‘/proc/16260/exe’: Permission denied
padrão
9

Esta é uma boa pergunta! O Unix usa o bit executável para distinguir entre programas e dados. O sistema operacional não requer o bit de execução, pois um script de origem não é passado ao sistema operacional para execução como um novo processo. Mas o shell trata um script de origem como um programa e procurará $PATHo arquivo que você deseja obter. Portanto, o próprio shell poderia ter exigido permissão de execução para arquivos de origem; mas não.

A questão deve ter surgido há muito tempo. O design do shell Bourne foi o resultado de "uma longa sequência de modificação, diálogo, discussão" entre os habitantes do Bell Labs, e muitas decisões de design foram discutidas por SR Bourne e outros ao longo dos anos. Infelizmente, meu olhar rápido não encontrou nenhuma discussão sobre o recurso de origem (em minha defesa, é difícil pesquisar no Google). O que eu encontrei é que o "." O comando não aparece nesta introdução inicial ao shell pelo próprio Bourne, mas está presente na versão mais madura da Versão 7 .

Autoridade ausente, aqui está minha própria interpretação:

  1. O .comando, aka source, é na verdade a inclusão de texto (como #includeno pré-processador C) no código-fonte do script em execução ou da sessão interativa. Como tal, o arquivo incluído não pode ser "executado".

  2. A filosofia do Unix sempre foi dar aos programadores corda suficiente para se enforcarem. Muitas restrições de mão-de-obra e arbitrárias simplesmente atrapalham. Recentemente, algumas distribuições se rm -r /recusaram a fazer o que você pede. (Este comando diz rmpara excluir tudo no seu computador. Não tente fazer root! Ou, melhor ainda, de jeito nenhum.) Então, talvez Bourne. apenas decidi que, quando você tenta obter um arquivo, você deve saber o que está fazendo. Isso também evita trabalho desnecessário, e os ciclos importavam muito na época.

alexis
fonte
Mas definitivamente não devemos fazer o que o @cat disse.
TOOGAM
Estou surpreso por não ter sido sinalizado e excluído @TOOGAM, mas coloquei o / s!
cat
Lá, eu não sei o que o @cat havia escrito, mas eu respondi à prova de crianças ainda mais. (Mas vamos lá, era bastante claro a partir do contexto já.)
alexis
4

No que diz respeito ao sistema operacional, um arquivo que contém shell script é apenas dados. Se você passa o nome desse arquivo de dados para o sourcecomando ou na linha de comando para uma chamada do shell bash, tudo o que o SO vê é uma sequência que coincide com o nome de um arquivo que contém dados.

Como o bit de execução seria relevante nesse caso?

Stephen M. Webb
fonte
3

A distinção é importante porque você pode ter um arquivo de comandos do shell que não é útil como executável, mas somente quando é originado. Para esse arquivo, você pode desativar o bit de execução e nunca será acessado, a menos que explicitamente em um comando de origem. A razão para isso é ter efeitos colaterais no shell do qual é executado. Para um exemplo específico, eu tenho um script chamado fix_path que analisa e modifica o caminho.

MAPA
fonte
1

Caso alguém esteja interessado em mais estudos e / ou esclarecimentos: Em um shell quase compatível com POSIX implementado há pouco tempo, o funcionamento interno das funções 'exec_program ()' e 'builtin_source ()' é muito exemplar. Nessas funções, você vê exatamente qual é a diferença entre elas:

https://github.com/rsenn/shish/blob/master/src/builtin/builtin_source.c

https://github.com/rsenn/shish/blob/master/src/exec/exec_program.c

basicamente o sourcing pode ser visto como o shell redirecionando temporariamente seu descritor de arquivo interno de onde analisa o shell script (o terminal no modo interativo). por isso, é muito semelhante a outros redirecionamentos como <input_file.txte >>append_to_something.liste estes só precisa abrir e fechar arquivos.

portanto, a execução é tratada pela execve()chamada do sistema para a qual o bit de execução é obrigatório.

Lembro-me de ver alguns sistemas que permitem a execução de binários ELF / a.out, mas executando o "/lib/ld-dynamic-linker.so" e com o programa binário (sem bit exec) como primeiro argumento. Acredito que isso ocorreu em algumas máquinas DEC Alpha ou VAX (poderia ter sido o SCO Unix?)

rsenn
fonte
-2

Outro ponto de vista:

O script de origem consiste basicamente em componentes internos do shell e chamadas de programa. sourceOs componentes internos do shell ( entre eles) são partes do shell e o shell deve ser executável em primeiro lugar. Todo programa chamado (que é ELF, outro script com shebang, qualquer que seja) deve ter o bit de execução definido, caso contrário, não será executado.

Portanto, não é contra o uso de um bit de execução, porque nada sem o bit de execução será executado. A validação não ocorre para o script de origem como um todo; é executado para cada parte separadamente, mas é.

Kamil Maciorowski
fonte
Buildins do shell não são originários de um script de shell. Normalmente eles fazem parte do executável do shell. No ksh93, no zsh e em alguns outros shells, eles podem ser extensões de shell.
fpmurphy
@ fpmurphy1 Não é esta a minha frase "embutidos no shell (...) são partes do shell"?
Kamil Maciorowski