Skip to content

Commit b35f6ff

Browse files
authored
Don't use runtime.Pinner to avoid false positive in cgo pointer check (#274)
* don't use runtime.Pinner to avoid false positive in cgo pointer check * fix TestIssue71943 * always return a referenceable pointer * use pbaseNeverEmpty * simplify code * fix hashOneShot * fix evpHashSign
1 parent cc9339b commit b35f6ff

File tree

9 files changed

+93
-38
lines changed

9 files changed

+93
-38
lines changed

cmd/mkcgo/generate.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,13 +407,20 @@ func generateCFn(typedefs map[string]string, fn *mkcgo.Func, w io.Writer) {
407407
}
408408

409409
// paramToGo converts C parameter p to Go parameter.
410-
func paramToGo(p *mkcgo.Param) string {
410+
func paramToGo(p *mkcgo.Param, hidden bool) string {
411411
goType, needCast := cTypeToGo(p.Type, true)
412412
if !needCast {
413+
if hidden && goType == "unsafe.Pointer" {
414+
// The parameter is not annotated with cgoCheckPointer by cgo
415+
// if it has the form unsafe.Pointer(&...).
416+
// See https://go-review.googlesource.com/c/go/+/404295.
417+
// TODO: support other types.
418+
return fmt.Sprintf("unsafe.Pointer(&*(*byte)(%s))", p.Name)
419+
}
413420
return p.Name
414421
}
415422
switch {
416-
case goType == "unsafe.Pointer" || goType == "":
423+
case goType == "":
417424
return p.Name
418425
case goType[0] == '*':
419426
return fmt.Sprintf("(%s)(unsafe.Pointer(%s))", goType, p.Name)
@@ -540,7 +547,7 @@ func fnToGoParams(fn *mkcgo.Func) string {
540547
// argList returns source code for C parameters for function f.
541548
func fnToGoArgs(fn *mkcgo.Func) string {
542549
return join(fn.Params, func(_ int, p *mkcgo.Param) string {
543-
return paramToGo(p)
550+
return paramToGo(p, slices.Contains(fn.NoCheckPtrParams, p.Name))
544551
}, ", ")
545552
}
546553

evp.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,10 @@ func evpHashSign(withKey withKeyFunc, h crypto.Hash, msg []byte) ([]byte, error)
441441
}); err != nil {
442442
return nil, err
443443
}
444-
if _, err := ossl.EVP_DigestUpdate(ctx, unsafe.Pointer(base(msg)), len(msg)); err != nil {
445-
return nil, err
444+
if len(msg) > 0 {
445+
if _, err := ossl.EVP_DigestUpdate(ctx, pbase(msg), len(msg)); err != nil {
446+
return nil, err
447+
}
446448
}
447449
// Obtain the signature length
448450
if _, err := ossl.EVP_DigestSignFinal(ctx, nil, &outLen); err != nil {
@@ -472,8 +474,10 @@ func evpHashVerify(withKey withKeyFunc, h crypto.Hash, msg, sig []byte) error {
472474
}); err != nil {
473475
return err
474476
}
475-
if _, err := ossl.EVP_DigestUpdate(ctx, unsafe.Pointer(base(msg)), len(msg)); err != nil {
476-
return err
477+
if len(msg) > 0 {
478+
if _, err := ossl.EVP_DigestUpdate(ctx, pbase(msg), len(msg)); err != nil {
479+
return err
480+
}
477481
}
478482
if _, err := ossl.EVP_DigestVerifyFinal(ctx, base(sig), len(sig)); err != nil {
479483
return err

hash.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ import (
1919
const maxHashSize = 64
2020

2121
func hashOneShot(ch crypto.Hash, p []byte, sum []byte) bool {
22-
if len(p) > 0 {
23-
var pinner runtime.Pinner
24-
defer pinner.Unpin()
25-
pinner.Pin(&p[0])
26-
}
27-
_, err := ossl.EVP_Digest(pbase(p), len(p), base(sum), nil, loadHash(ch).md, nil)
22+
_, err := ossl.EVP_Digest(pbaseNeverEmpty(p), len(p), base(sum), nil, loadHash(ch).md, nil)
2823
return err == nil
2924
}
3025

@@ -253,8 +248,7 @@ type evpHash struct {
253248
// ctx2 is used in evpHash.sum to avoid changing
254249
// the state of ctx. Having it here allows reusing the
255250
// same allocated object multiple times.
256-
ctx2 ossl.EVP_MD_CTX_PTR
257-
pinner runtime.Pinner
251+
ctx2 ossl.EVP_MD_CTX_PTR
258252
}
259253

260254
func newEvpHash(ch crypto.Hash) *evpHash {
@@ -318,8 +312,6 @@ func (h *evpHash) Write(p []byte) (int, error) {
318312
if len(p) == 0 {
319313
return 0, nil
320314
}
321-
defer h.pinner.Unpin()
322-
h.pinner.Pin(&p[0])
323315
h.init()
324316
if _, err := ossl.EVP_DigestUpdate(h.ctx, pbase(p), len(p)); err != nil {
325317
panic(err)

hash_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding"
77
"hash"
88
"io"
9+
"runtime"
910
"strings"
1011
"testing"
1112

@@ -261,6 +262,7 @@ func TestHash_StringWriter(t *testing.T) {
261262
}
262263
h := cryptoToHash(ch)()
263264
initSum := h.Sum(nil)
265+
h.(io.StringWriter).WriteString("")
264266
h.(io.StringWriter).WriteString(string(msg))
265267
h.Reset()
266268
sum := h.Sum(nil)
@@ -327,6 +329,7 @@ func TestHash_OneShot(t *testing.T) {
327329
if !openssl.SupportsHash(tt.h) {
328330
t.Skip("not supported")
329331
}
332+
_ = tt.oneShot(nil) // test that does not panic
330333
got := tt.oneShot(msg)
331334
h := cryptoToHash(tt.h)()
332335
h.Write(msg)
@@ -382,6 +385,29 @@ func TestHashAllocations(t *testing.T) {
382385
}
383386
}
384387

388+
func verifySHA256(token, salt string) [32]byte {
389+
return openssl.SHA256([]byte(token + salt))
390+
}
391+
392+
func TestIssue71943(t *testing.T) {
393+
// https://github.com/golang/go/issues/71943
394+
if Asan() {
395+
t.Skip("skipping allocations test with sanitizers")
396+
}
397+
n := int(testing.AllocsPerRun(10, func() {
398+
runtime.KeepAlive(verifySHA256("teststring", "test"))
399+
}))
400+
want := 2
401+
if compareCurrentVersion("go1.25") >= 0 {
402+
want = 0
403+
} else if compareCurrentVersion("go1.24") >= 0 {
404+
want = 1
405+
}
406+
if n > want {
407+
t.Errorf("allocs = %d, want %d", n, want)
408+
}
409+
}
410+
385411
func BenchmarkSHA256(b *testing.B) {
386412
b.StopTimer()
387413
h := openssl.NewSHA256()

internal/mkcgo/mkcgo.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ type Enum struct {
3434

3535
// FuncAttrs contains attributes of a function.
3636
type FuncAttrs struct {
37-
Tags []TagAttr
38-
VariadicTarget string
39-
Optional bool
40-
NoError bool
41-
ErrCond string
42-
NoEscape bool
43-
NoCallback bool
37+
Tags []TagAttr
38+
VariadicTarget string
39+
Optional bool
40+
NoError bool
41+
ErrCond string
42+
NoEscape bool
43+
NoCallback bool
44+
NoCheckPtrParams []string
4445
}
4546

4647
func (attrs *FuncAttrs) String() string {
@@ -70,6 +71,16 @@ func (attrs *FuncAttrs) String() string {
7071
if attrs.NoCallback {
7172
bld.WriteString(", nocallback")
7273
}
74+
if len(attrs.NoCheckPtrParams) != 0 {
75+
bld.WriteString(", nocheckptr(")
76+
for i, p := range attrs.NoCheckPtrParams {
77+
if i > 0 {
78+
bld.WriteString(", ")
79+
}
80+
bld.WriteString(p)
81+
}
82+
bld.WriteByte(')')
83+
}
7384
return strings.TrimPrefix(bld.String(), ", ")
7485
}
7586

@@ -116,7 +127,7 @@ func (f *Func) String() string {
116127
}
117128
bld.WriteString(")")
118129
if attrs := f.FuncAttrs.String(); attrs != "" {
119-
bld.WriteString(" ")
130+
bld.WriteByte(' ')
120131
bld.WriteString(attrs)
121132
}
122133
return bld.String()
@@ -231,4 +242,12 @@ var attributes = [...]attribute{
231242
return nil
232243
},
233244
},
245+
{
246+
name: "nocheckptr",
247+
description: "The parameter will be hidden from the Go compiler.",
248+
handle: func(opts *FuncAttrs, s ...string) error {
249+
opts.NoCheckPtrParams = append(opts.NoCheckPtrParams, s[0])
250+
return nil
251+
},
252+
},
234253
}

internal/mkcgo/mkcgo_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,16 @@ func TestFuncString(t *testing.T) {
177177
name: "Function with attributes",
178178
function: &mkcgo.Func{Name: "TestFunc", Ret: "void", Params: []*mkcgo.Param{{Type: "int", Name: "param1"}},
179179
FuncAttrs: mkcgo.FuncAttrs{
180-
Tags: []mkcgo.TagAttr{{Tag: "tag1"}, {Tag: "tag2", Name: "name"}},
181-
Optional: true,
182-
NoError: true,
183-
ErrCond: "error_condition",
184-
NoEscape: true,
185-
NoCallback: true,
180+
Tags: []mkcgo.TagAttr{{Tag: "tag1"}, {Tag: "tag2", Name: "name"}},
181+
Optional: true,
182+
NoError: true,
183+
ErrCond: "error_condition",
184+
NoEscape: true,
185+
NoCallback: true,
186+
NoCheckPtrParams: []string{"param1"},
186187
},
187188
},
188-
want: "void TestFunc(int param1) [{tag1 } {tag2 name}], optional, noerror, errcond(error_condition), noescape, nocallback",
189+
want: "void TestFunc(int param1) [{tag1 } {tag2 name}], optional, noerror, errcond(error_condition), noescape, nocallback, nocheckptr(param1)",
189190
},
190191
}
191192

@@ -247,7 +248,7 @@ enum {
247248
}, {
248249
content: `
249250
void F0(void) __attribute__((tag("t0"),noerror,tag("t1","tn0")));
250-
int F1(int p1) __attribute__((errcond("ec0"), noescape, nocallback));
251+
int F1(int p1) __attribute__((errcond("ec0"), noescape, nocallback, nocheckptr(p1)));
251252
int * F2(int **p1, void * p2);
252253
int *F3(int p1, void***) __attribute__((optional));
253254
unsigned long F4(int func, int type, ...);
@@ -256,7 +257,7 @@ void F6() __attribute__(());`,
256257
want: &mkcgo.Source{
257258
Funcs: []*mkcgo.Func{
258259
{Name: "F0", Ret: "void", Params: []*mkcgo.Param{{"void", ""}}, FuncAttrs: mkcgo.FuncAttrs{Tags: []mkcgo.TagAttr{{Tag: "t0"}, {Tag: "t1", Name: "tn0"}}, NoError: true}},
259-
{Name: "F1", Ret: "int", Params: []*mkcgo.Param{{"int", "p1"}}, FuncAttrs: mkcgo.FuncAttrs{ErrCond: "ec0", NoEscape: true, NoCallback: true}},
260+
{Name: "F1", Ret: "int", Params: []*mkcgo.Param{{"int", "p1"}}, FuncAttrs: mkcgo.FuncAttrs{ErrCond: "ec0", NoEscape: true, NoCallback: true, NoCheckPtrParams: []string{"p1"}}},
260261
{Name: "F2", Ret: "int*", Params: []*mkcgo.Param{{"int**", "p1"}, {"void*", "p2"}}},
261262
{Name: "F3", Ret: "int*", Params: []*mkcgo.Param{{"int", "p1"}, {"void***", ""}}, FuncAttrs: mkcgo.FuncAttrs{Optional: true}},
262263
{Name: "F4", Ret: "unsigned long", Params: []*mkcgo.Param{{"int", "__func"}, {"int", "__type"}, {"...", ""}}},

internal/ossl/shims.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,10 @@ _EVP_MD_CTX_PTR EVP_MD_CTX_new(void);
189189
void EVP_MD_CTX_free(_EVP_MD_CTX_PTR ctx);
190190
int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in);
191191
int EVP_MD_CTX_copy_ex(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in);
192-
int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback));
192+
int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback,nocheckptr("data")));
193193
int EVP_DigestInit_ex(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type, _ENGINE_PTR impl);
194194
int EVP_DigestInit(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type);
195-
int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback));
195+
int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback,nocheckptr("d")));
196196
int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s);
197197
int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111"),noescape,nocallback));
198198
int EVP_DigestSignInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey);

internal/ossl/zossl.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openssl.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,12 @@ func baseNeverEmpty(b []byte) *byte {
252252
return unsafe.SliceData(b)
253253
}
254254

255+
// pbaseNeverEmpty returns the address of the underlying array in b.
256+
// If b has zero length, it returns a pointer to a zero byte.
257+
func pbaseNeverEmpty(b []byte) unsafe.Pointer {
258+
return unsafe.Pointer(baseNeverEmpty(b))
259+
}
260+
255261
// pbase returns the address of the underlying array in b,
256262
// being careful not to panic when b has zero length.
257263
func pbase(b []byte) unsafe.Pointer {

0 commit comments

Comments
 (0)