Viewing: SECURITY_FIX_PLAN_2026-05-26.md
📄 SECURITY_FIX_PLAN_2026-05-26.md (Read Only) ⬅ To go back
# Security Fix Plan - 2026-05-26

## Contexto

Este plano é baseado na reanálise completa do AdrOS (ZIP CDFA4C08) realizada em 2026-05-25, cruzada com as correções já implementadas em sessões anteriores (documentadas nas memórias do sistema).

## Status dos Itens do Relatório

### Itens JÁ Corrigidos (sessões anteriores)

| ID | Descrição | Status |
|---|---|---|
| A16 | pmm_decref underflow check | ✅ Corrigido |
| A03 | pread/pwrite mode validation | ✅ Corrigido |
| A04 | O_RDONLY\|O_TRUNC rejeição | ✅ Corrigido |
| A14 | truncate/ftruncate VFS integration | ✅ Corrigido |
| A17 | varargs open/openat/fcntl | ✅ Corrigido |
| U04 | execl/execlp varargs | ✅ Corrigido |
| U02 | scanf %s limit (255 bytes) | ✅ Corrigido |
| A18 | shell command substitution | ✅ Corrigido |
| A13 | posix_spawn PID fix | ✅ Corrigido |
| A19 | access() mode implementation | ✅ Corrigido |
| K21 | O_NOFOLLOW implementation | ✅ Corrigido |
| K35 | socket bounce buffers (SMAP) | ✅ Corrigido |
| K14 | SHM permissions | ✅ Corrigido |
| K22 | dlopen per-process isolation | ✅ Corrigido |
| K17 | futex keying (mm, uaddr) | ⚠️ Parcial (addr_space adicionado, mas não robusto) |
| K24 | NX flag in SHM | ⚠️ TODO (desabilitado temporariamente) |

### Itens Tentados mas Causaram Regressões

| ID | Descrição | Status |
|---|---|---|
| K12/K13/K23 | /proc access control (UID checks) | ❌ Requer infraestrutura UID completa |
| K15 | raw socket privilege (UID check) | ❌ Requer infraestrutura UID completa |

### Itens Remanescentes (não abordados)

Abaixo estão os itens que ainda precisam ser corrigidos, organizados por prioridade.

---

## Fase 1: Críticos de Memória e Parser (7 itens)

### C1: ELF Loader p_filesz > p_memsz Validation
**Severidade:** CRÍTICA  
**Arquivos:** `src/arch/x86/elf.c`  
**Problema:** O loader valida `p_offset + p_filesz <= file_len` mas não valida `p_filesz <= p_memsz`. Isso permite escrever além da área mapeada.  
**Solução:**
- Adicionar validação `if (ph[i].p_filesz > ph[i].p_memsz) return -EINVAL;` em `elf32_load_segments`
- Aplicar mesma validação em `elf32_load_shared_lib_at` (dlopen path)
- Validar também `p_align`, `e_phentsize`, `e_phnum`, tipo ELF, endianess e ABI

### C2: NX por Default em Todos os Mapeamentos User
**Severidade:** CRÍTICA  
**Arquivos:** `src/kernel/syscall.c`, `src/arch/x86/elf.c`, `src/kernel/shm.c`, `src/drivers/vbe.c`  
**Problema:** NX não é aplicado por default em mmap anônimo, brk, stack inicial, SHM e fb0_mmap.  
**Solução:**
- `mmap`: Adicionar `VMM_FLAG_NX` por default, remover apenas se `PROT_EXEC` presente
- `brk`: Adicionar `VMM_FLAG_NX` ao criar heap
- `elf32_load_user_from_initrd`: Stack inicial deve ter NX
- `shm_at`: Mapear com NX por default (já documentado como TODO K24)
- `fb0_mmap`: Aplicar NX e respeitar argumento `prot`

### C3: Initrd TAR Parser com Limites de Tamanho
**Severidade:** CRÍTICA  
**Arquivo:** `src/drivers/initrd.c`  
**Problema:** Loop TAR sem limite pelo tamanho do initrd, `tar_is_zero_block` sem validar 512 bytes disponíveis, acesso a header/payload sem bounds checking.  
**Solução:**
- Manter `end = base + size` e validar cada acesso
- Validar `p + TAR_BLOCK <= end` antes de `tar_is_zero_block`
- Validar `p + sizeof(tar_header_t) + payload <= end` antes de ler
- Validar checksum TAR e overflow em `adv`
- Validar `LZ4B_HDR_SIZE + comp_sz <= size`

### C4: pmm_boot.c Multiboot2 Parsing com Cursor/Limit
**Severidade:** CRÍTICA  
**Arquivo:** `src/arch/x86/pmm_boot.c`  
**Problema:** Loops sem validação de `total_size`, `tag->size >= 8`, limite do buffer, nem `mmap->entry_size`.  
**Solução:**
- Portar lógica de cursor/limite de `arch_early_setup.c` para `pmm_boot.c`
- Validar `tag->size >= 8` antes de acessar campos
- Validar `cursor + tag->size <= end` antes de `MB2_TAG_NEXT`
- Validar `mmap->entry_size` e bounds do array de entradas

### C5: VMA Table per Process para mprotect Robusto
**Severidade:** CRÍTICA  
**Arquivos:** `src/kernel/syscall.c`, `include/process.h`  
**Problema:** `mprotect` tem fallback permissivo para endereços não modelados como VMA.  
**Solução:**
- Criar tabela VMA unificada por processo (struct vma_region com start, end, prot)
- Registrar cada mmap, brk, stack, ELF segment na tabela VMA
- `mprotect` deve aceitar apenas ranges completamente cobertos por VMAs
- Remover fallback permissivo

### C6: Ext2 Superblock/GDT Validation
**Severidade:** CRÍTICA  
**Arquivo:** `src/kernel/ext2.c`  
**Problema:** `s_blocks_per_group`, `s_inodes_per_group` podem ser zero; `num_groups` e `gdt_bytes` podem overflowar; validação incompleta.  
**Solução:**
- Validar `s_blocks_per_group > 0` e `s_inodes_per_group > 0`
- Validar overflow em `num_groups = (s_blocks_count - s_first_data_block + s_blocks_per_group - 1) / s_blocks_per_group`
- Validar `gdt_bytes = num_groups * sizeof(ext2_group_desc_t)` não overflow
- Validar `block_size` é potência de 2 e >= 1024
- Validar inode size, GDT blocks e bitmap blocks contra limites do dispositivo

### C7: FAT BPB Validation
**Severidade:** CRÍTICA  
**Arquivo:** `src/kernel/fat.c`  
**Problema:** `total_sectors - (data_lba - partition_lba)` pode underflowar; `sectors_per_cluster` não validado como potência de 2; cadeias de cluster sem limite.  
**Solução:**
- Validar `data_lba >= partition_lba` antes de subtração
- Validar `sectors_per_cluster` é potência de 2 entre 1 e 128
- Validar `fat_size`, `reserved_sectors`, `root_cluster` contra limites do dispositivo
- Limitar caminhada de cluster por `total_clusters` em `fat_extend_chain`/`fat_free_chain`
- Detectar ciclos em cadeias de cluster

---

## Fase 2: Isolamento e Políticas de Segurança (8 itens)

### H1: AIO fd Mode e Read-Only Mount Validation
**Severidade:** ALTA  
**Arquivo:** `src/kernel/syscall.c`  
**Problema:** `syscall_aio_rw_impl` não reutiliza política de read/write/pread/pwrite.  
**Solução:**
- Centralizar validação de modo e mount em helper usado por todas as rotas de I/O
- `aio_write` deve rejeitar fd `O_RDONLY` e mount `MS_RDONLY`
- `aio_read` deve rejeitar fd `O_WRONLY`

### H2: /proc Access Control
**Severidade:** ALTA  
**Arquivo:** `src/kernel/procfs.c`  
**Problema:** Entradas `/proc/dmesg`, `/proc/cmdline`, `/proc/<pid>/status`, `/proc/<pid>/maps`, `/proc/<pid>/cmdline` sem controle de acesso.  
**Solução:**
- Implementar função `proc_may_access(pid, uid)` similar a `ptrace_may_access`
- Redigir endereços em `/proc/<pid>/maps` para não-root
- Implementar hidepid (2 = hide all pids from non-root)
- Usar pin/refcount ou snapshot sob lock para evitar UAF
- **NOTA:** Requer infraestrutura UID completa (ver Fase 4)

### H3: SHM Permissões Completas
**Severidade:** ALTA  
**Arquivo:** `src/kernel/shm.c`  
**Problema:** Falta mode por segmento, grupos, attach read-only, NX default.  
**Solução:**
- Adicionar campo `mode`, `gid` em `struct shm_segment`
- Implementar attach read-only (sem flag SHM_RDONLY no momento)
- Aplicar NX por default (já documentado como TODO K24)
- Modelar uid/gid/mode como SysV/POSIX SHM

### H4: SOCK_RAW Privilege Check
**Severidade:** ALTA  
**Arquivo:** `src/kernel/socket.c`  
**Problema:** `ksocket_create` aceita `SOCK_RAW` sem checar `current_process->euid`.  
**Solução:**
- Adicionar verificação `if (type == SOCK_RAW && current_process->euid != 0) return -EPERM`
- **NOTA:** Requer infraestrutura UID completa (ver Fase 4)

### H5: Futex Robusto com Spinlock e Shared Memory Keying
**Severidade:** ALTA  
**Arquivo:** `src/kernel/syscall.c`  
**Problema:** Tabela global sem lock; futex em memória compartilhada não usa objeto físico/inode+offset.  
**Solução:**
- Adicionar spinlock para proteger tabela global de futex
- Chavear futex privado por `(mm, uaddr)` (já parcialmente feito)
- Chavear futex compartilhado por página física ou inode+offset
- Coordenar wait/wake com scheduler de forma mais robusta

### H6: Remover Fixed VAs (KVA_PHYS_MAP)
**Severidade:** ALTA  
**Arquivos:** `src/hal/x86/mm.c`, `src/drivers/virtio_blk.c`, `include/arch/x86/kernel_va_map.h`  
**Problema:** `hal_mm_map_physical_range` usa sempre `KVA_PHYS_MAP` sem alocador; colisão virtio-blk/E1000 em VAs fixas.  
**Solução:**
- Criar alocador de VA kernel para MMIO/DMA/mapeamentos temporários
- Remover VAs fixas locais (KVA_PHYS_MAP)
- Resolver colisão virtio-blk (0xC0340000) com E1000 (0xC0330000..0xC034FFFF)

### H7: Tmpfs Locking, Quotas e Overflow Validation
**Severidade:** ALTA  
**Arquivo:** `src/kernel/tmpfs.c`  
**Problema:** Sem locks, sem quotas, `new_cap *= 2` pode virar zero, metadados não inicializados.  
**Solução:**
- Adicionar lock por árvore/inode
- Adicionar quota global
- Validar overflow em `new_cap *= 2`
- Inicializar `uid/gid/mode` corretamente

### H8: rumpuser_free Alignment Handling
**Severidade:** ALTA  
**Arquivo:** `src/rump/rumpuser_adros.c`  
**Problema:** Quando `alignment > 16`, `rumpuser_free` chama `kfree(mem)` diretamente em vez do ponteiro bruto.  
**Solução:**
- Armazenar header uniforme para todas as alocações rumpuser
- Recuperar ponteiro bruto antes de chamar `kfree`

---

## Fase 3: Polimento de VFS e Userspace (12 itens)

### M1: VFS Permissions - mode != 0 e execve Bit Check
**Severidade:** MÉDIA  
**Arquivos:** `src/kernel/fs.c`, `src/kernel/syscall.c`  
**Problema:** `vfs_check_permission` retorna sucesso quando `node->mode == 0`; `execve` não verifica bit executável.  
**Solução:**
- Remover caso permissivo quando `mode == 0`
- `execve` deve verificar bit executável do inode (S_IXUSR|S_IXGRP|S_IXOTH)

### M2: POSIX access() Completo
**Severidade:** MÉDIA  
**Arquivo:** `src/kernel/syscall.c`  
**Problema:** `access()` simplificado - `R_OK` assume legível se existir, `X_OK` testa apenas arquivo regular.  
**Solução:**
- Implementar `F_OK/R_OK/W_OK/X_OK` sobre mesma função de permissão
- Usar uid/gid reais em vez de efetivos

### M3: O_NOFOLLOW -ELOOP Return
**Severidade:** MÉDIA  
**Arquivos:** `src/kernel/syscall.c`, `src/kernel/fs.c`  
**Problema:** `vfs_lookup_nofollow` evita seguir symlink mas não retorna erro POSIX claro quando componente final é symlink.  
**Solução:**
- Se `O_NOFOLLOW` presente e componente final for `FS_SYMLINK`, retornar `-ELOOP`

### M4: Truncate Fallback Remoção
**Severidade:** MÉDIA  
**Arquivo:** `src/kernel/fs.c`  
**Problema:** `vfs_truncate` altera apenas `node->length` se backend não implementa truncate.  
**Solução:**
- Sem `i_ops->truncate`, retornar `-ENOSYS`
- Limitar fallback a filesystems explicitamente marcados como seguros (se necessário)

### M5: Socket copy_to_user Ignored Failures
**Severidade:** MÉDIA  
**Arquivo:** `src/kernel/syscall.c`  
**Problema:** `accept`, `recvfrom`, `recvmsg`, `getsockname`, `getpeername` usam `(void)copy_to_user(...)`.  
**Solução:**
- Retornar `-EFAULT` quando copyout obrigatório falhar
- Para campos opcionais, validar ponteiro e tamanho antes

### M6: Overlayfs Wrapper Lifetime/Refcount
**Severidade:** MÉDIA  
**Arquivo:** `src/kernel/overlayfs.c`  
**Problema:** `overlay_wrap_child` aloca wrappers sem caminho claro de liberação.  
**Solução:**
- Introduzir cache/refcount/release para wrappers
- Ou simplificar overlayfs até haver ownership claro

### M7: fb0_mmap Respeitar prot e NX
**Severidade:** MÉDIA  
**Arquivo:** `src/drivers/vbe.c`  
**Problema:** `fb0_mmap` ignora argumento `prot` e sempre mapeia RW sem NX.  
**Solução:**
- Respeitar `PROT_READ/PROT_WRITE`
- Negar execução e aplicar NX

### M8: CSPRNG Implementation
**Severidade:** MÉDIA  
**Arquivos:** `src/kernel/devfs.c`, `include/net/lwipopts.h`, `user/ulibc/src/rand.c`, `src/rump/rumpuser_adros.c`  
**Problema:** `/dev/random`, `/dev/urandom`, lwIP, libc usam RNGs previsíveis.  
**Solução:**
- Implementar CSPRNG de kernel com seed de entropia real (RDRND se disponível, ou TSC + timer)
- Fazer lwIP/devfs/userspace consumirem essa fonte

### M9: mkstemp Retry Multiple Names
**Severidade:** MÉDIA  
**Arquivo:** `user/ulibc/src/stdlib.c`  
**Problema:** `mkstemp` tenta apenas um nome; se colidir, falha.  
**Solução:**
- Após corrigir CSPRNG, fazer `mkstemp` tentar múltiplos nomes (loop com limite)
- Documentar/desencorajar `tmpnam`

### M10: fcntl Varargs em ulibc
**Severidade:** MÉDIA  
**Arquivo:** `user/ulibc/src/unistd.c`  
**Problema:** `fcntl` lê vararg para `F_GETFD` e `F_GETFL` que não exigem argumento.  
**Solução:**
- Ler vararg somente nos comandos que precisam: `F_SETFD`, `F_SETFL`, `F_DUPFD`, `F_DUPFD_CLOEXEC`

