Pergunta

Considere um driver Linux que usa get_user_pages (ou get_page) para mapear as páginas do processo de chamada. O endereço físico das páginas é passado para um dispositivo de hardware. Tanto o processo quanto o dispositivo podem ler e gravar nas páginas até que as partes decidam encerrar a comunicação. Em particular, a comunicação pode continuar usando as páginas após a chamada do sistema que chama get_user_pages retorna. A chamada do sistema está na criação de um zona de memória compartilhada entre o processo e o dispositivo de hardware.

Estou preocupado com o que acontece se o processo ligar fork (Pode ser de outro tópico, e pode acontecer enquanto o syscall que chama get_user_pages está em andamento ou mais tarde). Em particular, se o pai gravar na área de memória compartilhada após o garfo, o que eu sei sobre o endereço físico subjacente (presumivelmente alterado devido a copiar na gravação)? Eu quero entender:

  1. O que o kernel precisa fazer para se defender contra um processo potencialmente de comportamento (não quero criar um segurança buraco!);
  2. quais restrições o processo precisa obedecer para que o funcionalidade do nosso motorista funciona corretamente (ou seja, a memória física permanece mapeada no mesmo endereço no processo pai).

    • Idealmente, eu gostaria do caso comum em que o processo infantil não usa nosso motorista (provavelmente liga exec quase imediatamente) para trabalhar.
    • Idealmente, o processo pai não deve precisar tomar nenhuma etapa especial ao alocar a memória, pois temos o código existente que passa um buffer alocado por pilha para o driver.
    • Estou ciente de madvise com MADV_DONTFORK, e seria bom que a memória desapareça do espaço do processo filho, mas não é aplicável a um buffer alocado por pilha.
    • “Não use o Fork enquanto você tem uma conexão ativa com o nosso motorista” seria irritante, mas aceitável como último recurso se o ponto 1 for satisfeito.

Estou disposto a ser apontado para documentação ou código -fonte. Eu olhei em particular em Drivers de dispositivo Linux, mas não encontrou esse problema abordado. Os RTFs aplicados até apenas a parte relevante da fonte do kernel é um pouco esmagadora.

A versão do kernel não é completamente fixa, mas é recente (digamos ≥2.6.26). Estamos apenas alvejando plataformas de braço (até agora, um único processador, mas o multicore está ao virar da esquina), se isso importa.

Foi útil?

Solução

UMA fork() não interferirá com get_user_pages(): get_user_pages() vai te dar um struct page.

Você precisaria kmap() Antes de poder acessá -lo, e esse mapeamento é feito no espaço do kernel, não no espaço de usuários.

EDITAR: get_user_pages() Toque na tabela da página, mas você não deve se preocupar com isso (apenas certifique -se de que as páginas sejam mapeadas no espaço do usuário) e retorna -efault se tiver algum problema em fazê -lo.

Se você fork (), até que a copia-na-gravação seja executada, a criança poderá ver essa página. Depois que a copiar-no-write for concluída (porque a criança/o driver/os pais escreveu para a página através do mapeamento do espaço do usuário-não o kernel kmap () que o driver possui), essa página não será mais compartilhada. Se você ainda possui um kmap () na página (no código do driver), não poderá saber se está segurando a página pai ou a criança.

1) Não é um buraco de segurança, porque depois que você executa (), tudo isso se foi.

2) Quando você fork (), você deseja que os dois processos sejam idênticos (é um garfo !!). Eu acho que seu design deve permitir que o pai e o filho acessem o motorista. Execve () irá lavar tudo.

Que tal adicionar alguma funcionalidade no espaço dos usuários como:

 f = open("/dev/your_thing")
 mapping = mmap(f, ...)

Quando o MMAP () é chamado no seu dispositivo, você instala um mapeamento de memória, com sinalizadores especiais:http://os1a.cs.columbia.edu/lxr/source/include/linux/mm.h#071

Você tem algumas coisas interessantes como:

#define VM_SHARED       0x00000008
#define VM_LOCKED       0x00002000
#define VM_DONTCOPY     0x00020000      /* Do not copy this vma on fork */

Vm_shared desativará a cópia em write vm_locked desativará a troca nessa página vm_dontcopy dirá ao kernel para não copiar a região VMA no garfo, embora eu não ache que seja uma boa ideia

Outras dicas

A resposta curta é usar madvise(addr, len, MADV_DONTFORK) Em qualquer Buffers de espaço de usuários que você der ao seu motorista. Isso diz ao kernel que o mapeamento não deve ser copiado de pai para filho e, portanto, não há vaca.

A desvantagem é que a criança herda o mapeamento nesse endereço; portanto, se você deseja que a criança comece a usar o driver, precisará remapear essa memória. Mas isso é bastante fácil de fazer no espaço dos usuários.

Atualizar: Um buffer na pilha é problemático, não tenho certeza de que você possa torná -lo seguro em geral.

Você não pode marcar DONTFORK, porque seu filho pode estar executando nessa página de pilha quando os bifurres, ou (pior de certa forma), pode fazer uma função retornar mais tarde e atingir a página de pilha não mapeada. (Eu até testei isso, você pode marcar com prazer sua pilha não fork, coisas ruins acontecem quando você bifurca).

A outra maneira de evitar uma vaca é criar um mapeamento compartilhado, mas você não pode mapear sua pilha compartilhada por razões óbvias.

Isso significa que você arrisca uma vaca se você forçar. Mesmo que a criança "apenas" execute, ela ainda poderá tocar na página da pilha e causar uma vaca, levando os pais a receber uma página diferente, o que é ruim.

O único ponto menor a seu favor é que o código usando um buffer na pilha só precisa se preocupar com o código que chama de forking, ou seja. Você não pode usar um buffer na pilha após o retorno da função. Portanto, você só precisa auditar o seu Callees e, se eles nunca formarem, você está seguro, mas isso ainda pode ser inviável e for frágil se o código mudar.

Eu acho que você realmente quer ter toda a memória que é dada ao seu driver para vir de um alocador personalizado no espaço dos usuários. Não deve ser tão intrusivo. O alocador pode mmap Seu dispositivo diretamente, como a outra resposta sugeriu, ou apenas use anônimo mmap, madvise(DONTFORK), e provavelmente mlock() Para evitar trocar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top