@@ -567,27 +567,29 @@ def oom_call(label, func, *args, **kwargs):
567567 f"""
568568 _OOM_WINDOW = { self .options .oom_window }
569569
570- def oom_run(label, thunk):
570+ def oom_run(label, thunk, window=_OOM_WINDOW ):
571571 # Stateful OOM sequence (Phase 4): sweep a bounded failure window
572572 # across a multi-step thunk so a failure in one step can corrupt
573- # state a later step trips over. set_nomemory(start, start+_OOM_WINDOW)
574- # fails _OOM_WINDOW allocations then resumes succeeding, so steps after
575- # the burst run on the damaged state (_OOM_WINDOW == 0 -> fail forever,
576- # the legacy single-call semantics). The thunk guards each step
577- # internally so the tail still runs after an earlier step raises; a real
578- # crash (segfault/abort) terminates the process and is scored.
573+ # state a later step trips over. set_nomemory(start, start+window)
574+ # fails `window` allocations then resumes succeeding, so steps after
575+ # the burst run on the damaged state (window == 0 -> fail forever,
576+ # the legacy single-call semantics). `window` defaults to _OOM_WINDOW
577+ # but is passed per-sequence when --oom-seq-randomize is set. The thunk
578+ # guards each step internally so the tail still runs after an earlier
579+ # step raises; a real crash (segfault/abort) terminates the process and
580+ # is scored.
579581 if not _OOM_AVAILABLE:
580582 try:
581583 thunk()
582584 except BaseException:
583585 pass
584586 return
585- print("[OOM-SEQ] " + label, file=stderr)
587+ print("[OOM-SEQ] " + label + " window=" + str(window) , file=stderr)
586588 for _start in range(_OOM_MAX_START):
587589 if _OOM_VERBOSE:
588- print("[OOM-SEQ] start=" + str(_start) + " window=" + str(_OOM_WINDOW ), file=stderr)
589- if _OOM_WINDOW > 0:
590- _set_nomemory(_start, _start + _OOM_WINDOW )
590+ print("[OOM-SEQ] start=" + str(_start) + " window=" + str(window ), file=stderr)
591+ if window > 0:
592+ _set_nomemory(_start, _start + window )
591593 else:
592594 _set_nomemory(_start, 0)
593595 try:
@@ -1091,7 +1093,27 @@ def _generate_oom_function_call(
10911093 self .write (0 , ")" )
10921094 self .emptyLine ()
10931095
1094- def _write_oom_sequence (self , fn_name : str , seq_label : str , steps ) -> None :
1096+ def _oom_seq_randomize (self ) -> bool :
1097+ return bool (getattr (self .options , "oom_seq_randomize" , False ))
1098+
1099+ def _oom_pick_seq_len (self ) -> int :
1100+ """Step count for ONE sequence. With --oom-seq-randomize, uniform in [1, oom_seq_len]
1101+ (the configured value is the upper bound); otherwise the fixed oom_seq_len."""
1102+ n = max (1 , self .options .oom_seq_len )
1103+ if self ._oom_seq_randomize () and n > 1 :
1104+ return randint (1 , n )
1105+ return n
1106+
1107+ def _oom_pick_window (self ):
1108+ """Failure window for ONE sequence, or None to emit the harness default (_OOM_WINDOW).
1109+ With --oom-seq-randomize, uniform in [1, oom_window] (upper bound); otherwise None so
1110+ the generated oom_run() call is unchanged. window 0 (legacy fail-forever) is left as
1111+ the static default and never randomized into."""
1112+ if self ._oom_seq_randomize () and self .options .oom_window > 1 :
1113+ return randint (1 , self .options .oom_window )
1114+ return None
1115+
1116+ def _write_oom_sequence (self , fn_name : str , seq_label : str , steps , window = None ) -> None :
10951117 """Emit a guarded multi-step thunk plus an oom_run() call (Phase 4 sequence).
10961118
10971119 steps: list of (sublabel, target_expr, num_args), where target_expr is a string
@@ -1100,6 +1122,9 @@ def _write_oom_sequence(self, fn_name: str, seq_label: str, steps) -> None:
11001122 the shared state (a live instance, or module/interpreter globals such as a pending
11011123 exception) is what a later step may trip over. Result values are not threaded
11021124 between steps yet (Phase 4b); the steps interact only through shared state.
1125+
1126+ window: per-sequence failure window (--oom-seq-randomize); None emits the default
1127+ oom_run(label, thunk) call (uses the module-level _OOM_WINDOW).
11031128 """
11041129 self .write (0 , f"def { fn_name } ():" )
11051130 saved = self .addLevel (1 )
@@ -1121,7 +1146,10 @@ def _write_oom_sequence(self, fn_name: str, seq_label: str, steps) -> None:
11211146 if not wrote :
11221147 self .write (0 , "pass" )
11231148 self .restoreLevel (saved )
1124- self .write (0 , f'oom_run("{ seq_label } ", { fn_name } )' )
1149+ if window is None :
1150+ self .write (0 , f'oom_run("{ seq_label } ", { fn_name } )' )
1151+ else :
1152+ self .write (0 , f'oom_run("{ seq_label } ", { fn_name } , window={ window } )' )
11251153 self .emptyLine ()
11261154
11271155 def _generate_oom_function_sequence (self , prefix : str ) -> None :
@@ -1134,7 +1162,7 @@ def _generate_oom_function_sequence(self, prefix: str) -> None:
11341162 """
11351163 steps = []
11361164 names = []
1137- for j in range (max ( 1 , self .options . oom_seq_len )):
1165+ for j in range (self ._oom_pick_seq_len ( )):
11381166 func_name = choice (self .module_functions )
11391167 try :
11401168 func_obj = getattr (self .module , func_name )
@@ -1154,7 +1182,9 @@ def _generate_oom_function_sequence(self, prefix: str) -> None:
11541182 return
11551183 seq_label = f"{ prefix } :{ self .module_name } [" + ">" .join (names ) + "]"
11561184 self .write (0 , f"# OOM sequence: { ' > ' .join (names )} " )
1157- self ._write_oom_sequence (f"_oom_seq_{ prefix } " , seq_label , steps )
1185+ self ._write_oom_sequence (
1186+ f"_oom_seq_{ prefix } " , seq_label , steps , window = self ._oom_pick_window ()
1187+ )
11581188
11591189 def _generate_oom_class_fuzzing (self , prefix : str , class_name : str , class_obj : type ) -> None :
11601190 """Emits an OOM sweep over a class constructor and, on a live instance, its methods.
@@ -1203,7 +1233,7 @@ def _generate_oom_class_fuzzing(self, prefix: str, class_name: str, class_obj: t
12031233 # method B trips over (e.g. OOM-0035: write... then getvalue()).
12041234 steps = []
12051235 mnames = []
1206- for j in range (max ( 1 , self .options . oom_seq_len )):
1236+ for j in range (self ._oom_pick_seq_len ( )):
12071237 m_name = choice (method_names )
12081238 m_obj = methods [m_name ]
12091239 min_arg , max_arg = get_arg_number (m_obj , m_name , 0 )
@@ -1218,7 +1248,9 @@ def _generate_oom_class_fuzzing(self, prefix: str, class_name: str, class_obj: t
12181248 mnames .append (m_name )
12191249 seq_label = f"{ prefix } :{ self .module_name } .{ class_name } [" + ">" .join (mnames ) + "]"
12201250 self .write (0 , f"# OOM sequence on { class_name } : { ' > ' .join (mnames )} " )
1221- self ._write_oom_sequence (f"_oom_seq_{ prefix } " , seq_label , steps )
1251+ self ._write_oom_sequence (
1252+ f"_oom_seq_{ prefix } " , seq_label , steps , window = self ._oom_pick_window ()
1253+ )
12221254 else :
12231255 for j in range (self .options .oom_methods ):
12241256 m_name = choice (method_names )
0 commit comments