Syscalls that take structs with embedded pointers

139 views
Skip to first unread message

sh...@tigera.io

unread,
May 14, 2021, 6:35:49 AM5/14/21
to golang-nuts
Hi all,

I'm looking for a safe way to make a unix syscall where the syscall takes a pointer to a struct and, in turn, the struct needs to contain a pointer to a buffer (for the kernel to read or write to).

According to the rules in the unsafe package, I don't think this is safe:

buf := [128]byte{}
wrapper := someStruct{buf: &buf}
... := unix.Syscall(trap, uintptr(unsafe.Pointer(&wrapper)), ...)

The point to "wrapper" is protect from being moved by unsafe.Pointer's "Conversion of a Pointer to a uintptr when calling syscall.Syscall" rule but I think the pointer to buf within that struct is not safe from being rewritten.

Lots of ioctls use this pattern as does the BPF syscall.  At the moment, we're resorting to CGo to make those calls but it'd be nice to get rid of that.

One way I could see to solve this would be to do some manual memory management with MMap or BRK to allocate the buffer but I wondered if there was an easier way?

Cheers,
-Shaun

Jan Mercl

unread,
May 14, 2021, 7:10:45 AM5/14/21
to sh...@tigera.io, golang-nuts
On Fri, May 14, 2021 at 12:36 PM sh...@tigera.io <sh...@tigera.io> wrote:

> One way I could see to solve this would be to do some manual memory management with MMap or BRK to allocate the buffer but I wondered if there was an easier way?

IMO using manual memory management _is_ the easy way in this case. I'm
using https://pkg.go.dev/modernc.org/memory for this.

sh...@tigera.io

unread,
May 14, 2021, 8:24:28 AM5/14/21
to golang-nuts
Thanks for the pointer to that package; looking at our go.mod, looks like we've already got it as a transitive dependency so it looks ideal for my use case.

sh...@tigera.io

unread,
May 14, 2021, 9:00:53 AM5/14/21
to golang-nuts
Now, is it technically legal to convert a uintptr to some location that was manually allocated and then cast it to an unsafe.Pointer?  When I look at the docs for unsafe.Pointer, I can't match that to any of the cases but it seems very likely to be safe by analogy with the GCO rules for handling "C" pointers and "Go" pointers.

Michael Pratt

unread,
May 14, 2021, 9:29:04 AM5/14/21
to sh...@tigera.io, golang-nuts
You can use a *byte for the buffer. For instance, unix.Iovec does this: https://pkg.go.dev/golang.org/x/sys/unix#Iovec

Users can cast a *unix.Iovec directly to unsafe.Pointer for Syscall without any special handling of the *byte field.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/317d44cf-8f2b-42d6-ab10-b42da7280616n%40googlegroups.com.

Shaun Crampton

unread,
May 14, 2021, 9:44:59 AM5/14/21
to Michael Pratt, golang-nuts
> You can use a *byte for the buffer. For instance, unix.Iovec does this

Are you sure that's not a bug? What's to stop the *Byte from being
moved by the GC?

Michael Pratt

unread,
May 14, 2021, 10:28:24 AM5/14/21
to Shaun Crampton, golang-nuts
Go has a non-moving GC [1], so that is not an issue. That said, unsafe.Pointer states "the referenced allocated object, if any, is retained and not moved until the call completes". It doesn't say that this recursively applies to objects referenced by the converted object, though I perhaps it should.

[1] Except for stacks, but taking the address of a byte slice and putting it in Iovec.Base will force it to escape anyways.

jake...@gmail.com

unread,
May 14, 2021, 11:40:25 AM5/14/21
to golang-nuts
On Friday, May 14, 2021 at 10:28:24 AM UTC-4 Michael Pratt wrote:
Go has a non-moving GC [1], so that is not an issue.
 
It is my understanding that the go team has always explicitly maintained the 'right' to change the GC to allow moving memory. Or to allow another implementation of the Go language to do so. That is the purpose of some of these rules. So if you write code that assumes a non-moving GC, then be prepared for breakage later.

I know that the standard libraries do things that violate those rules, because they are tied to the Go version they ship with. So the Go team will adjust them as necessary when changes are made to Go. I am not entirely clear if this logic applies to the  golang.org/x libraries as well.
 

Ian Lance Taylor

unread,
May 19, 2021, 12:42:19 PM5/19/21
to Michael Pratt, Shaun Crampton, golang-nuts
On Fri, May 14, 2021 at 7:28 AM 'Michael Pratt' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> Go has a non-moving GC [1], so that is not an issue. That said, unsafe.Pointer states "the referenced allocated object, if any, is retained and not moved until the call completes". It doesn't say that this recursively applies to objects referenced by the converted object, though I perhaps it should.

To date we have explicitly decided that it shouldn't. See the
discussion at https://go.googlesource.com/proposal/+/refs/heads/master/design/12416-cgo-pointers.md.

Ian
Reply all
Reply to author
Forward
0 new messages