Skip to content

uuid.uuid1() returns corrupted uuids if called concurrently on free-threaded builds #150367

@Bobronium

Description

@Bobronium

Bug report

Bug description:

uuid.uuid1() produces UUIDs with corrupted variant bits when called concurrently with the GIL disabled.

The variant bits (byte 8, top 2 bits) should be 0b10 (RFC 4122) but are
observed as 0b11.

Workaround

If uuid._generate_time_safe is called at least once at import time, issue is no longer reproducible.

Reproduction
import os
import platform
import subprocess
import sys
import threading
import time
import uuid

THREADS = 32
SECONDS = 0.01
BATCH = 1024
RUNS = 1000

def valid_bytes(data):
    return (data[6] >> 4) == 1 and (data[8] & 0xC0) == 0x80


def print_corrupted(data, call_number):
    masked = data[:10] + b"\xcc" * 6
    v = uuid.UUID(bytes=masked)
    label = "FIRST CALL" if call_number == 1 else f"call #{call_number}"
    print(
        f"Bug reproduced on {label}:\n"
        f"  {v = !r}\n"
        f"  {v.bytes.hex() = !r}\n"
        f"  {v.clock_seq_hi_variant = }\n"
        f"  {v.version = !r}\n"
        f"  {v.variant = !r}\n"
        f"  variant_bits: {bin(masked[8] >> 6) = }\n"
        f"  {v.is_safe = !r}\n"
    )


def worker_main(mode):
    generate_time_safe = getattr(uuid, "_generate_time_safe", None)
    if generate_time_safe is None:
        print("uuid._generate_time_safe is not available")
        raise SystemExit(2)

    raw_mode = mode == "uuid._generate_time_safe"
    stop = threading.Event()
    barrier = threading.Barrier(THREADS + 1)
    corrupted = []

    def thread_main():
        barrier.wait()
        call_number = 0
        end = time.perf_counter() + SECONDS

        while time.perf_counter() < end and not stop.is_set():
            for _ in range(BATCH):
                call_number += 1
                if raw_mode:
                    data, _ = generate_time_safe()
                    ok = valid_bytes(data)
                else:
                    v = uuid.uuid1()
                    data = v.bytes
                    ok = v.version == 1 and v.variant == uuid.RFC_4122 and v.is_safe

                if not ok:
                    corrupted.append((data, call_number))
                    stop.set()
                    break

    threads = [threading.Thread(target=thread_main) for _ in range(THREADS)]
    any(t.start() for t in threads)
    barrier.wait()
    any(t.join() for t in threads)
    if corrupted:
        data, call_number = corrupted[0]
        print_corrupted(data, call_number)
        raise SystemExit(1)


def run_mode(mode):
    env = {**os.environ, "PYTHON_GIL": "0"}

    print(f"\nTrying: {mode}")
    for run in range(1, RUNS + 1):
        result = subprocess.run(
            [sys.executable, __file__, "worker", mode],
            env=env,
            text=True,
            capture_output=True,
        )
        if result.returncode:
            print()
            print(result.stdout.strip())
            if result.stderr.strip():
                print(result.stderr.strip())
            return result.returncode
        print("Run: ", run, end="\r")

    print("No failure after", RUNS, "runs")
    return 0


def master_main():
    print(f"Python: {sys.version.replace(chr(10), ' ')}")
    print(f"GIL: {getattr(sys, '_is_gil_enabled', lambda: 'unknown')()}")
    print(f"Platform: {platform.platform()}")
    print(f"Machine: {platform.machine()}")
    print(f"CPU: {platform.processor()}")
    print(f"CPU_COUNT: {os.cpu_count()}")
    print(f"uuid._generate_time_safe: {getattr(uuid, '_generate_time_safe', None)}")

    r1 = run_mode("uuid._generate_time_safe")
    if not r1:
        print(f"\nplain `_generate_time_safe` did not reproduce")
    else:
        print(f"\nplain `_generate_time_safe` reproduced; now checking public uuid.uuid1()")

    r2 = run_mode("uuid1")
    raise SystemExit(r1 or r2)


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "worker":
        worker_main(sys.argv[2])
    else:
        master_main()
Python: 3.14.3 free-threading build (main, Mar 10 2026, 18:06:03) [Clang 21.1.4 ]
GIL: False
Platform: macOS-15.0.1-arm64-arm-64bit-Mach-O
Machine: arm64
CPU: arm
CPU_COUNT: 8
uuid._generate_time_safe: <built-in function generate_time_safe>

Trying: uuid._generate_time_safe

Bug reproduced on call #2:
  v = UUID('c864280e-57c0-11f1-e2b2-cccccccccccc')
  v.bytes.hex() = 'c864280e57c011f1e2b2cccccccccccc'
  v.clock_seq_hi_variant = 226
  v.version = None
  v.variant = 'reserved for future definition'
  variant_bits: bin(masked[8] >> 6) = '0b11'
  v.is_safe = <SafeUUID.unknown: None>

plain `_generate_time_safe` reproduced; now checking public uuid.uuid1()

Trying: uuid1
Run:  18
Bug reproduced on FIRST CALL:
  v = UUID('c9c58206-57c0-11f1-d967-cccccccccccc')
  v.bytes.hex() = 'c9c5820657c011f1d967cccccccccccc'
  v.clock_seq_hi_variant = 217
  v.version = None
  v.variant = 'reserved for Microsoft compatibility'
  variant_bits: bin(masked[8] >> 6) = '0b11'
  v.is_safe = <SafeUUID.unknown: None>

CPython versions tested on:

3.14.3t, 3.15t

Operating systems tested on:

macOS, Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirpendingThe issue will be closed if no feedback is providedtype-bugAn unexpected behavior, bug, or error
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions