Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible conflict with how go runtime might work #18

Open
jazzy-crane opened this issue Sep 11, 2020 · 4 comments
Open

Possible conflict with how go runtime might work #18

jazzy-crane opened this issue Sep 11, 2020 · 4 comments

Comments

@jazzy-crane
Copy link

Alluded to here:

#16 (comment)

I'm not 100% sure about this, and it's more to pick @alexbrainman brain as I think you'll have better insight into this than me.

Functions such as https://docs.microsoft.com/en-us/windows/win32/printdocs/getprinter take a single chunk of memory to return an array of (for level 5, for example) PRINTER_INFO_5 structures. This are correctly represented as such:

type PRINTER_INFO_5 struct {
	PrinterName              *uint16
	PortName                 *uint16
	Attributes               uint32
	DeviceNotSelectedTimeout uint32
	TransmissionRetryTimeout uint32
}

The way this works with the string pointers is that space is allocated for N PRINTER_INFO_5 structs + an additional amount of space at the bottom for the strings. The string pointers within the structs then point at the correct offset into that additional space.

This can be seen below (I added some debug):

buf extent C00007C000 -> C00007C558
p.PrinterName for printer  0 @ C00007C504
p.PrinterName for printer  1 @ C00007C4C8
p.PrinterName for printer  2 @ C00007C48C
p.PrinterName for printer  3 @ C00007C406
p.PrinterName for printer  4 @ C00007C3C0
p.PrinterName for printer  5 @ C00007C29A
p.PrinterName for printer  6 @ C00007C254
p.PrinterName for printer  7 @ C00007C20E
p.PrinterName for printer  8 @ C00007C1EE
p.PrinterName for printer  9 @ C00007C192

My concern is, (and I believe I'm correct here, but may not be so would love your input) the go runtime is free to move the address of the buf allocation? However the string pointers "within" the buf allocation would not change the address they are pointing at, so would no longer point to the correct place? As I understand it, that is why the cGo rules do not allow you to pass a pointer to a Go struct containing Go pointers into cGo? - this in effect is circumventing that with a byte buffer but since it's cast it's the same thing

If this is a problem how can it be addressed? I presume using an allocater like LocalAlloc would do the trick ensuring the memory is not in view of the Go runtime. Though I can't see anywhere in the standard library or x/sys/windows which exposes this. C.malloc a possibility, though that introduces an (undesirable?) CGo dependency.

@jazzy-crane
Copy link
Author

Sorry for the repeated weasel words - my understanding here isn't 100%!

@alexbrainman
Copy link
Owner

Please see my fix in

https://github.com/alexbrainman/printer/tree/fix16

Hopefully it has all the answers to your questions.

Alex

@jazzy-crane
Copy link
Author

I'm happy that that fixes issue #16 but this is a different issue to the string conversion and checkptr issues - more to do with whether the memory allocated for buf could move, leaving the uint16* pointers pointing to memory within buf pointing to outside where buf now resides. I think as buf is heap allocated (my understanding is it escapes so can't ever be put on a stack, and thus moveable) this can't happen, but I'm wondering if that's a guarantee that holds for the future.

@alexbrainman
Copy link
Owner

My concern is, (and I believe I'm correct here, but may not be so would love your input) the go runtime is free to move the address of the buf allocation?

Yes Go runtime can move buf if it was allocated on Go stack. If it is allocated on Go heap, it is not moved (but that rule can change in the future).

However the string pointers "within" the buf allocation would not change the address they are pointing at, so would no longer point to the correct place?

I am not clear what you are saying. But, if you are talking about

var v PRINTER_INFO

then Go garbage collector adjusts v.PrinterName when it moves v. Both v and memory pointed by v.PrinterName will live inside of single memory block allocated by Go. They will move together, and gc will adjust all pointers.

As I understand it, that is why the cGo rules do not allow you to pass a pointer to a Go struct containing Go pointers into cGo? - this in effect is circumventing that with a byte buffer but since it's cast it's the same thing

I do not know much about Go runtime rules. Perhaps there is a problem here, but I don't see it. If you are interested, you should discuss at

https://groups.google.com/g/golang-nuts

Alex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants