# PID reuse agent session attribution को कैसे भ्रष्ट करता है

Bare PID status screen के लिए ठीक हो सकता है। लेकिन approval, audit record या credential रखने वाले agent session के पीछे identity के रूप में यह स्वीकार्य नहीं है। Operating system process IDs को दोबारा इस्तेमाल करता है। अगर agent system recycled number को उसी actor की पहचान मान ले, तो वह बाद के process को पहले process की approval से जोड़ सकता है।

इसके लिए kernel access वाला attacker या कोई असाधारण race जरूरी नहीं है। कोई short-lived agent बंद होता है, व्यस्त machine इतने processes बनाती है कि उसका PID दोबारा मिल जाता है, और कोई component PID के आधार पर call reconnect करता है, display refresh करता है या पुरानी audit row ढूंढता है। बाद वाला process सामान्य software हो सकता है। Attribution फिर भी गलत है, और गलत attribution पर चलने वाली approval system ने approvals रखने का उद्देश्य ही खो दिया है।

Fix मुश्किल नहीं है, लेकिन vocabulary बदलनी होगी। PID को process identity कहना बंद करें। Captured process record रखें जिसमें PID, start time, code-signing facts और authorization decision के समय collected parent provenance हो। इसके बाद हर comparison में साबित करें कि live process अभी भी उसी record से मेल खाता है।

## PID slot का नाम है, process lifetime का नहीं

Process ID उस समय allocated kernel process entry की पहचान करता है। यह हमेशा के लिए किसी एक program का नाम नहीं होता और समय के साथ global uniqueness की गारंटी नहीं देता। Process बंद होने पर उसका PID फिर इस्तेमाल के लिए उपलब्ध हो जाता है।

यह अंतर तब महत्वपूर्ण हो जाता है जब system पुरानी approval को आगे ले जाता है। मान लें agent client PID 4812 से शुरू होता है, deployment credential इस्तेमाल करने की request करता है और user उस run को approve कर देता है। Client बंद हो जाता है। बाद में किसी दूसरे process को PID 4812 मिल जाता है। अगर आपका gateway पूछता है, «क्या PID 4812 का approved session है?», तो वह पुरानी approval निकालकर नए process से जोड़ सकता है।

खतरनाक बात यह है कि पुराने और नए process का एक साथ चलना जरूरी नहीं है। कई implementations database join, cache lookup या देर से चलने वाले enrichment job में यह गलती करती हैं। वे approval को `pid = 4812` के रूप में save करती हैं, calls को भी इसी तरह save करती हैं और मानती हैं कि बाद की query पहले वाला अर्थ रखती है। ऐसा नहीं है।

Human-facing record में PID फिर भी उपयोगी है, क्योंकि इससे operator live system की जांच कर सकता है। इसे रखें। बस authorization या attribution में इस्तेमाल होने वाली primary identity न बनाएं।

एक दूसरी समस्या `fork` और `exec` से आती है। Fork अलग PID वाला नया child बनाता है। Exec मौजूदा process में program image बदलता है और PID वही रखता है। अगर आप shell runner को approve करते हैं और वह किसी दूसरे binary को exec करता है, तो PID परिचित रहता है, लेकिन अगली request करने वाला code बदल सकता है। Identity design को exit के बाद PID reuse और जीवित process के भीतर program replacement, दोनों का ध्यान रखना चाहिए।

## Session शुरू होते ही process record capture करें

विश्वसनीय session record security boundary पर लिया गया snapshot होता है, बाद में जोड़ी गई facts का संग्रह नहीं। Agent process पहली बार session बनाने की request करे, उसी समय इसे capture करें, approval में caller को दिखाने से पहले।

macOS agent gateway के लिए मैं इन fields को एक immutable process snapshot में रखूंगा:

- PID और effective user ID।
- Process start time, जिसमें platform उपलब्ध कराए तो seconds और microseconds भी हों।
- Capture के समय देखा गया executable path।
- Signing identifier, team या signer context और उपलब्ध होने पर code directory hash।
- Parent process snapshot, जिसमें उसका अपना PID और start time भी हो।

Process start time PID को एक खास lifetime वाले reference में बदल देता है। Practical tuple ऐसा दिख सकता है:

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

यह tuple संकीर्ण सवाल का जवाब देता है, «क्या यह वही kernel process lifetime है जिसे मैंने पहले देखा था?» यह नहीं बताता कि process trustworthy है या नहीं। Signing record अलग सवाल का जवाब देता है, «जब मैंने process inspect किया, तब macOS ने किस code को validate किया था?» Parent snapshot एक और सवाल का जवाब देता है, «Session शुरू होने पर observed रूप में इस process को किसने बनाया?»

इन सवालों को `Claude Code (PID 4812)` जैसे एक text label में न समेटें। Approval card के लिए यह label ठीक हो सकता है, लेकिन reviewer जब पूछे कि किसी session को authorize क्यों किया गया, तब जरूरी facts गायब हो जाएंगे।

macOS पर process inspector `PROC_PIDTBSDINFO` के साथ `proc_pidinfo` इस्तेमाल करके `proc_bsdinfo` प्राप्त कर सकता है। इसमें `pbi_start_tvsec` और `pbi_start_tvusec` शामिल होते हैं। Apple अपने Endpoint Security process model में process `start_time` भी देता है। कौन-सी API चुनते हैं, यह मुख्य बात नहीं है। मुख्य बात यह है कि decision boundary पर start time capture हो और बाद में उसकी तुलना की जाए।

Lifetime वाले हिस्से के लिए एक छोटा C helper ऐसा हो सकता है:

```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;
}
```

Output का format language binding से ज्यादा महत्वपूर्ण है:

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

अगर बाद की read में वही PID किसी अलग start timestamp के साथ मिले, तो आप अलग process देख रहे हैं। Session match deny करें, भले ही convenience layer उस number को परिचित मानना चाहती हो।

## Code signing code के बारे में बताता है, एक running instance के बारे में नहीं

Code-signing data उपयोगी provenance देता है, लेकिन अपने आप process identity नहीं बन जाता। Signing identifier किसी application के हर released version में समान हो सकता है। Team identifier signing organization की पहचान करता है, किसी खास executable की नहीं। Code directory hash signed code content के लिए अधिक specific है, फिर भी एक executable के कई concurrent instances इसे साझा करेंगे।

Apple का code-signing documentation एक महत्वपूर्ण अंतर बताता है जिसे security products अक्सर मिटा देते हैं। Signing identifier कई signers द्वारा claim किया जा सकता है, इसलिए Apple validation category और non-Apple code के लिए team identifier भी जांचने की सलाह देता है। Apple की technical note TN3127 यह भी बताती है कि designated requirement identifier को signing requirements के साथ मिलाकर updates के दौरान code identity स्थापित करती है।

सही mental model यह है: signing facts बताते हैं कि macOS ने किस executable को validate किया है और क्या वह आपकी trust requirement पूरी करता है। वे यह नहीं बताते कि यह वही process है जिसे user ने पांच मिनट पहले approve किया था।

Session authorization में दोनों layers इस्तेमाल करें:

```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
```

सिर्फ display name, bundle identifier या path की तुलना न करें। Paths बदलते हैं। Unsigned command-line tools की identity कमजोर या केवल local हो सकती है। User किसी परिचित tool की copy दूसरी location से भी चला सकता है। Approval UI friendly label दिखा सकता है, लेकिन authorization code को वही evaluated signing facts सुरक्षित रखने चाहिए जिनसे वह label बना था।

एक आम गलत सलाह कहती है कि application updates के दौरान continuity बनाए रखने के लिए केवल signing identifier से authorize करें। यह इसलिए लोकप्रिय है क्योंकि update continuity सुविधाजनक लगती है। Per-run approval के लिए यह गलत है। अगर किसी व्यक्ति ने एक चल रहे agent process को approve किया है, तो उसी signed program का बाद में शुरू हुआ instance नया run है और उसे नया session decision मिलना चाहिए। Stable code identity approval card को समझने योग्य बनाने का कारण है। यह किसी एक run की approval को भविष्य के हर instance तक चुपचाप बढ़ाने की अनुमति नहीं है।

## Parent PID तभी evidence है जब उसका lifetime भी रखें

Parent process data reviewers को यह समझने में मदद करता है कि agent कैसे शुरू हुआ। इससे terminal से शुरू हुए client को editor, scheduler या किसी दूसरे agent से शुरू हुए process से अलग किया जा सकता है। लेकिन `ppid = 4760` में भी child PID जैसी reuse समस्या है।

गलत pattern आसानी से दिखता है:

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

तीन घंटे बाद audit viewer PID 4760 को किसी process से resolve करता है और उसे session का parent बताता है। Original parent बहुत पहले बंद हो चुका हो सकता है। अब 4760 किसी दूसरे process के पास है। Audit page ने historical claim को live lookup में बदलकर चुपचाप history बदल दी।

Child snapshot के साथ parent snapshot भी capture करें:

```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"
)
```

आखिरी relationship field साधारण लगती है, लेकिन यह गलत भाषा से बचाती है। Parent record का अर्थ है, «जब system ने child को देखा, तब यह उसका direct parent था।» इसका अर्थ यह नहीं है कि «इस parent ने child को authorize किया», «यह parent हमेशा child का owner है» या «यह अभी भी current parent है»। इन दावों के लिए अलग evidence चाहिए।

अगर आपके पास Endpoint Security process events हैं, तो Apple का `es_process_t` original parent PID देता है और `parent_audit_token`, `responsible_audit_token`, start time तथा signing data भी उपलब्ध कराता है। API दे तो audit tokens raw PIDs से बेहतर हैं, क्योंकि उनमें अधिक context रहता है। फिर भी event के process object को captured observation मानें। बाद के PID lookup से उसे बदलकर equivalent न कहें।

Endpoint Security के बिना systems में उपलब्ध सर्वोत्तम process APIs इस्तेमाल करें, जल्दी capture करें और uncertainty स्पष्ट रखें। Child पढ़ने और parent पढ़ने के बीच parent बंद हो सकता है। उस race में certainty गढ़ें नहीं। Parent को unavailable या partial mark करें, captured child identity रखें और ऐसा parent relationship न बताएं जिसे आप देख नहीं सके।

## Approved session से action कराने से पहले फिर जांचें

Session approval में identity handling के दो क्षण होने चाहिए: session खुलते समय capture और authority इस्तेमाल करते समय revalidation। एक बार capture करने से audit trail सुरक्षित रहता है। Revalidation अगला action सुरक्षित करती है।

क्रम की कल्पना करें:

1. Agent process session खोलता है और आप PID 4812, start time, signer facts और parent details capture करते हैं।
2. कोई व्यक्ति उसी agent run को approve करता है।
3. Session token memory या local IPC connection में रहते हुए process बंद हो जाता है।
4. बाद के process को PID 4812 मिल जाता है और वह stale token पेश करता है या bug के जरिए पुरानी association तक पहुंचता है।
5. Credentialed call करने से पहले आपका gateway live process जांचता है।

Final check में PID समान हो सकता है। Start time समान नहीं होगा। इस mismatch पर session बंद होना चाहिए। New timestamp से record को ठीक न करें, चुपचाप replacement session न बनाएं और action को पुराने process से attribute न करें।

Inspection fail होने पर भी check fail closed होना चाहिए। Process बंद हो गया हो, permissions बदल गई हों या operating system ने अधूरा data दिया हो, तो यह साबित नहीं किया जा सकता कि requester approved process है। सही response नया session मांगना है।

Comparison को संकीर्ण और literal रखें। «Same command name», «same working directory» या «same terminal window» जैसी fuzzy matching न करें। ये fields human context समझने में मदद करती हैं, लेकिन adversarial या accidental ambiguity में भरोसेमंद नहीं रहतीं। Process working directory बदल सकता है और दो unrelated processes एक ही command name चुन सकते हैं।

Cache authorization decision को captured process instance से जोड़कर करें। PID से approval state तक map बनाना गलत जगह है। PID-keyed map शांत laptop पर tests पास कर देगा और process churn में fail होगा। Users तक पहुंचने के बाद ऐसी समस्या diagnose करना खास तौर पर कठिन होता है।

## Sessions को mutable process rows नहीं, immutable events की तरह model करें

Session journal को gateway द्वारा हर समय देखी गई बात सुरक्षित रखनी चाहिए। «PID 4812 active है» कहने वाली mutable row यह नहीं बता सकती कि fields original agent से आईं, replacement process से या देर से चलने वाले background refresh से।

Explicit references वाले append-only event records रखें। Structure सरल हो सकती है:

```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"
  }
}
```

अगला call event `sess_7d9f` को reference करे और अपना time, requested channel, action target तथा result दर्ज करे। केवल `pid: 4812` copy करके यह उम्मीद न करें कि analyst बाकी जानकारी फिर से बना लेगा। Revalidation fail हो तो observed mismatch के साथ अलग denial event लिखें।

```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"
}
```

इससे reviewer को ठोस जानकारी मिलती है: process number reuse हुआ, gateway ने mismatch पहचाना और request deny की। दोनों timestamps के बिना event केवल इतना बताता है कि कुछ fail हुआ। जब software defect और authority पाने की hostile कोशिश में अंतर करना हो, तो इतना काफी नहीं है।

Sallyport का Sessions journal और Activity journal को अलग रखना यहां उपयोगी है, क्योंकि session-level authorization और individual actions अलग audit questions का जवाब देते हैं। दोनों में session decision के समय मौजूद process snapshot रहना चाहिए। बाद के projection या review में bare PID पर निर्भर नहीं होना चाहिए।

## Approval card provenance बताए, certainty का दिखावा न करे

«Agent wants access» कहने वाला dialog user को अच्छा decision लेने के लिए पर्याप्त नहीं है। Caller को पहचानने के लिए पर्याप्त identity context दें, लेकिन card को ऐसे fields से न भरें जो precise दिखें और कम जानकारी दें।

जिस code-signing authority का आपने मूल्यांकन किया है, उसे सबसे पहले दिखाएं। जरूरत हो तो executable name या path शामिल करें। Process relationship को सरल भाषा में दिखाएं, जैसे «signed terminal application ने शुरू किया» या «editor helper से launch हुआ», लेकिन केवल तब जब supporting data capture किया गया हो। PID को troubleshooting detail की तरह दिखाएं, claimed identity की तरह नहीं।

Parent chain को certificate chain की तरह प्रस्तुत न करें। Parentage उस समय का operating-system observation है। Signed parent unsigned child शुरू कर सकता है। Expected parent ऐसा wrapper हो सकता है जो किसी और चीज को exec करे। Approval card को स्पष्ट करना चाहिए कि अभी session कौन मांग रहा है, और parent information को context की तरह देना चाहिए।

यह अंतर revocation को भी बदलता है। PID नहीं, session identifier revoke करें। User session revoke करे तो immutable session record को revoked mark करें और उससे जुड़े future action attempts reject करें। Original process अभी चल रहा हो तो access खो दे। वह बंद हो चुका हो और PID reuse हो गया हो, तब भी revocation सही रहेगी, क्योंकि वह numeric PID को control करने पर निर्भर नहीं थी।

एक approval का अर्थ एक observed run होना चाहिए। User को व्यापक trust decision चाहिए तो स्पष्ट scope वाला अलग feature बनाएं, जैसे signed code requirement और तय duration। Per-session approval में ऐसा broader scope सिर्फ इसलिए न छिपाएं कि implementation में convenient PID cache था।

## सामान्य process behavior पर भरोसा करने के बजाय failure test करें

PID reuse bugs छिपे रहते हैं क्योंकि सामान्य manual testing पर्याप्त process turnover नहीं बनाती। Test suite को यह साबित करना चाहिए कि inherited number वाले replacement process को reject किया जाता है और surviving process के पीछे code बदलने वाले exec को भी reject किया जाता है।

macOS के किसी खास PID को स्वाभाविक रूप से reuse करने का इंतजार जरूरी नहीं है। Process-inspection layer को interface के पीछे रखें और controlled snapshots दें। अच्छा test approved record और बाद की live observation में same PID, लेकिन अलग start time रखता है:

```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
```

एक दूसरा case जोड़ें जिसमें PID और start time match हों, लेकिन signing requirement fail हो। एक और case रखें जिसमें child जीवित रहे, पर exec ने उसके executable facts बदल दिए हों। ये tests साबित करते हैं कि authorization code सही fields को जोड़ता है, केवल process inspection से data मिलने की जांच नहीं करता।

Audit view को अलग से test करें। Parent PID वाला पुराना session seed करें, parent के बंद होने का simulation करें और बाद में उसी PID वाला unrelated process दें। Rendered historical session में original captured parent fields बने रहने चाहिए। Current process table से मिले name से उन्हें replace नहीं करना चाहिए।

Cancellation और inspection failures भी test करें। IPC request स्वीकार करने और details query करने के बीच process गायब हो सकता है। Code को स्पष्ट denial reason रिकॉर्ड करना चाहिए और caller से नया session स्थापित करने को कहना चाहिए। ऐसे failures को best-effort matches में बदलने वाले systems वही ambiguity बनाते हैं जिसे identity model हटाने के लिए बनाया गया था।

## उपयोगी invariant सरल और सख्त है

हर privileged agent action ऐसे session से जुड़ा होना चाहिए जो एक captured process lifetime से bound हो। Matching live request को साबित करना चाहिए कि वह उसी lifetime से आई है और उसका executable अभी भी approved code identity पूरी करता है। Reused PID पहला test fail करता है। Unchanged PID के पीछे अलग binary दूसरा test fail करती है।

यह invariant process facts की सही भूमिका बनाए रखता है। Start data lifetimes अलग करता है। Signer data validated code की पहचान करता है। Parent details provenance देते हैं। Session ID human decision लेकर चलता है। इनमें से कोई field दूसरे का काम नहीं कर सकती।

Sallyport session authorization की शुरुआत process की code-signing authority से करके और runs तथा calls के अलग records रखकर इस decision को स्पष्ट बना सकता है। Implementation को फिर भी किसी सुविधाजनक number को, log में कितना भी परिचित दिखे, approval पाने वाले process की जगह लेने नहीं देना चाहिए।

अगली बार जब आपको `pid` से keyed table दिखे, तो पूछें कि process बंद होने के बाद क्या होगा। अगर जवाब है «हम इसे फिर lookup करते हैं», तो वह table session identity store नहीं है। किसी मामूली process lifecycle event के authorization error बनने से पहले इसे ठीक करें।
