gographics / imagick

Go binding to ImageMagick's MagickWand C API
https://godoc.org/github.com/gographics/imagick/imagick
Other
1.77k stars 184 forks source link

ImageMagick panics when running (C binding issue?) #280

Closed ping-localhost closed 1 year ago

ping-localhost commented 2 years ago

Machine

MacBook Pro 16 (2019) - Core Intel Core i7
Version macOS Monterey 12.5 (21G72)

Versions:

- gopkg.in/gographics/imagick.v3 v3.4.1
- imagemagick: stable 7.1.0-45 (bottled)

go env

GO111MODULE="auto"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/mitchell/Library/Caches/go-build"
GOENV="/Users/mitchell/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/mitchell/go/pkg/mod"
GOOS="darwin"
GOPATH="/Users/mitchell/go"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/opt/go/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/opt/go/libexec/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.19"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/mitchell/go/src/foo/bar/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/c6/4l4ylj_530z56dccw0b7_pq00000gn/T/go-build3480031562=/tmp/go-build -gno-record-gcc-switches -fno-common"

.zshrc

export CGO_CFLAGS_ALLOW='-Xpreprocessor'

Example file:

package main

import (
    "gopkg.in/gographics/imagick.v3/imagick"
)

func main() {
    imagick.NewMagickWand()
}

Resulting panic:

➜ go run main.go
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x44073f4]

runtime stack:
runtime.throw({0x409c9b0?, 0x43d3373?})
        /usr/local/opt/go/libexec/src/runtime/panic.go:1047 +0x5d fp=0x7ff7bfeff390 sp=0x7ff7bfeff360 pc=0x4032bfd
runtime.sigpanic()
        /usr/local/opt/go/libexec/src/runtime/signal_unix.go:819 +0x369 fp=0x7ff7bfeff3e0 sp=0x7ff7bfeff390 pc=0x4046a29

goroutine 1 [syscall]:
runtime.cgocall(0x4076540, 0xc00005ff58)
        /usr/local/opt/go/libexec/src/runtime/cgocall.go:158 +0x5c fp=0xc00005ff30 sp=0xc00005fef8 pc=0x40052dc
gopkg.in/gographics/imagick.v3/imagick._Cfunc_NewMagickWand()
        _cgo_gotypes.go:8518 +0x49 fp=0xc00005ff58 sp=0xc00005ff30 pc=0x4073e69
gopkg.in/gographics/imagick.v3/imagick.NewMagickWand()
        /Users/mitchell/go/src/foo/bar/vendor/gopkg.in/gographics/imagick.v3/imagick/magick_wand.go:36 +0x19 fp=0xc00005ff70 sp=0xc00005ff58 pc=0x4073f39
main.main()
        /Users/mitchell/go/src/foo/bar/cmd/main.go:8 +0x17 fp=0xc00005ff80 sp=0xc00005ff70 pc=0x40743f7
runtime.main()
        /usr/local/opt/go/libexec/src/runtime/proc.go:250 +0x212 fp=0xc00005ffe0 sp=0xc00005ff80 pc=0x4035412
runtime.goexit()
        /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00005ffe8 sp=0xc00005ffe0 pc=0x405d4e1

goroutine 2 [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/opt/go/libexec/src/runtime/proc.go:363 +0xd6 fp=0xc00004cfb0 sp=0xc00004cf90 pc=0x40357d6
runtime.goparkunlock(...)
        /usr/local/opt/go/libexec/src/runtime/proc.go:369
runtime.forcegchelper()
        /usr/local/opt/go/libexec/src/runtime/proc.go:302 +0xad fp=0xc00004cfe0 sp=0xc00004cfb0 pc=0x403566d
runtime.goexit()
        /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00004cfe8 sp=0xc00004cfe0 pc=0x405d4e1
created by runtime.init.6
        /usr/local/opt/go/libexec/src/runtime/proc.go:290 +0x25

goroutine 3 [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/opt/go/libexec/src/runtime/proc.go:363 +0xd6 fp=0xc00004d790 sp=0xc00004d770 pc=0x40357d6
runtime.goparkunlock(...)
        /usr/local/opt/go/libexec/src/runtime/proc.go:369
runtime.bgsweep(0x0?)
        /usr/local/opt/go/libexec/src/runtime/mgcsweep.go:278 +0x8e fp=0xc00004d7c8 sp=0xc00004d790 pc=0x402346e
runtime.gcenable.func1()
        /usr/local/opt/go/libexec/src/runtime/mgc.go:178 +0x26 fp=0xc00004d7e0 sp=0xc00004d7c8 pc=0x4018326
runtime.goexit()
        /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00004d7e8 sp=0xc00004d7e0 pc=0x405d4e1
created by runtime.gcenable
        /usr/local/opt/go/libexec/src/runtime/mgc.go:178 +0x6b

goroutine 4 [GC scavenge wait]:
runtime.gopark(0xc000074000?, 0x40b2780?, 0x1?, 0x0?, 0x0?)
        /usr/local/opt/go/libexec/src/runtime/proc.go:363 +0xd6 fp=0xc00004df70 sp=0xc00004df50 pc=0x40357d6
runtime.goparkunlock(...)
        /usr/local/opt/go/libexec/src/runtime/proc.go:369
runtime.(*scavengerState).park(0x4119780)
        /usr/local/opt/go/libexec/src/runtime/mgcscavenge.go:389 +0x53 fp=0xc00004dfa0 sp=0xc00004df70 pc=0x4021513
runtime.bgscavenge(0x0?)
        /usr/local/opt/go/libexec/src/runtime/mgcscavenge.go:617 +0x45 fp=0xc00004dfc8 sp=0xc00004dfa0 pc=0x4021ae5
runtime.gcenable.func2()
        /usr/local/opt/go/libexec/src/runtime/mgc.go:179 +0x26 fp=0xc00004dfe0 sp=0xc00004dfc8 pc=0x40182c6
runtime.goexit()
        /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00004dfe8 sp=0xc00004dfe0 pc=0x405d4e1
created by runtime.gcenable
        /usr/local/opt/go/libexec/src/runtime/mgc.go:179 +0xaa

goroutine 5 [finalizer wait]:
runtime.gopark(0x4119b80?, 0xc000007860?, 0x0?, 0x0?, 0xc00004c770?)
        /usr/local/opt/go/libexec/src/runtime/proc.go:363 +0xd6 fp=0xc00004c628 sp=0xc00004c608 pc=0x40357d6
runtime.goparkunlock(...)
        /usr/local/opt/go/libexec/src/runtime/proc.go:369
runtime.runfinq()
        /usr/local/opt/go/libexec/src/runtime/mfinal.go:180 +0x10f fp=0xc00004c7e0 sp=0xc00004c628 pc=0x401742f
runtime.goexit()
        /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00004c7e8 sp=0xc00004c7e0 pc=0x405d4e1
created by runtime.createfing
        /usr/local/opt/go/libexec/src/runtime/mfinal.go:157 +0x45
exit status 2
ping-localhost commented 2 years ago

I've already updated all Go dependencies, removed the go cache, reinstalled ImageMagick via homebrew, all to no avail. 😅

ping-localhost commented 2 years ago

Seems to be an issue with the latest version, as I've just downgraded to ImageMagick 7.1.0-37 (by building from source) and that works just fine. No idea how/where to report this issue though.

justinfx commented 2 years ago

Does it segfault because you haven't first initialized the ImageMagick C library as per the example?

func main() { 
    imagick.Initialize() 
    imagick.NewMagickWand()
} 

If that is the cause, I agree it would be nice if the constructor functions checked that you have called Initialize before calling into the C API to produce an error instead of a seg fault.

ping-localhost commented 2 years ago

Interesting, the imagick.Initialize() does resolve my issue. How odd that it worked fine on an older version, must have been undefined behaviour. Either way, totally my own fault. Thank you @justinfx

It would be nice if a clear error was returned, even if it was a panic, as the seg fault is quite unclear.

justinfx commented 2 years ago

Yea it would be undefined if nothing is checking ahead of time. The segfault is from the C library. So the behaviour was entirely dependant on what that does. We do have some internal counters so I would think we could add check on the Go bindings side to panic with a clear message first.

justinfx commented 2 years ago

Strangely, when I try to reproduce this using the same ImageMagick7 version, I don't see it segfault. I'm on linux with gcc, so I don't know if it has to be even more specific to clang and/or macos and/or go 1.19

ben-swit commented 2 years ago

i have same issue

in my case, use go 1.18, macos m1 ,imagemagick/7.1.0-51,imagick.v3 v3.4.2

justinfx commented 2 years ago

@ben-swit and it was because you had not initialized the library first?

ben-swit commented 2 years ago

@justinfx yeah before when i used lagacy version initialized in init() function so run test code not rise that error

but resent that rise error so i init test main then not rised i think go 1.19 (module 1.18 ) different run before version

justinfx commented 2 years ago

Hmm. So If I understand correctly, you are saying that by calling Initialize() it will prevent the crash from happening. This is expected and aligns with what would happen in the C ImageMagick library. I did try to reproduce it earlier, but was unable to. I can have another look. The only thing I can think to try and improve this situation is to put a check into every New*() constructors to see if Initialize() has been called. The problem though is we do not return an error from the constructors right now. So all we could do is turn the C segfault into a Go panic, which I don't know if you think would be an improvement.