# A reutilização de PIDs corrompe a atribuição de sessões de agentes

Um PID isolado serve para uma tela de status. Ele não serve como identidade por trás de uma aprovação, de um registro de auditoria ou de uma sessão de agente que contém credenciais. O sistema operacional recicla IDs de processos. Quando um sistema de agentes trata um número reutilizado como se fosse o mesmo ator, pode associar um processo posterior à aprovação de um processo anterior.

Esse problema não exige um invasor com acesso ao kernel nem uma condição de corrida exótica. Ele aparece quando um agente de curta duração termina, uma máquina ocupada cria processos suficientes para reutilizar seu PID e algum componente reconecta uma chamada, atualiza uma tela ou resolve uma linha antiga de auditoria pelo PID. O processo posterior pode ser um software comum. A atribuição ainda estará errada, e um sistema de aprovação que age com base em uma atribuição incorreta já perdeu o motivo de existir.

A correção não é complicada, mas exige mudar o vocabulário. Pare de chamar um PID de identidade de processo. Armazene um registro capturado do processo que contenha o PID, o horário de início, os dados de assinatura de código e a proveniência do processo pai, coletados no momento em que você tomou a decisão de autorização. Depois, faça cada comparação posterior provar que o processo ativo ainda corresponde a esse registro.

## Um PID identifica uma posição, não a vida útil de um processo

Um ID de processo identifica uma entrada de processo atualmente alocada no kernel. Ele não nomeia um programa para sempre nem oferece uma garantia de exclusividade global ao longo do tempo. Quando o processo termina, seu PID pode ser reutilizado.

Essa distinção parece minuciosa até que um sistema permita que uma aprovação anterior continue válida. Imagine que um cliente de agente seja iniciado como PID 4812, peça para usar uma credencial de implantação e receba a aprovação do usuário. O cliente termina. Mais tarde, outro processo recebe o PID 4812. Se o seu gateway perguntar «o PID 4812 tem uma sessão aprovada?», poderá recuperar a aprovação antiga e associá-la ao novo processo.

A parte perigosa é que os processos antigo e novo não precisam se sobrepor. Muitas implementações cometem esse erro em uma junção de banco de dados, em uma consulta ao cache ou em um trabalho tardio de enriquecimento. Elas salvam uma aprovação como `pid = 4812`, salvam as chamadas da mesma forma e confiam que uma consulta posterior terá o mesmo significado de antes. Não terá.

Um PID ainda pode ser útil em um registro voltado para pessoas, porque ajuda um operador a inspecionar um sistema ativo. Mantenha-o. Apenas não faça dele a identidade principal usada para autorização ou atribuição.

Há uma segunda armadilha: `fork` e `exec` complicam as explicações simplistas. Um fork cria um novo filho com um PID diferente. Um exec substitui a imagem do programa em um processo existente, mantendo o PID. Se você aprovar um executor de shell e ele fizer exec de outro binário, o PID continuará familiar, mas o código que fará a próxima solicitação poderá ser diferente. Um projeto de identidade precisa considerar tanto a reutilização do PID depois do encerramento quanto a substituição do programa dentro de um processo que continua ativo.

## Capture um registro do processo quando a sessão começar

Um registro de sessão confiável é um instantâneo obtido na fronteira de segurança, não uma coleção de fatos reconstruída depois. Capture-o quando o processo do agente pedir pela primeira vez a criação de uma sessão, antes de exibir uma aprovação que descreva o solicitante.

Para um gateway de agentes no macOS, eu armazenaria estes campos como um único instantâneo imutável do processo:

- PID e ID efetivo do usuário.
- Horário de início do processo, com segundos e microssegundos quando a plataforma os fornecer.
- Caminho do executável observado no momento da captura.
- Identificador de assinatura, contexto da equipe ou do assinante e hash do diretório de código quando disponíveis.
- Instantâneo do processo pai, incluindo seu próprio PID e horário de início.

O horário de início transforma um PID em uma referência específica de uma vida útil. Uma tupla prática seria:

```text
process_instance = (
  pid = 4812,
  start_time = 2026-07-22T14:03:18.482911Z,
  euid = 501
)
```

Essa tupla responde à pergunta específica «Esta é a mesma vida útil de processo do kernel que vi antes?». Ela não responde se o processo é confiável. O registro de assinatura responde a outra pergunta: «Que código o macOS validou quando inspecionei este processo?». O instantâneo do pai responde a mais uma: «O que criou este processo, conforme observado quando a sessão começou?»

Não reduza essas perguntas a um único rótulo textual, como `Claude Code (PID 4812)`. Esse rótulo pode funcionar em um cartão de aprovação, mas elimina os fatos necessários quando um revisor pergunta por que determinada sessão foi autorizada.

No macOS, um inspetor de processos pode usar `proc_pidinfo` com `PROC_PIDTBSDINFO` para obter `proc_bsdinfo`, que inclui `pbi_start_tvsec` e `pbi_start_tvusec`. A Apple também expõe `start_time` do processo em seu modelo de processos do Endpoint Security. O importante não é qual API você escolhe. O importante é capturar o horário de início no momento da decisão e compará-lo depois.

Um auxiliar C específico para a parte da vida útil pode ser assim:

```c
#include <libproc.h>
#include <sys/proc_info.h>
#include <cstdio.h>

int read_process_lifetime(pid_t pid) {
    struct proc_bsdinfo info = {0};
    int size = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0,
                            &info, sizeof(info));
    if (size != sizeof(info)) {
        return -1;
    }

    printf("pid=%d start=%lld.%06d parent=%d uid=%d\n",
           info.pbi_pid,
           info.pbi_start_tvsec,
           info.pbi_start_tvusec,
           info.pbi_ppid,
           info.pbi_uid);
    return 0;
}
```

O formato da saída importa mais do que a linguagem usada:

```text
pid=4812 start=1784738598.482911 parent=4760 uid=501
```

Se uma leitura posterior retornar o mesmo PID com um horário de início diferente, você está diante de outro processo. Negue a correspondência da sessão, mesmo que todas as camadas de conveniência queiram tratar o número como familiar.

## A assinatura de código descreve o código, não uma instância em execução

Os dados de assinatura de código acrescentam uma proveniência útil, mas não se transformam magicamente em uma identidade de processo. Um identificador de assinatura pode ser compartilhado por todas as versões lançadas de um aplicativo. Um identificador de equipe identifica uma organização assinante, não um executável específico. Um hash do diretório de código é mais específico para o conteúdo assinado, mas muitas instâncias simultâneas do mesmo executável ainda compartilharão esse hash.

A documentação de assinatura de código da Apple faz uma distinção importante que os produtos de segurança costumam ignorar. Um identificador de assinatura pode ser reivindicado por vários assinantes. Por isso, a Apple recomenda verificar a categoria de validação e, para código que não é da Apple, também o identificador da equipe. A nota técnica TN3127 da Apple explica ainda que um requisito designado combina um identificador com requisitos de assinatura para estabelecer a identidade do código entre atualizações.

Esse é o modelo mental correto: os dados de assinatura informam qual executável o macOS validou e se ele atende ao seu requisito de confiança. Eles não informam se este é o mesmo processo que o usuário aprovou cinco minutos atrás.

Para autorizar sessões, use as duas camadas:

```text
same_process_lifetime:
  pid, start_time, euid all match the captured record

same_expected_code:
  captured signing requirement still validates for the live process

same_session:
  the session token refers to this captured process record, not only its PID
```

Não compare apenas um nome de exibição, um identificador de pacote ou um caminho. Os caminhos mudam. Ferramentas de linha de comando sem assinatura podem ter propriedades de identidade fracas ou válidas apenas localmente. Um usuário também pode executar uma cópia de uma ferramenta conhecida a partir de outro local. A interface de aprovação pode mostrar um rótulo amigável, mas o código de autorização deve preservar os dados de assinatura avaliados que deram origem a esse rótulo.

Uma recomendação ruim comum diz para autorizar apenas pelo identificador de assinatura, porque ele permanece estável entre atualizações do aplicativo. Ela é popular porque a continuidade entre atualizações parece conveniente. Está errada para uma aprovação por execução. Se uma pessoa aprovou um processo de agente em execução, uma inicialização posterior do mesmo programa assinado é uma nova execução e deve receber uma nova decisão de sessão. A identidade estável do código é um motivo para tornar o cartão de aprovação fácil de entender. Ela não dá permissão para estender silenciosamente uma aprovação de uma execução a todas as instâncias futuras.

## O PID do pai só é evidência quando sua vida útil é preservada

Os dados do processo pai ajudam os revisores a entender como um agente foi iniciado. Eles podem distinguir um cliente iniciado no terminal de um processo iniciado por um editor, um agendador ou outro agente. Mas `ppid = 4760` tem o mesmo problema de reutilização que o PID do filho.

O padrão errado é fácil de identificar:

```text
session.agent_pid = 4812
session.parent_pid = 4760
```

Três horas depois, um visualizador de auditoria resolve o PID 4760 para um processo que tem esse número e o apresenta como o pai da sessão. O pai original pode ter terminado há muito tempo. Outro processo agora ocupa o 4760. A página de auditoria transformou uma afirmação histórica em uma consulta ao sistema ativo e reescreveu a história silenciosamente.

Capture o instantâneo do pai junto com o instantâneo do filho:

```text
parent_instance = (
  pid = 4760,
  start_time = 2026-07-22T14:01:02.117604Z,
  euid = 501,
  executable_path = "/usr/bin/login",
  signing_requirement = "captured evaluation",
  relationship = "observed_parent_at_session_open"
)
```

Esse último campo de relação parece banal, mas evita muita linguagem inadequada. O registro do pai significa «este era o pai direto quando o sistema observou o filho». Ele não significa «este pai autorizou o filho», «este pai será o dono do filho para sempre» ou «este continua sendo o pai atual». Essas afirmações exigem evidências separadas.

Se você tiver eventos de processo do Endpoint Security, `es_process_t` da Apple fornece o PID do pai original e expõe `parent_audit_token` e `responsible_audit_token`, além do horário de início e dos dados de assinatura. Os tokens de auditoria são preferíveis aos PIDs simples quando a API os fornece, porque preservam mais contexto. Ainda assim, trate o objeto de processo de um evento como uma observação capturada. Não o substitua por uma consulta posterior ao PID e diga que são equivalentes.

Para sistemas sem Endpoint Security, use as melhores APIs de processo disponíveis, capture rapidamente e torne a incerteza visível. O pai pode terminar entre a leitura do filho e a leitura do pai. Não invente certeza nessa condição de corrida. Marque o pai como indisponível ou parcial, retenha a identidade do filho que você conseguiu capturar e evite afirmar uma relação com o pai que não pôde observar.

## Revalide antes que uma sessão aprovada execute uma ação

A aprovação de uma sessão exige dois momentos de tratamento da identidade: a captura quando a sessão é aberta e a revalidação quando a sessão usa a autoridade. A captura protege o registro de auditoria. A revalidação protege a próxima ação.

Imagine esta sequência:

1. Um processo de agente abre uma sessão, e você captura o PID 4812, o horário de início, os dados do assinante e os detalhes do pai.
2. Uma pessoa aprova exatamente essa execução do agente.
3. O processo termina enquanto o token da sessão permanece na memória ou em uma conexão IPC local.
4. Um processo posterior recebe o PID 4812 e apresenta um token antigo ou alcança uma associação antiga por causa de um erro.
5. Seu gateway verifica o processo ativo antes de executar uma chamada que usa credenciais.

Na verificação final, o PID pode coincidir. O horário de início não coincidirá. Essa diferença deve encerrar a sessão. Não corrija o registro com o novo horário, não crie uma sessão substituta silenciosamente e não atribua a ação ao processo anterior.

A verificação também deve falhar de forma segura quando a inspeção falhar. Se o seu código não consegue ler as informações do processo porque ele terminou, as permissões mudaram ou o sistema operacional retornou dados incompletos, não pode estabelecer que o solicitante é o processo aprovado. A resposta correta é exigir uma nova sessão.

Mantenha a comparação restrita e literal. Não use correspondência flexível como «mesmo nome do comando», «mesmo diretório de trabalho» ou «mesma janela do terminal». Esses campos ajudam uma pessoa a entender o contexto, mas não resistem à ambiguidade acidental ou criada por um invasor. Um processo pode mudar seu diretório de trabalho, e dois processos sem relação podem escolher o mesmo nome de comando.

O lugar certo para armazenar em cache é a decisão de autorização vinculada à instância de processo capturada. O lugar errado é um mapa de PID para estado de aprovação. Um mapa indexado pelo PID passará nos testes em um laptop tranquilo e falhará sob grande rotatividade de processos, o que torna o problema especialmente difícil de diagnosticar depois que chega aos usuários.

## Modele as sessões como eventos imutáveis, não como linhas mutáveis de processos

Um diário de sessões deve preservar o que o gateway observou em cada momento. Uma linha mutável que sempre diga «PID 4812 está ativo» não consegue responder se seus campos vieram do agente original, de um processo substituto ou de uma atualização tardia em segundo plano.

Use registros de eventos somente para acréscimo, com referências explícitas. A estrutura pode ser simples:

```json
{
  "event_type": "session_authorized",
  "session_id": "sess_7d9f",
  "process_instance": {
    "pid": 4812,
    "start_time": "2026-07-22T14:03:18.482911Z",
    "euid": 501,
    "signing_id": "com.example.agent",
    "team_id": "A1B2C3D4E5",
    "cdhash": "captured-code-directory-hash"
  },
  "parent_instance": {
    "pid": 4760,
    "start_time": "2026-07-22T14:01:02.117604Z"
  },
  "approval": {
    "scope": "this process run",
    "decision": "approved"
  }
}
```

O evento da próxima chamada faz referência a `sess_7d9f` e registra seu próprio horário, o canal solicitado, o alvo da ação e o resultado. Ele não copia apenas `pid: 4812` esperando que um analista consiga reconstruir o restante. Se a revalidação falhar, escreva um evento de negação separado com a diferença observada.

```json
{
  "event_type": "action_denied",
  "session_id": "sess_7d9f",
  "reason": "process_start_time_mismatch",
  "captured_pid": 4812,
  "captured_start_time": "2026-07-22T14:03:18.482911Z",
  "observed_start_time": "2026-07-22T15:47:09.031882Z"
}
```

Isso dá ao revisor algo concreto: o número do processo foi reutilizado, o gateway reconheceu a diferença e negou a solicitação. Sem os dois horários, o evento apenas diz que algo falhou. Isso não basta quando é preciso distinguir um defeito de software de uma tentativa hostil de herdar autoridade.

A separação do Sallyport entre um diário de sessões e um diário de atividades é útil aqui, porque a autorização de uma sessão e as ações individuais respondem a perguntas de auditoria diferentes. Ambos precisam manter o instantâneo do processo que existia quando a decisão da sessão foi tomada, em vez de depender de um PID isolado durante uma projeção ou revisão posterior.

## O cartão de aprovação deve descrever a proveniência sem fingir certeza

As pessoas não conseguem tomar uma boa decisão diante de uma caixa de diálogo que diga apenas «O agente quer acesso». Elas precisam de contexto de identidade suficiente para reconhecer o solicitante, mas o cartão não deve ser carregado com campos que parecem precisos e dizem pouco.

Comece pela autoridade de assinatura de código que você avaliou. Inclua o nome ou o caminho do executável quando isso ajudar. Mostre a relação com o processo em linguagem simples, como «iniciado por um aplicativo de terminal assinado» ou «iniciado por um auxiliar de editor», somente se você tiver capturado os dados que sustentam essa afirmação. Inclua o PID como detalhe para solução de problemas, não como a identidade declarada.

Evite apresentar uma cadeia de processos pais como se fosse uma cadeia de certificados. A relação com o pai é uma observação pontual do sistema operacional. Um pai assinado pode iniciar um filho sem assinatura. Um pai esperado pode ser um wrapper que faz exec de outra coisa. O cartão de aprovação deve deixar claro quem está solicitando a sessão agora e oferecer os dados do pai como contexto.

