about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-10-23 11:06:17 +0000
committerTheofilos Augoustis <theofilos.augoustis@gmail.com>2025-11-06 17:20:13 +0000
commit10aecdf0a8ed8823a44a0fe0fa271e0458241c58 (patch)
treec1bede0fd373da4c6a578e5e1ec16a93b0c96377
parent18e084b4cfc1ee5c963cae351b84668325bb43c7 (diff)
downloadfocaccia-10aecdf0a8ed8823a44a0fe0fa271e0458241c58.tar.gz
focaccia-10aecdf0a8ed8823a44a0fe0fa271e0458241c58.zip
Enable forced mode to push through errors (in hopes of tracing even partially-unsupported programs)
-rw-r--r--src/focaccia/symbolic.py49
-rwxr-xr-xsrc/focaccia/tools/capture_transforms.py7
2 files changed, 36 insertions, 20 deletions
diff --git a/src/focaccia/symbolic.py b/src/focaccia/symbolic.py
index f47bbb3..3ace906 100644
--- a/src/focaccia/symbolic.py
+++ b/src/focaccia/symbolic.py
@@ -64,7 +64,8 @@ def eval_symbol(symbol: Expr, conc_state: ReadableProgramState) -> int:
     # but ExprLocs are disallowed by the
     # ConcreteStateWrapper
     if not isinstance(res, ExprInt):
-        raise Exception(f'{res} from symbol {symbol} is not an instance of ExprInt but only ExprInt can be evaluated')
+        raise Exception(f'{res} from symbol {symbol} is not an instance of ExprInt'
+                        f' but only ExprInt can be evaluated')
     return int(res)
 
 class Instruction:
@@ -382,9 +383,9 @@ class SymbolicTransform:
             try:
                 return Instruction.from_string(text, arch, offset=0, length=length)
             except Exception as err:
-                warn(f'[In SymbolicTransform.from_json] Unable to parse'
-                     f' instruction string "{text}": {err}.')
-                return None
+                # Note: from None disables chaining in traceback
+                raise ValueError(f'[In SymbolicTransform.from_json] Unable to parse'
+                                 f' instruction string "{text}": {err}.') from None
 
         arch = supported_architectures[data['arch']]
         start_addr = int(data['from_addr'])
@@ -410,10 +411,10 @@ class SymbolicTransform:
             try:
                 return [inst.length, inst.to_string()]
             except Exception as err:
-                warn(f'[In SymbolicTransform.to_json] Unable to serialize'
-                     f' "{inst}" as string: {err}. This instruction will not'
-                     f' be serialized.')
-                return None
+                # Note: from None disables chaining in traceback
+                raise Exception(f'[In SymbolicTransform.to_json] Unable to serialize'
+                                f' "{inst}" as string: {err}. This instruction will not'
+                                f' be serialized.') from None
 
         instrs = [encode_inst(inst) for inst in self.instructions]
         instrs = [inst for inst in instrs if inst is not None]
@@ -584,8 +585,7 @@ def run_instruction(instr: miasm_instr,
         loc = lifter.add_instr_to_ircfg(instr, ircfg, None, False)
         assert(isinstance(loc, Expr) or isinstance(loc, LocKey))
     except NotImplementedError as err:
-        warn(f'[WARNING] Unable to lift instruction {instr}: {err}. Skipping.')
-        return None, {}  # Create an empty transform for the instruction
+        raise Exception(f'Unable to lift instruction {instr}: {err}. Skipping.') from None
 
     # Execute instruction symbolically
     new_pc, modified = execute_location(loc)
@@ -690,8 +690,7 @@ class SymbolicTracer:
                     raise Exception()
         return True
 
-    def trace(self,
-              start_addr: int | None = None) -> Trace[SymbolicTransform]:
+    def trace(self, start_addr: int | None = None) -> Trace[SymbolicTransform]:
         """Execute a program and compute state transformations between executed
         instructions.
 
@@ -718,7 +717,7 @@ class SymbolicTracer:
             except:
                 err = sys.exc_info()[1]
 
-                # Try to get the LLDB disassembly instead to simplify debugging
+                # Try to recovery by using the LLDB disassembly instead
                 try:
                     alt_disas = target.get_disassembly(pc)
                     instr = Instruction.from_string(alt_disas, ctx.arch, pc,
@@ -726,14 +725,22 @@ class SymbolicTracer:
                     info(f'Disassembled instruction {instr} at {hex(pc)}')
                     instr = instr.instr
                 except:
-                    warn(f'Unable to disassemble instruction {hex(pc)}: {err}.'
-                         f' Skipping.')
-                    target.step()
-                    continue
+                    if self.force:
+                        warn(f'Unable to disassemble instruction {hex(pc)}: {err}.'
+                             f' Skipping.')
+                        target.step()
+                        continue
+                    raise # forward exception
 
             # Run instruction
             conc_state = MiasmSymbolResolver(lldb_state, ctx.loc_db)
-            new_pc, modified = run_instruction(instr, conc_state, ctx.lifter)
+
+            try:
+                new_pc, modified = run_instruction(instr, conc_state, ctx.lifter)
+            except:
+                if not self.force:
+                    raise
+                new_pc, modified = None, {}
 
             # Create symbolic transform
             instruction = Instruction(instr, ctx.machine, ctx.arch, ctx.loc_db)
@@ -745,7 +752,11 @@ class SymbolicTracer:
             strace.append(transform)
 
             if len(strace) == 0:
-                raise Exception(f'Unable to collect trace for instruction {instr}')
+                msg = f'Unable to collect trace for instruction {instr}'
+                if not self.force:
+                    raise Exception(msg)
+                else:
+                    warn(msg)
 
             # Predict next concrete state.
             # We verify the symbolic execution backend on the fly for some
diff --git a/src/focaccia/tools/capture_transforms.py b/src/focaccia/tools/capture_transforms.py
index fa8c92a..0adba43 100755
--- a/src/focaccia/tools/capture_transforms.py
+++ b/src/focaccia/tools/capture_transforms.py
@@ -26,6 +26,10 @@ def main():
                       help='Remote target to trace (e.g. 127.0.0.1:12345)')
     prog.add_argument('--log-level',
                       help='Set the logging level')
+    prog.add_argument('--force',
+                      default=False,
+                      action='store_true',
+                      help='Force Focaccia to continue tracing even when something goes wrong')
     prog.add_argument('--debug',
                       default=False,
                       action='store_true',
@@ -43,7 +47,8 @@ def main():
         logging.basicConfig(level=logging.INFO)
 
     env = TraceEnvironment(args.binary, args.args, utils.get_envp())
-    tracer = SymbolicTracer(env, remote=args.remote, cross_validate=args.cross_validate)
+    tracer = SymbolicTracer(env, remote=args.remote, cross_validate=args.cross_validate,
+                            force=args.force)
     trace = tracer.trace()
     with open(args.output, 'w') as file:
         parser.serialize_transformations(trace, file)