lxc / go-lxc

Go bindings for liblxc
https://linuxcontainers.org/lxc
Other
430 stars 76 forks source link

Create fails when custom dir is specified; possible use-after-free? #161

Open sblinch opened 2 years ago

sblinch commented 2 years ago

Create exhibits unexpected behavior if you specify a custom options.BackendSpecs.Dir. I've made this simple modification to examples/create/create.go:

--- create.go.orig      2022-04-06 14:34:24.287690956 -0700
+++ create.go   2022-04-06 14:35:20.088339284 -0700
@@ -8,6 +8,7 @@

 import (
        "flag"
+       "fmt"
        "log"

        "github.com/lxc/go-lxc"
@@ -71,6 +72,7 @@
                }
        }

+       dir := fmt.Sprintf("%s/%s/rootfs",lxcpath,name)
        options := lxc.TemplateOptions{
                Template:             template,
                Distro:               distro,
@@ -81,6 +83,7 @@
                Backend:              backend,
                BackendSpecs: &lxc.BackendStoreSpecs{
                        FSSize: uint64(bdevSize),
+                       Dir: &dir,
                },
        }

This results in:

root@lxc:~# ./create -validation -verbose -distro centos -release 8-Stream -name testy
2022/04/06 14:40:25 Creating container...
Using image from local cache
Unpacking the rootfs
tar: /usr/share/lxc/templates/lxc-download: Cannot open: Not a directory
tar: Error is not recoverable: exiting now
2022/04/06 14:40:25 ERROR: creating the container failed

The created config file then contains:

lxc.log.file = log
lxc.log.level = DEBUG
lxc.rootfs.path = dir:/usr/share/lxc/templates/lxc-download

It seems that the list of arguments internally passed to liblxc somehow gets out-of-sync with what liblxc is expecting.

I haven't had a chance to track down the definitive cause, but I did see something that piqued my interest in container.go:2316 in buildBdevSpecs():

    if o.Dir != nil {
        dir := C.CString(*o.Dir)
        specs.dir = dir
        defer C.free(unsafe.Pointer(dir))
    }

I don't have much experience with cgo so forgive me if I'm misreading this, but if we allocate a C.CString and assign it to specs.dir, isn't it incorrect to then free it on return? Wouldn't this cause the dir field in the returned C.struct_bdev_specs to point to freed memory?