{
  "schema_version": "1.6.1",
  "id": "RPI-CVE-2025-21899",
  "modified": "2026-10-09T11:39:44Z",
  "upstream": [
    "CVE-2025-21899"
  ],
  "published": "2025-04-01T15:31:03Z",
  "summary": "tracing: Fix bad hist from corrupting named_triggers list",
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\ntracing: Fix bad hist from corrupting named_triggers list\n\nThe following commands causes a crash:\n\n ~# cd /sys/kernel/tracing/events/rcu/rcu_callback\n ~# echo 'hist:name=bad:keys=common_pid:onmax(bogus).save(common_pid)' > trigger\n bash: echo: write error: Invalid argument\n ~# echo 'hist:name=bad:keys=common_pid' > trigger\n\nBecause the following occurs:\n\nevent_trigger_write() {\n  trigger_process_regex() {\n    event_hist_trigger_parse() {\n\n      data = event_trigger_alloc(..);\n\n      event_trigger_register(.., data) {\n        cmd_ops->reg(.., data, ..) [hist_register_trigger()] {\n          data->ops->init() [event_hist_trigger_init()] {\n            save_named_trigger(name, data) {\n              list_add(&data->named_list, &named_triggers);\n            }\n          }\n        }\n      }\n\n      ret = create_actions(); (return -EINVAL)\n      if (ret)\n        goto out_unreg;\n[..]\n      ret = hist_trigger_enable(data, ...) {\n        list_add_tail_rcu(&data->list, &file->triggers); <<<---- SKIPPED!!! (this is important!)\n[..]\n out_unreg:\n      event_hist_unregister(.., data) {\n        cmd_ops->unreg(.., data, ..) [hist_unregister_trigger()] {\n          list_for_each_entry(iter, &file->triggers, list) {\n            if (!hist_trigger_match(data, iter, named_data, false))   <- never matches\n                continue;\n            [..]\n            test = iter;\n          }\n          if (test && test->ops->free) <<<-- test is NULL\n\n            test->ops->free(test) [event_hist_trigger_free()] {\n              [..]\n              if (data->name)\n                del_named_trigger(data) {\n                  list_del(&data->named_list);  <<<<-- NEVER gets removed!\n                }\n              }\n           }\n         }\n\n         [..]\n         kfree(data); <<<-- frees item but it is still on list\n\nThe next time a hist with name is registered, it causes an u-a-f bug and\nthe kernel can crash.\n\nMove the code around such that if event_trigger_register() succeeds, the\nnext thing called is hist_trigger_enable() which adds it to the list.\n\nA bunch of actions is called if get_named_trigger_data() returns false.\nBut that doesn't need to be called after event_trigger_register(), so it\ncan be moved up, allowing event_trigger_register() to be called just\nbefore hist_trigger_enable() keeping them together and allowing the\nfile->triggers to be properly populated.",
  "severity": [
    {
      "type": "CVSS_V3",
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"
    }
  ],
  "affected": [
    {
      "package": {
        "ecosystem": "Raspberry Pi OS:12",
        "name": "linux"
      },
      "versions": [
        "1:6.1.47-1+rpt4"
      ],
      "ecosystem_specific": {
        "binaries": [
          {
            "binary_name": "linux-image-6.1.0-rpi3-rpi-2712",
            "binary_version": "1:6.1.47-1+rpt4"
          },
          {
            "binary_name": "linux-image-6.1.0-rpi3-rpi-v6",
            "binary_version": "1:6.1.47-1+rpt4"
          },
          {
            "binary_name": "linux-image-6.1.0-rpi3-rpi-v7",
            "binary_version": "1:6.1.47-1+rpt4"
          },
          {
            "binary_name": "linux-image-6.1.0-rpi3-rpi-v7l",
            "binary_version": "1:6.1.47-1+rpt4"
          },
          {
            "binary_name": "linux-image-6.1.0-rpi3-rpi-v8",
            "binary_version": "1:6.1.47-1+rpt4"
          }
        ]
      },
      "database_specific": {
        "per_version": {
          "1:6.1.47-1+rpt4": {
            "linux_commit": "655fc658a15ae7a6f37103754adb39ba52a9a14e",
            "upstream_version": "6.1.47",
            "assessment": "commit ancestry (strak rule)",
            "upstream_fixed": "6.1.130",
            "compiled_filter": false
          }
        }
      }
    }
  ],
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://www.cve.org/CVERecord?id=CVE-2025-21899"
    },
    {
      "type": "FIX",
      "url": "https://git.kernel.org/stable/c/5ae1b18f05ee2b849dc03b6c15d7da0c1c6efa77"
    },
    {
      "type": "FIX",
      "url": "https://git.kernel.org/stable/c/43b254d46c740bf9dbe65709afa021dd726dfa99"
    },
    {
      "type": "FIX",
      "url": "https://git.kernel.org/stable/c/435d2964af815aae456db554c62963b4515f19d0"
    },
    {
      "type": "FIX",
      "url": "https://git.kernel.org/stable/c/f1ae50cfb818ce1ac7a674406dfadb7653e2552d"
    },
    {
      "type": "FIX",
      "url": "https://git.kernel.org/stable/c/6f86bdeab633a56d5c6dccf1a2c5989b6a5e323e"
    }
  ],
  "database_specific": {
    "source": "https://git.kernel.org/pub/scm/linux/security/vulns.git",
    "upstream_modified": "2026-08-05T12:19:11Z",
    "severity": {
      "source": "kernel CNA",
      "cvss_v3_score": 7.8,
      "cvss_v3_severity": "High",
      "scenario": "AV:L - The vulnerability is reached only by writing a hist trigger command to a tracefs file (`/sys/kernel/tracing/events/*/trigger`) via the write(2) syscall. There is no network or remote data path into `event_hist_trigger_parse()`.\nAC:L - Triggering is fully deterministic \u2014 one failing `hist:name=bad:...onmax(bogus)...` write leaves the freed object linked in the global `named_triggers` list, and a second `hist:name=bad` write dereferences it; no race and no uncontrollable preconditions. `CONFIG_HIST_TRIGGERS=y` is standard in mainstream distro and Android kernels, and slab reclaim of the freed `event_trigger_data` is attacker-influenced via ordinary heap spraying.\nPR:L - No capability check exists on the code path; access is gated purely by tracefs file permissions, and tracefs is routinely delegated to a non-root tracing/shell group (Android shell, ChromeOS debugd, perf/tracing groups) via the `gid=` mount option. A local unprivileged user with that ordinary tracing access can execute the full sequence.\nUI:N - The attacker performs both writes themselves; no victim action or cooperation is needed at any point.\nS:U - The use-after-free and list corruption occur in kernel memory within the same security authority; no VM, IOMMU, or sandbox boundary is crossed.\nC:H - `find_named_trigger()` and `hist_trigger_match()` dereference the freed object's `name` and `private_data` pointers, so after reclaiming the slab object the attacker controls those pointers and obtains an arbitrary kernel-memory read primitive, plus leakage of the `named_triggers` list-head address.\nI:H - The freed object remains on a global list, so subsequent `list_add`/`list_del` operations write through attacker-controlled `named_list` pointers, and `data->private_data = named_data->private_data` produces type confusion into `hist_trigger_data` \u2014 a classic UAF write primitive usable for control-flow hijack.\nA:H - As stated in the commit, the second named-hist registration causes a use-after-free that crashes the kernel; list corruption reliably produces an oops or panic, denying service to the whole system."
    }
  }
}
