
Controllore per Lifetimes e altri tipi di raffinamento
Controllore di
Lifetime e altri
Raffinamenti di tipo
per Zig
Video: https://www.youtube.com/watch?v=mf0WzTOe-40 Sponsorizza: https://buymeacoffee.com/dnautics
discuti su HN: https://news.ycombinator.com/item?id=42923829
discuti su lobste.rs: https://lobste.rs/s/9sitsj/clr_checker_for_lifetimes_other
video demo dal vivo: https://www.youtube.com/watch?v=ZY_Z-aGbYm8
Questo progetto crea un transpiler Zig per il compilatore Zig, che trasforma AIR (Abstract Intermediate Representation) in codice sorgente Zig che esegue analisi statica in fase di compilazione. L'analizzatore generato rileva problemi di sicurezza della memoria come use-before-assignment, use-after-free, stack pointer escapes, oltre a UB specifici di Zig come asserzioni di non-nullità, violazioni di union taggate o uso improprio di fieldParentPtr.
L'obiettivo è portare le garanzie di sicurezza della memoria a livello di Rust a Zig tramite analisi statica di AIR, senza modificare il linguaggio stesso.
CLR dipende da una versione fork del compilatore Zig (inclusa come sottomodulo in zig/) che aggiunge il supporto per instradare AIR a plugin esterni. Quando invocato con -ofmt=air -fair-out=<plugin.so>, il compilatore carica la libreria condivisa specificata e passa l'AIR generato per l'elaborazione.
CLR ha lo scopo di spingere i programmi verso pattern di ciclo di vita che siano espliciti e verificabili localmente, non semplicemente riconoscere ogni programma Zig tecnicamente valido. Quando sono possibili due rappresentazioni, CLR preferisce quella che rende lo stato delle risorse visibile nel tipo e nella struttura del flusso di controllo.
Ad esempio, evita di chiudere condizionalmente un descrittore di file non opzionale:
const file = try std.fs.cwd().openFile(path, .{});
if (should_close) {
file.close(); // Male: file è ambiguamente aperto dopo questo ramo.
}
Preferisci rappresentare la proprietà condizionale con un optional:
var file: ?std.fs.File = null;
if (should_open) {
file = try std.fs.cwd().openFile(path, .{});
}
if (file) |open_file| {
open_file.close();
}
Chiudere condizionalmente un descrittore non opzionale lascia il suo ciclo di vita ambiguo dopo il ramo. La politica prevista da CLR è di rifiutare questo pattern piuttosto che portare uno stato permanente "forse chiuso".
Lo stesso principio si applica ai puntatori allocati. Non liberare tramite un puntatore derivato:
const allocation = try allocator.alloc(u8, size);
const payload = allocation[header_size..];
allocator.free(payload); // Male: payload non è la base dell'allocazione.
Mantieni il puntatore base dell'allocazione disponibile per la deallocazione, e usa i puntatori derivati solo per l'accesso:
const allocation = try allocator.alloc(u8, size);
defer allocator.free(allocation);
const payload = allocation[header_size..];
use(payload);
Liberare un puntatore a campo, sottosezione o puntatore prodotto da aritmetica è rifiutato a meno che una regola interna documentata non ripristini la provenienza della base di allocazione.
Queste politiche sono rigorose per impostazione predefinita perché producono codice con cicli di vita delle risorse più semplici e revisionabili. Un futuro meccanismo di annotazione unsafe permetterà a determinati GID o operazioni di rinunciare a singole analisi. Ciò supporterà codice che accetta deliberatamente controlli più deboli in cambio di prestazioni, senza indebolire il modello predefinito per il resto del programma.
Questa è una riscrittura attiva del proof-of-concept originale basato su Elixir in Zig. L'implementazione Zig si carica come plugin del compilatore e analizza AIR direttamente.
Attualmente implementato:
std.mem.Allocator:
create/destroy - allocazione di un singolo elementoalloc/free - allocazione di slice (incluse alignedAlloc, allocSentinel, ecc.)realloc/remap - riallocazione di slice con tracciamento della vecchia slice liberatadupe/dupeZ - duplicazione di sliceinit/deinit/allocator - ciclo di vita completo dell'arenastd.process.args, std.mem.asBytes, std.HashMap e API di allocator/filestd.HashMap con identità canonica di archiviazione metadati/chiave/valore attraverso put, get, getPtr e iterazione dei valoriposix.open/close/dup/dup2/socket/accept/epoll_create/pipePianificato (vedi LIMITATIONS.md per dettagli):