Segmentation Fault — Como Resolver em Zig
O Que Este Erro Significa
O Segmentation Fault (segfault ou SIGSEGV) ocorre quando o programa tenta acessar uma região de memória que não lhe pertence ou que não está mapeada. O sistema operacional detecta o acesso ilegal e encerra o processo com um sinal SIGSEGV. Em Zig, apesar do sistema de segurança em builds Debug, segfaults ainda podem ocorrer em determinadas situações, especialmente quando se usa ponteiros brutos, código @cImport-ado ou builds ReleaseFast.
A mensagem típica é:
Segmentation fault at address 0x0
Ou o programa simplesmente é encerrado pelo sistema operacional.
Causas Comuns
1. Desreferenciar Ponteiro Null
pub fn main() void {
var ptr: ?*u32 = null;
// Converte optional para ponteiro bruto (bypass de segurança)
const raw: *u32 = @ptrFromInt(0);
_ = raw.*; // SEGFAULT: endereço 0x0
_ = ptr;
}
2. Uso de Ponteiro Após Free (Dangling Pointer)
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const dados = try allocator.alloc(u8, 100);
allocator.free(dados);
// SEGFAULT: memória já foi liberada
dados[0] = 42;
}
3. Acesso a Memória Não Inicializada
pub fn main() void {
var buffer: [100]u8 = undefined;
// Em ReleaseFast, undefined pode ser qualquer valor
const ptr: *u32 = @ptrCast(@alignCast(&buffer));
_ = ptr.*;
// Pode causar SEGFAULT dependendo do conteúdo de buffer
}
4. Interação com Código C Inseguro
const c = @cImport({
@cInclude("stdlib.h");
@cInclude("string.h");
});
pub fn main() void {
const ptr = c.malloc(100);
c.free(ptr);
// SEGFAULT: uso após free via API C
_ = c.memset(ptr, 0, 100);
}
5. Stack Overflow Que Se Manifesta como Segfault
fn recursaoInfinita() void {
recursaoInfinita(); // Estoura a stack
// Pode aparecer como segfault ao invés de stack overflow
}
pub fn main() void {
recursaoInfinita();
}
Como Corrigir
Solucao 1: Sempre Verificar Optionals Antes de Usar
const std = @import("std");
pub fn main() void {
var ptr: ?*u32 = null;
if (ptr) |p| {
std.debug.print("valor: {}\n", .{p.*});
} else {
std.debug.print("Ponteiro é null\n", .{});
}
}
Solucao 2: Evitar Ponteiros Após Free
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const dados = try allocator.alloc(u8, 100);
defer allocator.free(dados); // defer garante free no momento certo
// Usa dados enquanto são válidos
dados[0] = 42;
dados[99] = 255;
// free acontece aqui automaticamente via defer
}
Solucao 3: Inicializar Memória Explicitamente
const std = @import("std");
pub fn main() void {
// Use valor concreto ao invés de undefined
var buffer = std.mem.zeroes([100]u8);
// Ou use @memset
@memset(&buffer, 0);
// Agora é seguro usar buffer
buffer[0] = 42;
}
Solucao 4: Usar Slices ao Invés de Ponteiros Brutos
const std = @import("std");
fn processar(dados: []u8) void {
// Slices têm verificação de limites em Debug
for (dados) |*byte| {
byte.* = 0;
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const buffer = try allocator.alloc(u8, 100);
defer allocator.free(buffer);
processar(buffer);
}
Solucao 5: Compilar em Modo Debug para Diagnóstico
Compile com modo Debug (padrão) para obter mensagens de erro melhores:
zig build # Modo Debug por padrão
zig build-exe main.zig # Também Debug por padrão
Em modo Debug, muitos segfaults são transformados em panics com stack trace legível:
thread 1 panic: index out of bounds
/home/user/main.zig:12:15: 0x20401a in main
Solucao 6: Wrapping Seguro de Código C
const c = @cImport({
@cInclude("stdlib.h");
});
fn alocarSeguro(tamanho: usize) !*anyopaque {
const ptr = c.malloc(tamanho);
if (ptr == null) return error.OutOfMemory;
return ptr.?;
}
fn liberarSeguro(ptr: *anyopaque) void {
c.free(ptr);
}
Ferramentas de Diagnóstico
Usando address sanitizer (ASan)
Zig pode compilar com sanitizers para detectar problemas de memória:
zig build-exe -fsanitize=address main.zig
Usando Valgrind
zig build-exe main.zig
valgrind ./main
Stack Trace em Debug
Em builds Debug, Zig fornece stack traces detalhados. Sempre desenvolva em modo Debug primeiro e só mude para ReleaseFast/ReleaseSafe quando necessário.
Diferenças entre Modos de Build
| Modo | Verificações de Segurança | Segfault Detectado Como |
|---|---|---|
| Debug | Todas | Panic com stack trace |
| ReleaseSafe | Todas | Panic com stack trace |
| ReleaseFast | Nenhuma | Segfault bruto |
| ReleaseSmall | Nenhuma | Segfault bruto |
Erros Relacionados
- Use after free — Uso de memória após liberação
- Stack overflow — Estouro da pilha de execução
- Index out of bounds — Acesso fora dos limites
- Null unwrap — Desempacotar optional null