Skip to content

Commit cd39a72

Browse files
committed
Fix segfault on exit when update check is enabled
Before this patch, the script could often crash with segfaults on Debian 12/Python 3.11. It apparently when processing small files. In that case, there was likely a race condition where Python tried to terminate the update thread while it was already dead. It was easy to reproduce with: ```sh for i in {1..100}; do ./MCE.py -skip -exit /lib/firmware/amd-ucode/microcode_amd_fam16h.bin >& /dev/null || echo failed at iteration $i done ``` GDB showed: ``` Thread 1 "python3" received signal SIGABRT, Aborted. __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44 44 ./nptl/pthread_kill.c: No such file or directory. [Thread debugging using libthread_db enabled] ``` Using multiprocessing with daemon=True, there are no more crashes. Processes are probably easier to kill properly. The result of the update check is now shared using a queue.
1 parent 52e7a45 commit cd39a72

1 file changed

Lines changed: 17 additions & 18 deletions

File tree

MCE.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
import hashlib
4040
import inspect
4141
import sqlite3
42-
import threading
42+
import multiprocessing
43+
import queue
4344
import traceback
4445
import urllib.request
4546
import importlib.util
@@ -128,17 +129,6 @@ def __init__(self, source) :
128129

129130
if self.mass_scan or self.search or self.build_repo or self.build_blob or self.get_last : self.skip_intro = True
130131

131-
# https://stackoverflow.com/a/65447493 by Shail-Shouryya
132-
class Thread_With_Result(threading.Thread) :
133-
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None) :
134-
self.result = None
135-
if kwargs is None : kwargs = {}
136-
137-
def function() :
138-
self.result = target(*args, **kwargs)
139-
140-
super().__init__(group=group, target=function, name=name, daemon=daemon)
141-
142132
class Intel_MC_Header(ctypes.LittleEndianStructure) :
143133
_pack_ = 1
144134
_fields_ = [
@@ -688,7 +678,14 @@ def mc_print(self) :
688678
def mce_exit(code) :
689679
try :
690680
# Before exiting, print output of MCE & DB update check Thread, if completed/dead
691-
if not thread_update.is_alive() and thread_update.result : print(thread_update.result)
681+
if not update_process.is_alive():
682+
try:
683+
update_process_result = update_result_queue.get(block=False)
684+
if update_process_result is not None:
685+
print(update_process_result)
686+
# This should not happen if the update process ended
687+
except queue.Empty:
688+
pass
692689

693690
# Remove temporary microcode files
694691
for temp_mc_path in temp_mc_paths:
@@ -804,7 +801,7 @@ def date_check(year, month, day) :
804801

805802
return True
806803

807-
def mce_upd_check(db_path) :
804+
def mce_upd_check(db_path, update_result_queue) :
808805
result = None
809806

810807
try :
@@ -843,7 +840,7 @@ def mce_upd_check(db_path) :
843840
except :
844841
result = None
845842

846-
return result
843+
update_result_queue.put(result)
847844

848845
def mce_is_latest(ver_before, ver_after) :
849846
# ver_before/ver_after = [X.X.X]
@@ -1028,9 +1025,11 @@ def mass_scan(f_path) :
10281025
# Set temp location
10291026
temp_path = os.path.join(mce_dir, 'Temporary')
10301027

1031-
# Initialize & Start background Thread for MCE & DB update check
1032-
thread_update = Thread_With_Result(target=mce_upd_check, args=(db_path,), daemon=True)
1033-
if not param.upd_dis : thread_update.start() # Start as soon as possible (mce_dir, db_path)
1028+
# Initialize & Start background process + queue for MCE & DB update check
1029+
update_result_queue = multiprocessing.Queue()
1030+
# Use daemon=True to make sure the update check process is terminated on exit
1031+
update_process = multiprocessing.Process(target=mce_upd_check, args=(db_path, update_result_queue), daemon=True)
1032+
if not param.upd_dis : update_process.start() # Start as soon as possible (mce_dir, db_path)
10341033

10351034
# Set MCB location
10361035
mcb_path = os.path.join(mce_dir, 'MCB.bin')

0 commit comments

Comments
 (0)