Gostaria de duplicar um descritor de arquivo em execução em um processo não relacionado no Linux. Eu sei sobreenviar mensagem(2)e SCM_RIGHTS
(por exemplohttps://stackoverflow.com/questions/4489433/sending-file-descriptor-over-unix-domain-socket-and-select) , mas isso só funciona se o outro processo estiver cooperando. Preciso de uma solução que não necessite da cooperação ativa do outro processo. Também sei que poderia criar primeiro o descritor de arquivo, manter uma cópia e depois criar o outro processo, mas preciso de uma solução em que o outro processo crie seu próprio descritor de arquivo.
Posso ver o descritor de arquivo:
$ ls -l /proc/13115/fd/3
lrwx------ 1 pts pts 64 2013-05-04 13:15 /proc/13115/fd/3 -> socket:[19445454]
Porém, open("/proc/13115/fd/3", O_RDWR)
executado em outro processo retorna o erroNenhum dispositivo ou endereço. Existe algo mais que funcionaria? Provavelmente comrastreamento?
Responder1
Isso ocorre intencionalmente: o compartilhamento de descritores de arquivos com outros processos é explícito. Por padrão, os descritores de arquivo são tão privados quanto a memória do próprio processo.
Como de costume, se você tiver o direito derastreamentoo processo, você pode fazer o que quiser, inclusive chamá-lo sendmsg
. Tradicionalmente, a chamada ptrace
exige a execução com o mesmo ID de usuário; restrições de segurança como SELinux, capacidades, prisões e assim por diante podem tornar ptrace
mais restritivas. Por exemplo, na configuração padrão do Ubuntu, um processo não-root só pode chamar ptrace
seus próprios descendentes (por meio do AppArmor).
Usar ptrace
de forma robusta é um pouco complicado: você precisa injetar os dados corretos, certificar-se de não sobrescrever nada e limpar tudo sozinho. Portanto, minha recomendação é injetar o código de forma indireta e acionar esse código com uma ferramenta existente.
Escreva uma pequena biblioteca compartilhada contendo o sendmsg
código e LD_PRELOAD
envie-o para o outro processo. Aqui estão algunsnão testadocódigo esqueleto com verificação de erros ausente.
int pts_gift_fd (char *path, int fd) {
int sock;
struct sockaddr_un addr = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
/* Populate msg, iov as in the code you've already found */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
sendmsg(sock, &msg, 0);
close(sock);
}
Então, para acionar o código, execute gdb -n -pid 13115 -batch -x /dev/stdin
e popen
alimente-o com uma entrada como esta (onde %d
está o fd que você deseja obter e %s
é um caminho para um soquete unix que você criou anteriormente e está ouvindo):
call pts_gift_fd("%s", %d)
detach
quit
Responder2
Você não pode fazer isso pela mesma razão que não pode acessar a memória de um processo não relacionado, porque um descritor de arquivoéparte da memória de outro processo. A única razão pela qual há informações sobre coisas como esta /proc
é porque o kernel as fornece lá, e é somente leitura (portanto, existem meios de examinaruma cópiade memória de processo).
Se estiver relacionado a um arquivo, você pode tentar acessar o arquivo. Se for um soquete, você pode bisbilhotar usandolibpcapou algo derivado dele.
A situação é basicamente esta: um descritor de arquivo faz (novamente) parte da memória de um processo. O descritor possui um buffer subjacente que existe no espaço do kernel; quando o processo lê ou grava de/para o descritor, ele está gravando de e para esse buffer. Para saída de dados, o kernel libera o buffer (para o hardware) apropriadamente; para os dados que chegam, ele recarrega o buffer (do hardware) quando o processo o esvazia. Esses buffers não são, AFAIK, acessíveis a outros processos, embora existam alguns meios (por exemplo, libpcap) deleituraos dados de alguma forma determinada pela interface do kernel específica, assim como a interface proc pode fornecer alguns dados da memória do espaço do usuário do processo.