-
-
Notifications
You must be signed in to change notification settings - Fork 236
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
symbol garbling is not reproducible across platforms within the same versions of Go/Garble/GOOS. #449
Comments
I think it's reasonable that building for This is not the case today, because we make use of Build IDs to seed the internal hashing, and those build IDs are rather platform-specific. We even use garble's own Build ID (embedded in its binary) to seed some globally-scoped hashes, such as exported field obfuscations. This will definitely change depending on what GOOS/GOARCH garble is built and ran on. Aside from the above - do we care about So my intuition is to only do what the first paragraph of this comment says: make obfuscation reproducible as long as all the versions (Go, garble, source modules) and build parameters (GOOS, GOARCH, |
Having said the above, I'm not sure how we could do this at a technical level. I'm thinking about what makes sense from the user's perspective first. |
Build parameters that are considered need to also include GOHOSTOS and GOHOSTARCH if the first paragraph does not become a goal. I agree that it's not sensible to try to make cross GOOS builds obfuscate the same way, there are too many differences between the platforms already to make that a sensible thing to aim for. |
If the first paragraph does not become a goal, then we have nothing to do; GOHOSTOS, GOHOSTARCH, and any other build parameters involved in building garble itself are already part of the seed used when obfuscating code. So I think we're in agreement that we want to remove those variables. |
I was thinking that documenting the requirement would be a worthwhile addition if it is not a goal. |
If the user manually specifies a seed but compiles the same package with different GOOS/GOARCH values, will the obfuscation be the same? iirc it won't, and I think that striving for that would be helpful for cases like building a client and server that both use a package for serialization, and that package requires reflection. I've seen that use case come up a few times before. |
I'm not sure I follow - what we aim for is to instead not obfuscate those RPC types. Detecting reflection like that is not perfect, but it's our only shot at solving that issue. The only alternative is to only use a user-supplied seed to obfuscate names in each package, and then the user would have to keep the same seed if they want multiple builds to be compatible in terms of RPC. I'm not a big fan of this solution because it doesn't work by default (the user needs to supply a seed), and it requires the user to rotate seeds correctly (e.g. if never rotated, this makes deobfuscation easier over time). |
Thinking about this some more: we could assume that, if the user supplies a This way, if the author wants a program to obfuscate the same way across platforms, they could accomplish it by controlling One feature we could lose if we did this is that name N0 in packages P1 and P2 would be obfuscated the same way, as we would no longer use P1 and P2's different build ID hashes as part of N0's obfuscated name hash. We can solve this by instead including P1 and P2's import paths in the obfuscated name hash. Edit: this idea comes from @capnspacehook and @moloch--, mostly :) |
That sounds perfect, as long as we document that if I'm personally a fan of designing systems that aim to 'just work' out of the box with opinionated defaults, but also let advanced users tweak some settings if they want. |
@kortschak do you have thoughts on #449 (comment)? Would it be a good solution to your problem? I realise that needing to supply |
It would solve my problem, but it makes me sort of sad (for aesthetic reasons more than anything — I can't provide any rational justification for this). Given that that my use in in CI to check that things are consistent, the weakening of obfuscation if seeds aren't rotated is not a real concern. I imagine that the approach could be made more secure in the future if another strategy is found. |
@kortschak you're pretty spot on. The reason I'm not super happy about this solution is that it makes the default behavior less reproducible, akin to how I've actually been thinking about places to source a good "default seed" from for some time, see #156. I think that could be a way to improve on the default case, so that we'd effectively always have a seed that's constant across target and host platforms, but still impossible to figure out unless you have the source code. |
If -seed is passed, the user wishes to explicitly provide a specific seed for some reason. It would make sense that building the same code for different OSes/architecures providing the same seed would result in names getting hashed the same way. That is not the case currently, because Build IDs are always used to salt the hashes of names, and Build IDs differ whenever any build arguments differ. Instead, salt hashes with the package's import path when -seed is passed, and use Build IDs as salts otherwise. This ensures also ensures the same name in different packages will always be hashed differently. Fixes burrowers#449.
If -seed is passed, the user wishes to explicitly provide a specific seed for some reason. It would make sense that building the same code for different OSes/architecures providing the same seed would result in names getting hashed the same way. That is not the case currently, because Build IDs are always used to salt the hashes of names, and Build IDs differ whenever any build arguments differ. Instead, salt hashes with the package's import path when -seed is passed, and use Build IDs as salts otherwise. This ensures also ensures the same name in different packages will always be hashed differently. Fixes burrowers#449.
The default behavior of garble is to seed via the build inputs, including the build IDs of the entire Go build of each package. This works well as a default, and does give us determinism, but it means that building for different platforms will result in different obfuscation per platform. Instead, when -seed is provided, don't use any other hash seed or salt. This means that a particular Go name will be obfuscated the same way as long as the seed, package path, and name itself remain constant. In other words, when the user supplies a custom -seed, we assume they know what they're doing in terms of storage and rotation. Expand the README docs with more examples and detail. Fixes burrowers#449.
The default behavior of garble is to seed via the build inputs, including the build IDs of the entire Go build of each package. This works well as a default, and does give us determinism, but it means that building for different platforms will result in different obfuscation per platform. Instead, when -seed is provided, don't use any other hash seed or salt. This means that a particular Go name will be obfuscated the same way as long as the seed, package path, and name itself remain constant. In other words, when the user supplies a custom -seed, we assume they know what they're doing in terms of storage and rotation. Expand the README docs with more examples and detail. Fixes burrowers#449.
The default behavior of garble is to seed via the build inputs, including the build IDs of the entire Go build of each package. This works well as a default, and does give us determinism, but it means that building for different platforms will result in different obfuscation per platform. Instead, when -seed is provided, don't use any other hash seed or salt. This means that a particular Go name will be obfuscated the same way as long as the seed, package path, and name itself remain constant. In other words, when the user supplies a custom -seed, we assume they know what they're doing in terms of storage and rotation. Expand the README docs with more examples and detail. Fixes burrowers#449.
The default behavior of garble is to seed via the build inputs, including the build IDs of the entire Go build of each package. This works well as a default, and does give us determinism, but it means that building for different platforms will result in different obfuscation per platform. Instead, when -seed is provided, don't use any other hash seed or salt. This means that a particular Go name will be obfuscated the same way as long as the seed, package path, and name itself remain constant. In other words, when the user supplies a custom -seed, we assume they know what they're doing in terms of storage and rotation. Expand the README docs with more examples and detail. Fixes #449.
What version of Garble and Go are you using?
What environment are you running Garble on?
go env
Output(similar on darwin)
What did you do?
Compile the following code on linux with GOOS=darwin and on darwin with GOOS unset (so implicitly darwin). Then run both executables on darwin.
What did you expect to see?
The same output from the two executables.
What did you see instead?
From the executable built on linux:
From the executable built on darwin:
The impact of this is that when doing
garble reverse
the HOSTOS needs to be known which will impact users who make use of cross-compilation for packaging, say building for all GOOSs on linux, which is not uncommon.The text was updated successfully, but these errors were encountered: