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

help the user when their program may break due to use of reflection #406

Open
mvdan opened this issue Oct 22, 2021 · 1 comment
Open

help the user when their program may break due to use of reflection #406

mvdan opened this issue Oct 22, 2021 · 1 comment

Comments

@mvdan
Copy link
Member

mvdan commented Oct 22, 2021

See #403, for example. When we're obfuscating user code, we can fairly easily detect when they are using reflection on an obfuscated type.

Sometimes that pattern will not break the program. For example, using encoding/json on an obfuscated struct type can behave normally without a problem, as long as the fields have json:"..." field tags. Many other uses of reflection, such as reflect.Type.Kind, reflect.Type.Len, or reflect.Type.Comparable are largely unaffected by obfuscation.

However, some other times, that pattern will lead to the program misbehaving at run-time. For example, using encoding/json on a struct type without field tags will result in different JSON object keys.

We can't automatically fix these problems for the user, because it's basically impossible to tell the two scenarios apart at compile time. Reflection is arbitrary logic with Go types at run-time, after all.

However, we can give the developer hints or tools to better deal with this wrinkle. Here are some ideas:

Idea 1: build warnings

In garble build, print a warning whenever we detect that an obfuscated Go type is being used with reflection. For example:

$ garble build
# my-mod/main
my-mod/main/main.go:12:3: warning: use of reflection via json.Marshal on obfuscated type my-mod/types.RequestMessage; see https://github.com/burrowers/garble/blob/HEAD/docs/reflection.md

The docs would then tell the user to add one of two type hints to the types package to remove the warning:

// Hint option A: do not obfuscate, as it would break the program
var _ = reflect.TypeOf(RequestMessage{})

// Hint option B: do obfuscate, as it won't break the program.
var _ = garble.AlwaysObfuscate(RequestMessage{})

type RequestMessage struct { ... }

The main upside is that the warning would be printed automatically for all users, so they wouldn't have to do anything extra.
The main downside is that the warnings would probably be prone to false positives by warning about code that won't break.
Another downside is that removing some warnings might be hard or downright impossible, if the type declarations or the uses of reflection are in third party modules.

Idea 2: opt into warnings via a flag or garble vet

Essentially, the same warnings as Idea 1, being explicitly enabled as part of a garble vet or garble -strict build as opposed to just garble build. The mechanism would otherwise work the same, by encouraging the user to add hints.

The main upside is that, since this won't interfere with builds by default, we don't need to be as worried about false positives.
The main downside is that users might forget to use this. Ideally, garble build would mostly work by default, and intuitively guide the user to add any necessary hints otherwise.

--

One extra thought is that we could also reuse the solution we come up here for another kind of run-time logic - interfaces. #3 (comment) already outlines how we could improve our current heuristic, but at the end of the day, if an interface is defined in a package that imports a separate package where the implementation of said interface is defined, we just can't know about that upfront. We can't know about "downstream" uses of interfaces just like we can't know about "downstream" uses of reflection, but we could warn about both.

@mvdan
Copy link
Member Author

mvdan commented Oct 22, 2021

Another downside is that removing some warnings might be hard or downright impossible, if the type declarations or the uses of reflection are in third party modules.

Another option, which you may call idea 1B, is to by default only print warnings for the current module. This way, we'd avoid the problem where the developer may be entirely unable to add the necessary type hints. We'd still not warn the user if an upstream library may break, but it's unclear what the user could do about such a warning if it's not their code.

mvdan added a commit to mvdan/garble-fork that referenced this issue Oct 22, 2021
We recently improved the automatic detection of reflection.
However, note that a hint may still be needed in some cases,
in particular when the reflection usage happens in a downstream package.

Keep making that suggestion.
Also mention that we obfuscate one package at a time.

I've also written down burrowers#406 with more ideas on how we could polish this
rough edge for end users in the future.

Fixes burrowers#403.
lu4p pushed a commit that referenced this issue Oct 22, 2021
We recently improved the automatic detection of reflection.
However, note that a hint may still be needed in some cases,
in particular when the reflection usage happens in a downstream package.

Keep making that suggestion.
Also mention that we obfuscate one package at a time.

I've also written down #406 with more ideas on how we could polish this
rough edge for end users in the future.

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

No branches or pull requests

1 participant