Skip to content

Commit a9c775e

Browse files
author
Nils Bars
committed
Forward KeyboardInterrupt from privilege-dropped child to parent
Register a KeyboardInterrupt codec in the IPC serializer and catch it alongside Exception in _drop_and_execute so a Ctrl+C delivered to the child is serialized over the pipe instead of escaping as an uncaught BaseException. The parent's drop_privileges wrapper now re-raises any BaseException it receives, letting ref_util_exception_hook format the interrupt as a single "[-] Keyboard Interrupt" line.
1 parent f2a52cc commit a9c775e

2 files changed

Lines changed: 18 additions & 3 deletions

File tree

ref_utils/process.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ def _drop_and_execute(
9292
exception_str = traceback.format_exc()
9393
print_err(f"[!] Unexpected error:\n{exception_str}")
9494
exit(1)
95-
except Exception as e:
96-
# Forward exception to our parent
95+
except (Exception, KeyboardInterrupt) as e:
96+
# Forward exception to our parent. KeyboardInterrupt is included so a
97+
# Ctrl+C delivered to this child does not escape as an uncaught
98+
# BaseException and produce a duplicate traceback from multiprocessing.
9799
serialized_e = safe_dumps(e)
98100
conn.send_bytes(serialized_e)
99101
finally:
@@ -127,7 +129,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
127129
# Deserialize using JSON-based serialization (secure alternative to pickle)
128130
ret = safe_loads(serialized_ret)
129131
p.join()
130-
if isinstance(ret, Exception):
132+
if isinstance(ret, BaseException):
131133
raise ret
132134
return ret
133135

ref_utils/serialization.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ def _register_standard_exceptions(serializer: IPCSerializer) -> None:
259259
)
260260
)
261261

262+
# KeyboardInterrupt is a BaseException, not an Exception, so the generic
263+
# Exception fallback below does not cover it. Register it explicitly so
264+
# privilege-dropping child processes can forward a Ctrl+C back to the
265+
# parent instead of producing a duplicate traceback.
266+
serializer.register(
267+
TypeCodec(
268+
type_tag="builtin.KeyboardInterrupt",
269+
target_type=KeyboardInterrupt,
270+
to_dict=lambda e: {"args": list(e.args)},
271+
from_dict=lambda d: KeyboardInterrupt(*d.get("args", [])),
272+
)
273+
)
274+
262275
# Generic Exception fallback - handles any Exception subclass
263276
# This must be registered LAST as it matches any Exception
264277
serializer.register(

0 commit comments

Comments
 (0)