### M11: newlib libgloss Varargs
**Severidade:** MÉDIA  
**Arquivo:** `newlib/libgloss/adros/posix_stubs.c`  
**Problema:** `fcntl` e `openat` leem vararg incondicionalmente.  
**Solução:**
- Aplicar mesma correção que ulibc: leitura condicional de vararg

### M12: scanf Field Width e Buffer Size
**Severidade:** MÉDIA  
**Arquivo:** `user/ulibc/src/stdio.c`  
**Problema:** Limite 255 bytes não resolve API - `%s` não sabe tamanho real do buffer.  
**Solução:**
- Respeitar largura de campo (ex: `%255s`)
- Suportar formatos de largura corretamente
- Auditar comandos que usam `%s`

---

## Fase 4: Infraestrutura UID (Pré-requisito para H2, H4)

### UID Infrastructure
**Severidade:** ALTA (pré-requisito)  
**Descrição:** AdrOS é um OS single-user sem infraestrutura de autenticação multi-usuário completa.  
**Solução:**
- Implementar `/etc/passwd` parsing
- Implementar login com autenticação
- UID/EUID inheritance em fork/clone
- Per-process mount namespaces (opcional, mas desejável)
- **Estimativa:** 3-4 dias de trabalho

---

## Fase 5: Low Priority (2 itens)

### L1: Path Truncation - ENAMETOOLONG
**Severidade:** BAIXA  
**Arquivos:** `src/kernel/syscall.c`, `src/kernel/fs.c`  
**Problema:** `path_resolve_user` e `vfs_lookup_parent` truncam strings silenciosamente.  
**Solução:**
- Tratar truncamento como erro em todos os caminhos de path
- Retornar `-ENAMETOOLONG` quando input excede limite

### L2: getlogin/who Session/TTY/utmp
**Severidade:** BAIXA  
**Arquivos:** `user/ulibc/src/unistd.c`, `user/cmds/who/who.c`  
**Problema:** `getlogin()` usa fallback `"root"`, `who` imprime linha fixa.  
**Solução:**
- Conectar login a sessões/TTY/utmp
- Retornar erro quando não houver informação confiável

---

## Itens NÃO Incluídos (Falsos Positivos ou Fora de Escopo)

### sysenter.S (A02)
**Status:** NÃO incluído neste plano  
**Motivo:** Relatório menciona problema mas validação atual (`ECX < 0xC0000000` e `ECX >= 8`) é razoável para ABI sysenter. Melhoria seria mudança de ABI, não correção de bug.

### posix_spawn Kernel Implementation (A13)
**Status:** NÃO incluído neste plano  
**Motivo:** Wrapper userspace foi corrigido. Implementação kernel como fork+execve é padrão POSIX e funciona corretamente.

---

## Ordem de Execução Sugerida

1. **Fase 1 (Críticos):** 7 itens - estimativa 10-14 dias
2. **Fase 4 (UID Infrastructure):** Pré-requisito para H2, H4 - estimativa 3-4 dias
3. **Fase 2 (Isolamento):** 8 itens (H1, H3, H5, H6, H7, H8 primeiro; H2, H4 após UID) - estimativa 8-10 dias
4. **Fase 3 (Polimento):** 12 itens - estimativa 6-8 dias
5. **Fase 5 (Low):** 2 itens - estimativa 1-2 dias

**Total estimado:** 28-38 dias de trabalho focado

---

## Notas Importantes

- Este plano assume que os itens já corrigidos (sessões anteriores) permanecem estáveis.
- Itens marcados como "REQUIRES UID INFRASTRUCTURE" devem ser implementados após Fase 4.
- Testes de regressão devem ser executados após cada fase completa.
- Alguns itens (como VMA table per process) são arquiteturais e podem ter impactos amplos.