strilight 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
strilight/__init__.py ADDED
@@ -0,0 +1,174 @@
1
+ """
2
+ Strilight: High-Performance O(1) SMT Loop Lifting & Strided Interval Domain
3
+ ============================================================================
4
+ A lightweight, high-performance abstract interpretation and symbolic loop-lifting library for x86_64 binaries.
5
+ """
6
+
7
+ import logging
8
+ import sys
9
+ from typing import List, Union, Optional, Any
10
+
11
+ __version__ = "0.2.0"
12
+
13
+ # Module-level logger with default NullHandler (zero unwanted stdout noise when imported)
14
+ logger = logging.getLogger("strilight")
15
+ logger.addHandler(logging.NullHandler())
16
+
17
+
18
+ def set_log_level(level: Union[int, str]):
19
+ """
20
+ Sets the logging level for the strilight root logger.
21
+ Example: sl.set_log_level(logging.DEBUG) or sl.set_log_level("INFO")
22
+ """
23
+ if isinstance(level, str):
24
+ level = getattr(logging, level.upper(), logging.INFO)
25
+ logger.setLevel(level)
26
+
27
+
28
+ def enable_logging(level: Union[int, str] = logging.INFO, stream=None):
29
+ """
30
+ Enables console logging for strilight with a clean, standard formatter.
31
+ """
32
+ if stream is None:
33
+ stream = sys.stderr
34
+ set_log_level(level)
35
+
36
+ # Avoid adding multiple StreamHandlers
37
+ if not any(isinstance(h, logging.StreamHandler) and not isinstance(h, logging.NullHandler) for h in logger.handlers):
38
+ handler = logging.StreamHandler(stream)
39
+ formatter = logging.Formatter("[%(levelname)s] [%(name)s] %(message)s")
40
+ handler.setFormatter(formatter)
41
+ logger.addHandler(handler)
42
+
43
+
44
+ # Core abstractions
45
+ from strilight.engine.vsa import LoopSummary, LoopInvariantContract
46
+ from strilight.engine.domains import Interval, StridedInterval, DisjointIntervalSet
47
+ from strilight.frontend import SourceLifter, CodeGenerator, accelerate, accelerate_c_source
48
+
49
+
50
+ def __getattr__(name: str):
51
+ """
52
+ Lazy load optional extension and architecture modules on demand (PEP 562).
53
+ Keeps core mathematical engine purely isolated from binary dependencies upon import.
54
+ """
55
+ try:
56
+ if name in ("LoopEvaluator", "X86LoopEvaluator"):
57
+ from strilight.arch.x86.evaluator import LoopEvaluator
58
+ return LoopEvaluator
59
+ if name in ("SymbolicInductionAnalyzer", "X86SymbolicInductionAnalyzer"):
60
+ from strilight.arch.x86.symbolic import SymbolicInductionAnalyzer
61
+ return SymbolicInductionAnalyzer
62
+ if name in ("Instruction", "LoopBlock", "TraceCompressor"):
63
+ import strilight.arch as a
64
+ return getattr(a, name)
65
+ if name in ("ConditionExtractor", "StaticFlagTracker"):
66
+ import strilight.arch.x86 as x86
67
+ return getattr(x86, name)
68
+ if name in (
69
+ "Tracker",
70
+ "TraceRecord",
71
+ "BackwardSliceTracker",
72
+ "ForwardSliceTracker",
73
+ "Descendant",
74
+ "Ancestor",
75
+ ):
76
+ import strilight.extensions.tracker as t
77
+ return getattr(t, name)
78
+ if name == "BackwardTracker":
79
+ import strilight.extensions.tracker as t
80
+ return getattr(t, "BackwardSliceTracker")
81
+ if name == "Z3Translator":
82
+ from strilight.extensions.translator import Z3Translator
83
+ return Z3Translator
84
+ if name in ("SymbolicStackEngine", "StackByteCell"):
85
+ import strilight.extensions.stack_engine as s
86
+ return getattr(s, name)
87
+ if name == "AnalyzerCore":
88
+ from strilight.extensions.core import AnalyzerCore
89
+ return AnalyzerCore
90
+ if name == "setup_hooks":
91
+ from strilight.extensions.hooks import setup_hooks
92
+ return setup_hooks
93
+ if name == "AngrBridge":
94
+ from strilight.extensions.angr_bridge import AngrBridge
95
+ return AngrBridge
96
+ except ImportError as e:
97
+ raise AttributeError(f"Optional module {name!r} could not be loaded: {e}")
98
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
99
+
100
+
101
+ # =============================================================================
102
+ # High-Level Facade API (Instant Developer Experience)
103
+ # =============================================================================
104
+
105
+ def disassemble(code_bytes: bytes, base_address: int = 0x1000, bit_mode: int = 64) -> List[Any]:
106
+ """
107
+ Disassembles raw machine code bytes into standard Instruction objects.
108
+ """
109
+ from strilight.arch.instruction import Instruction
110
+ return Instruction.disassemble_bytes(code_bytes, base_address=base_address, bit_mode=bit_mode)
111
+
112
+
113
+ def compress(trace: List[Any], min_iterations: int = 3) -> List[Any]:
114
+ """
115
+ Compresses repeated instruction execution traces into LoopBlock hierarchies.
116
+ """
117
+ from strilight.arch.loop_compressor import TraceCompressor
118
+ return TraceCompressor.compress_trace(trace, min_iterations=min_iterations)
119
+
120
+
121
+ def evaluate(block_or_trace: Any, k_passes: int = 100, iterations: int = 1000) -> LoopSummary:
122
+ """
123
+ Evaluates abstract strided intervals and generates the closed-form loop invariant contract.
124
+ """
125
+ from strilight.arch.loop_compressor import LoopBlock
126
+ from strilight.arch.x86.evaluator import LoopEvaluator
127
+ if isinstance(block_or_trace, list):
128
+ block_or_trace = LoopBlock(body=block_or_trace, iterations=iterations)
129
+ evaluator = LoopEvaluator(k_passes=k_passes)
130
+ return evaluator.evaluate(block_or_trace)
131
+
132
+
133
+ def analyze(code_bytes: bytes, iterations: int = 1000, base_address: int = 0x1000, bit_mode: int = 64, k_passes: int = 100) -> LoopSummary:
134
+ """
135
+ One-line end-to-end loop analysis:
136
+ Disassembles machine code bytes, wraps into a LoopBlock, and extracts closed-form deltas & invariant contracts.
137
+ """
138
+ from strilight.arch.loop_compressor import LoopBlock
139
+ instructions = disassemble(code_bytes, base_address=base_address, bit_mode=bit_mode)
140
+ block = LoopBlock(body=instructions, iterations=iterations)
141
+ return evaluate(block, k_passes=k_passes)
142
+
143
+
144
+ __all__ = [
145
+ # High-Level Facade Functions & Decorators
146
+ "disassemble",
147
+ "compress",
148
+ "evaluate",
149
+ "analyze",
150
+ "accelerate",
151
+ "accelerate_c_source",
152
+
153
+ # Logging Configuration
154
+ "logger",
155
+ "set_log_level",
156
+ "enable_logging",
157
+
158
+ # Core Classes
159
+ "Instruction",
160
+ "LoopBlock",
161
+ "TraceCompressor",
162
+ "LoopEvaluator",
163
+ "LoopSummary",
164
+ "LoopInvariantContract",
165
+ "ConditionExtractor",
166
+ "StaticFlagTracker",
167
+ "Interval",
168
+ "StridedInterval",
169
+ "DisjointIntervalSet",
170
+ "SymbolicStackEngine",
171
+ "StackByteCell",
172
+ "SourceLifter",
173
+ "CodeGenerator",
174
+ ]
@@ -0,0 +1,16 @@
1
+ """
2
+ Strilight Engine Subpackage (strilight.engine)
3
+ ==============================================
4
+ Pure mathematical foundation: abstract interpretation domains, abstract state,
5
+ recurrence equation models, and SMT closed-form translation.
6
+ """
7
+
8
+ from strilight.engine.abstract_state import AbstractState
9
+ import strilight.engine.domains as domains
10
+ import strilight.engine.vsa as vsa
11
+
12
+ __all__ = [
13
+ "AbstractState",
14
+ "domains",
15
+ "vsa",
16
+ ]
@@ -0,0 +1,106 @@
1
+ from typing import Dict, List, Tuple, Union
2
+ from strilight.engine.domains import Interval, StridedInterval, DisjointIntervalSet
3
+
4
+ class StridedMemoryMap:
5
+ """
6
+ Abstract memory mapping using Strided Intervals for fast, safe loop execution.
7
+ Implements 'Safe Approximation' and 'Bézout Modulo Congruence Non-Aliasing'
8
+ (Notion Sections 3 & 8) to maintain O(1) mathematical speed and zero false aliasing.
9
+ """
10
+ def __init__(self):
11
+ # List of memory transactions: (address_interval, size_in_bytes, value_dset)
12
+ self.writes: List[Tuple[Union[Interval, StridedInterval], int, DisjointIntervalSet]] = []
13
+
14
+ def write(self, addr: Union[Interval, StridedInterval], size: int, value: DisjointIntervalSet):
15
+ """Records an abstract write transaction."""
16
+ self.writes.append((addr, size, value))
17
+
18
+ def read(self, addr: Union[Interval, StridedInterval], size: int) -> DisjointIntervalSet:
19
+ """
20
+ Reads from the abstract memory.
21
+ Uses Bézout Modulo Congruence Non-Aliasing (Rule 8.b.1) and Must-Alias (Rule 8.b.2):
22
+ - If proven Definite Non-Alias via gcd(s1, s2) or disjoint bounds: skip safely.
23
+ - If read exactly matches a recent write (Must-Alias): returns the precise value.
24
+ - If complex partial overlap is detected: returns TOP (UNKNOWN).
25
+ """
26
+ # Search backwards (most recent write first)
27
+ for w_addr, w_size, w_value in reversed(self.writes):
28
+ # 1. Modulo Congruence Non-Alias Test (Bézout GCD)
29
+ if hasattr(addr, 'is_disjoint_modulo') and hasattr(w_addr, 'is_disjoint_modulo'):
30
+ if addr.is_disjoint_modulo(w_addr):
31
+ continue # Definite Non-Alias: No interference!
32
+
33
+ # 2. Must-Alias Test
34
+ if hasattr(addr, 'is_must_alias') and hasattr(w_addr, 'is_must_alias'):
35
+ if addr.is_must_alias(w_addr) and size == w_size:
36
+ return w_value
37
+
38
+ # 3. Fallback Intersection Check for Interval objects
39
+ if hasattr(addr, 'intersect') and hasattr(w_addr, 'intersect'):
40
+ intersected = addr.intersect(w_addr)
41
+ if intersected.min_val <= intersected.max_val:
42
+ # Physical overlap detected
43
+ if (addr.min_val == w_addr.min_val and
44
+ addr.max_val == w_addr.max_val and
45
+ size == w_size and
46
+ getattr(addr, 'stride', 1) == getattr(w_addr, 'stride', 1) and
47
+ getattr(addr, 'stride_offset', 0) == getattr(w_addr, 'stride_offset', 0)):
48
+ return w_value
49
+ else:
50
+ # Complex partial overlap -> TOP (Safe Approximation)
51
+ dset = DisjointIntervalSet(k_limit=8)
52
+ dset.add(Interval(0, (1 << (size * 8)) - 1, size * 8))
53
+ return dset
54
+ else:
55
+ continue
56
+
57
+ # Not found in writes, return TOP (Symbolic/Unknown initial memory)
58
+ dset = DisjointIntervalSet(k_limit=8)
59
+ dset.add(Interval(0, (1 << (size * 8)) - 1, size * 8))
60
+ return dset
61
+
62
+
63
+ class AbstractState:
64
+ """
65
+ Represents the full hardware state in the Abstract Domain for the Loop Evaluator.
66
+ """
67
+ def __init__(self):
68
+ # Registers are stored as DisjointIntervalSets for Surgical Precision + K-Limit
69
+ self.registers: Dict[str, DisjointIntervalSet] = {}
70
+
71
+ # Strided Memory to track loop array access
72
+ self.memory = StridedMemoryMap()
73
+
74
+ # Flags are stored as Interval (3-valued logic: 0, 1, or Unknown/TOP)
75
+ # 1-bit width: [0, 0] means False, [1, 1] means True, [0, 1] means Unknown
76
+ self.flags: Dict[str, Interval] = {}
77
+
78
+ def get_register(self, reg_name: str, bit_width: int = 64) -> DisjointIntervalSet:
79
+ if reg_name not in self.registers:
80
+ dset = DisjointIntervalSet(k_limit=8)
81
+ # Default to TOP (Unknown)
82
+ dset.add(Interval(0, (1 << bit_width) - 1, bit_width))
83
+ self.registers[reg_name] = dset
84
+ return self.registers[reg_name]
85
+
86
+ def set_register(self, reg_name: str, value: DisjointIntervalSet):
87
+ self.registers[reg_name] = value
88
+
89
+ # Generic mathematical variable aliases
90
+ get_variable = get_register
91
+ set_variable = set_register
92
+
93
+ @property
94
+ def variables(self) -> Dict[str, DisjointIntervalSet]:
95
+ """Generic alias for abstract variables/registers."""
96
+ return self.registers
97
+
98
+ def get_flag(self, flag_name: str) -> Interval:
99
+ if flag_name not in self.flags:
100
+ # TOP for 1 bit is [0, 1] (Unknown)
101
+ self.flags[flag_name] = Interval(0, 1, 1)
102
+ return self.flags[flag_name]
103
+
104
+ def set_flag(self, flag_name: str, value: Interval):
105
+ assert value.bit_width == 1, f"Flags must be 1-bit Intervals, got {value.bit_width}-bit"
106
+ self.flags[flag_name] = value
@@ -0,0 +1,18 @@
1
+ """
2
+ Strilight Mathematical Abstract Domains (strilight.engine.domains)
3
+ ==================================================================
4
+ Modular ring arithmetic intervals, Strided Intervals, Bézout GCD congruence,
5
+ dual-mask reduced products, and disjoint set management for VSA.
6
+ """
7
+
8
+ from strilight.engine.domains.interval import (
9
+ Interval,
10
+ StridedInterval,
11
+ DisjointIntervalSet,
12
+ )
13
+
14
+ __all__ = [
15
+ "Interval",
16
+ "StridedInterval",
17
+ "DisjointIntervalSet",
18
+ ]