ight years. Exactly so much condition race CVE-2017-2636 (CVSS 7.0 HIGH) lived quietly in the mainline Linux core. The Committee be10eb75893 brought the race into the driver n_hdlc in 2009, syzkaller came across a suspicious crash only in 2017 - and between the detection and stable reproduction passed the days of manual fiddling: reassembling the kernel with point mdelay(), overkill delays in nanosecond range, linking streams to CPU cores through sched_setaffinity(). Who has ever done this - knows the feeling: you do not explore the bug, you play roulette with the planner.
In September 2026, Project Zero laid out MAccConc - Memory Access Concurrency - a toolkit that translates this process from shamanism to an algorithm. Tracing memory calls via ASAN outline and KCOV determines the points of potential racing, and the stack-based delay injection automatically checks each alternation. Below is a review of the approach from the theory of communication points to the practice of A-B-A testing, with the anatomy of real CVE and the nuances of CWE-classification, which Russian-language sources have not yet covered.
Why is race condition in the core difficult to confirm
Race condition - vulnerabilities in which multithreaded execution must occur with a certain alternation (interleaving) for the manifestation of the bug. Publication Project Zero (Testing race conditions with access memory tracing and stack-based delay) highlights three scenarios where it creates a headache. More details - in our material about binary analysis of vulnerabilities.
Confirmation of candidates. When manually auditing the core code, you find a potential race condition - but it is often impossible to prove or refute it without modifying the kernel. Standard approach: recompilation with conditional challenges mdelay() (backlink for a given time) at strategic points. The condition is usually tied to the current flow name, sometimes more difficult. On platforms with DTrace (macOS, Windows) samples with chill(), but DTrace traces only at non-inline functions boundaries or explicit tracepoints - not on every instruction. In both cases, samples and errors.
Regression testing core. After fixing the race, there is no reliable way to write a regression test that consistently reproduces the bug in the CI/CD-pipeline. A characteristic feature: patches for race condition in the Linux kernel are regularly accompanied by hand-drawn ASCII-diagrams of problematic alternations with call graphs and memory referrals. Before MACCC, there was no automated tool for generating such diagrams.
Phasing Linux kernel. It is difficult for the phasser to retake all the interesting alternations of competitive operations or to get to the execution paths that are activated only during the race. Syzkaller finds race condition "accidentally" - through the massive parallel launch of syscals on multi-core VM. A purposeful study of alternations in phasing is an unsolved task, and MAccConc is laying nuclear infrastructure for this.
MACCCConc: Project Zero's kernel memory tracing toolkit
MACCCConc is a set of tools to investigate possible alternations of multi-threaded test cases in the Linux kernel, available on GitHub. Three components:
Automatic tester - goes over all A-B-A alternation of the test case, injecting delays at each potential point in the race.
Terminal UI - manual study of alternations in the terminal with navigation on communication points.
GUI is a graphical interface for visualizing alternations and tracing.
The nuclear part of MAccConc is also designed for integration with phasers, although the custom strapping for this is not yet written.
Ideological predecessors: sockfuzter Neda Williamson (a user planner with redevelopment on synchronization primitives for concurrency bugs research) and the SKI project (recording memory calls and managing vCPU planning through patched QEMU in TCG mode with VM snapshots for overkill alternations).
ASAN outline and KCOV: the basis of the tooling
To detect potential races, you need a mechanism to collect trace messages to memory. SKI solved this with a patch to QEMU TCG. MAccConc goes fundamentally another way - ASAN-instrumentation in outline mode.
When compiling the core with CONFIG_KASAN_OUTLINE (backend flag asan-instrumentation-with-call-threshold=0) the compiler generates helper functions calls at each memory call. Usually, ASAN combines callback and for serial calls - the Linux kernel explicitly disables this backend-flag optimization asan-opt-same-temp, to get one callback for each appeal. By default, ASAN does not tool the appeals to global variables - it is also disabled by the flag asan-opt-globals.
To transfer tracing to userspace, MACConc uses KCOV - a mechanism originally created to transfer the coverage of the base blocks to custom phasers (syzkaller works through KCOV). Why KCOV and not ftrace? The publication of Project Zero gives three arguments: a simple in-memory representation of data (critical for recovery from the crumbled VM); static always-on tooling with a near-zero overhead in the off state; designing for high-frequency trade events (referrals to memory by orders of magnitude more than calls of functions).
The principal solution is the tooling in the core, not the emulator. This allows for potentially collecting high-level information about lock-in captures and releases, and theoretically testing on bare-metal.
Blind spot ASAN outline. ASAN is designed to detect use-after-free and does not generate callbacks for direct referrals to stek memory (unless there is potential out-of-bounds). Racing with on-stack objects - waits, completed structures - can fly by. The alternative is TSAN-instrumentation, sharpened under data race and giving information about the atom of the appeals. But the compilers do not support the simultaneous generation of ASAN and TSAN hooks - to save the UAF working detector when using TSAN, it would be necessary to start the ASAN-sale of the kernel on top of TSAN hooks or modify the compiler. At the time of the publication of Project Zero, this barrier has not been overcome.
Communication points: search for potential races
The concept is borrowed from the work of SKI: communication points - a pair of memory calls on different streams that can interact. Criterion: At least one message is a record, and the address ranges overlap.
Search algorithm:
Collect the trace of messages to the memory of all streams of the test case through KCOV.
Find pairs (flow A, stream B) satisfying the contact point criterion.
For each pair, check whether the change in the order of execution leads to different behaviors.
This reduces the task from "to re-take all the alternations of all instructions" (exponential complexity) to "check the final many points of communication." For a test case with thousands of messages to memory, the number of communication points is less than the total number of possible alternations - and automatic overtaking becomes practicable.
Count-augmented stack traces: stable identification of memory calls
To test the various orders of appeals, you need a way to consistently identify a specific appeal between the test case launches. Two naive approaches do not work:
At the data address - if the object is allocated again at each start, the address changes due to ASLR and the state of the slab-allocator. At the address of the instruction - does not work for calls from generic-functions such as memcpy() or spin_lock(), caused from a bunch of contexts.
The MAccConc solution is count-augmented stack traces: each access to memory is identified by a complete stack track, supplemented by a counter - how many times the same stack track has already met in the current KCOV-collection session.
A specific example: if spin_lock() summoned twice from n_hdlc_send_frames() with identical call stack - the first appeal receives count=0, the second count=1. The next time you start the test case, the appeals will receive the same identifiers, even if the absolute addresses of the objects have gone away. The author of MACCC distinguishes this approach as the central theoretical contribution of the project - and in the case: without stable identification, the entire automatic overkill is crumbled.
Stack-based delay injection: stream alternation control
When communication points are detected and consistently identified, MACConc uses a collision transfer to enforce the order of referrals to memory. "Stack-based" - because the delay is tied to the stack-track: not "somewhere in the function X", but "with the N-m of the message to memory with the data count-augmented stack trace." No kernel reassembly is needed - the injection point is determined algorithmically at each start.
Constraint-style and fully order specified
MAccConc implements two delay injection modes:
Constraint-style (A-happens-before-B) sets a single limitation: the circulation A on the stream 1 must occur before circulation B on stream 2. The delay insert on the stream 2 at point B until the flow 1 executes A. The minimally invasive approach - fixes the order of one pair, everything else is performed freely.
Fully specified ordering (context-switch-style) - a complete specification of the order of all memory calls, emulating the deterministic context switching. Allows you to check a specific alternation scenario up to each instruction. Computing is more expensive, but full control over interleaving.
The fundamental difference from the manual approach with mdelay(): the injection is tied to a stable identifier (count-augmented stack track) rather than a position in the source code. Reproducible without modification of the kernel.
Automatic testing and regression testing kernels
Automatic tester MAccConc goes over all A-B-A alternations of the test case:
Running a test case with memory tracing via KCOV.
Identification of communication points between streams.
Generation of sympot (A-happens-before-B) for each communication point pair.
Re-run with injected delay.
Fixation of the result: KASAN-report, kernel panic, incorrect state.
For KCOV remote coverage, some of the subsystems are already integrated (bluetooth, USB). For the processing of loopback-packs and RCU callbacks - key participants of many race condition - the author of MACCConc has a draft-patch, but in upstream these subsystems are not yet connected to remote coverage.
Directions of development indicated in the publication Project Zero:
Human-readable memory access traces - adding information about types to the tracing of calls.
High-level semantics: not just an address, but the identification of a kernel object.
Detection of impossible alternations through the detection of deadlocks - acceleration of overkill by cutting off deliberately unrealized orderings.
Building test cases with potential contact points on the Snowboard model (incremental test case build-up instead of a complete overkill).
Output KCOV in host-shared memory to reduce overhead when phasing the Linux kernel.
Operation of condition race in the core: CVE-2017-2636 and classification CWE
CVE-2017-2636 - a model example of condition race, for confirmation and regression testing which MAccConc would become indispensable.
The point. Race condition in drivers/tty/n_hdlc.c Linux kernel to version 4.10.1. CVSS 7.0 HIGH, vector CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H. Local access (AV:L), high difficulty - you need to win the race (AC:H), low privileges (PR:L), without user interaction (UI:N), full impact on privacy, integrity and accessibility. Operation leads to increased privileges to root - Exploitation for Privilege Escalation (T1068, privilege).
Mechanics of the race. Functions flush_tx_queue() and n_hdlc_send_frames() competitively address the pointer n_hdlc.tbuf. If the data transfer error is error, the buffer is saved in this pointer. When performed simultaneously, both functions can double-place one buffer on the list tx_free_buf_list - double free when calling n_hdlc_release(). Buffers n_hdlc_buf allocated in slab-cache kmalloc-8192.
A chain of attack. Module n_hdlc downloads automatically when you install the N_HDLC protocol for a sequential line - it is enough to open the pseudoterminal through /dev/ptmx and to call ioctl(ptmd, TIOCSETD, &ldisc). Further exploit (described in the publication xakep.ru) uses sk_buff with destructor_arg in skb_shared_info to overwrite the pointer to the function. The ~7500 bytes fitting package skb_shared_info in kmalloc-8192 - the same cache where the double free happened. Heap testing through pairs of sockets gives two copies sk_buff with the field head, referring to one area of memory. The approach is borrowed from decommissioning CVE-2016-2384 (double free in snd_usbmidi_create, EDB-41999, by Andrey Konovalov).
To play the race, the exploit uses stream synchronization on pthread_barrier, variable delays tmo1/tmo2 in the idle cycle and linking flows through sched_setaffinity(). MACCC automatics this manual delay injection: instead of overkilling increment delays loop % MAX_RACE_LAG_USEC - automatic definition of communication points between flush_tx_queue() and n_hdlc_send_frames() and ast-style injection at points of reference to n_hdlc.tbuf.
CWE classification: discrepancy between sources. NVD classifies CVE-2017-2636 At the same time as CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization) and CWE-415 (Double Free). Red Hat in RHSA-2017:0892 only indicates CWE-362 and estimates CVSS v3 base at 7.8 (above NVD-shny 7.0). At the same time KASAN when triggered fixes the error as use-after-free (CWE-416) occurring just before the second kfree().
This is not a contradiction - it is different projections of one bug. CWE-362 describes the root cause (race condition, lack of synchronization of kernel flows). CWE-415 - direct consequence (double release of one block). CWE-416 - how the error manifests itself in operation, when between the first and second kfree() slab-allocator reuses the block for another object. A similar discrepancy is characteristic of CVE-2016-2384: NVD describes as "Double free vulnerability," Red Hat classifies as CWE-416 (Use After Free).
Scope. Vulnerability affected RHEL 6/7 (RHSA-2017:0892, package kernel-0:2.6.32-696.1.1.el6), Fedora, SUSE, Debian (DSA-3804-1, fixed in linux 3.16.39-1+deb8u2 for jessie) and Ubuntu (USN-3220-3, USN-3221-1/2). EPSS - 0.0102, 61st percentile (above the median). All distributions with CONFIG_N_HDLC=m were vulnerable.
MSG_OOB and transfer injection in the context of operation
In September 2026, Project Zero laid out MAccConc - Memory Access Concurrency - a toolkit that translates this process from shamanism to an algorithm. Tracing memory calls via ASAN outline and KCOV determines the points of potential racing, and the stack-based delay injection automatically checks each alternation. Below is a review of the approach from the theory of communication points to the practice of A-B-A testing, with the anatomy of real CVE and the nuances of CWE-classification, which Russian-language sources have not yet covered.
Why is race condition in the core difficult to confirm
Race condition - vulnerabilities in which multithreaded execution must occur with a certain alternation (interleaving) for the manifestation of the bug. Publication Project Zero (Testing race conditions with access memory tracing and stack-based delay) highlights three scenarios where it creates a headache. More details - in our material about binary analysis of vulnerabilities.
Confirmation of candidates. When manually auditing the core code, you find a potential race condition - but it is often impossible to prove or refute it without modifying the kernel. Standard approach: recompilation with conditional challenges mdelay() (backlink for a given time) at strategic points. The condition is usually tied to the current flow name, sometimes more difficult. On platforms with DTrace (macOS, Windows) samples with chill(), but DTrace traces only at non-inline functions boundaries or explicit tracepoints - not on every instruction. In both cases, samples and errors.
Regression testing core. After fixing the race, there is no reliable way to write a regression test that consistently reproduces the bug in the CI/CD-pipeline. A characteristic feature: patches for race condition in the Linux kernel are regularly accompanied by hand-drawn ASCII-diagrams of problematic alternations with call graphs and memory referrals. Before MACCC, there was no automated tool for generating such diagrams.
Phasing Linux kernel. It is difficult for the phasser to retake all the interesting alternations of competitive operations or to get to the execution paths that are activated only during the race. Syzkaller finds race condition "accidentally" - through the massive parallel launch of syscals on multi-core VM. A purposeful study of alternations in phasing is an unsolved task, and MAccConc is laying nuclear infrastructure for this.
MACCCConc: Project Zero's kernel memory tracing toolkit
MACCCConc is a set of tools to investigate possible alternations of multi-threaded test cases in the Linux kernel, available on GitHub. Three components:
Automatic tester - goes over all A-B-A alternation of the test case, injecting delays at each potential point in the race.
Terminal UI - manual study of alternations in the terminal with navigation on communication points.
GUI is a graphical interface for visualizing alternations and tracing.
The nuclear part of MAccConc is also designed for integration with phasers, although the custom strapping for this is not yet written.
Ideological predecessors: sockfuzter Neda Williamson (a user planner with redevelopment on synchronization primitives for concurrency bugs research) and the SKI project (recording memory calls and managing vCPU planning through patched QEMU in TCG mode with VM snapshots for overkill alternations).
ASAN outline and KCOV: the basis of the tooling
To detect potential races, you need a mechanism to collect trace messages to memory. SKI solved this with a patch to QEMU TCG. MAccConc goes fundamentally another way - ASAN-instrumentation in outline mode.
When compiling the core with CONFIG_KASAN_OUTLINE (backend flag asan-instrumentation-with-call-threshold=0) the compiler generates helper functions calls at each memory call. Usually, ASAN combines callback and for serial calls - the Linux kernel explicitly disables this backend-flag optimization asan-opt-same-temp, to get one callback for each appeal. By default, ASAN does not tool the appeals to global variables - it is also disabled by the flag asan-opt-globals.
To transfer tracing to userspace, MACConc uses KCOV - a mechanism originally created to transfer the coverage of the base blocks to custom phasers (syzkaller works through KCOV). Why KCOV and not ftrace? The publication of Project Zero gives three arguments: a simple in-memory representation of data (critical for recovery from the crumbled VM); static always-on tooling with a near-zero overhead in the off state; designing for high-frequency trade events (referrals to memory by orders of magnitude more than calls of functions).
The principal solution is the tooling in the core, not the emulator. This allows for potentially collecting high-level information about lock-in captures and releases, and theoretically testing on bare-metal.
Blind spot ASAN outline. ASAN is designed to detect use-after-free and does not generate callbacks for direct referrals to stek memory (unless there is potential out-of-bounds). Racing with on-stack objects - waits, completed structures - can fly by. The alternative is TSAN-instrumentation, sharpened under data race and giving information about the atom of the appeals. But the compilers do not support the simultaneous generation of ASAN and TSAN hooks - to save the UAF working detector when using TSAN, it would be necessary to start the ASAN-sale of the kernel on top of TSAN hooks or modify the compiler. At the time of the publication of Project Zero, this barrier has not been overcome.
Communication points: search for potential races
The concept is borrowed from the work of SKI: communication points - a pair of memory calls on different streams that can interact. Criterion: At least one message is a record, and the address ranges overlap.
Search algorithm:
Collect the trace of messages to the memory of all streams of the test case through KCOV.
Find pairs (flow A, stream B) satisfying the contact point criterion.
For each pair, check whether the change in the order of execution leads to different behaviors.
This reduces the task from "to re-take all the alternations of all instructions" (exponential complexity) to "check the final many points of communication." For a test case with thousands of messages to memory, the number of communication points is less than the total number of possible alternations - and automatic overtaking becomes practicable.
Count-augmented stack traces: stable identification of memory calls
To test the various orders of appeals, you need a way to consistently identify a specific appeal between the test case launches. Two naive approaches do not work:
At the data address - if the object is allocated again at each start, the address changes due to ASLR and the state of the slab-allocator. At the address of the instruction - does not work for calls from generic-functions such as memcpy() or spin_lock(), caused from a bunch of contexts.
The MAccConc solution is count-augmented stack traces: each access to memory is identified by a complete stack track, supplemented by a counter - how many times the same stack track has already met in the current KCOV-collection session.
A specific example: if spin_lock() summoned twice from n_hdlc_send_frames() with identical call stack - the first appeal receives count=0, the second count=1. The next time you start the test case, the appeals will receive the same identifiers, even if the absolute addresses of the objects have gone away. The author of MACCC distinguishes this approach as the central theoretical contribution of the project - and in the case: without stable identification, the entire automatic overkill is crumbled.
Stack-based delay injection: stream alternation control
When communication points are detected and consistently identified, MACConc uses a collision transfer to enforce the order of referrals to memory. "Stack-based" - because the delay is tied to the stack-track: not "somewhere in the function X", but "with the N-m of the message to memory with the data count-augmented stack trace." No kernel reassembly is needed - the injection point is determined algorithmically at each start.
Constraint-style and fully order specified
MAccConc implements two delay injection modes:
Constraint-style (A-happens-before-B) sets a single limitation: the circulation A on the stream 1 must occur before circulation B on stream 2. The delay insert on the stream 2 at point B until the flow 1 executes A. The minimally invasive approach - fixes the order of one pair, everything else is performed freely.
Fully specified ordering (context-switch-style) - a complete specification of the order of all memory calls, emulating the deterministic context switching. Allows you to check a specific alternation scenario up to each instruction. Computing is more expensive, but full control over interleaving.
The fundamental difference from the manual approach with mdelay(): the injection is tied to a stable identifier (count-augmented stack track) rather than a position in the source code. Reproducible without modification of the kernel.
Automatic testing and regression testing kernels
Automatic tester MAccConc goes over all A-B-A alternations of the test case:
Running a test case with memory tracing via KCOV.
Identification of communication points between streams.
Generation of sympot (A-happens-before-B) for each communication point pair.
Re-run with injected delay.
Fixation of the result: KASAN-report, kernel panic, incorrect state.
For KCOV remote coverage, some of the subsystems are already integrated (bluetooth, USB). For the processing of loopback-packs and RCU callbacks - key participants of many race condition - the author of MACCConc has a draft-patch, but in upstream these subsystems are not yet connected to remote coverage.
Directions of development indicated in the publication Project Zero:
Human-readable memory access traces - adding information about types to the tracing of calls.
High-level semantics: not just an address, but the identification of a kernel object.
Detection of impossible alternations through the detection of deadlocks - acceleration of overkill by cutting off deliberately unrealized orderings.
Building test cases with potential contact points on the Snowboard model (incremental test case build-up instead of a complete overkill).
Output KCOV in host-shared memory to reduce overhead when phasing the Linux kernel.
Operation of condition race in the core: CVE-2017-2636 and classification CWE
CVE-2017-2636 - a model example of condition race, for confirmation and regression testing which MAccConc would become indispensable.
The point. Race condition in drivers/tty/n_hdlc.c Linux kernel to version 4.10.1. CVSS 7.0 HIGH, vector CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H. Local access (AV:L), high difficulty - you need to win the race (AC:H), low privileges (PR:L), without user interaction (UI:N), full impact on privacy, integrity and accessibility. Operation leads to increased privileges to root - Exploitation for Privilege Escalation (T1068, privilege).
Mechanics of the race. Functions flush_tx_queue() and n_hdlc_send_frames() competitively address the pointer n_hdlc.tbuf. If the data transfer error is error, the buffer is saved in this pointer. When performed simultaneously, both functions can double-place one buffer on the list tx_free_buf_list - double free when calling n_hdlc_release(). Buffers n_hdlc_buf allocated in slab-cache kmalloc-8192.
A chain of attack. Module n_hdlc downloads automatically when you install the N_HDLC protocol for a sequential line - it is enough to open the pseudoterminal through /dev/ptmx and to call ioctl(ptmd, TIOCSETD, &ldisc). Further exploit (described in the publication xakep.ru) uses sk_buff with destructor_arg in skb_shared_info to overwrite the pointer to the function. The ~7500 bytes fitting package skb_shared_info in kmalloc-8192 - the same cache where the double free happened. Heap testing through pairs of sockets gives two copies sk_buff with the field head, referring to one area of memory. The approach is borrowed from decommissioning CVE-2016-2384 (double free in snd_usbmidi_create, EDB-41999, by Andrey Konovalov).
To play the race, the exploit uses stream synchronization on pthread_barrier, variable delays tmo1/tmo2 in the idle cycle and linking flows through sched_setaffinity(). MACCC automatics this manual delay injection: instead of overkilling increment delays loop % MAX_RACE_LAG_USEC - automatic definition of communication points between flush_tx_queue() and n_hdlc_send_frames() and ast-style injection at points of reference to n_hdlc.tbuf.
CWE classification: discrepancy between sources. NVD classifies CVE-2017-2636 At the same time as CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization) and CWE-415 (Double Free). Red Hat in RHSA-2017:0892 only indicates CWE-362 and estimates CVSS v3 base at 7.8 (above NVD-shny 7.0). At the same time KASAN when triggered fixes the error as use-after-free (CWE-416) occurring just before the second kfree().
This is not a contradiction - it is different projections of one bug. CWE-362 describes the root cause (race condition, lack of synchronization of kernel flows). CWE-415 - direct consequence (double release of one block). CWE-416 - how the error manifests itself in operation, when between the first and second kfree() slab-allocator reuses the block for another object. A similar discrepancy is characteristic of CVE-2016-2384: NVD describes as "Double free vulnerability," Red Hat classifies as CWE-416 (Use After Free).
Scope. Vulnerability affected RHEL 6/7 (RHSA-2017:0892, package kernel-0:2.6.32-696.1.1.el6), Fedora, SUSE, Debian (DSA-3804-1, fixed in linux 3.16.39-1+deb8u2 for jessie) and Ubuntu (USN-3220-3, USN-3221-1/2). EPSS - 0.0102, 61st percentile (above the median). All distributions with CONFIG_N_HDLC=m were vulnerable.
MSG_OOB and transfer injection in the context of operation