syscall - o que significa sistema operacional linux



Por que as chamadas do sistema Linux x86-64 modificam o RCX e o que o valor significa? (1)

Estou tentando alocar alguma memória no linux com o sys_brk . Aqui está o que eu tentei:

BYTES_TO_ALLOCATE equ 0x08

section .text
    global _start

_start:
    mov rax, 12
    mov rdi, BYTES_TO_ALLOCATE
    syscall

    mov rax, 60
    syscall

A coisa é conforme a convenção de chamada do linux Eu esperava que o valor de retorno estivesse no registrador de rax (ponteiro para a memória alocada). Eu corri isso no gdb e depois de fazer sys_brk notei o seguinte conteúdo do registro

Antes do syscall

rax            0xc      12
rbx            0x0      0
rcx            0x0      0
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

Depois syscall

rax            0x401000 4198400
rbx            0x0      0
rcx            0x40008c 4194444 ; <---- What does this value mean?
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

Eu não entendo muito bem o valor no registro rcx neste caso. Qual deles usar como um ponteiro para o começo de 8 bytes que eu aloquei com sys_brk ?

https://ffff65535.com


O valor de retorno da chamada do sistema está em rax , como sempre. Veja Quais são as convenções de chamada para chamadas do sistema UNIX e Linux em i386 e x86-64 .

Note que sys_brk tem uma interface ligeiramente diferente das funções POSIX do brk / sbrk ; veja a seção C / diferenças do kernel da página man do Linux brk(2) . Especificamente, o sys_brk Linux define a quebra do programa ; o arg e o valor de retorno são ambos ponteiros. Consulte Uso de chamada do assembly x86 brk () . Essa resposta precisa de votos positivos, porque é a única boa nessa questão.

A outra parte interessante da sua pergunta é:

Eu não entendo muito bem o valor no registro rcx neste caso

Você está vendo a mecânica de como as instruções sysret / sysret são projetadas para permitir que o kernel retome a execução do espaço do usuário, mas ainda seja rápido.

syscall não faz nenhum carregamento ou armazenamento, apenas modifica registros. Em vez de usar registradores especiais para salvar um endereço de retorno, ele simplesmente usa registradores inteiros regulares.

Não é uma coincidência que RCX=RIP e R11=RFLAGS após o kernel retornar ao seu código de espaço do usuário . A única maneira de não ser esse o caso é se uma ptrace sistema ptrace modificou o valor rcx ou rcx salvo do processo enquanto ele estava dentro do kernel. ( ptrace é a chamada do sistema gdb usa). Nesse caso, o Linux usaria iret vez de sysret para retornar ao espaço do usuário, porque o iret caso geral mais lento pode fazer isso. (Veja o que acontece se você usar o int de 0x-bit 0x80 Linux ABI em código de 64 bits? Para alguns passos dos pontos de entrada de chamada de sistema do Linux. Principalmente os pontos de entrada de processos de 32 bits, não de syscall em um 64 processo de bits, no entanto.

Em vez de empurrar um endereço de retorno para a pilha do kernel (como int 0x80 ), syscall :

  • define RCX = RIP, R11 = RFLAGS (então é impossível que o kernel veja os valores originais desses regs antes de você executar o syscall ).
  • mascara RFLAGS com uma máscara pré-configurada a partir de um registrador de configuração (o IA32_FMASK MSR). Isso permite que o kernel desative as interrupções (IF) até que ele swapgs e configure rsp para apontar para a pilha do kernel. Mesmo com cli como a primeira instrução no ponto de entrada, haveria uma janela de vulnerabilidade. Você também obtém cld gratuitamente mascarando DF para que os movimentos de rep movs / stos rep movs mesmo que o espaço do usuário tenha usado std .

    Curiosidade: O primeiro projeto swapgs / swapgs proposto pela AMD não mascarou o RFLAGS, mas o mudou após o feedback dos desenvolvedores do kernel na lista de discussão do amd64 (em ~ 2000, alguns anos antes do primeiro silício).

  • salta para o ponto de entrada syscall configurado (configuração CS: RIP = IA32_LSTAR ). O antigo valor do CS não é salvo em nenhum lugar, eu acho.

  • Ele não faz mais nada, o kernel tem que usar swapgs para obter acesso a um bloco de informações onde ele salvou o ponteiro da pilha do kernel, porque o rsp ainda tem seu valor do espaço do usuário.

Portanto, o design do syscall requer uma ABI de chamada do sistema que bloqueia os registradores, e é por isso que os valores são o que são.





system-calls