Fix invalid conversion between unsafe.Pointer and uintptr#6673
Conversation
a30bd29 to
b2e1f7a
Compare
| syscallN := func(trap uintptr, args ...uintptr) (r1, r2 uintptr, err syscall.Errno) { | ||
| // This mocks the 1st call to syscallN made by getIPForwardTable. It makes args[1] point to the table. | ||
| if len(args) > 1 { | ||
| ptr := unsafe.Pointer(args[1]) |
There was a problem hiding this comment.
Somehow checkptr thinks "pointer arithmetic result points to invalid allocation".
There is no problem when -race is not used. Need more investigation.
7ed92a0 to
10983b1
Compare
| type netIO struct { | ||
| syscallN func(trap uintptr, args ...uintptr) (r1, r2 uintptr, err syscall.Errno) | ||
| syscallN func(trap uintptr, args ...uintptr) (r1, r2 uintptr, err syscall.Errno) | ||
| getIPForwardTableFn func(family uint16, ipForwardTable **MibIPForwardTable) (errcode error) |
There was a problem hiding this comment.
couldn't we just fake syscallN in tests, instead of faking individual functions like getIPForwardTable? It seems that getIPForwardTable is just a thin wrapper around a syscallN call.
There was a problem hiding this comment.
That's what I did in an earlier version: b2e1f7a. However, checkptr failed when --race is enabled, as I commented in #6673 (comment). The error can be found here: https://github.com/antrea-io/antrea/actions/runs/10863106542/job/30146883490.
I suspect the compiler doesn't recognize the fake syscall and mistakenly raise the error. But even the latest version failed due to another checkptr error in code (as opposed the previous error in test code), and this error seems valid: the code shouldn't assign uintptr to a variable.
86e1eb8 to
db964e2
Compare
|
/test-windows-all |
| return nil, os.NewSyscallError("iphlpapi.GetIpForwardTable", err) | ||
| } | ||
| rows := make([]MibIPForwardRow, table.NumEntries, table.NumEntries) | ||
| defer n.freeMibTable(unsafe.Pointer(table)) |
There was a problem hiding this comment.
The reason for removing “if table != nil” is that when err != nil, the memory release logic has already been correctly handled, so it won't cause a memory leak, right?
There was a problem hiding this comment.
when err is not nil, the syscall failed and there is no memory to release, isn't it?
There was a problem hiding this comment.
It should be, but it makes the original implementation a bit confusing. I don't understand why "if table != nil" check is needed before "if err != nil"(I recall that this part was copied from another project). @wenyingd Do you know where the source code is, and is there's any potential risk? Based on the function doc I found at https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getipforwardtable, the current code change is safe.
There was a problem hiding this comment.
https://github.com/tailscale/winipcfg-go/blob/main/wt_mib_ipforward_row2.go#L58
This should be the place where we originally referred.
It should be good with the version in this change.
There was a problem hiding this comment.
The code is safe unless the syscall returns a table and an error at the same time, which doesn't seem normal in golang. And wireguard doesn't try to free the memory either when there is an error: https://github.com/WireGuard/wireguard-windows/blob/e70799b1440690e7d4140bffc7c73baf903c7b54/tunnel/winipcfg/winipcfg.go#L145
| }, | ||
| } | ||
| gotRows, gotErr := testNetIO.ListIPForwardRows(AF_INET) | ||
| require.Nil(t, gotErr) |
| assert.True(t, freeMibTableCalled) | ||
| // It verifies that the returned rows are independent copies, not referencing to the original table's memory, by | ||
| // asserting they retain the exact same content as the original table whose rows have been reset by freeMibTable. | ||
| expectedRows := []MibIPForwardRow{ |
There was a problem hiding this comment.
it feels like we could declare row1 and row2 variables, to avoid repeating the row definitions in the test
uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
db964e2 to
9396532
Compare
|
@wenyingd @XinShuYang will the windows e2e tests cover the updated code? |
The OSS e2e test cases can cover the function logic of the updated code. But the original failure is hit in scale setup, OSS e2e doesn't cover scale test. |
OK, it's fine as long as it covers the function logic. The original failure was due to misuse of memory, which has been covered by the new unit test. The unit test failed with the original code: https://github.com/tnqn/antrea/actions/runs/11344284459/job/31549023056?pr=46. |
|
/test-windows-all |
|
/test-windows-all |
|
/test-windows-all |
…6673) uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
…6673) uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
…6673) uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
…6673) uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The issue can be detected by checkptr when running unit test with race detector: checkptr: pointer arithmetic result points to invalid allocation ``` pFirstRow := uintptr(unsafe.Pointer(&table.Table[0])) row := *(*MibIPForwardRow)(unsafe.Pointer(pFirstRow + rowSize*uintptr(i))) ``` While it can be fixed by performing both conversions in the same expression like below, using `unsafe.Slice` to get the slice is much simpler. ``` row := *(*MibIPForwardRow)(unsafe.Pointer(uintptr(unsafe.Pointer(&table.Table[0])) + rowSize*uintptr(i))) ``` The patch also adds an unit test to validate it. Signed-off-by: Quan Tian <quan.tian@broadcom.com>
uintptr cannot be stored in variable before conversion back to Pointer. This is because the variable is an integer with no pointer semantics, even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.
The issue can be detected by checkptr when running unit test with race detector:
checkptr: pointer arithmetic result points to invalid allocation
While it can be fixed by performing both conversions in the same expression like below, using
unsafe.Sliceto get the slice is much simpler.The patch also adds an unit test to validate it.