-
Notifications
You must be signed in to change notification settings - Fork 901
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
GODRIVER-3470 Correct BSON unmarshaling logic for null values #1924
Conversation
API Change ReportNo changes found! |
bson/unmarshaling_cases_test.go
Outdated
PtrTracker *unmarshalCallTracker `bson:"ptr_tracker"` | ||
} | ||
|
||
func (ms *unmarshalCallTracker) UnmarshalBSONValue(bsontype.Type, []byte) error { |
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.
Should we also test this behavior for UnmarshalBSON
?
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.
Good idea!
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.
LGTM!
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.
Overall, looks good! 👍
Open question about using the same set of conditions in ValueUnmarshalerDecodeValue
and UnmarshalerDecodeValue
so it's easier to understand in the future.
// directly set to nil here. Since the pointer is being replaced with nil, | ||
// there is no opportunity (or reason) for the custom UnmarshalBSONValue logic | ||
// to be called. | ||
if vr.Type() == bsontype.Null && val.Kind() == reflect.Ptr { |
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.
There is a similar block in UnmarshalerDecodeValue
after the value is read into a []byte
:
if val.Kind() == reflect.Ptr && len(src) == 0 {
val.Set(reflect.Zero(val.Type()))
return nil
}
Can we use the same condition in both methods? Or are they distinct scenarios?
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 len(src)
check was implemented here: https://github.com/mongodb/mongo-go-driver/pull/833/files
Since the bytes represent BSON null are copied, the type is converted from null to invalid:
_, src, err := bsonrw.Copier{}.CopyValueToBytes(vr)
if err != nil {
return err
}
Which means that this check doesn’t work:
if val.Kind() == reflect.Ptr && vr.Type() == bsontype.Null {
val.Set(reflect.Zero(val.Type()))
return nil
}
Checking after copying is weaker since validity should be checked on the first block:
if !val.IsValid() || (!val.Type().Implements(tValueUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tValueUnmarshaler)) {
return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val}
}
So I think BSON null is the only case where you would get an invalid type after copying. I suggest we mirror ValueUnmarshalerDecodeValue
.
32190fe
if val.Kind() == reflect.Ptr && len(src) == 0 { | ||
val.Set(reflect.Zero(val.Type())) | ||
return 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.
Removing this will result in UnmarshalBSON
being called with an empty byte slice for BSON minkey and maxkey value. Because the byte slice passed to UnmarshalBSON
has the BSON type byte stripped off it, zero-length BSON values will be indistinguishable from each other (null, minkey, maxkey). Because of that limitation, we should keep the existing behavior.
Here are two test cases that can be added to unmarshalingTestCases
that demonstrate the behavior:
{
name: "nil pointer and non-pointer type with BSON minkey",
sType: reflect.TypeOf(unmarshalBehaviorTestCase{}),
want: &unmarshalBehaviorTestCase{
BSONValueTracker: unmarshalBSONValueCallTracker{
called: true,
},
BSONValuePtrTracker: &unmarshalBSONValueCallTracker{
called: true,
},
BSONTracker: unmarshalBSONCallTracker{
called: true,
},
BSONPtrTracker: nil,
},
data: docToBytes(D{
{Key: "bv_tracker", Value: primitive.MinKey{}},
{Key: "bv_ptr_tracker", Value: primitive.MinKey{}},
{Key: "b_tracker", Value: primitive.MinKey{}},
{Key: "b_ptr_tracker", Value: primitive.MinKey{}},
}),
},
{
name: "nil pointer and non-pointer type with BSON maxkey",
sType: reflect.TypeOf(unmarshalBehaviorTestCase{}),
want: &unmarshalBehaviorTestCase{
BSONValueTracker: unmarshalBSONValueCallTracker{
called: true,
},
BSONValuePtrTracker: &unmarshalBSONValueCallTracker{
called: true,
},
BSONTracker: unmarshalBSONCallTracker{
called: true,
},
BSONPtrTracker: nil,
},
data: docToBytes(D{
{Key: "bv_tracker", Value: primitive.MaxKey{}},
{Key: "bv_ptr_tracker", Value: primitive.MaxKey{}},
{Key: "b_tracker", Value: primitive.MaxKey{}},
{Key: "b_ptr_tracker", Value: primitive.MaxKey{}},
}),
},
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.
Nice catch, updated!
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.
Looks good! 👍
… [master] (#1945) Co-authored-by: Preston Vasquez <[email protected]>
drivers-pr-bot please backport to release/1.17 |
… [master] (#1945) Co-authored-by: Preston Vasquez <[email protected]> (cherry picked from commit 25df82f)
Sorry, unable to cherry-pick to release/1.17, please backport manually. Here are approximate instructions:
|
… [master] (#1945) [release/2.0] (#1947) Co-authored-by: Matt Dale <[email protected]>
…#1955) Co-authored-by: Preston Vasquez <[email protected]>
* release/1.17: Use different credentials for merge-up PRs (#1968) Add GitHub Actions workflow for merge ups (#1962) BUMP v1.17.3 GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) [release/1.17] (#1957) Update reviewers.txt (#1855) [v1] (#1883) [release/1.17] (#1958) Cherry pick 1.17.2 work to release/1.17 (#1956) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) (#1955) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) [release/1.17] (#1954) GODRIVER-3340 Bump github.com/klauspost/compress from 1.13.6 to 1.16.7 [release/1.17] (#1880) GODRIVER-3374 Add ReadCompressedCompressedMessage back to wiremessage API (#1870) Fix data race in 'discard connections' pool test. [v1] (#1877) Bump golangci-lint for 1.23 compatibility [v1] (#1875) BUMP v1.17.1 GODRIVER-3156 Detect and discard closed idle connections. (#1815) [release/1.17] (#1841) GODRIVER-3313 [release/1.17] Skip CSOT spec tests on Windows and macOS. (#1838) GODRIVER-3358 [release/1.17] Do not override authSource from TXT record (#1840) GODRIVER-2589 [release/1.17] Clarify `*Cursor.All()` behavior in comment. (#1839) DEVPROD-10453 Use assume_role for s3 uploads [release/1.17] (#1824) (#1837) update repo metadata
* commit '9c485751': BUMP v1.17.3 GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) [release/1.17] (#1957) Update reviewers.txt (#1855) [v1] (#1883) [release/1.17] (#1958) Cherry pick 1.17.2 work to release/1.17 (#1956) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) (#1955) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) [release/1.17] (#1954) GODRIVER-3340 Bump github.com/klauspost/compress from 1.13.6 to 1.16.7 [release/1.17] (#1880) GODRIVER-3374 Add ReadCompressedCompressedMessage back to wiremessage API (#1870) Fix data race in 'discard connections' pool test. [v1] (#1877) Bump golangci-lint for 1.23 compatibility [v1] (#1875) BUMP v1.17.1 GODRIVER-3156 Detect and discard closed idle connections. (#1815) [release/1.17] (#1841) GODRIVER-3313 [release/1.17] Skip CSOT spec tests on Windows and macOS. (#1838) GODRIVER-3358 [release/1.17] Do not override authSource from TXT record (#1840) GODRIVER-2589 [release/1.17] Clarify `*Cursor.All()` behavior in comment. (#1839) DEVPROD-10453 Use assume_role for s3 uploads [release/1.17] (#1824) (#1837) update repo metadata
* commit '0dc2e05e': (184 commits) GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) BUMP v1.17.2 GODRIVER-3436 Avoid initializing null data given custom decoder (#1902) GODRIVER-3340 Add a test for goroutine leaks. (#1874) Update reviewers.txt (#1855) [v1] (#1883) Fix data race in 'discard connections' pool test. [v1] (#1877) Bump golangci-lint for 1.23 compatibility [v1] (#1875) GODRIVER-3340 Bump github.com/klauspost/compress from 1.13.6 to 1.16.7 [v1] (#1869) GODRIVER-3374 Add ReadCompressedCompressedMessage back to wiremessage API (#1870) GODRIVER-3156 Detect and discard closed idle connections. (#1815) GODRIVER-3358 Do not override authSource from TXT record (#1830) DEVPROD-10453 Use assume_role for s3 uploads [v1] (#1824) GODRIVER-2589 Clarify `*Cursor.All()` behavior in comment. (#1804) GODRIVER-3313 Skip CSOT spec tests on Windows and macOS. (#1818) BUMP v1.17.0 GODRIVER-3302 Handle malformatted message length properly. (#1758) GODRIVER-3312 Use remaining test secrets from the vault [v1] (#1811) Remove GCP from supplied callback example (#1809) ...
* v1: Use different credentials for merge-up PRs (#1968) Add GitHub Actions workflow for merge ups (#1962) BUMP v1.17.3 GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) [release/1.17] (#1957) Update reviewers.txt (#1855) [v1] (#1883) [release/1.17] (#1958) Cherry pick 1.17.2 work to release/1.17 (#1956) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) (#1955) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) [release/1.17] (#1954) GODRIVER-3340 Bump github.com/klauspost/compress from 1.13.6 to 1.16.7 [release/1.17] (#1880) GODRIVER-3374 Add ReadCompressedCompressedMessage back to wiremessage API (#1870) Fix data race in 'discard connections' pool test. [v1] (#1877) Bump golangci-lint for 1.23 compatibility [v1] (#1875) BUMP v1.17.1 GODRIVER-3156 Detect and discard closed idle connections. (#1815) [release/1.17] (#1841) GODRIVER-3313 [release/1.17] Skip CSOT spec tests on Windows and macOS. (#1838) GODRIVER-3358 [release/1.17] Do not override authSource from TXT record (#1840) GODRIVER-2589 [release/1.17] Clarify `*Cursor.All()` behavior in comment. (#1839) DEVPROD-10453 Use assume_role for s3 uploads [release/1.17] (#1824) (#1837) update repo metadata
* release/2.0: BUMP v2.0.1 GODRIVER-3477 Bump golang.org/x/crypto from 0.29.0 to 0.31.0 [release/2.0] (#1952) GODRIVER-3284 Allow valid SRV hostnames with less than 3 parts. (#1898) [release/2.0] (#1949) GODRIVER-3452 MergeClientOptions returns object when given nil arguments (#1917) [release/2.0] (#1948) GODRIVER-3443 Remove internal APIs in migration guide. (#1911) [release/2.0] (#1951) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) [master] (#1945) [release/2.0] (#1947) Fix erroneous nil error return in FindOne. (#1926) [release/2.0] (#1944) GODRIVER-3298 Handle joined errors correctly in WithTransaction. (#1928) [release/2.0] (#1931) GODRIVER-3307 clone returned byte slice in MarshalValue (#1913) [release/2.0] (#1921)
…41094483929 * release/2.0: (203 commits) Use different credentials for merge-up PRs (#1968) Add GitHub Actions workflow for merge ups (#1962) BUMP v1.17.3 GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) [release/1.17] (#1957) Update reviewers.txt (#1855) [v1] (#1883) [release/1.17] (#1958) Cherry pick 1.17.2 work to release/1.17 (#1956) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) (#1955) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) [release/1.17] (#1954) GODRIVER-3448 Limit GOMAXPROCS for fuzz tests (#1939) [v1] (#1943) GODRIVER-3470 Correct BSON unmarshaling logic for null values (#1924) GODRIVER-3370 Add bypassEmptyTsReplacement option. (#1927) BUMP v1.17.2 GODRIVER-3436 Avoid initializing null data given custom decoder (#1902) GODRIVER-3340 Add a test for goroutine leaks. (#1874) Update reviewers.txt (#1855) [v1] (#1883) GODRIVER-3340 Bump github.com/klauspost/compress from 1.13.6 to 1.16.7 [release/1.17] (#1880) GODRIVER-3374 Add ReadCompressedCompressedMessage back to wiremessage API (#1870) Fix data race in 'discard connections' pool test. [v1] (#1877) Bump golangci-lint for 1.23 compatibility [v1] (#1875) Fix data race in 'discard connections' pool test. [v1] (#1877) ...
GODRIVER-3470
Summary
Ensure UnmarshalBSONValue is bypassed and the Go pointer is set to nil ONLY when the Go type is a pointer and the BSON value is null.
Background & Motivation
PR #1903 introduced logic where UnmarshalBSONValue() is not called for bson.TypeNull, which breaks applications that rely on this behavior for initializing fields.