-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
Fix accidental mutation of shared cty.Path
s in ValueMarks funcs
#32543
Conversation
Go's `append()` reserves the right to mutate its primary argument in-place, and expects the caller to assign its return value to the same variable that was passed as the primary argument. Due to what was almost definitely a typo (followed by copy-paste mishap), the configschema `Block.ValueMarks` and `Object.ValueMarks` functions were treating it like an immutable function that returns a new slice. In rare and hard-to-reproduce cases, this was causing bizarre malfunctions when marking sensitive schema attributes in deeply-nested block structures -- omitting the marks for some sensitive values (🚨), and marking other entire blocks as sensitive (which is supposed to be impossible). The chaotic and unreliable nature of the bugs is likely related to `append()`'s automatic slice reallocation behavior (if the append operation overflows the original array allocation, the resulting behavior can _look_ immutable), but there might be other contributing factors too. This commit fixes existing instances of the problem, and wraps the desired copy-and-append behavior in a helper function to simplify handling shared parent paths in an immutable way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sick!
I think I now have a slightly more nuanced (but still incomplete) theory of how the bad behavior occurs. If you have a path (step slice) with a bit of extra headroom left in its underlying array, then any of its direct children who append to it will successively clobber the next slot out past the parent's length. So you'll end up with multiple PathValueMarks, one for each sibling child, but every one of their paths will be set to whatever the LAST one to be processed was. And then when they get marshalled into their final structure, they get de-duplicated, leaving only one. That de-duplication probably favors the first one, even though the last one was the one that contributed the path. So:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
Reminder for the merging maintainer: if this is a user-visible change, please update the changelog on the appropriate release branch. |
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions. |
Go's
append()
reserves the right to mutate (the backing storage of) its primary argument in-place, and expects the caller to assign its return value to the same variable that was passed as the primary argument. Due to what was almost definitely a typo (followed by copy-paste mishap), the configschemaBlock.ValueMarks
andObject.ValueMarks
functions were treating it like an immutable function that returns a new slice.In rare and hard-to-reproduce cases, this was causing bizarre malfunctions when marking sensitive schema attributes in deeply-nested block structures -- omitting the marks for some sensitive values (🚨), and marking other entire blocks as sensitive (which is supposed to be impossible). The chaotic and unreliable nature of the bugs is likely related to
append()
's automatic slice reallocation behavior (if the append operation overflows the original array allocation, the resulting behavior can look immutable), but there might be other contributing factors too.This commit fixes existing instances of the problem, and wraps the desired copy-and-append behavior in a helper function to simplify handling shared parent paths in an immutable way.
Discussion for Reviewers
We originally started investigating this as a structured log bug (@brandonc had a previous PR for that, on which @alisdair pointed out that the "bug" should have been impossible), and ultimately chased it back to legit inaccurate info in the
after_sensitive
object in the JSON plan.Reproducing this problem is incredibly dicey! Minimal repro attempts using the
tfcoremock
andalisdair/nested
providers all failed, and I only eventually succeeded by copy-pasting the actual schema fromrancher/rancher2
into a dummy provider and playing with the nesting levels.To test the problem (on main or any released 1.x Terraform) and the fix (on this branch), use:
terraform plan -out plan.tfplan
/terraform show -json plan.tfplan > plan.json
Once you've got the plan json, jump to the
after_sensitive
property for the one resource_change:etcd
block should have bothcert
andkey
attrs marked as sensitive.s3_backup_config
block should have bothaccess_key
andsecret_key
attrs marked as sensitive.In trying to pin down the problem, I duplicated the problematic nested schema a few times in my dummy resource, with one instance at the original nesting level, one instance nested another level down, and instances out-dented by one and two levels. As you can see in this side-by-side of excerpted values, the bug behaves differently for all of them.
To me, that smells like something profoundly order-dependent and haunted.
And that, dear reader, is why I don't have tests on this PR -- nailing down this chaotic behavior enough to get a minimal, reliable repro using only an in-memory schema was going to take at least another two days, and since the problem originates with some blatant and inarguable typos, I couldn't quite justify the extra expense to my team.
(...But if reviewers insist on giving me an excuse to, obviously I'm chomping at the bit to blow another two days nailing a stake through this thing's heart, y'all know what I'm about. Just, trying to be pragmatic here.)
Target Release
1.4.x
Draft CHANGELOG entry
BUG FIXES
before_sensitive
/after_sensitive
annotations in JSON plan output for deeply nested structures. This was only observed in the wild on the rancher/rancher2 provider, and resulted in glitched display in Terraform Cloud's structured plan log view.