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

GODRIVER-3240 Code hardening. #1684

Merged
merged 6 commits into from
Jun 27, 2024
Merged

Conversation

qingyang-hu
Copy link
Collaborator

@qingyang-hu qingyang-hu commented Jun 25, 2024

GODRIVER-3240

Summary

Background & Motivation

@mongodb-drivers-pr-bot mongodb-drivers-pr-bot bot added the priority-3-low Low Priority PR for Review label Jun 25, 2024
Copy link
Contributor

API Change Report

No changes found!

Comment on lines 99 to 106
i, err := strconv.ParseUint(val.v.(string), 16, 64)
if err != nil {
return nil, 0, fmt.Errorf("invalid $binary subType string: %s", val.v.(string))
}

subType = byte(i)
b := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(b, i)
subType = b[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use ParseUint to limit the range of the parsed subtype value by using bitSize=8. I also recommend adding the parse error to returned error.

E.g.

i, err := strconv.ParseUint(val.v.(string), 16, 8)
if err != nil {
	return nil, 0, fmt.Errorf("invalid $binary subType string %q: %w", val.v.(string), err)
}
subType = byte(i)

@qingyang-hu qingyang-hu force-pushed the godriver3240 branch 2 times, most recently from c34137f to dfd8a33 Compare June 26, 2024 17:59
@qingyang-hu qingyang-hu changed the title GODRIVER-3240 Replace bit-shifting; apply size check for integer conversions. GODRIVER-3240 Code hardening. Jun 26, 2024
@qingyang-hu qingyang-hu marked this pull request as ready for review June 26, 2024 18:43
@blink1073 blink1073 added priority-1-high High Priority PR for Review and removed priority-3-low Low Priority PR for Review labels Jun 26, 2024
Comment on lines 104 to 106
b := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(b, i)
subType = b[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we already apply the bounds check in the ParseUint call, it should be safe to assume that i is in the range [0, 255]. In that case, we can keep the existing uint64 -> byte conversion.

Suggested change
b := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(b, i)
subType = b[0]
subType = byte(i)

Comment on lines 1181 to 1190
dataSize := len(keyData)
certSize := len(certData)
if math.MaxInt-dataSize < certSize {
return "", errors.New("size overflow")
}
dataSize += certSize
if math.MaxInt-dataSize < 1 {
return "", errors.New("size overflow")
}
dataSize++
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simplify the logic here by adding some additional data size validation. AFAIK there's no case where an X.509 key and cert should be anywhere near 64MiB, so we can avoid the integer overflow and check for reasonable data size at the same time.

Suggested change
dataSize := len(keyData)
certSize := len(certData)
if math.MaxInt-dataSize < certSize {
return "", errors.New("size overflow")
}
dataSize += certSize
if math.MaxInt-dataSize < 1 {
return "", errors.New("size overflow")
}
dataSize++
keySize := len(keyData)
if keySize > 64*1024*1024 {
return "", errors.New("X.509 key must be less than 64 MiB")
}
certSize := len(certData)
if certSize > 64*1024*1024 {
return "", errors.New("X.509 certificate must be less than 64 MiB")
}
data := make([]byte, 0, keySize+certSize+1)

@@ -164,11 +164,15 @@ func (uic *UIntCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t refl

return reflect.ValueOf(uint64(i64)), nil
case reflect.Uint:
if i64 < 0 || int64(uint(i64)) != i64 { // Can we fit this inside of an uint
if i64 < 0 {
return emptyValue, fmt.Errorf("%d overflows uint", i64)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a more accurate error message.

Suggested change
return emptyValue, fmt.Errorf("%d overflows uint", i64)
return emptyValue, fmt.Errorf("%d underflows uint", i64)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems better to keep the error message consistent with other unsigned types.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable. We can fix them all at the same time. Consider this comment resolved.

return "", errors.New("X.509 certificate must be less than 64 MiB")
}
dataSize := keySize + certSize + 1
if dataSize > math.MaxInt {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An additional fail-safe check.

@blink1073
Copy link
Member

The TestDefaultValueDecoders test failures are relevant.

Copy link
Collaborator

@matthewdale matthewdale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 👍

@qingyang-hu qingyang-hu merged commit d343798 into mongodb:v1 Jun 27, 2024
25 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority-1-high High Priority PR for Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants