From d43d199046c30db9bef90de20c09843bb8b45737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Fri, 10 Jan 2020 16:40:46 +0100 Subject: [PATCH 1/3] Ensure Data is not dellocated while C code is still using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as Go is concerned, Data.dh and Data.cbc are just values, so it is OK to implement > cgo_call(d.dh) as > tmp := d.dh > dropReferenceTo(d) > // d's finalizer can be called at any point from now on, possibly deallocating d.dh > cgo_call(tmp) // possibly using free memory To fix this, explicitly extend the lifetime of Data: > cgo_call(d.dh) > runtime.KeepAlive(d) becomes > tmp := d.dh > cgo_call(tmp) // d.dh is certainly still valid > dropReferenceTo(d) > // d's finalizer can be called from now on, possibly deallocating d.dh Signed-off-by: Miloslav Trmač --- data.go | 12 +++++++++--- gpgme.go | 29 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/data.go b/data.go index 309c430929..eee32c0323 100644 --- a/data.go +++ b/data.go @@ -62,13 +62,13 @@ func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) // The Data buffer used to communicate with GPGME type Data struct { - dh C.gpgme_data_t + dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C buf []byte cbs C.struct_gpgme_data_cbs r io.Reader w io.Writer s io.Seeker - cbc uintptr + cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh) } func newData() *Data { @@ -154,12 +154,14 @@ func (d *Data) Close() error { callbackDelete(d.cbc) } _, err := C.gpgme_data_release(d.dh) + runtime.KeepAlive(d) d.dh = nil return err } func (d *Data) Write(p []byte) (int, error) { n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) + runtime.KeepAlive(d) if err != nil { return 0, err } @@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) { func (d *Data) Read(p []byte) (int, error) { n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) + runtime.KeepAlive(d) if err != nil { return 0, err } @@ -182,10 +185,13 @@ func (d *Data) Read(p []byte) (int, error) { func (d *Data) Seek(offset int64, whence int) (int64, error) { n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence)) + runtime.KeepAlive(d) return int64(n), err } // Name returns the associated filename if any func (d *Data) Name() string { - return C.GoString(C.gpgme_data_get_file_name(d.dh)) + res := C.GoString(C.gpgme_data_get_file_name(d.dh)) + runtime.KeepAlive(d) + return res } diff --git a/gpgme.go b/gpgme.go index d730cece28..f750e2e9e0 100644 --- a/gpgme.go +++ b/gpgme.go @@ -395,11 +395,17 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { } func (c *Context) Decrypt(ciphertext, plaintext *Data) error { - return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) + err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(ciphertext) + runtime.KeepAlive(plaintext) + return err } func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { - return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) + err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(ciphertext) + runtime.KeepAlive(plaintext) + return err } type Signature struct { @@ -426,6 +432,13 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err plainPtr = plain.dh } err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr)) + runtime.KeepAlive(sig) + if signedText != nil { + runtime.KeepAlive(signedText) + } + if plain != nil { + runtime.KeepAlive(plain) + } if err != nil { return "", nil, err } @@ -461,6 +474,8 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe *ptr = recipients[i].k } err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) + runtime.KeepAlive(plaintext) + runtime.KeepAlive(ciphertext) return handleError(err) } @@ -472,7 +487,10 @@ func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { return err } } - return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) + err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) + runtime.KeepAlive(plain) + runtime.KeepAlive(sig) + return err } type AssuanDataCallback func(data []byte) error @@ -553,7 +571,9 @@ const ( func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error { pat := C.CString(pattern) defer C.free(unsafe.Pointer(pat)) - return handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) + err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) + runtime.KeepAlive(data) + return err } // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned". @@ -592,6 +612,7 @@ type ImportResult struct { func (c *Context) Import(keyData *Data) (*ImportResult, error) { err := handleError(C.gpgme_op_import(c.ctx, keyData.dh)) + runtime.KeepAlive(keyData) if err != nil { return nil, err } From 7e8c79da5ec1bd810f01f46df83a8a914e49f4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Fri, 10 Jan 2020 17:26:47 +0100 Subject: [PATCH 2/3] Ensure Context is not dellocated while C code is still using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as Go is concerned, Context.ctx and Context.cbc are just values, so it is OK to implement > cgo_call(c.ctx) as > tmp := c.ctx > dropReferenceTo(c) > // c's finalizer can be called at any point from now on, possibly deallocating c.ctx > cgo_call(tmp) // possibly using free memory To fix this, explicitly extend the lifetime of Context: > cgo_call(c.ctx) > runtime.KeepAlive(c) becomes > tmp := c.ctx > cgo_call(tmp) // c.ctx is certainly still valid > dropReferenceTo(c) > // c's finalizer can be called from now on, possibly deallocating c.ctx To be extra careful, this adds runtime.KeepAlive calls even in cases where the current code path necessarily keeps c alive afterwards, so that the patern is clear and so that future possible changes to the code don't invalidate this assumption. Secondarily, in a few cases GPGMe returns internal pointers inside Context.ctx; in that case, extend the lifetime of Context until we make a complete copy of the data. As a special case, EngineInfo is now not just a wrapper around a C pointer with an unknown lifetime, but we make a full copy of the data when obtaining the pointer so that we can safely use it after dropping the context that returned the information, or after changing the information and possibly invalidating the pointers. Signed-off-by: Miloslav Trmač --- gpgme.go | 135 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 31 deletions(-) diff --git a/gpgme.go b/gpgme.go index f750e2e9e0..fa99e00462 100644 --- a/gpgme.go +++ b/gpgme.go @@ -164,39 +164,60 @@ func EngineCheckVersion(p Protocol) error { } type EngineInfo struct { - info C.gpgme_engine_info_t + next *EngineInfo + protocol Protocol + fileName string + homeDir string + version string + requiredVersion string } -func (e *EngineInfo) Next() *EngineInfo { - if e.info.next == nil { - return nil +func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo { + res := &EngineInfo{ + next: nil, + protocol: Protocol(info.protocol), + fileName: C.GoString(info.file_name), + homeDir: C.GoString(info.home_dir), + version: C.GoString(info.version), + requiredVersion: C.GoString(info.req_version), + } + if info.next != nil { + res.next = copyEngineInfo(info.next) } - return &EngineInfo{info: e.info.next} + return res +} + +func (e *EngineInfo) Next() *EngineInfo { + return e.next } func (e *EngineInfo) Protocol() Protocol { - return Protocol(e.info.protocol) + return e.protocol } func (e *EngineInfo) FileName() string { - return C.GoString(e.info.file_name) + return e.fileName } func (e *EngineInfo) Version() string { - return C.GoString(e.info.version) + return e.version } func (e *EngineInfo) RequiredVersion() string { - return C.GoString(e.info.req_version) + return e.requiredVersion } func (e *EngineInfo) HomeDir() string { - return C.GoString(e.info.home_dir) + return e.homeDir } func GetEngineInfo() (*EngineInfo, error) { - info := &EngineInfo{} - return info, handleError(C.gpgme_get_engine_info(&info.info)) + var cInfo C.gpgme_engine_info_t + err := handleError(C.gpgme_get_engine_info(&cInfo)) + if err != nil { + return nil, err + } + return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done. } func SetEngineInfo(proto Protocol, fileName, homeDir string) error { @@ -257,9 +278,9 @@ type Context struct { KeyError error callback Callback - cbc uintptr + cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx) - ctx C.gpgme_ctx_t + ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C } func New() (*Context, error) { @@ -277,47 +298,66 @@ func (c *Context) Release() { callbackDelete(c.cbc) } C.gpgme_release(c.ctx) + runtime.KeepAlive(c) c.ctx = nil } func (c *Context) SetArmor(yes bool) { C.gpgme_set_armor(c.ctx, cbool(yes)) + runtime.KeepAlive(c) } func (c *Context) Armor() bool { - return C.gpgme_get_armor(c.ctx) != 0 + res := C.gpgme_get_armor(c.ctx) != 0 + runtime.KeepAlive(c) + return res } func (c *Context) SetTextMode(yes bool) { C.gpgme_set_textmode(c.ctx, cbool(yes)) + runtime.KeepAlive(c) } func (c *Context) TextMode() bool { - return C.gpgme_get_textmode(c.ctx) != 0 + res := C.gpgme_get_textmode(c.ctx) != 0 + runtime.KeepAlive(c) + return res } func (c *Context) SetProtocol(p Protocol) error { - return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) + err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) + runtime.KeepAlive(c) + return err } func (c *Context) Protocol() Protocol { - return Protocol(C.gpgme_get_protocol(c.ctx)) + res := Protocol(C.gpgme_get_protocol(c.ctx)) + runtime.KeepAlive(c) + return res } func (c *Context) SetKeyListMode(m KeyListMode) error { - return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) + err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListMode() KeyListMode { - return KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) + res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) + runtime.KeepAlive(c) + return res } func (c *Context) SetPinEntryMode(m PinEntryMode) error { - return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) + err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) + runtime.KeepAlive(c) + return err } func (c *Context) PinEntryMode() PinEntryMode { - return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) + res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) + runtime.KeepAlive(c) + return res } func (c *Context) SetCallback(callback Callback) error { @@ -334,11 +374,17 @@ func (c *Context) SetCallback(callback Callback) error { c.cbc = 0 _, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0) } + runtime.KeepAlive(c) return err } func (c *Context) EngineInfo() *EngineInfo { - return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)} + cInfo := C.gpgme_ctx_get_engine_info(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing cInfo. + res := copyEngineInfo(cInfo) + runtime.KeepAlive(c) // for accesses to cInfo + return res } func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error { @@ -351,19 +397,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error chome = C.CString(homeDir) defer C.free(unsafe.Pointer(chome)) } - return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) + err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListStart(pattern string, secretOnly bool) error { cpattern := C.CString(pattern) defer C.free(unsafe.Pointer(cpattern)) - err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)) - return handleError(err) + err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly))) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListNext() bool { c.Key = newKey() err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) + runtime.KeepAlive(c) if err != nil { if e, ok := err.(Error); ok && e.Code() == ErrorEOF { c.KeyError = nil @@ -377,7 +427,9 @@ func (c *Context) KeyListNext() bool { } func (c *Context) KeyListEnd() error { - return handleError(C.gpgme_op_keylist_end(c.ctx)) + err := handleError(C.gpgme_op_keylist_end(c.ctx)) + runtime.KeepAlive(c) + return err } func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { @@ -385,6 +437,7 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { cfpr := C.CString(fingerprint) defer C.free(unsafe.Pointer(cfpr)) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) + runtime.KeepAlive(c) if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { return nil, fmt.Errorf("key %q not found", fingerprint) } @@ -396,6 +449,7 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { func (c *Context) Decrypt(ciphertext, plaintext *Data) error { err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(c) runtime.KeepAlive(ciphertext) runtime.KeepAlive(plaintext) return err @@ -403,6 +457,7 @@ func (c *Context) Decrypt(ciphertext, plaintext *Data) error { func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(c) runtime.KeepAlive(ciphertext) runtime.KeepAlive(plaintext) return err @@ -432,6 +487,7 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err plainPtr = plain.dh } err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr)) + runtime.KeepAlive(c) runtime.KeepAlive(sig) if signedText != nil { runtime.KeepAlive(signedText) @@ -443,6 +499,8 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err return "", nil, err } res := C.gpgme_op_verify_result(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing res. sigs := []Signature{} for s := res.signatures; s != nil; s = s.next { sig := Signature{ @@ -462,7 +520,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err } sigs = append(sigs, sig) } - return C.GoString(res.file_name), sigs, nil + fileName := C.GoString(res.file_name) + runtime.KeepAlive(c) // for all accesses to res above + return fileName, sigs, nil } func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error { @@ -474,6 +534,7 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe *ptr = recipients[i].k } err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) + runtime.KeepAlive(c) runtime.KeepAlive(plaintext) runtime.KeepAlive(ciphertext) return handleError(err) @@ -481,13 +542,18 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { C.gpgme_signers_clear(c.ctx) + runtime.KeepAlive(c) for _, k := range signers { - if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil { + err := handleError(C.gpgme_signers_add(c.ctx, k.k)) + runtime.KeepAlive(c) + if err != nil { C.gpgme_signers_clear(c.ctx) + runtime.KeepAlive(c) return err } } err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) + runtime.KeepAlive(c) runtime.KeepAlive(plain) runtime.KeepAlive(sig) return err @@ -519,6 +585,7 @@ func (c *Context) AssuanSend( C.uintptr_t(statusPtr), &operr, ) + runtime.KeepAlive(c) if handleError(operr) != nil { return handleError(operr) @@ -572,6 +639,7 @@ func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error pat := C.CString(pattern) defer C.free(unsafe.Pointer(pat)) err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) + runtime.KeepAlive(c) runtime.KeepAlive(data) return err } @@ -612,11 +680,14 @@ type ImportResult struct { func (c *Context) Import(keyData *Data) (*ImportResult, error) { err := handleError(C.gpgme_op_import(c.ctx, keyData.dh)) + runtime.KeepAlive(c) runtime.KeepAlive(keyData) if err != nil { return nil, err } res := C.gpgme_op_import_result(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing res. imports := []ImportStatus{} for s := res.imports; s != nil; s = s.next { imports = append(imports, ImportStatus{ @@ -625,7 +696,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) { Status: ImportStatusFlags(s.status), }) } - return &ImportResult{ + importResult := &ImportResult{ Considered: int(res.considered), NoUserID: int(res.no_user_id), Imported: int(res.imported), @@ -640,7 +711,9 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) { SecretUnchanged: int(res.secret_unchanged), NotImported: int(res.not_imported), Imports: imports, - }, nil + } + runtime.KeepAlive(c) // for all accesses to res above + return importResult, nil } type Key struct { From d575e5df6a8359a0ad12f59a8377d362c3eb6afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Fri, 10 Jan 2020 17:50:31 +0100 Subject: [PATCH 3/3] Ensure Key is not dellocated while C code is still using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as Go is concerned, Key.k is just a value, so it is OK to implement > cgo_call(k.k) as > tmp := k.k > dropReferenceTo(k) > // k's finalizer can be called at any point from now on, possibly deallocating k.k > cgo_call(tmp) // possibly using free memory To fix this, explicitly extend the lifetime of Key: > cgo_call(k.k) > runtime.KeepAlive(k) becomes > tmp := k.k > cgo_call(tmp) // k.k is certainly still valid > dropReferenceTo(k) > // k's finalizer can be called from now on, possibly deallocating k.k Signed-off-by: Miloslav Trmač --- gpgme.go | 88 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/gpgme.go b/gpgme.go index fa99e00462..9833057a66 100644 --- a/gpgme.go +++ b/gpgme.go @@ -413,7 +413,7 @@ func (c *Context) KeyListStart(pattern string, secretOnly bool) error { func (c *Context) KeyListNext() bool { c.Key = newKey() err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) - runtime.KeepAlive(c) + runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key) if err != nil { if e, ok := err.(Error); ok && e.Code() == ErrorEOF { c.KeyError = nil @@ -438,7 +438,10 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { defer C.free(unsafe.Pointer(cfpr)) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) runtime.KeepAlive(c) - if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { + runtime.KeepAlive(key) + keyKIsNil := key.k == nil + runtime.KeepAlive(key) + if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF { return nil, fmt.Errorf("key %q not found", fingerprint) } if err != nil { @@ -535,6 +538,7 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe } err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) runtime.KeepAlive(c) + runtime.KeepAlive(recipients) runtime.KeepAlive(plaintext) runtime.KeepAlive(ciphertext) return handleError(err) @@ -546,6 +550,7 @@ func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { for _, k := range signers { err := handleError(C.gpgme_signers_add(c.ctx, k.k)) runtime.KeepAlive(c) + runtime.KeepAlive(k) if err != nil { C.gpgme_signers_clear(c.ctx) runtime.KeepAlive(c) @@ -717,7 +722,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) { } type Key struct { - k C.gpgme_key_t + k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C } func newKey() *Key { @@ -728,85 +733,122 @@ func newKey() *Key { func (k *Key) Release() { C.gpgme_key_release(k.k) + runtime.KeepAlive(k) k.k = nil } func (k *Key) Revoked() bool { - return C.key_revoked(k.k) != 0 + res := C.key_revoked(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Expired() bool { - return C.key_expired(k.k) != 0 + res := C.key_expired(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Disabled() bool { - return C.key_disabled(k.k) != 0 + res := C.key_disabled(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Invalid() bool { - return C.key_invalid(k.k) != 0 + res := C.key_invalid(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanEncrypt() bool { - return C.key_can_encrypt(k.k) != 0 + res := C.key_can_encrypt(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanSign() bool { - return C.key_can_sign(k.k) != 0 + res := C.key_can_sign(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanCertify() bool { - return C.key_can_certify(k.k) != 0 + res := C.key_can_certify(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Secret() bool { - return C.key_secret(k.k) != 0 + res := C.key_secret(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanAuthenticate() bool { - return C.key_can_authenticate(k.k) != 0 + res := C.key_can_authenticate(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) IsQualified() bool { - return C.key_is_qualified(k.k) != 0 + res := C.key_is_qualified(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Protocol() Protocol { - return Protocol(k.k.protocol) + res := Protocol(k.k.protocol) + runtime.KeepAlive(k) + return res } func (k *Key) IssuerSerial() string { - return C.GoString(k.k.issuer_serial) + res := C.GoString(k.k.issuer_serial) + runtime.KeepAlive(k) + return res } func (k *Key) IssuerName() string { - return C.GoString(k.k.issuer_name) + res := C.GoString(k.k.issuer_name) + runtime.KeepAlive(k) + return res } func (k *Key) ChainID() string { - return C.GoString(k.k.chain_id) + res := C.GoString(k.k.chain_id) + runtime.KeepAlive(k) + return res } func (k *Key) OwnerTrust() Validity { - return Validity(k.k.owner_trust) + res := Validity(k.k.owner_trust) + runtime.KeepAlive(k) + return res } func (k *Key) SubKeys() *SubKey { - if k.k.subkeys == nil { + subKeys := k.k.subkeys + runtime.KeepAlive(k) + if subKeys == nil { return nil } - return &SubKey{k: k.k.subkeys, parent: k} + return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid } func (k *Key) UserIDs() *UserID { - if k.k.uids == nil { + uids := k.k.uids + runtime.KeepAlive(k) + if uids == nil { return nil } - return &UserID{u: k.k.uids, parent: k} + return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid } func (k *Key) KeyListMode() KeyListMode { - return KeyListMode(k.k.keylist_mode) + res := KeyListMode(k.k.keylist_mode) + runtime.KeepAlive(k) + return res } type SubKey struct {