etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
gen_corpus.py (18931B)
1 #!/usr/bin/env python3 2 """ 3 setup-corpus.sh — generate seed corpus for libusb fuzzing harnesses. 4 5 Produces ~90 binary seed files across four subdirectories: 6 corpus/descriptor/ config/interface/endpoint chain seeds 7 corpus/bos/ BOS descriptor and device capability seeds 8 corpus/iad/ IAD two-pass parsing seeds 9 corpus/usbfs/ Raw device descriptor blob seeds (linux_usbfs path) 10 """ 11 12 import os 13 import struct 14 15 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 16 CORPUS_DIR = os.path.join(SCRIPT_DIR, "corpus") 17 18 def write_seed(subdir, name, data): 19 path = os.path.join(CORPUS_DIR, subdir, name) 20 os.makedirs(os.path.dirname(path), exist_ok=True) 21 with open(path, "wb") as f: 22 f.write(data) 23 print(f" {subdir}/{name} ({len(data)} bytes)") 24 25 # ─── CONFIG DESCRIPTOR SEEDS ───────────────────────────────────────────────── 26 # Format: [bLength][bDescriptorType=0x02][wTotalLength LE16][bNumInterfaces] 27 # [bConfigurationValue][iConfiguration][bmAttributes][MaxPower] 28 # [... interface + endpoint descriptors ...] 29 30 def config_header(total_len, num_ifaces=1, value=1): 31 return struct.pack("<BBHBBBBB", 32 9, 0x02, total_len, num_ifaces, value, 0, 0x80, 50) 33 34 def iface_desc(num_eps=1, iface_num=0, alt=0): 35 return struct.pack("<BBBBBBBBB", 36 9, 0x04, iface_num, alt, num_eps, 0xFF, 0x00, 0x00, 0) 37 38 def endpoint_desc(addr=0x81, attrs=0x02, mps=512): 39 return struct.pack("<BBBBHB", 40 7, 0x05, addr, attrs, mps, 0) 41 42 print("[*] Generating descriptor/ seeds...") 43 44 # Minimal valid: 9-byte header, zero interfaces, wTotalLength=9 45 write_seed("descriptor", "config_minimal_valid", 46 config_header(9, num_ifaces=0)) 47 48 # One interface + one endpoint (total = 9+9+7 = 25 bytes) 49 body = config_header(25, num_ifaces=1) + iface_desc(num_eps=1) + endpoint_desc() 50 write_seed("descriptor", "config_one_iface_one_ep", body) 51 52 # One interface + max endpoints (32 endpoints = 9+9+32*7 = 242 bytes) 53 eps = b"".join(endpoint_desc(addr=0x80 | i, attrs=i % 4) for i in range(32)) 54 total = 9 + 9 + len(eps) 55 body = config_header(total, num_ifaces=1) + iface_desc(num_eps=32) + eps 56 write_seed("descriptor", "config_max_endpoints", body) 57 58 # wTotalLength > actual data (triggers short-read path) 59 write_seed("descriptor", "config_wtotallength_mismatch", 60 config_header(0xFFFF, num_ifaces=1) + iface_desc(1) + endpoint_desc()) 61 62 # wTotalLength = 0 63 write_seed("descriptor", "config_wtotallength_zero", 64 struct.pack("<BBHBBBBB", 9, 0x02, 0, 0, 1, 0, 0x80, 50)) 65 66 # wTotalLength = 1 (below minimum) 67 write_seed("descriptor", "config_wtotallength_one", 68 struct.pack("<BBHBBBBB", 9, 0x02, 1, 0, 1, 0, 0x80, 50)) 69 70 # bNumInterfaces = 32 (USB_MAXINTERFACES limit) 71 ifaces = b"".join(iface_desc(0, i) for i in range(32)) 72 total = 9 + len(ifaces) 73 write_seed("descriptor", "config_bNumInterfaces_32", 74 config_header(total, num_ifaces=32) + ifaces) 75 76 # bNumInterfaces = 255 (over-max, exercises the > USB_MAXINTERFACES check) 77 write_seed("descriptor", "config_bNumInterfaces_255", 78 struct.pack("<BBHBBBBB", 9, 0x02, 9, 255, 1, 0, 0x80, 50)) 79 80 # bLength = 1 (below LIBUSB_DT_CONFIG_SIZE=9) 81 write_seed("descriptor", "config_bLength_1", 82 struct.pack("<BB", 1, 0x02) + b"\x00" * 16) 83 84 # bLength = 2 (just a header) 85 write_seed("descriptor", "config_bLength_2", 86 struct.pack("<BB", 2, 0x02) + b"\x00" * 16) 87 88 # Truncated at various offsets 89 for n in [0, 1, 4, 8]: 90 write_seed("descriptor", f"config_truncated_{n}", b"\x09\x02" + b"\x20\x00" + b"\x00" * n) 91 92 # All 0xFF bytes 93 write_seed("descriptor", "config_all_ff", b"\xff" * 64) 94 95 # All 0x00 bytes (hits multiple early-return paths) 96 write_seed("descriptor", "config_all_zeros", b"\x00" * 32) 97 98 # Class-specific descriptor before interface (bDescriptorType=0x24) 99 class_desc = struct.pack("<BBB", 4, 0x24, 0x01) # 4-byte class desc 100 total = 9 + len(class_desc) + 9 + 7 101 body = config_header(total) + class_desc + iface_desc(1) + endpoint_desc() 102 write_seed("descriptor", "config_class_desc_before_iface", body) 103 104 # Two interfaces, two alt settings each 105 alts = iface_desc(1, 0, 0) + iface_desc(1, 0, 1) + iface_desc(1, 1, 0) + iface_desc(1, 1, 1) 106 eps2 = endpoint_desc() * 4 107 total = 9 + len(alts) + len(eps2) 108 write_seed("descriptor", "config_two_ifaces_two_alts", 109 config_header(total, num_ifaces=2) + alts + eps2) 110 111 # Audio endpoint (bLength=9 for synch fields) 112 audio_ep = struct.pack("<BBBBHBBB", 9, 0x05, 0x81, 0x05, 256, 1, 0, 0) 113 total = 9 + 9 + len(audio_ep) 114 write_seed("descriptor", "config_audio_endpoint", 115 config_header(total) + iface_desc(1) + audio_ep) 116 117 # ─── BOS DESCRIPTOR SEEDS ──────────────────────────────────────────────────── 118 # Format: [bLength=5][bDescriptorType=0x0F][wTotalLength LE16][bNumDeviceCaps] 119 # [capability descriptors...] 120 121 def bos_header(total_len, num_caps): 122 return struct.pack("<BBHB", 5, 0x0F, total_len, num_caps) 123 124 def cap_usb20_ext(): 125 # bLength=7, bDescriptorType=0x10, bDevCapabilityType=0x02, bmAttributes=LE32 126 return struct.pack("<BBB I", 7, 0x10, 0x02, 0x00000002) 127 128 def cap_ss(): 129 # bLength=10, type=0x10, captype=0x03, bmAttr, wSpeedsSupported, bFuncSupport, bU1, bU2 130 return struct.pack("<BBB B H B B B", 10, 0x10, 0x03, 0, 0x000E, 1, 10, 40) 131 132 def cap_container_id(): 133 # bLength=20, type=0x10, captype=0x04, bReserved=0, ContainerID[16] 134 return struct.pack("<BBB B", 20, 0x10, 0x04, 0) + b"\xDE\xAD\xBE\xEF" * 4 135 136 def cap_ssplus(num_sublinks): 137 # bLength = 12 + 4*num_sublinks 138 # bmAttr lower nibble = (num_sublinks - 1) for sublinks count 139 blen = 12 + 4 * num_sublinks 140 bmattr = struct.pack("<I", (num_sublinks - 1) & 0xF) 141 sublinks = struct.pack("<I", 0x000A4000) * num_sublinks # lane speed attr 142 return struct.pack("<BBB", blen, 0x10, 0x0A) + bmattr + struct.pack("<HBB", 0, 0, 0) + sublinks 143 144 def cap_platform(cap_data_len): 145 # bLength = 3 + 1 + 16 + cap_data_len = 20 + cap_data_len 146 blen = 20 + cap_data_len 147 uuid = b"\x38\xB6\x08\xBF\xD0\xBB\x3D\x4B\x87\xC0\x52\x0E\x3B\x8F\xE8\xB7" # WinUSB UUID 148 return struct.pack("<BBB B", blen, 0x10, 0x05, 0) + uuid + b"\x00" * cap_data_len 149 150 print("[*] Generating bos/ seeds...") 151 152 # Minimal BOS: 5-byte header, no capabilities 153 write_seed("bos", "bos_minimal", bos_header(5, 0)) 154 155 # One USB 2.0 extension capability 156 c = cap_usb20_ext() 157 write_seed("bos", "bos_one_usb20_ext", bos_header(5 + len(c), 1) + c) 158 159 # One SuperSpeed capability 160 c = cap_ss() 161 write_seed("bos", "bos_one_ss_cap", bos_header(5 + len(c), 1) + c) 162 163 # One Container ID capability 164 c = cap_container_id() 165 write_seed("bos", "bos_one_container_id", bos_header(5 + len(c), 1) + c) 166 167 # SSPlus with 1 sublink (minimum, triggers sublink loop) 168 c = cap_ssplus(1) 169 write_seed("bos", "bos_ssplus_1_sublink", bos_header(5 + len(c), 1) + c) 170 171 # SSPlus with 15 sublinks (high sublink count, stresses OOB path at line 1115) 172 c = cap_ssplus(15) 173 write_seed("bos", "bos_ssplus_15_sublinks", bos_header(5 + len(c), 1) + c) 174 175 # Platform with 0 cap_data bytes (bLength=20, minimum, edge case for underflow) 176 c = cap_platform(0) 177 write_seed("bos", "bos_platform_cap_data_0", bos_header(5 + len(c), 1) + c) 178 179 # Platform with 4 cap_data bytes (bLength=24, normal) 180 c = cap_platform(4) 181 write_seed("bos", "bos_platform_cap_data_4", bos_header(5 + len(c), 1) + c) 182 183 # Platform with bLength manually set to 19 (below MIN=20, triggers check) 184 raw = bytearray(cap_platform(0)) 185 raw[0] = 19 186 write_seed("bos", "bos_platform_blen_19", bos_header(5 + 19, 1) + bytes(raw[:19])) 187 188 # Platform with bLength=18 (well below minimum) 189 raw[0] = 18 190 write_seed("bos", "bos_platform_blen_18", bos_header(5 + 18, 1) + bytes(raw[:18])) 191 192 # All 5 capability types in one BOS 193 caps = cap_usb20_ext() + cap_ss() + cap_container_id() + cap_ssplus(2) + cap_platform(2) 194 write_seed("bos", "bos_all_five_caps", bos_header(5 + len(caps), 5) + caps) 195 196 # bNumDeviceCaps=255 with truncated data (tests allocation vs actual caps) 197 write_seed("bos", "bos_bNumCaps_255_truncated", 198 bos_header(5 + 7, 255) + cap_usb20_ext()) 199 200 # wTotalLength=0xFFFF 201 write_seed("bos", "bos_wtotal_max", 202 struct.pack("<BBHB", 5, 0x0F, 0xFFFF, 1) + cap_usb20_ext()) 203 204 # All zeros 205 write_seed("bos", "bos_all_zeros", b"\x00" * 16) 206 207 # Truncated BOS (4 bytes — below LIBUSB_DT_BOS_SIZE=5) 208 write_seed("bos", "bos_truncated_4", b"\x05\x0f\x05\x00") 209 210 # Unknown capability type (bDevCapabilityType=0xFF) 211 c = struct.pack("<BBB I", 7, 0x10, 0xFF, 0xDEADBEEF) 212 write_seed("bos", "bos_unknown_cap_type", bos_header(5 + len(c), 1) + c) 213 214 # Capability with bLength=2 (below 3-byte minimum) 215 c = struct.pack("<BB", 2, 0x10) + b"\xFF" * 5 216 write_seed("bos", "bos_cap_blen_2", bos_header(5 + 2, 1) + c[:2]) 217 218 # SSPlus bmAttributes sublink count = 0 (no sublink speed attrs) 219 c = struct.pack("<BBB I H B B", 12, 0x10, 0x0A, 0, 0, 0, 0) 220 write_seed("bos", "bos_ssplus_0_sublinks", bos_header(5 + len(c), 1) + c) 221 222 # ─── IAD SEEDS ─────────────────────────────────────────────────────────────── 223 # IAD embedded within a config descriptor 224 # [config header 9B][IAD 8B each][interface descriptors 9B each] 225 226 def iad_desc(first_iface=0, count=2, func_class=0xEF): 227 return struct.pack("<BBBBBBBB", 228 8, 0x0B, first_iface, count, func_class, 0x02, 0x01, 0) 229 230 print("[*] Generating iad/ seeds...") 231 232 # No IADs (just a valid config) 233 body = config_header(9, 0) 234 write_seed("iad", "iad_none", body) 235 236 # One IAD + two interfaces 237 iad = iad_desc(0, 2) 238 ifaces = iface_desc(1, 0) + iface_desc(1, 1) 239 total = 9 + len(iad) + len(ifaces) + 7 * 2 240 body = config_header(total, 2) + iad + ifaces + endpoint_desc() + endpoint_desc(0x02) 241 write_seed("iad", "iad_one", body) 242 243 # Two IADs with non-overlapping interface ranges 244 iad1 = iad_desc(0, 2) 245 iad2 = iad_desc(2, 2) 246 ifaces = b"".join(iface_desc(0, i) for i in range(4)) 247 total = 9 + len(iad1) + len(iad2) + len(ifaces) 248 write_seed("iad", "iad_two_sequential", 249 config_header(total, 4) + iad1 + iad2 + ifaces) 250 251 # Two IADs with overlapping interface ranges 252 iad_overlap1 = iad_desc(0, 3) 253 iad_overlap2 = iad_desc(1, 3) 254 ifaces = b"".join(iface_desc(0, i) for i in range(4)) 255 total = 9 + len(iad_overlap1) + len(iad_overlap2) + len(ifaces) 256 write_seed("iad", "iad_two_overlapping", 257 config_header(total, 4) + iad_overlap1 + iad_overlap2 + ifaces) 258 259 # bInterfaceCount = 0 260 iad_zero = struct.pack("<BBBBBBBB", 8, 0x0B, 0, 0, 0xEF, 0x02, 0x01, 0) 261 write_seed("iad", "iad_bInterfaceCount_0", 262 config_header(9 + 8, 0) + iad_zero) 263 264 # bInterfaceCount = 255 265 iad_max = struct.pack("<BBBBBBBB", 8, 0x0B, 0, 255, 0xEF, 0x02, 0x01, 0) 266 write_seed("iad", "iad_bInterfaceCount_255", 267 config_header(9 + 8, 0) + iad_max) 268 269 # IAD bLength = 2 (minimum header — too short for IAD but passes header check) 270 iad_short = struct.pack("<BB", 2, 0x0B) + b"\xFF" * 6 271 write_seed("iad", "iad_bLength_2", 272 config_header(9 + 2, 0) + iad_short[:2]) 273 274 # IAD bLength = 7 (one below standard 8) 275 iad_7 = struct.pack("<BBBBBBB", 7, 0x0B, 0, 2, 0xEF, 0x02, 0x01) 276 write_seed("iad", "iad_bLength_7", 277 config_header(9 + 7, 0) + iad_7) 278 279 # Many IADs (tests uint8_t count field — 32 IADs) 280 iads_32 = b"".join(iad_desc(i * 2, 2) for i in range(32)) 281 total = 9 + len(iads_32) 282 write_seed("iad", "iad_32_entries", 283 config_header(total, 0) + iads_32) 284 285 # IAD after interface (not at start of config data) 286 iface_first = iface_desc(0, 0) 287 iad_after = iad_desc(1, 1) 288 iface_second = iface_desc(0, 1) 289 total = 9 + len(iface_first) + len(iad_after) + len(iface_second) 290 write_seed("iad", "iad_after_interface", 291 config_header(total, 2) + iface_first + iad_after + iface_second) 292 293 # All zeros (hits early-return paths in both passes) 294 write_seed("iad", "iad_all_zeros", b"\x00" * 32) 295 296 # ─── USBFS SEEDS ───────────────────────────────────────────────────────────── 297 # Format: [18-byte device descriptor][config descriptor(s)] 298 # The device descriptor's bNumConfigurations controls how many configs are expected. 299 300 def device_desc(num_configs=1, vendor=0x0483, product=0x5740): 301 return struct.pack("<BBHBBBBHHHBBBB", 302 18, 0x01, # bLength, bDescriptorType 303 0x0200, # bcdUSB (USB 2.0) 304 0x00, # bDeviceClass 305 0x00, # bDeviceSubClass 306 0x00, # bDeviceProtocol 307 64, # bMaxPacketSize0 308 vendor, # idVendor 309 product, # idProduct 310 0x0100, # bcdDevice 311 1, 2, 3, # iManufacturer, iProduct, iSerialNumber 312 num_configs) 313 314 print("[*] Generating usbfs/ seeds...") 315 316 # Valid 1-config device 317 dev = device_desc(1) 318 cfg = config_header(25) + iface_desc(1) + endpoint_desc() 319 write_seed("usbfs", "usbfs_valid_1config", dev + cfg) 320 321 # Valid 2-config device 322 dev = device_desc(2) 323 cfg1 = config_header(18, 1, value=1) + iface_desc(0) 324 cfg2 = config_header(18, 1, value=2) + iface_desc(0) 325 write_seed("usbfs", "usbfs_valid_2configs", dev + cfg1 + cfg2) 326 327 # bNumConfigurations = 0 (should return immediately) 328 write_seed("usbfs", "usbfs_bNumConfigs_0", device_desc(0)) 329 330 # bNumConfigurations = 255 with no config data (only device desc) 331 write_seed("usbfs", "usbfs_bNumConfigs_255_no_data", device_desc(255)) 332 333 # bNumConfigurations = 255 with one small config (loop runs until remaining exhausted) 334 write_seed("usbfs", "usbfs_bNumConfigs_255_one_config", 335 device_desc(255) + config_header(9, 0)) 336 337 # wTotalLength > remaining (truncation path) 338 dev = device_desc(1) 339 cfg = struct.pack("<BBHBBBBB", 9, 0x02, 0xFFFF, 0, 1, 0, 0x80, 50) 340 write_seed("usbfs", "usbfs_wtotallength_overflow", dev + cfg) 341 342 # Device descriptor only (no config data) 343 write_seed("usbfs", "usbfs_device_desc_only", device_desc(1)) 344 345 # All zeros (18+ bytes — hits bDescriptorType != LIBUSB_DT_CONFIG check) 346 write_seed("usbfs", "usbfs_all_zeros", b"\x00" * 32) 347 348 # Short descriptor (17 bytes — below LIBUSB_DT_DEVICE_SIZE=18, harness skips) 349 write_seed("usbfs", "usbfs_short_17", b"\x12\x01" + b"\x00" * 15) 350 351 # Device descriptor + config with bLength < LIBUSB_DT_CONFIG_SIZE (bad config) 352 dev = device_desc(1) 353 bad_cfg = struct.pack("<BB", 1, 0x02) + b"\x00" * 20 354 write_seed("usbfs", "usbfs_config_blen_too_short", dev + bad_cfg) 355 356 # Normal device + config where bDescriptorType != 0x02 (not a config desc) 357 dev = device_desc(1) 358 not_cfg = struct.pack("<BBHBBBBB", 9, 0x04, 9, 0, 1, 0, 0x80, 50) # type=interface 359 write_seed("usbfs", "usbfs_wrong_config_type", dev + not_cfg) 360 361 # wTotalLength = 0 (below minimum) 362 dev = device_desc(1) 363 zero_len = struct.pack("<BBHBBBBB", 9, 0x02, 0, 0, 1, 0, 0x80, 50) 364 write_seed("usbfs", "usbfs_wtotallength_0", dev + zero_len) 365 366 # ─── EXTRA / ENDPOINT COMPANION SEEDS ─────────────────────────────────────── 367 # Seeds for fuzz_extra.c: config + endpoint with SS companion extra data. 368 # LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30, size = 6 bytes. 369 370 def ss_companion(blen=6, bulk_max_burst=15, bmAttr=0, wBytesPerInterval=0): 371 return struct.pack("<BBBBH", blen, 0x30, bulk_max_burst, bmAttr, wBytesPerInterval) 372 373 print("[*] Generating extra/ seeds...") 374 375 # Valid endpoint + SS companion (6 bytes) 376 comp = ss_companion(6) 377 ep = endpoint_desc(0x81, 0x02, 512) # bulk-in 378 total = 9 + 9 + len(ep) + len(comp) 379 body = config_header(total) + iface_desc(1) + ep + comp 380 write_seed("extra", "extra_ep_with_companion", body) 381 382 # Endpoint + companion with bLength=5 (below LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE=6) 383 comp_short = ss_companion(5) 384 total = 9 + 9 + len(ep) + 5 385 body = config_header(total) + iface_desc(1) + ep + comp_short[:5] 386 write_seed("extra", "extra_companion_blen_5", body) 387 388 # Endpoint + companion with bLength=2 (minimum header only) 389 total = 9 + 9 + len(ep) + 2 390 body = config_header(total) + iface_desc(1) + ep + b"\x02\x30" 391 write_seed("extra", "extra_companion_blen_2", body) 392 393 # Endpoint + companion with bLength=0 (zero — hits bLength < 2 guard) 394 total = 9 + 9 + len(ep) + 2 395 body = config_header(total) + iface_desc(1) + ep + b"\x00\x30" 396 write_seed("extra", "extra_companion_blen_0", body) 397 398 # Multiple endpoints each with a companion 399 eps_with_comp = b"" 400 for i in range(4): 401 eps_with_comp += endpoint_desc(0x80 | (i + 1), 0x02, 512) + ss_companion(6, i, 0, 0) 402 total = 9 + 9 + len(eps_with_comp) 403 body = config_header(total) + iface_desc(4) + eps_with_comp 404 write_seed("extra", "extra_four_eps_with_companions", body) 405 406 # Endpoint with class-specific extra data (not a companion) before the companion 407 class_extra = struct.pack("<BBB", 3, 0x24, 0x01) 408 comp = ss_companion(6) 409 total = 9 + 9 + len(ep) + len(class_extra) + len(comp) 410 body = config_header(total) + iface_desc(1) + ep + class_extra + comp 411 write_seed("extra", "extra_class_desc_then_companion", body) 412 413 # No endpoints (nothing to walk) 414 write_seed("extra", "extra_no_endpoints", 415 config_header(9, 0)) 416 417 # Many class-specific descriptors between interfaces (seeds config->extra_length int accumulation) 418 # 30 interfaces, each preceded by several 3-byte class-specific descriptors 419 class_blobs = struct.pack("<BBB", 3, 0x24, 0xFF) * 30 420 ifaces_many = b"" 421 for i in range(30): 422 ifaces_many += class_blobs + iface_desc(0, i) 423 total = 9 + len(ifaces_many) 424 body = config_header(total, 30) + ifaces_many 425 write_seed("extra", "extra_class_desc_accumulation", body) 426 427 # ─── ADDITIONAL DESCRIPTOR SEEDS ───────────────────────────────────────────── 428 # Extra seeds to stress config->extra_length signed int accumulation. 429 430 print("[*] Generating additional descriptor/ seeds...") 431 432 # Config with many interfaces each with large class-specific blobs (bLength=0xFF) 433 # Accumulates config->extra_length toward INT_MAX with enough mutations 434 big_class = struct.pack("<BB", 0xFF, 0x24) + b"\x42" * 253 # 255-byte class desc 435 ifaces_with_big_class = b"" 436 for i in range(8): 437 ifaces_with_big_class += big_class + iface_desc(0, i) 438 total = 9 + len(ifaces_with_big_class) 439 body = config_header(total, 8) + ifaces_with_big_class 440 write_seed("descriptor", "config_large_class_descs", body) 441 442 # Config where every byte after the header is a class-specific descriptor type 443 class_chain = b"" 444 for _ in range(50): 445 class_chain += struct.pack("<BBB", 3, 0x25, 0x00) # 3-byte class desc, type 0x25 446 total = 9 + len(class_chain) + 9 # one interface at the end 447 body = config_header(total, 1) + class_chain + iface_desc(0) 448 write_seed("descriptor", "config_class_desc_chain", body) 449 450 print(f"\n[+] Corpus generated in {CORPUS_DIR}/") 451 for d in ["descriptor", "bos", "iad", "usbfs", "extra"]: 452 path = os.path.join(CORPUS_DIR, d) 453 if os.path.isdir(path): 454 n = len(os.listdir(path)) 455 print(f" {d}/: {n} seeds")