436 lines
14 KiB
C++
436 lines
14 KiB
C++
// swan_sil_parser.hpp — SWAN-inspired SIL parser for fold-swift
|
|
//
|
|
// This is a focused C++ reimplementation of the SIL parser concepts from
|
|
// SWAN (themaplelab/swan, ESEC/FSE '20). It parses canonical SIL text
|
|
// (output of `swiftc -emit-sil`) into a lightweight AST and exports
|
|
// RustExprNode-compatible JSON for the fold symbolic IR encoder.
|
|
//
|
|
// Architecture:
|
|
// SIL text → recursive-descent parser → SIL AST nodes
|
|
// SIL AST → SWIRL-like operator extractor → RustExprNode JSON
|
|
//
|
|
// The parser focuses on security-relevant SIL operations:
|
|
// - unchecked casts (ref_cast, addr_cast, trivial_bit_cast)
|
|
// - load/store through pointer types
|
|
// - function calls (apply)
|
|
// - cond_fail (force-unwrap)
|
|
// - struct/tuple/enum access patterns
|
|
// - reference-counting operations
|
|
//
|
|
// The existing regex-based parser is kept as a fallback for SIL constructs
|
|
// that this parser doesn't yet handle.
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace fold_swift {
|
|
|
|
// =========================================================================
|
|
// SIL Lexer / Source Location
|
|
// =========================================================================
|
|
|
|
struct SilLoc {
|
|
std::string file;
|
|
std::uint32_t line = 0;
|
|
std::uint32_t column = 0;
|
|
|
|
[[nodiscard]] bool valid() const { return !file.empty() && line != 0; }
|
|
};
|
|
|
|
struct SilSourceInfo {
|
|
std::uint32_t scope_ref = 0;
|
|
SilLoc loc;
|
|
[[nodiscard]] bool valid() const { return loc.valid(); }
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Type (simplified — only what we need for security analysis)
|
|
// =========================================================================
|
|
|
|
enum class SilTypeKind : std::uint8_t {
|
|
Unknown,
|
|
Named, // e.g. Int, String, UnsafePointer<Int>
|
|
Address, // @inout or *T
|
|
Function, // function type
|
|
Tuple, // tuple type
|
|
Optional, // Optional<T>
|
|
};
|
|
|
|
struct SilType {
|
|
SilTypeKind kind = SilTypeKind::Unknown;
|
|
std::string name; // for Named
|
|
std::vector<SilType> params; // generic params / tuple elements / function params
|
|
bool is_optional = false;
|
|
bool is_pointer = false; // UnsafePointer, UnsafeMutablePointer, etc.
|
|
bool is_raw_pointer = false; // UnsafeRawPointer
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Operand / Result
|
|
// =========================================================================
|
|
|
|
struct SilOperand {
|
|
std::string value; // %0, %1, etc.
|
|
SilType type;
|
|
};
|
|
|
|
struct SilResult {
|
|
std::vector<std::string> value_names; // %0 = ...
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL DeclRef (declaration reference — class/struct member, etc.)
|
|
// =========================================================================
|
|
|
|
struct SilDeclRef {
|
|
std::vector<std::string> parts; // e.g. ["MyClass", "myMethod"]
|
|
std::string kind; // func, allocator, enumelt, etc.
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Operators (the ~75 SIL instructions)
|
|
// We model the security-relevant subset explicitly; the rest go through
|
|
// a generic "Other" path.
|
|
// =========================================================================
|
|
|
|
enum class SilOperatorKind : std::uint8_t {
|
|
// Allocation
|
|
AllocStack,
|
|
AllocRef,
|
|
AllocBox,
|
|
DeallocStack,
|
|
DeallocRef,
|
|
DeallocBox,
|
|
ProjectBox,
|
|
// Memory Access
|
|
Load,
|
|
Store,
|
|
LoadBorrow,
|
|
BeginBorrow,
|
|
EndBorrow,
|
|
EndLifetime,
|
|
CopyAddr,
|
|
DestroyAddr,
|
|
IndexAddr,
|
|
IndexRawPointer,
|
|
BeginAccess,
|
|
EndAccess,
|
|
// Reference Counting
|
|
StrongRetain,
|
|
StrongRelease,
|
|
SetDeallocating,
|
|
UnownedRetain,
|
|
UnownedRelease,
|
|
LoadWeak,
|
|
StoreWeak,
|
|
FixLifetime,
|
|
IsUnique,
|
|
IsEscapingClosure,
|
|
// Literals
|
|
FunctionRef,
|
|
DynamicFunctionRef,
|
|
GlobalAddr,
|
|
IntegerLiteral,
|
|
FloatLiteral,
|
|
StringLiteral,
|
|
// Dynamic Dispatch
|
|
ClassMethod,
|
|
ObjcMethod,
|
|
SuperMethod,
|
|
WitnessMethod,
|
|
// Function Application
|
|
Apply,
|
|
BeginApply,
|
|
AbortApply,
|
|
EndApply,
|
|
PartialApply,
|
|
Builtin,
|
|
// Aggregate Types
|
|
RetainValue,
|
|
ReleaseValue,
|
|
CopyValue,
|
|
DestroyValue,
|
|
Tuple,
|
|
TupleExtract,
|
|
TupleElementAddr,
|
|
DestructureTuple,
|
|
Struct,
|
|
StructExtract,
|
|
StructElementAddr,
|
|
DestructureStruct,
|
|
RefElementAddr,
|
|
RefTailAddr,
|
|
// Enums
|
|
Enum,
|
|
UncheckedEnumData,
|
|
InitEnumDataAddr,
|
|
InjectEnumAddr,
|
|
UncheckedTakeEnumDataAddr,
|
|
SelectEnum,
|
|
SelectEnumAddr,
|
|
// Protocol / Existential
|
|
InitExistentialAddr,
|
|
DeinitExistentialAddr,
|
|
OpenExistentialAddr,
|
|
OpenExistentialRef,
|
|
// Unchecked Conversions (security-critical)
|
|
Upcast,
|
|
AddressToPointer,
|
|
PointerToAddress,
|
|
UncheckedRefCast,
|
|
UncheckedRefCastAddr,
|
|
UncheckedAddrCast,
|
|
UncheckedTrivialBitCast,
|
|
UncheckedBitwiseCast,
|
|
UncheckedOwnershipConversion,
|
|
RefToRawPointer,
|
|
RawPointerToRef,
|
|
RefToUnowned,
|
|
UnownedToRef,
|
|
RefToUnmanaged,
|
|
UnmanagedToRef,
|
|
ConvertFunction,
|
|
ConvertEscapeToNoescape,
|
|
// Checked Conversions
|
|
UnconditionalCheckedCast,
|
|
UnconditionalCheckedCastAddr,
|
|
// Runtime Failures
|
|
CondFail,
|
|
// Other
|
|
Metatype,
|
|
ValueMetatype,
|
|
ExistentialMetatype,
|
|
SelectValue,
|
|
Keypath,
|
|
DebugValue,
|
|
DebugValueAddr,
|
|
Unknown,
|
|
};
|
|
|
|
struct SilOperator {
|
|
SilOperatorKind kind = SilOperatorKind::Unknown;
|
|
SilResult result;
|
|
std::vector<SilOperand> operands;
|
|
SilType type; // result type or target type for casts
|
|
SilType from_type; // source type for addr casts
|
|
SilDeclRef decl_ref; // for struct_extract, enum, etc.
|
|
std::int64_t int_value = 0;
|
|
std::string string_value;
|
|
std::string callee_name; // for function_ref, apply
|
|
std::vector<std::string> apply_args; // value operands for apply
|
|
bool nothrow = false;
|
|
SilSourceInfo source_info;
|
|
// Metadata
|
|
bool is_unchecked_cast = false;
|
|
bool is_load_through_pointer = false;
|
|
bool is_cond_fail = false;
|
|
std::string raw_text; // original SIL text line for fallback
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Terminators
|
|
// =========================================================================
|
|
|
|
enum class SilTerminatorKind : std::uint8_t {
|
|
Unreachable,
|
|
Return,
|
|
Throw,
|
|
Yield,
|
|
Unwind,
|
|
Branch,
|
|
CondBranch,
|
|
SwitchValue,
|
|
SwitchEnum,
|
|
SwitchEnumAddr,
|
|
DynamicMethodBranch,
|
|
CheckedCastBranch,
|
|
CheckedCastAddrBranch,
|
|
TryApply,
|
|
Unknown,
|
|
};
|
|
|
|
struct SilTerminator {
|
|
SilTerminatorKind kind = SilTerminatorKind::Unknown;
|
|
SilOperand operand; // ret operand, switch operand, etc.
|
|
std::vector<SilOperand> args; // branch args
|
|
std::string target_label; // branch / cond_br target
|
|
std::string true_label; // cond_br true
|
|
std::string false_label; // cond_br false
|
|
std::string normal_label; // try_apply normal
|
|
std::string error_label; // try_apply error
|
|
SilSourceInfo source_info;
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Basic Block
|
|
// =========================================================================
|
|
|
|
struct SilBlock {
|
|
std::string identifier;
|
|
std::vector<SilOperand> arguments;
|
|
std::vector<SilOperator> operators;
|
|
SilTerminator terminator;
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Function
|
|
// =========================================================================
|
|
|
|
struct SilFunction {
|
|
std::string name; // demangled
|
|
std::string mangled_name;
|
|
SilType type;
|
|
std::vector<SilBlock> blocks;
|
|
int start_line = 0;
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Module (full parse result)
|
|
// =========================================================================
|
|
|
|
struct SilModule {
|
|
std::string source_file;
|
|
std::vector<SilFunction> functions;
|
|
std::vector<std::string> imports;
|
|
bool parse_success = false;
|
|
std::string parse_error;
|
|
// Statistics
|
|
std::uint32_t total_blocks = 0;
|
|
std::uint32_t total_operators = 0;
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL Parser (recursive-descent, character-at-a-time)
|
|
// =========================================================================
|
|
|
|
class SilTextParser {
|
|
public:
|
|
explicit SilTextParser(std::string text, std::string source_path = "<memory>");
|
|
|
|
SilModule parseModule();
|
|
void setDemangleHook(std::function<std::string(const std::string&)> hook);
|
|
|
|
private:
|
|
// --- Lexer ---
|
|
char peek() const;
|
|
char peekAt(int offset) const;
|
|
bool peekStr(std::string_view s) const;
|
|
void skipWhitespace();
|
|
void skipWhitespaceAndComments();
|
|
bool skipToken(std::string_view s);
|
|
void takeToken(std::string_view s);
|
|
std::string takeIdentifier();
|
|
std::string takeUntil(char delim);
|
|
std::string takeWhile(int (*pred)(int));
|
|
bool tryTake(const std::string& s);
|
|
bool atEnd() const;
|
|
[[noreturn]] void parseError(const std::string& msg) const;
|
|
|
|
// --- Parser helpers ---
|
|
std::int64_t parseInt();
|
|
std::string parseMangledName();
|
|
std::string parseValueOperand();
|
|
int parseScopeRef();
|
|
SilLoc parseLoc();
|
|
SilSourceInfo parseSourceInfo();
|
|
SilType parseType();
|
|
SilType parseFunctionType();
|
|
SilType parseNamedType();
|
|
SilType parseOptionalType(SilType base);
|
|
SilType parseAddressType();
|
|
SilType parseTupleType();
|
|
SilType parseAttributedType();
|
|
SilDeclRef parseDeclRef();
|
|
std::string parseQuotedString();
|
|
void resolveSourceInfo(SilSourceInfo& info) const;
|
|
|
|
// --- Module-level ---
|
|
void parseImport();
|
|
SilFunction parseFunction();
|
|
SilBlock parseBlock();
|
|
SilOperator parseOperator();
|
|
SilTerminator parseTerminator();
|
|
SilOperand parseOperand();
|
|
SilResult parseResult();
|
|
|
|
// --- Operator parsers (grouped by category) ---
|
|
SilOperator parseAllocationOp(const std::string& name);
|
|
SilOperator parseMemoryAccessOp(const std::string& name);
|
|
SilOperator parseRefCountingOp(const std::string& name);
|
|
SilOperator parseLiteralOp(const std::string& name);
|
|
SilOperator parseDispatchOp(const std::string& name);
|
|
SilOperator parseApplyOp(const std::string& name);
|
|
SilOperator parseAggregateOp(const std::string& name);
|
|
SilOperator parseEnumOp(const std::string& name);
|
|
SilOperator parseConversionOp(const std::string& name);
|
|
SilOperator parseMiscOp(const std::string& name);
|
|
|
|
// --- Source location tracking ---
|
|
// SIL embeds source locations in comments: // file:line:col
|
|
// Track the most recently seen location to attach to next operator.
|
|
void trackCommentLoc();
|
|
|
|
// --- State ---
|
|
std::string source_;
|
|
std::string text_;
|
|
std::size_t cursor_ = 0;
|
|
std::size_t len_ = 0;
|
|
SilLoc last_comment_loc_; // tracked from // file:line:col comments
|
|
std::unordered_map<int, SilLoc> scope_map_; // scope_id -> loc
|
|
std::function<std::string(const std::string&)> demangle_hook_;
|
|
};
|
|
|
|
// =========================================================================
|
|
// SIL → RustExprNode JSON Converter
|
|
// =========================================================================
|
|
|
|
// Converts parsed SIL operators/terminators to RustExprNode-compatible JSON
|
|
// trees. Also produces finding NDJSON lines for security-relevant operations.
|
|
class SilToJsonConverter {
|
|
public:
|
|
// Convert a full SIL module to fold NDJSON output lines.
|
|
// Each line is a JSON object written to `out`.
|
|
void convertModule(const SilModule& mod, std::ostream& out);
|
|
|
|
private:
|
|
// Emit fold NDJSON record kinds
|
|
void emitInfo(const std::string& unit, std::ostream& out);
|
|
void emitFunction(const SilFunction& fn, const std::string& file, std::ostream& out);
|
|
void emitInterface(const SilFunction& fn, const std::string& file, std::ostream& out);
|
|
void emitIr(const SilFunction& fn, const std::string& file, std::ostream& out);
|
|
void emitFinding(const std::string& file, int line, int col, const std::string& function,
|
|
const std::string& kind, const std::string& hazard, const std::string& message,
|
|
std::ostream& out);
|
|
|
|
// Build RustExprNode JSON from a SIL operator
|
|
nlohmann::json operatorToExpr(const SilOperator& op, const std::string& file);
|
|
nlohmann::json buildCallExpr(const std::string& callee, const std::vector<nlohmann::json>& args,
|
|
int line, int col, const std::string& file);
|
|
nlohmann::json buildLiteralExpr(std::int64_t val, int width, bool signedness, int line, int col,
|
|
const std::string& file);
|
|
nlohmann::json buildStringLiteralExpr(const std::string& val, int line, int col,
|
|
const std::string& file);
|
|
nlohmann::json buildDeclRefExpr(const std::string& symbol, int line, int col,
|
|
const std::string& file);
|
|
nlohmann::json buildUnaryExpr(const std::string& op_name, nlohmann::json sub, int line, int col,
|
|
const std::string& file);
|
|
nlohmann::json buildBinaryExpr(const std::string& op_name, nlohmann::json lhs,
|
|
nlohmann::json rhs, int line, int col, const std::string& file);
|
|
nlohmann::json buildCastExpr(const std::string& target_type, nlohmann::json sub, int line,
|
|
int col, const std::string& file);
|
|
nlohmann::json buildMemberExpr(nlohmann::json base, const std::string& field, int line, int col,
|
|
const std::string& file);
|
|
|
|
// Build a source location object
|
|
nlohmann::json makeLoc(int line, int col, const std::string& file);
|
|
};
|
|
|
|
} // namespace fold_swift
|