-
Notifications
You must be signed in to change notification settings - Fork 911
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
Consume latest crossplane-runtime's fake.Composite to prevent panics caught by CNCF fuzzing tests #3394
Conversation
// runtime.DefaultUnstructuredConverter.ToUnstructured | ||
// calls to convert the Composite to an Unstructured | ||
// panic. | ||
if cp.GetConnectionDetailsLastPublishedTime() == nil { |
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.
It's no-op for the function signature change but I couldn't catch why we can't have a nil
published time. Is runtime.DefaultUnstructuredConverter.ToUnstructured
not able to work with nil
time values at all or is it about how we call it? I'm pretty sure we have time fields in other types that may be nil in certain cases.
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.
Yes, correct, the runtime.DefaultUnstructuredConverter.ToUnstructured
cannot handle the nested nil *Time
field and fails with:
panic: value method k8s.io/apimachinery/pkg/apis/meta/v1.Time.ToUnstructured called using nil *Time pointer
goroutine 17 [running, locked to thread]:
k8s.io/apimachinery/pkg/apis/meta/v1.(*Time).ToUnstructured(0x0)
<autogenerated>:1 +0xf1
sigs.k8s.io/structured-merge-diff/v4/value.TypeReflectCacheEntry.ToUnstructured({0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, {0x0, 0x0, 0x0}}, ...)
sigs.k8s.io/structured-merge-diff/v4@v4.2.1/value/reflectcache.go:188 +0x127
k8s.io/apimachinery/pkg/runtime.toUnstructured({0x2b36fc0?, 0x10c000020a78?, 0x263f805?}, {0x299e7e0?, 0x10c0007e4590?, 0x10c000836270?})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:655 +0x1e59
k8s.io/apimachinery/pkg/runtime.structToUnstructured({0x29e2d40?, 0x10c000020a78?, 0x64097c?}, {0x299e7e0?, 0x10c0007e4520?, 0x2b5e770?})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:843 +0x1685
k8s.io/apimachinery/pkg/runtime.toUnstructured({0x29e2d40?, 0x10c000020a78?, 0x263f805?}, {0x299e7e0?, 0x10c0007e4520?, 0x98?})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:692 +0x1d08
k8s.io/apimachinery/pkg/runtime.structToUnstructured({0x2ad9880?, 0x10c000020900?, 0x0?}, {0x29b48e0?, 0x10c000844558?, 0x0?})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:843 +0x1685
k8s.io/apimachinery/pkg/runtime.toUnstructured({0x2ad9880?, 0x10c000020900?, 0x10c000020900?}, {0x29b48e0?, 0x10c000844558?, 0x0?})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:692 +0x1d08
k8s.io/apimachinery/pkg/runtime.(*unstructuredConverter).ToUnstructured(0x3f76840, {0x2b35700?, 0x10c000020900})
k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:586 +0xc2e
github.com/crossplane/crossplane/apis/apiextensions/v1.(*Patch).applyFromFieldPathPatch(0x10c0005014a0, {0x2b432d8?, 0x10c000020900}, {0x2b432b0, 0x10c000597c20})
github.com/crossplane/crossplane/apis/apiextensions/v1/composition_patches.go:224 +0x1b2
github.com/crossplane/crossplane/apis/apiextensions/v1.(*Patch).Apply(0x10c0005014a0, {0x2b432b0, 0x10c000597c20}, {0x2b432d8, 0x10c000020900}, {0x0, 0x0, 0x0?})
github.com/crossplane/crossplane/apis/apiextensions/v1/composition_patches.go:131 +0x688
github.com/crossplane/crossplane/apis/apiextensions/v1.FuzzPatchApply({0x603000001900, 0x16, 0x16})
github.com/crossplane/crossplane/apis/apiextensions/v1/patch_fuzzer.go:84 +0x61e
main.LLVMFuzzerTestOneInput(...)
./main.3877846031.go:21
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.
I tested the PR with a Composite resource, and as you mentioned above @muvaf, our composite.Unstructured
can bypass this panic with a shortcut taken in unstructuredConverter.ToUnstructured
, i.e., because Crossplane's composite.Unstructured
implements runtime.Unstructured
, the map is directly returned without attempting a conversion, and hence no panic occurs. This explains why we do not observe this panic in runtime.DefaultUnstructuredConverter.ToUnstructured
with crossplane-runtime's composite.Unstructured
implementation. Thus, it turns out that a nil value here is a valid case. I will adapt the PR accordingly. Thank you :)
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.
As I suspected, adding the omitempty
annotation to fake.ConnectionDetailsLastPublishedTimer.Time
field did the job. I will also check the behavior in the latest apimachinery runtime and see if there are any existing issues there.
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.
I also did the following simple two tests to confirm the observed behavior in a simpler setting:
- The following program:
package main
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type Test struct {
Time *metav1.Time
}
func main() {
if _, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&Test{}); err != nil {
panic(err)
}
}
panics as follows:
panic: value method k8s.io/apimachinery/pkg/apis/meta/v1.Time.ToUnstructured called using nil *Time pointer
goroutine 1 [running]:
k8s.io/apimachinery/pkg/apis/meta/v1.(*Time).ToUnstructured(0x100000001?)
<autogenerated>:1 +0x48
sigs.k8s.io/structured-merge-diff/v4/value.TypeReflectCacheEntry.ToUnstructured({0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, {0x0, 0x0, 0x0}}, ...)
/Users/alper/data/workspaces/go/pkg/mod/sigs.k8s.io/structured-merge-diff/v4@v4.2.1/value/reflectcache.go:188 +0x6b0
k8s.io/apimachinery/pkg/runtime.toUnstructured({0x100d10ba0?, 0x140000103e0?, 0x0?}, {0x100c92f60?, 0x14000021f30?, 0x0?})
/Users/alper/data/workspaces/go/pkg/mod/k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:655 +0x8a0
k8s.io/apimachinery/pkg/runtime.structToUnstructured({0x100cb3260?, 0x140000103e0?, 0x100d135e0?}, {0x100c9e3c0?, 0x140000103e8?, 0x1007f5548?})
/Users/alper/data/workspaces/go/pkg/mod/k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:843 +0x7ac
k8s.io/apimachinery/pkg/runtime.toUnstructured({0x100cb3260?, 0x140000103e0?, 0x140000103e0?}, {0x100c9e3c0?, 0x140000103e8?, 0x140000021a0?})
/Users/alper/data/workspaces/go/pkg/mod/k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:692 +0x7f4
k8s.io/apimachinery/pkg/runtime.(*unstructuredConverter).ToUnstructured(0x10100b530, {0x100c70a80?, 0x140000103e0})
/Users/alper/data/workspaces/go/pkg/mod/k8s.io/apimachinery@v0.24.0/pkg/runtime/converter.go:586 +0x364
main.main()
/Users/alper/data/workspaces/github.com/ulucinar/crossplane/crossplane/cmd/test/main.go:13 +0x44
- However, the following exits with a zero code:
package main
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type Test struct {
Time *metav1.Time `json:"time,omitempty"`
}
func main() {
if _, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&Test{}); err != nil {
panic(err)
}
}
I have observed the same behavior both with k8s.io/apimachinery@v0.24.0
(the dependency version in Crossplane current HEAD) and also with k8s.io/apimachinery@v0.25.3
.
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.
The problematic code that breaks runtime.DefaultUnstructuredConverter.ToUnstructured
lives in sigs.k8s.io/structured-merge-diff/v4
. I've reported the issue there.
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.
Thanks @ulucinar ! I think the fix in crossplane-runtime is appropriate if it works - json tags should've been there anyway. Seems like you have a possible solution for the upstream issue as well, I'd say opening a PR there would help moving it forward faster.
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.
Agreed. Thank you @muvaf.
3237278
to
c312cce
Compare
059bdd1
to
d49d028
Compare
- Consume crossplane-runtime where fake.ConnectionDetailsLastPublishedTimer.Time has an `omitempty` json tag so that we keep runtime.DefaultUnstructuredConverter.ToUnstructured happy. Signed-off-by: Alper Rifat Ulucinar <ulucinar@users.noreply.github.com>
d49d028
to
43b18d6
Compare
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.
Thanks @ulucinar !
Thank you @muvaf, for the excellent guidance in this PR. |
Description of your changes
Depends on: crossplane/crossplane-runtime#361
CNCF Crossplane fuzzing tests are currently failing (specifically the
fuzz_patch_apply
test) because if we pass aresource.Composite
with a nil connection details last published time toPatch.Apply
, subsequent calls toruntime.DefaultUnstructuredConverter.ToUnstructured
to convert it to anunstructured.Unstructured
panic. This PR proposes a change to thePatch.Apply
signature so that we make sure the first arg is actually aresource.Composite
and consumes a new implementation from crossplane-runtime'sfake.Composite
, which annotates thefake.ConnectionDetailsLastPublishedTimer.Time
field with anomitempty
json tag so thatruntime.DefaultUnstructuredConverter.ToUnstructured
does not attempt to convert it.The function signature change has been motivated by observing that the fuzzer passes a
resource.Composed
as the first argument toPatch.Apply
but whatPatch.Apply
expects is aresource.Composite
and we can enforce this at compile-time.Note: I've also reported an upstream issue here.
I have:
make reviewable
to ensure this PR is ready for review.backport release-x.y
labels to auto-backport this PR if necessary.How has this code been tested
Tested against the
fuzz_patch_apply
fuzzer using the provided minimized testcase.Also tested with the following XRD, composition and claim: