Igris Blog
A Journey Through Linux Kernel Memory Management

A Journey Through Linux Kernel Memory Management

June 25, 2025
12 min read
Table of Contents

Introduction

CVE-2022-2588 represents a critical privilege escalation vulnerability in the Linux kernel that demonstrates the subtle complexities of memory management in kernel-space networking code. This vulnerability was discovered by Zhenpeng Lin working with Trend Micro’s Zero Day Initiative, and it affects various systems running the Linux kernel up to version 5.19.17. With a CVSS score of 7.8, this vulnerability allows a local attacker with general user permission to obtain root privileges.

The Vulnerability Discovery Process

Initial Research Focus

The discovery of CVE-2022-2588 likely began with a focused examination of the Linux kernel’s traffic control (tc) subsystem, specifically the cls_route filter implementation. This area of the kernel handles network packet classification and filtering, making it an attractive target for security researchers due to its complexity and privileged operations.

Methodology and Approach

The vulnerability discovery process for this type of issue typically involves several key phases:

  1. Code Auditing: Systematic review of the cls_route filter implementation in net/sched/cls_route.c
  2. Control Flow Analysis: Mapping the lifecycle of filter objects and their associated memory
  3. Edge Case Identification: Looking for unusual conditions that might break normal operation
  4. Memory Management Tracking: Following allocation and deallocation patterns

The Critical Insight

The breakthrough came when examining what happens when a route filter handle has the value 0. It was discovered that the cls_route filter implementation in the Linux kernel would not remove an old filter from the hashtable before freeing it if its handle had the value 0. This seemingly minor edge case revealed a fundamental flaw in the cleanup logic.

Technical Deep Dive

Understanding cls_route Filters

The cls_route classifier in Linux is part of the traffic control system that allows administrators to classify network packets based on routing information. It maintains filters in a hashtable structure for efficient lookup and management.

    ┌─────────────────────────────────────────────────┐
    │              cls_route Hashtable                │
    └─────────────────────────────────────────────────┘
           │                │                │
           ▼                ▼                ▼
    ┌──────────┐    ┌──────────┐    ┌──────────┐
    │ Filter 1 │    │ Filter 2 │    │ Filter N │
    │Handle: X │    │Handle: Y │    │Handle: 0 │ ◄── Special case!
    └──────────┘    └──────────┘    └──────────┘

The Vulnerable Code Pattern

The vulnerability lies in the route4_change() function in net/sched/cls_route.c. The issue occurs when route4_change() allocates a new filter and copies values from the old one, but mistakenly removes the new filter instead of the old one during cleanup.

Here’s the problematic code pattern (simplified):

// In route4_change() function - VULNERABLE VERSION
static int route4_change(struct net *net, struct sk_buff *in_skb,
                        struct tcf_proto *tp, unsigned long base,
                        u32 handle, struct nlattr **tca,
                        void **arg, bool ovr, bool rtnl_held,
                        struct netlink_ext_ack *extack)
{
    // ... code ...
    
    if (fold) {
        route4_reset_fastmap(head);
        *arg = fold;
        
        // VULNERABLE: Handle 0 case not properly handled
        if (fold->handle && fold->handle != handle) {
            // Only removes from hashtable if handle != 0
            route4_delete_filter(tp, fold, true, rtnl_held, extack);
        } else {
            // Handle 0 case: filter freed but not removed from hashtable!
            tcf_unbind_filter(tp, &fold->res);
            tcf_exts_destroy(&fold->exts);
            if (tcf_exts_get_net(&fold->exts))
                tcf_queue_work(&fold->rwork, route4_delete_filter_work);
            else
                kfree(fold);
        }
    }
    // ... code ...
}

The Root Cause Analysis

    Memory State Before Vulnerability Trigger:
    
    Hashtable:   [Filter A] → [Filter B(handle=0)] → [Filter C]
    Memory:      [  Valid  ] → [     Valid      ] → [  Valid  ]
    
    
    After Filter Update with Handle 0:
    
    Hashtable:   [Filter A] → [Filter B(handle=0)] → [Filter C]
    Memory:      [  Valid  ] → [     FREED!     ] → [  Valid  ]
                                        ↑
                                   DANGLING POINTER!
    
    
    Subsequent Hashtable Traversal:
    
    Access Pattern: Filter A → Filter B → ??? (Use-After-Free!)
                                    ↓
                            Potential Code Execution

The vulnerability stems from improper memory management in the filter deletion process:

  1. Filter Creation: When a cls_route filter is created, it gets added to a hashtable with a specific handle
  2. Special Case Handling: Filters with handle value 0 are treated as a special case and not removed from hashtable before freeing
  3. Deletion Logic Flaw: During deletion, if a filter’s handle is 0, the code path skips the hashtable removal step
  4. Memory Deallocation: The filter’s memory is freed despite still being referenced in the hashtable
  5. Use-After-Free Condition: Subsequent operations on the hashtable can access the freed memory

The Fix: Patch Analysis

The fix for CVE-2022-2588 was straightforward but crucial. The patch titled “net_sched: cls_route: remove from list when handle is 0” was applied to ensure proper cleanup regardless of handle value.

Here’s how the fix works:

// FIXED VERSION - Proper cleanup logic
static int route4_change(struct net *net, struct sk_buff *in_skb,
                        struct tcf_proto *tp, unsigned long base,
                        u32 handle, struct nlattr **tca,
                        void **arg, bool ovr, bool rtnl_held,
                        struct netlink_ext_ack *extack)
{
    // ... code ...
    
    if (fold) {
        route4_reset_fastmap(head);
        *arg = fold;
        
        // FIXED: Always remove from hashtable before freeing
        if (fold->handle) {
            // Remove from hashtable regardless of handle value
            route4_delete_filter(tp, fold, true, rtnl_held, extack);
        } else {
            // Even for handle 0, ensure proper cleanup
            route4_delete_filter_work(&fold->rwork);
        }
    }
    // ... code ...
}

Visual Representation of the Fix

    Before Fix (Vulnerable):
    
    ╔══════════════════════════════════════════════════════╗
    ║                 Filter Update Process                ║
    ╚══════════════════════════════════════════════════════╝
    
    Step 1: Create new filter
    Hashtable: [A] → [B(h=0)] → [C]    Memory: [✓] [✓] [✓]
    
    Step 2: Update existing filter with handle 0
    Hashtable: [A] → [B(h=0)] → [C]    Memory: [✓] [!] [✓]
                          ↑                        ↑
                    Still linked                Freed!
    
    Step 3: Later access = UAF vulnerability
    
    
    After Fix (Secure):
    
    ╔══════════════════════════════════════════════════════╗
    ║              Proper Cleanup Process                  ║
    ╚══════════════════════════════════════════════════════╝
    
    Step 1: Create new filter
    Hashtable: [A] → [B(h=0)] → [C]    Memory: [✓] [✓] [✓]
    
    Step 2: Remove from hashtable FIRST
    Hashtable: [A] → [NULL] → [C]      Memory: [✓] [✓] [✓]
    
    Step 3: Then free memory safely
    Hashtable: [A] → [NULL] → [C]      Memory: [✓] [X] [✓]
                                                   ↑
                                              Safely freed

The key changes in the patch:

  1. Consistent Cleanup: The fix ensures that when route4_change() updates a filter, it removes the OLD filter from the hashtable before freeing it, rather than mistakenly removing the new one
  2. Handle 0 Treatment: Special handling for filters with handle value 0 now includes proper hashtable cleanup
  3. Memory Safety: Prevents the use-after-free condition by ensuring no dangling pointers remain in the hashtable

Attack Vector Analysis

    Exploitation Flow (High-Level):
    
    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
    │  Create Filter  │───▶│  Trigger UAF    │───▶│ Heap Feng Shui  │
    │   (handle = 0)  │    │   Condition     │    │   + Control     │
    └─────────────────┘    └─────────────────┘    └─────────────────┘
                                                           │
    ┌─────────────────┐    ┌─────────────────┐           ▼
    │ Privilege       │◄───│  Code Execution │    ┌─────────────────┐
    │  Escalation     │    │   via Corrupt   │    │ Memory Layout   │
    └─────────────────┘    │   Pointers      │    │  Manipulation   │
                           └─────────────────┘    └─────────────────┘

The Vulnerability in Action

Code Flow Analysis

    ╭─────────────────────────────────────────────────────────╮
    │                  CVE-2022-2588 Flow                     │
    ╰─────────────────────────────────────────────────────────╯
    
    User Space                    Kernel Space
    ──────────                    ────────────
    
    tc filter add...         ┌──────────────────────┐
    (handle = 0)      ──────▶│  route4_change()     │
                             │                      │
                             │  1. Allocate new     │
                             │  2. Copy from old    │
                             │  3. Insert new       │
                             │  4. ❌ Skip hashtable│
                             │     removal for h=0  │
                             │  5. Free old memory  │
                             └──────────────────────┘
                                       │
                                       ▼
                             ┌──────────────────────┐
                             │    Use-After-Free    │
                             │    Vulnerability     │
                             │                      │
                             │  • Freed memory      │
                             │  • Still in hashtable│
                             │  • Accessible via tc │
                             └──────────────────────┘
                                       │
                                       ▼
                             ┌──────────────────────┐
                             │   Exploitation       │
                             │                      │
                             │  • Heap spray        │
                             │  • Control memory    │
                             │  • ROP chain         │
                             │  • Privilege esc     │
                             └──────────────────────┘

I can explain the vulnerable code patterns and the fix, but I cannot provide or analyze exploit code as that could enable harmful activities. The technical details above show:

Additional Technical Insights

Debugging and Detection

    Memory Debugging Tools for CVE-2022-2588:
    
    ┌─────────────────────────────────────────────────────┐
    │                    KASAN Output                     │
    ├─────────────────────────────────────────────────────┤
    │ BUG: KASAN: use-after-free in route4_walk+0x4f/0x90 │
    │ Write of size 8 at addr ffff888123456789 by task    │
    │ tc/1234                                             │
    │                                                     │
    │ The buggy address belongs to the object at          │
    │ ffff888123456780 which belongs to the cache         │
    │ kmalloc-128 of size 128                             │
    │                                                     │
    │ The buggy address is located 9 bytes inside of      │
    │ 128-byte region [ffff888123456780,                  │
    │ ffff888123456800)                                   │
    └─────────────────────────────────────────────────────┘

Patch Timeline and Versions

The patch “net_sched: cls_route: remove from list when handle is 0” was backported to stable kernel versions, including:

  • Linux 4.14-stable
  • Linux 4.19-stable
  • Linux 5.4-stable
  • Linux 5.10-stable
  • Linux 5.15-stable
  • Linux 5.18-stable

The Complete Attack Surface

        ╔═══════════════════════════════════════════════════════════╗
        ║                    CVE-2022-2588 Attack Surface           ║
        ╚═══════════════════════════════════════════════════════════╝
        
        Network Namespace          Traffic Control Layer
        ─────────────────          ────────────────────
        
        ┌─────────────────┐       ┌─────────────────────────────────┐
        │   User Space    │◄──────│          cls_route              │
        │                 │       │                                 │
        │  ┌───────────┐  │       │  ┌─────────────────────────────┐│
        │  │  tc tool  │  │       │  │        Hashtable            ││
        │  └───────────┘  │       │  │                             ││
        └─────────────────┘       │  │  [Filter] → [Filter] → [...]││
                                  │  │     ↓         ↓         ↓   ││
                System Call       │  │  Handle≠0  Handle=0  Handle ││
                Interface         │  │   (Safe)   (Vuln!)   (Safe) ││
                    │             │  └─────────────────────────────┘│
                    ▼             └─────────────────────────────────┘
        ┌─────────────────────────────────────────────────────────┐
        │                Kernel Memory                            │
        │                                                         │
        │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
        │  │   Filter    │  │   Filter    │  │   Filter    │      │
        │  │   Object    │  │   Object    │  │   Object    │      │
        │  │             │  │  (FREED!)   │  │             │      │
        │  └─────────────┘  └─────────────┘  └─────────────┘      │
        │        ↑                ↑                ↑              │
        │     Valid           UAF Risk         Valid              │
        └─────────────────────────────────────────────────────────┘
                                 │
                                 ▼
                    ┌─────────────────────────────────┐
                    │        Exploitation             │
                    │                                 │
                    │  • Heap Grooming                │
                    │  • Memory Corruption            │
                    │  • Control Flow Hijacking       │
                    │  • Privilege Escalation         │
                    └─────────────────────────────────┘

Affected Systems

The vulnerability impacts various systems running the Linux kernel up to version 5.19.17, including several versions of Canonical’s Ubuntu. This broad impact scope made the vulnerability particularly concerning for the Linux ecosystem.

Attack Prerequisites

For this vulnerability to be exploited, an attacker needs:

  • Local access to the target system
  • Ability to create and manipulate tc (traffic control) filters
  • Sufficient privileges to interact with the networking subsystem (typically requires CAP_NET_ADMIN or root)

Risk Factors

The combination of local privilege escalation capability and the widespread use of affected kernel versions created a significant security risk. Currently, the details of this vulnerability have been disclosed and the risk is high.

The Research Journey: Challenges and Insights

Debugging Kernel Memory Issues

Discovering this vulnerability required sophisticated debugging techniques:

  • Static Analysis: Code review to identify potential memory management issues
  • Dynamic Analysis: Runtime testing with memory debugging tools like KASAN
  • Fuzzing: Systematic testing of edge cases in the tc filter API
  • Memory Corruption Detection: Using tools to detect use-after-free conditions

Verification and Proof of Concept

Once the vulnerability was identified, the research process involved:

  1. Reproducible Test Case: Creating a minimal example that triggers the use-after-free
  2. Exploitation Feasibility: Determining whether the memory corruption could be controlled
  3. Privilege Escalation Chain: Developing a complete exploit that achieves root privileges
  4. Cross-Version Testing: Verifying the vulnerability across different kernel versions

The Fix

The fix for CVE-2022-2588 involved ensuring that filters are properly removed from the hashtable before being freed, regardless of their handle value. This addresses the root cause by preventing the use-after-free condition from occurring.

Detection and Mitigation Strategies

Organizations can protect against this vulnerability through:

  • Kernel Updates: Applying patches to affected kernel versions
  • System Monitoring: Watching for unusual privilege escalation attempts
  • Access Controls: Limiting who can manipulate traffic control settings
  • Runtime Protection: Using kernel hardening features like KASLR and SMEP/SMAP

Lessons for Security Researchers

Code Patterns to Watch

This vulnerability highlights several important patterns that security researchers should be aware of:

  • Special Case Handling: Edge cases in conditional logic often hide bugs
  • Resource Lifecycle Management: Improper cleanup is a common source of vulnerabilities
  • Complex State Machines: Network stack components often have intricate state management

Research Methodologies

Effective kernel vulnerability research requires:

  • Systematic Approach: Methodical code review and testing
  • Tool Proficiency: Mastery of debugging and analysis tools
  • Domain Knowledge: Understanding of kernel subsystems and their interactions
  • Persistence: Many vulnerabilities are found in corner cases that require deep investigation

Conclusion

CVE-2022-2588 serves as an excellent case study in kernel vulnerability research, demonstrating how seemingly minor edge cases can lead to critical security flaws. The discovery process showcases the importance of thorough code analysis, systematic testing, and understanding the subtle interactions between different kernel subsystems.

For security researchers, this vulnerability underscores the value of focusing on complex, privileged kernel subsystems where small mistakes can have significant security implications. The networking stack, with its intricate memory management and numerous code paths, continues to be a rich area for security research.

The responsible disclosure and patching of this vulnerability also highlights the importance of coordinated vulnerability disclosure in maintaining the security of critical infrastructure components like the Linux kernel.


This analysis focuses on the technical aspects and discovery methodology of CVE-2022-2588 for educational purposes. Organizations should ensure their systems are updated with the latest security patches to protect against this and other vulnerabilities.