rustsec-2026-0190
Vulnerability from osv_rustsec
Published
2026-06-25 12:00
Modified
2026-06-29 14:05
Summary
Unsoundness in `Error::downcast_mut()`
Details

Affected versions of this crate violate borrow rules, resulting in undefined behavior, when the user adds context to an error via Error::context and then later calls Error::downcast_mut on the returned Error.

The flaw was corrected in commit 6e8c000 by revising how the mutable reference is constructed, avoiding inclusion of a shared reference in the resulting borrow chain.

Example

use anyhow::Error;
use std::fmt;

#[derive(Debug)]
struct ErrorContext(&'static str);

impl fmt::Display for ErrorContext {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

fn main() {
    let mut error = Error::msg("inner error").context(ErrorContext("old context"));
    let context: &mut ErrorContext = error.downcast_mut().unwrap();
    context.0 = "new context";
    println!("{:?}", error);
}

Miri output

error: Undefined Behavior: trying to retag from <1538> for Unique permission at alloc602[0x38], but that tag only grants SharedReadOnly permission for this location
   --> src/ptr.rs:170:18
    |
170 |         unsafe { &mut *self.ptr.as_ptr() }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of retag at alloc602[0x38..0x48]
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <1538> was created by a SharedReadOnly retag at offsets [0x38..0x48]
   --> src/ptr.rs:89:18
    |
 89 |             ptr: NonNull::from(ptr),
    |                  ^^^^^^^^^^^^^^^^^^
    = note: stack backtrace:
            0: anyhow::ptr::Mut::<'_, ErrorContext>::deref_mut
                at src/ptr.rs:170:18: 170:41
            1: anyhow::error::<impl anyhow::Error>::downcast_mut::<ErrorContext>
                at src/error.rs:560:18: 560:46
            2: main
                at examples/downcast_mut.rs:15:38: 15:58

{
  "affected": [
    {
      "database_specific": {
        "categories": [
          "memory-corruption"
        ],
        "cvss": null,
        "informational": "unsound"
      },
      "ecosystem_specific": {
        "affected_functions": null,
        "affects": {
          "arch": [],
          "functions": [
            "anyhow::Error::downcast_mut"
          ],
          "os": []
        }
      },
      "package": {
        "ecosystem": "crates.io",
        "name": "anyhow",
        "purl": "pkg:cargo/anyhow"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.0.0-0"
            },
            {
              "fixed": "1.0.103"
            }
          ],
          "type": "SEMVER"
        }
      ],
      "versions": []
    }
  ],
  "aliases": [],
  "database_specific": {
    "license": "CC0-1.0"
  },
  "details": "Affected versions of this crate violate borrow rules, resulting in undefined behavior, when the user adds context to an error via `Error::context` and then later calls `Error::downcast_mut` on the returned `Error`.\n\nThe flaw was corrected in commit `6e8c000` by revising how the mutable reference is constructed, avoiding inclusion of a shared reference in the resulting borrow chain.\n\n## Example\n\n```rust\nuse anyhow::Error;\nuse std::fmt;\n\n#[derive(Debug)]\nstruct ErrorContext(\u0026\u0027static str);\n\nimpl fmt::Display for ErrorContext {\n    fn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c\u0027_\u003e) -\u003e fmt::Result {\n        fmt::Display::fmt(\u0026self.0, f)\n    }\n}\n\nfn main() {\n    let mut error = Error::msg(\"inner error\").context(ErrorContext(\"old context\"));\n    let context: \u0026mut ErrorContext = error.downcast_mut().unwrap();\n    context.0 = \"new context\";\n    println!(\"{:?}\", error);\n}\n```\n\n## Miri output\n\n```\nerror: Undefined Behavior: trying to retag from \u003c1538\u003e for Unique permission at alloc602[0x38], but that tag only grants SharedReadOnly permission for this location\n   --\u003e src/ptr.rs:170:18\n    |\n170 |         unsafe { \u0026mut *self.ptr.as_ptr() }\n    |                  ^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of retag at alloc602[0x38..0x48]\n    |\n    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental\n    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information\nhelp: \u003c1538\u003e was created by a SharedReadOnly retag at offsets [0x38..0x48]\n   --\u003e src/ptr.rs:89:18\n    |\n 89 |             ptr: NonNull::from(ptr),\n    |                  ^^^^^^^^^^^^^^^^^^\n    = note: stack backtrace:\n            0: anyhow::ptr::Mut::\u003c\u0027_, ErrorContext\u003e::deref_mut\n                at src/ptr.rs:170:18: 170:41\n            1: anyhow::error::\u003cimpl anyhow::Error\u003e::downcast_mut::\u003cErrorContext\u003e\n                at src/error.rs:560:18: 560:46\n            2: main\n                at examples/downcast_mut.rs:15:38: 15:58\n```",
  "id": "RUSTSEC-2026-0190",
  "modified": "2026-06-29T14:05:53Z",
  "published": "2026-06-25T12:00:00Z",
  "references": [
    {
      "type": "PACKAGE",
      "url": "https://crates.io/crates/anyhow"
    },
    {
      "type": "ADVISORY",
      "url": "https://rustsec.org/advisories/RUSTSEC-2026-0190.html"
    },
    {
      "type": "REPORT",
      "url": "https://github.com/dtolnay/anyhow/issues/451"
    }
  ],
  "related": [],
  "severity": [],
  "summary": "Unsoundness in `Error::downcast_mut()`"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…