Skip to content

Commit c3d444d

Browse files
ianlancetaylorgopherbot
authored andcommitted
os/user: allocate buffers in Go memory, not C memory
Since the first implementation of os/user, it's called C malloc to allocate memory for buffers. However, the buffers are just used for temporary storage, and we can just a []byte instead. To make this work without causing cgo pointer errors, we move the pwd and grp structs into C stack memory, and just return them. It's OK to store a Go pointer on the C stack temporarily. Change-Id: I9f8ffb6e51df1e585276c259fe99359d7835df87 Reviewed-on: https://go-review.googlesource.com/c/go/+/449335 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Russ Cox <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent d2aa787 commit c3d444d

File tree

1 file changed

+67
-91
lines changed

1 file changed

+67
-91
lines changed

src/os/user/cgo_lookup_unix.go

Lines changed: 67 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,36 @@ import (
2222
#include <grp.h>
2323
#include <stdlib.h>
2424
25-
static int mygetpwuid_r(int uid, struct passwd *pwd,
26-
char *buf, size_t buflen, struct passwd **result) {
27-
return getpwuid_r(uid, pwd, buf, buflen, result);
25+
static struct passwd mygetpwuid_r(int uid, char *buf, size_t buflen, int *found, int *perr) {
26+
struct passwd pwd;
27+
struct passwd *result;
28+
*perr = getpwuid_r(uid, &pwd, buf, buflen, &result);
29+
*found = result != NULL;
30+
return pwd;
2831
}
2932
30-
static int mygetpwnam_r(const char *name, struct passwd *pwd,
31-
char *buf, size_t buflen, struct passwd **result) {
32-
return getpwnam_r(name, pwd, buf, buflen, result);
33+
static struct passwd mygetpwnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) {
34+
struct passwd pwd;
35+
struct passwd *result;
36+
*perr = getpwnam_r(name, &pwd, buf, buflen, &result);
37+
*found = result != NULL;
38+
return pwd;
3339
}
3440
35-
static int mygetgrgid_r(int gid, struct group *grp,
36-
char *buf, size_t buflen, struct group **result) {
37-
return getgrgid_r(gid, grp, buf, buflen, result);
41+
static struct group mygetgrgid_r(int gid, char *buf, size_t buflen, int *found, int *perr) {
42+
struct group grp;
43+
struct group *result;
44+
*perr = getgrgid_r(gid, &grp, buf, buflen, &result);
45+
*found = result != NULL;
46+
return grp;
3847
}
3948
40-
static int mygetgrnam_r(const char *name, struct group *grp,
41-
char *buf, size_t buflen, struct group **result) {
42-
return getgrnam_r(name, grp, buf, buflen, result);
49+
static struct group mygetgrnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) {
50+
struct group grp;
51+
struct group *result;
52+
*perr = getgrnam_r(name, &grp, buf, buflen, &result);
53+
*found = result != NULL;
54+
return grp;
4355
}
4456
*/
4557
import "C"
@@ -50,28 +62,22 @@ func current() (*User, error) {
5062

5163
func lookupUser(username string) (*User, error) {
5264
var pwd C.struct_passwd
53-
var result *C.struct_passwd
65+
var found bool
5466
nameC := make([]byte, len(username)+1)
5567
copy(nameC, username)
5668

57-
buf := alloc(userBuffer)
58-
defer buf.free()
59-
60-
err := retryWithBuffer(buf, func() syscall.Errno {
61-
// mygetpwnam_r is a wrapper around getpwnam_r to avoid
62-
// passing a size_t to getpwnam_r, because for unknown
63-
// reasons passing a size_t to getpwnam_r doesn't work on
64-
// Solaris.
65-
return syscall.Errno(C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])),
66-
&pwd,
67-
(*C.char)(buf.ptr),
68-
C.size_t(buf.size),
69-
&result))
69+
err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno {
70+
var cfound, cerr C.int
71+
pwd = C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])),
72+
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)),
73+
&cfound, &cerr)
74+
found = cfound != 0
75+
return syscall.Errno(cerr)
7076
})
7177
if err != nil {
7278
return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
7379
}
74-
if result == nil {
80+
if !found {
7581
return nil, UnknownUserError(username)
7682
}
7783
return buildUser(&pwd), err
@@ -87,24 +93,20 @@ func lookupUserId(uid string) (*User, error) {
8793

8894
func lookupUnixUid(uid int) (*User, error) {
8995
var pwd C.struct_passwd
90-
var result *C.struct_passwd
91-
92-
buf := alloc(userBuffer)
93-
defer buf.free()
94-
95-
err := retryWithBuffer(buf, func() syscall.Errno {
96-
// mygetpwuid_r is a wrapper around getpwuid_r to avoid using uid_t
97-
// because C.uid_t(uid) for unknown reasons doesn't work on linux.
98-
return syscall.Errno(C.mygetpwuid_r(C.int(uid),
99-
&pwd,
100-
(*C.char)(buf.ptr),
101-
C.size_t(buf.size),
102-
&result))
96+
var found bool
97+
98+
err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno {
99+
var cfound, cerr C.int
100+
pwd = C.mygetpwuid_r(C.int(uid),
101+
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)),
102+
&cfound, &cerr)
103+
found = cfound != 0
104+
return syscall.Errno(cerr)
103105
})
104106
if err != nil {
105107
return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
106108
}
107-
if result == nil {
109+
if !found {
108110
return nil, UnknownUserIdError(uid)
109111
}
110112
return buildUser(&pwd), nil
@@ -128,24 +130,23 @@ func buildUser(pwd *C.struct_passwd) *User {
128130

129131
func lookupGroup(groupname string) (*Group, error) {
130132
var grp C.struct_group
131-
var result *C.struct_group
133+
var found bool
132134

133-
buf := alloc(groupBuffer)
134-
defer buf.free()
135135
cname := make([]byte, len(groupname)+1)
136136
copy(cname, groupname)
137137

138-
err := retryWithBuffer(buf, func() syscall.Errno {
139-
return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])),
140-
&grp,
141-
(*C.char)(buf.ptr),
142-
C.size_t(buf.size),
143-
&result))
138+
err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno {
139+
var cfound, cerr C.int
140+
grp = C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])),
141+
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)),
142+
&cfound, &cerr)
143+
found = cfound != 0
144+
return syscall.Errno(cerr)
144145
})
145146
if err != nil {
146147
return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
147148
}
148-
if result == nil {
149+
if !found {
149150
return nil, UnknownGroupError(groupname)
150151
}
151152
return buildGroup(&grp), nil
@@ -161,24 +162,20 @@ func lookupGroupId(gid string) (*Group, error) {
161162

162163
func lookupUnixGid(gid int) (*Group, error) {
163164
var grp C.struct_group
164-
var result *C.struct_group
165-
166-
buf := alloc(groupBuffer)
167-
defer buf.free()
168-
169-
err := retryWithBuffer(buf, func() syscall.Errno {
170-
// mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t
171-
// because C.gid_t(gid) for unknown reasons doesn't work on linux.
172-
return syscall.Errno(C.mygetgrgid_r(C.int(gid),
173-
&grp,
174-
(*C.char)(buf.ptr),
175-
C.size_t(buf.size),
176-
&result))
165+
var found bool
166+
167+
err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno {
168+
var cfound, cerr C.int
169+
grp = C.mygetgrgid_r(C.int(gid),
170+
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)),
171+
&cfound, &cerr)
172+
found = cfound != 0
173+
return syscall.Errno(cerr)
177174
})
178175
if err != nil {
179176
return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
180177
}
181-
if result == nil {
178+
if !found {
182179
return nil, UnknownGroupIdError(strconv.Itoa(gid))
183180
}
184181
return buildGroup(&grp), nil
@@ -214,44 +211,23 @@ func (k bufferKind) initialSize() C.size_t {
214211
return C.size_t(sz)
215212
}
216213

217-
type memBuffer struct {
218-
ptr unsafe.Pointer
219-
size C.size_t
220-
}
221-
222-
func alloc(kind bufferKind) *memBuffer {
223-
sz := kind.initialSize()
224-
return &memBuffer{
225-
ptr: C.malloc(sz),
226-
size: sz,
227-
}
228-
}
229-
230-
func (mb *memBuffer) resize(newSize C.size_t) {
231-
mb.ptr = C.realloc(mb.ptr, newSize)
232-
mb.size = newSize
233-
}
234-
235-
func (mb *memBuffer) free() {
236-
C.free(mb.ptr)
237-
}
238-
239214
// retryWithBuffer repeatedly calls f(), increasing the size of the
240215
// buffer each time, until f succeeds, fails with a non-ERANGE error,
241216
// or the buffer exceeds a reasonable limit.
242-
func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
217+
func retryWithBuffer(startSize bufferKind, f func([]byte) syscall.Errno) error {
218+
buf := make([]byte, startSize)
243219
for {
244-
errno := f()
220+
errno := f(buf)
245221
if errno == 0 {
246222
return nil
247223
} else if errno != syscall.ERANGE {
248224
return errno
249225
}
250-
newSize := buf.size * 2
226+
newSize := len(buf) * 2
251227
if !isSizeReasonable(int64(newSize)) {
252228
return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
253229
}
254-
buf.resize(newSize)
230+
buf = make([]byte, newSize)
255231
}
256232
}
257233

0 commit comments

Comments
 (0)