Essa distinção também muda a forma como a revogação funciona. Revogue um identificador de sessão, não um PID. Quando uma pessoa revogar a sessão, marque o registro imutável da sessão como revogado e rejeite futuras tentativas de ação que façam referência a ele. Se o processo original ainda estiver em execução, perderá o acesso. Se tiver terminado e seu PID tiver sido reutilizado, a revogação continuará correta porque nunca dependeu do controle daquele PID numérico.

Uma aprovação deve significar uma execução observada. Se os usuários quiserem uma decisão de confiança mais ampla, crie um recurso explícito separado, com um escopo claro, como um requisito de código assinado junto com uma duração definida. Não introduza esse escopo mais amplo em uma aprovação por sessão apenas porque a implementação tinha um cache de PID conveniente.

## Teste a falha em vez de confiar no comportamento normal dos processos

Os erros de reutilização de PID ficam escondidos porque os testes manuais comuns não geram rotatividade suficiente. Uma suíte de testes deve obrigar seu código a provar que rejeita um processo substituto que herdou um número e que rejeita um exec que altera o código por trás de um processo que continua ativo.

Você não precisa esperar que o macOS reutilize naturalmente um PID específico. Coloque a camada de inspeção de processos atrás de uma interface e forneça a ela instantâneos controlados. Um bom teste tem um registro aprovado e uma observação ativa posterior com o mesmo PID, mas um horário de início diferente:

```text
captured: pid=4812 start=1784738598.482911 signer=team-A
observed: pid=4812 start=1784744029.031882 signer=team-B
expected: deny with process_start_time_mismatch
```

Adicione um segundo caso em que o PID e o horário de início coincidam, mas o requisito de assinatura falhe. Adicione outro em que o filho continue ativo, mas o exec tenha alterado os dados do executável. Esses testes provam que o código de autorização combina os campos corretos, em vez de apenas testar se a inspeção do processo retorna dados.

Teste a visualização de auditoria separadamente. Crie uma sessão antiga com um PID de pai, simule o encerramento desse pai e depois forneça um processo sem relação que tenha o mesmo PID. A sessão histórica renderizada deve manter os campos capturados do pai original. Ela não deve substituí-los por um nome obtido da tabela atual de processos.

Por fim, teste o cancelamento e as falhas de inspeção. Um processo pode desaparecer no breve intervalo entre o recebimento de uma solicitação IPC e a consulta dos seus detalhes. Seu código deve registrar um motivo claro para a negação e exigir que o solicitante estabeleça uma nova sessão. Sistemas que transformam essas falhas em correspondências de melhor esforço criam exatamente a ambiguidade que o modelo de identidade foi criado para eliminar.

## A invariante útil é simples e rigorosa

Toda ação privilegiada de um agente deve fazer referência a uma sessão vinculada à vida útil de um único processo capturado. Uma solicitação ativa correspondente deve provar que vem dessa mesma vida útil, e seu executável ainda deve atender à identidade de código aprovada. Um PID reutilizado falha no primeiro teste. Um binário diferente por trás de um PID inalterado falha no segundo.

Essa invariante mantém os dados do processo em seus papéis corretos. Os dados de início distinguem vidas úteis. Os dados do assinante identificam o código validado. Os detalhes do pai fornecem proveniência. O ID da sessão carrega a decisão humana. Nenhum desses campos pode fazer o trabalho dos outros.

O Sallyport pode tornar essa decisão visível ao iniciar a autorização da sessão com a autoridade de assinatura de código do processo e manter registros separados para execuções e chamadas. A implementação ainda deve impedir que um número conveniente, por mais familiar que pareça em um log, substitua o processo que conquistou a aprovação.

Na próxima vez que você vir uma tabela indexada por `pid`, pergunte o que acontece depois que o processo termina. Se a resposta for «consultamos de novo», essa tabela não é um armazenamento de identidade de sessão. Corrija isso antes que um evento inofensivo do ciclo de vida do processo se transforme em um erro de autorização.
