-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
x/sys/windows Crash in ReadFile() when race enabled #65365
Comments
Thanks for reporting this bug @derekwbrown. ReadFile does define |
Change https://go.dev/cl/559375 mentions this issue: |
@qmuntal the fix provided will fix the crash. Howver, it will break code that uses ReadFile() in this way. From the ReadFile() docs:
However, the code change is to simply pass |
Change https://go.dev/cl/559502 mentions this issue: |
This reverts CL 559375. Reason for revert: introduced a different regression (golang/go#65378). Fixes golang/go#65378. Updates golang/go#65365. Change-Id: Ie2a602415913b04b9d9b65fee5c6a54c0267b35e Cq-Include-Trybots: luci.golang.try:x_sys-gotip-windows-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/sys/+/559502 Auto-Submit: Bryan Mills <bcmills@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
Thanks for catching this. Although it is true that the docs warns about passing a non-null
What I want to avoid is users side stepping the race detector by just passing a nil func ReadFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error {
var n uint32
if done == nil && overlapped == nil {
// Non-asynchronous case, done must be set.
done = &n
}
err := readFile(fd, p, &n, overlapped)
if raceenabled {
if done != nil && *done > 0 {
raceWriteRange(unsafe.Pointer(&p[0]), int(n))
}
raceAcquire(unsafe.Pointer(&ioSync))
}
return err
} |
so this case
is explicitly an error:
So, unless the caller is doing something blatantly wrong, then the assignment So, if overlapped is not nil, then it's OK (and in fact expected) that done is nil, and can/should proceed with calling if overlapped is nil, then done must not be nil, and so everything's fine. Given that, I think your suggestion above simplifies back to
(or similar; it's not clear to me whether the Full disclosure... I don't totally understand what the race detector is testing/how it's designed to work. But in general, overlapped I/O requires care (by the caller). If the caller initiates overlapped I/O, the caller guarantees the buffer to be valid and not used until the operation completes, via separate call to GetOverlappedResult() or GetQueuedCompletionStatus() So trying to do race detection on the buffer in the overlapped case doesn't seem valid, at least to my understanding. |
@qmuntal any update? |
Thanks for the explanation, I now see that my last proposal doesn't make much sense.
Agree. The problem is that Given that it is not possible to directly know if the read/write will be asynchronous by just looking at the parameters, I propose another approach: if the error returned is func ReadFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error {
var n uint32
err := readFile(fd, p, &n, overlapped)
if done != nil {
*done = n
}
if raceenabled {
// If err is ERROR_IO_PENDING, the operation is is pending
// completion asynchronously. n is not reliable and can't be
// used for the race detector.
if err != ERROR_IO_PENDING && n > 0 {
raceWriteRange(unsafe.Pointer(&p[0]), int(n))
}
raceAcquire(unsafe.Pointer(&ioSync))
}
return err
}
func WriteFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error {
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
err := writeFile(fd, p, done, overlapped)
// If err is ERROR_IO_PENDING, the operation is is pending
// completion asynchronously. n is not reliable and can't be
// used for the race detector.
if raceenabled && err != ERROR_IO_PENDING && *done > 0 {
raceReadRange(unsafe.Pointer(&p[0]), int(*done))
}
return err
} What do you think? |
I'm still not wild about the implementation changing the semantics of the call. I would suggest
(it's not clear to me if the In the event that it also seems like While this also adds additional semantics, you might even put in
Which is more aggressive than the underlying API, but would at least guarantee that (go) callers can't confuse the interface (and the race checker). |
One can pass an overlapped object with synchronous files, in which case
--
It says "The lpNumberOfBytesRead parameter should be set to NULL", that's a recommendation, not an obligation, else it would say "must be set to NULL". AFAIU, our usage of
|
Go version
go 1.21.5
Output of
go env
in your module/workspace:What did you do?
I have windows/go code which crashes unexpectedly.
Specifically, for ReadFile() and WriteFile() any call crashes when the third parameter
done
is Nil, which is a legal value, whenraceenabled
What did you see happen?
Code crashes when race is enabled and done is nil.
What did you expect to see?
I would expect it to not crash.
the
done
pointer may benil
when initiating an overlapped operation; in this case the done value isn't filled in until the operation completes (by callingGetQueuedCompletionStatus()
.The text was updated successfully, but these errors were encountered: