---
title: "Use After Free — Como Resolver em Zig"
url: "https://ziglang.com.br/erros/use-after-free-como-resolver-em-zig/"
markdown_url: "https://ziglang.com.br/erros/use-after-free-como-resolver-em-zig.MD"
description: "Entenda o erro Use After Free em Zig, que ocorre ao acessar memória já liberada. Veja como evitar dangling pointers e garantir segurança de memória."
date: "2026-02-21"
author: "Zig Brasil"
---

# Use After Free — Como Resolver em Zig

Entenda o erro Use After Free em Zig, que ocorre ao acessar memória já liberada. Veja como evitar dangling pointers e garantir segurança de memória.


# Use After Free — Como Resolver em Zig

## O Que Este Erro Significa

O erro Use After Free ocorre quando o programa tenta acessar (ler ou escrever) uma região de memória que já foi devolvida ao alocador com `free`. Após a liberação, aquela região pode ser reutilizada para outras alocações ou desmapeada pelo sistema operacional. Acessar essa memória resulta em comportamento indefinido: pode funcionar por acaso, retornar lixo, ou causar um Segmentation Fault.

Em Zig, o `GeneralPurposeAllocator` em modo Debug detecta este erro e emite um panic:

```
thread 1 panic: Use of freed memory
```

O `GeneralPurposeAllocator` preenche a memória liberada com um padrão especial (`0xaa`) justamente para facilitar a detecção deste problema.

## Causas Comuns

### 1. Acessar Slice Após Free

```zig
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);

    // ERRO: Use After Free — dados já foi liberado
    dados[0] = 42;
}
```

### 2. Guardar Referência a Memória Liberada

```zig
const std = @import("std");

var referencia_global: []u8 = undefined;

fn criarDados(allocator: std.mem.Allocator) !void {
    const dados = try allocator.alloc(u8, 100);
    referencia_global = dados;
    allocator.free(dados);
    // referencia_global agora aponta para memória liberada!
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    try criarDados(gpa.allocator());

    // ERRO: Use After Free via referência global
    referencia_global[0] = 42;
}
```

### 3. Retornar Ponteiro para Memória Liberada

```zig
const std = @import("std");

fn obterBuffer(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 256);
    // Faz algum processamento...
    @memset(buffer, 0);

    // BUG: libera antes de retornar
    allocator.free(buffer);
    return buffer; // Retorna ponteiro inválido!
}
```

### 4. Invalidação por Realocação de ArrayList

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var lista = std.ArrayList(u32).init(allocator);
    defer lista.deinit();

    try lista.append(1);
    try lista.append(2);

    // Salva ponteiro para os itens internos
    const ptr = &lista.items[0];

    // Muitos appends podem causar realocação interna
    var i: u32 = 0;
    while (i < 1000) : (i += 1) {
        try lista.append(i);
    }

    // ERRO: ptr pode ser inválido após realocação
    _ = ptr.*;
}
```

### 5. Captura de Slice em Closure/Callback

```zig
const std = @import("std");

fn registrarCallback(dados: []const u8) *const fn () void {
    // dados pode ser liberado antes do callback ser executado
    return struct {
        fn callback() void {
            // ERRO: dados pode já ter sido liberado
            _ = dados;
        }
    }.callback;
}
```

## Como Corrigir

### Solucao 1: Usar defer para Gerenciar Ciclo de Vida

```zig
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); // Free acontece ao final do escopo

    // Usa dados com segurança durante todo o escopo
    dados[0] = 42;
    dados[99] = 255;
    // Ao sair de main, defer libera a memória
}
```

### Solucao 2: Transferência Clara de Propriedade

```zig
const std = @import("std");

/// Quem chama esta função é responsável por liberar o resultado
fn criarDados(allocator: std.mem.Allocator) ![]u8 {
    const dados = try allocator.alloc(u8, 100);
    @memset(dados, 0);
    return dados; // Transfere propriedade para quem chamou
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const dados = try criarDados(allocator);
    defer allocator.free(dados); // Quem recebeu é responsável pelo free

    dados[0] = 42;
}
```

### Solucao 3: Evitar Ponteiros Internos de ArrayList

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var lista = std.ArrayList(u32).init(allocator);
    defer lista.deinit();

    // Pré-aloca para evitar realocações
    try lista.ensureTotalCapacity(1000);

    var i: u32 = 0;
    while (i < 1000) : (i += 1) {
        lista.appendAssumeCapacity(i);
    }

    // Agora é seguro acessar por índice
    const valor = lista.items[0];
    _ = valor;
}
```

### Solucao 4: Usar ArenaAllocator para Ciclo de Vida Compartilhado

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();

    const allocator = arena.allocator();

    // Todas as alocações vivem até arena.deinit()
    const a = try allocator.alloc(u8, 100);
    const b = try allocator.alloc(u8, 200);

    // Seguro: ambos são válidos até o defer arena.deinit()
    a[0] = 1;
    b[0] = 2;
}
```

### Solucao 5: Copiar Dados Quando Necessário

```zig
const std = @import("std");

fn copiarTexto(allocator: std.mem.Allocator, original: []const u8) ![]u8 {
    const copia = try allocator.alloc(u8, original.len);
    @memcpy(copia, original);
    return copia; // Retorna cópia independente
}
```

## Detecção com GeneralPurposeAllocator

O GPA detecta use-after-free automaticamente em builds Debug:

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const buf = try allocator.alloc(u8, 10);
    allocator.free(buf);

    // O GPA detecta isso e emite panic com stack trace
    buf[0] = 1;
}
```

## Regras de Ouro

1. **Quem aloca, libera** — ou documente explicitamente a transferência de propriedade.
2. **Use `defer` imediatamente após alocar** para garantir que o free aconteça.
3. **Nunca guarde ponteiros para memória que outro código pode liberar.**
4. **Evite ponteiros internos** para estruturas que podem realocar (como ArrayList).
5. **Use ArenaAllocator** quando múltiplas alocações compartilham o mesmo ciclo de vida.

Em linguagens com gerenciamento automático de memória, use-after-free simplesmente não existe. O <a href="https://rustlang.com.br/artigos/rust-ownership-borrowing/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">borrow checker de Rust elimina esse bug em tempo de compilação</a>, tornando-o uma das principais vantagens de segurança sobre C e Zig.

## Erros Relacionados

- [Double free](/erros/erro-double-free/) — Liberar a mesma memória duas vezes
- [Segmentation fault](/erros/erro-segmentation-fault/) — Acesso a memória inválida
- [Memory leak detected](/erros/erro-allocator-leak-detected/) — Memória nunca liberada
- [Invalid free](/erros/erro-allocator-invalid-free/) — Liberar ponteiro inválido

## Links Úteis

- [Documentação oficial do Zig — Allocators](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
- [Receitas sobre gerenciamento de memória](/receitas/)
- [Tutorial sobre ponteiros e slices](/tutoriais/)
