Gerenciamento de Memória Geração de Código (Registros de Ativação) Compiladores II A memória é afetada pelo compilador e/ou código fonte: Código objeto gerado Espaço para variáveis globais Ativação de procedimentos Alocação dinâmica Von Neumann dados/código na mesma memória Código objeto Fluxo de controle if-else, while, for, switch expressões, atribuições parte da operação de chamada de funções Espaço para globais Reserva de espaço em segmento de dados (DS no 8086) Declaração de atributos de classe ou instância (JVM) 1 Alocação dinâmica Com (des)alocação explícita malloc, sbrk, free Sem controle explícito Linguagens funcionais Java Necessita coleta de lixo (garbage collection) Registros de Ativação Funções em Ling. Prog. imperativas possuem variáveis locais Criadas no inicio da função Destruídas quando a função acaba Cada chamada (invocação) de uma função tem sua instância própria de variáveis locais. Chamadas recursivas forçam a existência simultânea de várias instâncias As funções retornam apenas depois do término de todas as funções chamadas por ela, ou seja, um comportamento last-infirst-out (LIFO). A pilha do sistema é a estrutura LIFO usada para guardar todas as instanciações A parte da pilha utilizada na chamada de uma função é denominada stack frame ou registro de ativação (activation record). Pilha do sistema Usada para armazenar variáveis locais de todas as instâncias Em geral, implementada como um array estático (muito grande) e crescendo para as posições iniciais da memória. Push(r1) Stack_pointer - -; M[stack_pointer]=r1; r1=Pop(); r1 = M[stack_pointer]; stack_pointer++; Registro de ativação e pilha do sistema Os registros de ativação são estruturas complexas, pop/push não são suficientes O stack frame é normalmente indexado à uma posição da pilha e tratado como um array Assim, podemos fazer um push/pop de todo registro de ativação 2 Registro de ativação e pilha do sistema Ex. 8086 e família: bp, sp (ebp,esp) Frame Pointer é chamado pela intel de Base Pointer Exemplo Suponha que f(a1,a2) chama g(b1,b2,b3) Originalmente temos para f Registro de ativação e pilha do sistema “Macro” pop/push Guardar e restaurar o bp, base registro ativação, também chamado de push bp, [bp-sp] pop bp, [bp+1] Exemplo Os argumentos de g são empilhados (ainda no stack frame de f) 3 Exemplo Cria-se o registro de g Organização do registro O compilador pode utilizar qualquer esquema que lhe convier Os fabricantes de CPU (ou SO) fornecem padrões ou esquemas para os compiladores Calling conventions Se todos compiladores utilizarem o mesmo esquema, as funcões de um podem ser utilizadas pelo outro Essencial para utilização de bibliotecas do SO Passagem de Parâmetros Registradores são mais rápidos do que memória O compilador deve usar os registradores o máximo possível Ao invés de emplihar a1,a2,..,an, colocar a1,a2 ,a3,a4 em registradores e a5,a6 ,.., an na pilha Registro de ativação Registradores Alguns parâmetros Valor de retorno Variáveis locais Temporários Stack frame Variáveis passadas por referência Variáveis de rotinas aninhadas Variáveis cujo valor é muito grande (e.g.double) Variáveis do tipo array 4 Registro de ativação Em geral, o compilador inicialmente atribui uma variável ao stack frame Se possível passa a variável p/ registrador Register allocation Processo de atribuição de variáveis à registradores Calling convention Quem salva os registradores: • Função Chamadora • Função Chamada Buffer overflow attack O stack parace da seguinte forma quando myFunction é chamada: Buffer overflow attack Explora um problema comum em linguagens (ou suas implementações) Sem teste de limites para acceso em arrays void myFunction(char *str) { void main(){ char bufferB[16]; char bufferA[256]; } } Buffer overflow attack Como existem, potencialmente, 256 bytes a serem copiados em bufferA Previous stack content Previous stack content bufferA bufferA Return address Return address OS data bufferB OS data 16 bytes strcpy(bufferB, str); myFunction(bufferA); 256 bytes bufferB 5 Buffer overflow attack Se bem escolhido, o valor que será escrito no endereço de retorno pode levar a execução de um código mal intencionado Pode, por exemplo, executar iniciar um shell com parâmetro indicando um programa e.g, sh /home/user/bad-guy/kidnap-code Em geral, programas executando em root O código a ser executado normalmente já está no sistema Buffer overflow attack O endereço de retorno normalmente aponta para dentro da área utilizada na chamada da função explorad Previous stack content bufferA Return address OS data 256 bytes bufferB Buffer overflow attack Possíveis soluções Checar limite de accesso em arrays Tornar buffers não-executáveis Incluir um “canary” em uma posição stack próxima ao endereço de retorno Antes de retornar, este valor é verificado. Se for inválido, o programa aborta Pode ser um checksum de um valor com um endereço de retorno, ou seja, preservar esta palavra e alterar o endereço de retorno também não ajuda 6