-
Notifications
You must be signed in to change notification settings - Fork 3.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: avoid panic when reading corrupt index in TSM file #24033
base: gw-master-1.x-24033
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1233,6 +1233,10 @@ func (d *indirectIndex) UnmarshalBinary(b []byte) error { | |
return fmt.Errorf("indirectIndex: not enough data for index entries count") | ||
} | ||
count := int32(binary.BigEndian.Uint16(b[i : i+indexCountSize])) | ||
|
||
if count <= 0 { | ||
return fmt.Errorf("invalid index %d; count must be greater than 0", count) | ||
} | ||
i += indexCountSize | ||
|
||
// Find the min time for the block | ||
|
@@ -1350,7 +1354,7 @@ func (m *mmapAccessor) init() (*indirectIndex, error) { | |
|
||
indexOfsPos := len(m.b) - 8 | ||
indexStart := binary.BigEndian.Uint64(m.b[indexOfsPos : indexOfsPos+8]) | ||
if indexStart >= uint64(indexOfsPos) { | ||
if indexStart >= uint64(indexOfsPos) || (uint64(indexOfsPos)-indexStart) > math.MaxInt32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This added check ensures that index can be accessed using signed 32-bit offsets. Since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does look like the correct check. The error message (all the error messages in this method) should include the file name, which we have through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the most effective way to wrap the file name onto error massage is the line 240 in tsdb/engine/tsm1/reader.go.
|
||
return nil, fmt.Errorf("mmapAccessor: invalid indexStart") | ||
} | ||
|
||
|
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 no way for count to be less than 0. All possible values of a uint16 will fit in an int32 and since uint16 is only positive, count will never be less than 0.
count
could be 0, though, and we definitely don't want to continue further in this case. It's possiblecount == 0
is valid, but having logged is good for now.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 fundamental solution for this case which I have considered was to add CRC for whole Index block as well, as it is done for other parts. But the solution obviously will break backward compatibility.
Are there any mechanism allowing to have support of different incompatible versions of *tsm file, with special hangling?
Another thing I have taken account during implementation is the following check in writer code tsdb/engine/tsm1/writer.go: 708 line , which skips count == 0 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.
It is unlikely we will introduce an incompatible TSM format at this point in the product's lifecycle.