-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
cmd/vet: warn about variables/values of type reflect.{Slice,String}Header #40701
Comments
Unfixed example in the wild: alecthomas/unsafeslice#4 |
Making it a compiler error will be a hard stop to this but creates friction when others use packages as dependencies that are effected by this. On the other side these code instances will lead to subtle memory corruption bugs that need the right circumstances come together and may not be caught by tests just checking for correct output values of the functioncs involved. They can be hard to diagnose which could justify this. |
@martisch, the compiler error gated on go version in the (dependency's) go.mod file solves the friction problem. If it's in your dependency and your dependency says it wants the old version of Go, the code still compiles. Definitely sounds like we should flag it in cmd/vet. I'm not really sure we need to move on to the compiler error. It would be a very specific thing for a compiler to check. (We don't, for example, flag bad regexp arguments to regexp.MustCompile in the compiler, even though we could plausibly do that in vet.) I suggest we do just the vet check and not plan on the compiler change. |
I think there's a difference that compilers already have to specially handle reflect.SliceHeader and reflect.StringHeader because of the unsafe.Pointer safety rules. E.g., escape analysis, walk, and SSA lowering currently have code specific to using these types. There's also the difference that misusing regexp.MustCompile still has defined semantics: it panics in a very loud and obvious way, which is clearly user error. However, misusing reflect.SliceHeader / reflect.StringHeader means silent memory corruption. Not only are these failures harder to track down, I wouldn't be surprised if they're responsible for some of the issues being reported against the Go runtime. E.g., #40397 was reported as a runtime.selectgo crash, which I spent a while looking into; but it's plausible (albeit not yet confirmed) it was actually due to misuse of reflect.SliceHeader in that package: https://github.com/ethereum/go-ethereum/pull/21372/files. |
Change https://golang.org/cl/248192 mentions this issue: |
I tried running cmd/vet patched with CL 248192 on the 20 packages I identified above. I wasn't able to immediately compile 6 of them due to missing C dependencies, but vet identifies reflect.SliceHeader misuse in the other 14: go vet warnings (annotated with source line)
|
@mdempsky, I see your point about silent memory corruption being the province of the compiler. There's at least a few precedents for this in the way we handle "temporary" uintptrs as unsafe.Pointers in the unsafe.Add-equivalent code today, and also in the way we handle uintptrs in system calls. We would also want to treat assignment of a plain uintptr to that field as a conversion to unsafe.Pointer in the current unsafe.Pointer vet check (the one that flags things like If there's a way to make existing code correct instead of flagging it being wrong, that might be preferable. What do you think? |
As I understand your counter-proposal, it's to change reflect.{Slice,String}Header such that (1) the I agree that would better match typical user expectations and make valid a lot of currently invalid user code, which would be great. I have two minor concerns though: one about backwards compatibility, and another about corner cases of language semantics. -- 1. Backwards compatibility. This has the risk of breaking code like:
This use of
has been invalid since Go 1.3. Now, the documentation for reflect.{Slice,String}Header already warns: "It cannot be used safely or portably and its representation may change in a later release." Also, checkptr would be able to catch this new form of misuse, so the breakage would be obvious and actionable. So I would argue this is an acceptable risk, on par with Go 1.3 breaking 2. Language semantics. I'm concerned this muddies type identity for I don't immediately see any ways this is likely to bite real-world Go programs, but I'd want to think about this some more. -- Counter-counter-proposal. If this counter-proposal (i.e., ratifying existing user practice) is a direction we're interested in going in, I'd suggest we consider a slight tweak to it: actually change As I pointed out above, we are allowed to change the types of these fields (even if it breaks currently valid programs). And if we're going to have to continue special casing loads/stores of the I wouldn't be surprised if simply allowing assignments of |
@ianlancetaylor raised some discomfort with putting this knowledge in the compiler (as did @mdempsky). We could still consider elevating the vet check to the compiler, under the rationale that the compiler can see that the program may cause memory corruption, but we could start with the vet check. There are of course plenty of other ways to cause memory corruption that the compiler does not reject. |
If we commit to having some solution here for Go 1.16, I'm okay with waiting. I think it would be a disservice to Go users if we know they're routinely misusing reflect.SliceHeader and reflect.StringHeader, and that misuse is easily caught, but we don't help them with that because we're unsure on the best spelling. |
I think a vet check is fine, and that's what you asked for in this proposal. I support that. I'm just not comfortable with a compiler change. It doesn't make sense to me. Separately, I think it's worth considering why people use The case of |
Generally, I agree with that. But it's a struct whose usage is very particular and specially enshrined in the unsafe.Pointer safety rules, which Go compilers have to implement. That makes it more than "just a struct" in my mind. I think the "just a struct" view is what misleads many Go programmers to write code like I identified in this issue.
That's why my original proposal for #19367 included options for both constructing and destructing both strings and slices. But then based on push back, I narrowed that down to just constructing them; and then to only constructing slices. |
The special rules about The use in the compiler is only for supporting I think I hear what you are saying, and I agree that there is a problem with these structs in practice, and I agree that a vet check is appropriate. I just don't see why prohibiting them in the compiler is right. |
No, it does affect code generation. Escape analysis has to know that pointers flow through assignments to For example, compile this code with
|
Ah, OK, thanks. |
It sounds like there is consensus here that adding a vet check is OK. Since the title of the issue is tracking the vet change, this (the vet change) seems like a likely accept. |
No change in consensus, so accepted. |
This check points out an issue in our mmap implementation. It's basically the same as the one in sys/unix, so this change is false positive-y on the golang/sys package too, this is the line: https://github.com/golang/sys/blob/0a15ea8d9b02651b828a1b41989a6af25c24cb64/unix/syscall_unix.go#L120. I think this shouldn't be a problem, since the memory pointed to is managed by the user code, not the go runtime. |
The warning is not about reflect.SliceHeader (the code doesn't use it anyway). The warning is about misuse of unsafe.Pointer. go1.15.5 also warns about that. |
Doesn't pass tests because of the type-alias issue, which can be fixed at the test level (if not by actually supporting them in Joker). But this addresses the failure to vet as a result of: golang/go#40701
See golang/go#40701 and alecthomas/unsafeslice#4 . Feature-Start: go-1.16
See golang/go#40701 and alecthomas/unsafeslice#4 . Feature-Start: go-1.16
Pointed by golangci-lint. For context see golang/go#40701
Pointed by golangci-lint. For context see golang/go#40701
Fixes go vet errors: # github.com/sipcapture/heplify-server/rotator rotator/rotator.go:222:2: unreachable code # github.com/sipcapture/heplify-server/decoder decoder/decoder.go:299:35: possible misuse of reflect.SliceHeader see golang/go#40701
- Fix bad usage of reflect package to convert unsafe! - checked by `go build -gcflags=-m` show all `req does not escape` - read more : golang/go#40701
The `go vet` command in Go 1.16 reports a warning for inappropriate use of reflect.StringHeader. golang/go#40701 Its use in prometheus/metric.go to convert a byte array to a string in place began to trigger the warning. That code has been replaced with a safer variant that avoids the `vet` warning and still converts the array without allocating new memory. https://stackoverflow.com/a/66865482
The `go vet` command in Go 1.16 reports a warning for inappropriate use of reflect.StringHeader. golang/go#40701 Its use in prometheus/metric.go to convert a byte array to a string in place began to trigger the warning. That code has been replaced with a safer variant that avoids the `vet` warning and still converts the array without allocating new memory. https://stackoverflow.com/a/66865482 Additionally, the CircleCI test now uses a pinned influxdb image of 1.8.9. The 2.x influxdb Docker images require authentication to use. Co-authored-by: Collin Van Dyck <collin@segment.com>
Here are three ways I commonly see developers misuse
reflect.SliceHeader
/reflect.StringHeader
:All three of these can lead to memory corruption, as escape analysis analyzes these as "p does not escape" (rather than "p leaks to result"), but none of them are currently diagnosed by either cmd/vet or checkptr.
I propose cmd/vet should look for variables and expressions of type
reflect.SliceHeader
orreflect.StringHeader
(as opposed to*reflect.SliceHeader
or*reflect.StringHeader
) and warn about them. There should be no false positives for this, and it's consistent with the unsafeptr check that cmd/vet already has.--
An alternative approach would be to outright disallow
reflect.SliceHeader
andreflect.StringHeader
to be used except as pointed-to types. (Notably, this would be similar to the//go:cgo_incomplete
directive suggested in #40507 to prevent misuse of incomplete C struct types.)I think this would be consistent with Go 1 compat. Go 1 compat is about guaranteeing that programs continue to build and run correctly in the future. I think if we're okay with these programs not running correctly today, we should be okay with them not building either, and I expect users would prefer compilation errors instead of silent memory corruption at runtime.
But perhaps a more friendly approach would be a cmd/vet warning in Go 1.N, and then make it into a compiler error in Go 1.N+1 (and maybe gated by Go language version specified in
go.mod
).The text was updated successfully, but these errors were encountered: