Skip to content

Commit 217b43e

Browse files
authored
Merge pull request #131230 from cockroachdb/blathers/backport-release-24.2.3-rc-131209
release-24.2.3-rc: sqlliveness: detect and handle invalid SessionIDs
2 parents b4abaa2 + a5db6e0 commit 217b43e

File tree

15 files changed

+138
-22
lines changed

15 files changed

+138
-22
lines changed

pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/logictestccl/tests/local-read-committed/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/catalog/lease/lease_internal_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,8 +1709,8 @@ func TestLeaseCountDetailSessionBased(t *testing.T) {
17091709
version := 1
17101710
region := enum.One
17111711
_, err := executor.Exec(ctx, "add-rows-for-test", nil,
1712-
fmt.Sprintf("INSERT INTO system.lease VALUES (%d, %d, %s, '%s', '\\x%x')",
1713-
descID, version, nodeID, session.ID(), region))
1712+
fmt.Sprintf("INSERT INTO system.lease VALUES (%d, %d, %s, '\\x%x', '\\x%x')",
1713+
descID, version, nodeID, session.ID().UnsafeBytes(), region))
17141714
if err != nil {
17151715
return err
17161716
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Validate that invalid sessionID's are always
2+
# considered dead.
3+
subtest invalid_sessions
4+
5+
# Legacy non-RBR format
6+
query B
7+
select crdb_internal.sql_liveness_is_alive(x'1f915e98f96145a5baa9f3a42c378eb6');
8+
----
9+
false
10+
11+
# Wrong length
12+
query B
13+
select crdb_internal.sql_liveness_is_alive(x'deadbeef');
14+
----
15+
false
16+
17+
subtest end
18+
19+
20+
subtest valid_sessions
21+
22+
# Sanity: All sessions are alive in sqlliveness.
23+
query I
24+
SELECT count(*) FROM system.sqlliveness WHERE crdb_internal.sql_liveness_is_alive(session_id) = false;
25+
----
26+
0
27+
28+
query B
29+
SELECT count(*) > 0 FROM system.sqlliveness WHERE crdb_internal.sql_liveness_is_alive(session_id) = true;
30+
----
31+
true
32+
33+
subtest end
34+
35+
36+

pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/fakedist/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local-legacy-schema-changer/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local-mixed-23.2/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local-vec-off/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/logictest/tests/local/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/schemachanger/comparator_generated_test.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/sqlliveness/slstorage/key_encoder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
type keyCodec interface {
2525
encode(sid sqlliveness.SessionID) (roachpb.Key, string, error)
2626
decode(key roachpb.Key) (sqlliveness.SessionID, error)
27+
validate(session sqlliveness.SessionID) error
2728

2829
// indexPrefix returns the prefix for an encoded key. encode() will return
2930
// something with the prefix and decode will expect a key with this prefix.
@@ -37,6 +38,10 @@ type rbrEncoder struct {
3738
rbrIndex roachpb.Key
3839
}
3940

41+
func (e *rbrEncoder) validate(session sqlliveness.SessionID) error {
42+
return ValidateSessionID(session)
43+
}
44+
4045
func (e *rbrEncoder) encode(session sqlliveness.SessionID) (roachpb.Key, string, error) {
4146
region, _, err := SafeDecodeSessionID(session)
4247
if err != nil {

pkg/sql/sqlliveness/slstorage/sessionid.go

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,8 @@ func MakeSessionID(region []byte, id uuid.UUID) (sqlliveness.SessionID, error) {
6767
// not be mutated.
6868
func UnsafeDecodeSessionID(session sqlliveness.SessionID) (region, id []byte, err error) {
6969
b := session.UnsafeBytes()
70-
if len(b) == legacyLen {
71-
return nil, nil, errors.Newf("unexpected legacy SessionID format")
72-
}
73-
if len(b) < minimumNonLegacyLen {
74-
// The smallest valid v1 session id is a [version, 1, single_byte_region, uuid...],
75-
// which is three bytes larger than a uuid.
76-
return nil, nil, errors.New("session id is too short")
77-
}
78-
79-
// Decode the version.
80-
if b[0] != sessionIDVersion {
81-
return nil, nil, errors.Newf("invalid session id version: %d", b[0])
70+
if err = ValidateSessionID(sqlliveness.SessionID(b)); err != nil {
71+
return nil, nil, err
8272
}
8373
regionLen := int(b[1])
8474
rest := b[2:]
@@ -91,24 +81,30 @@ func UnsafeDecodeSessionID(session sqlliveness.SessionID) (region, id []byte, er
9181
return rest[:regionLen], rest[regionLen:], nil
9282
}
9383

94-
// SafeDecodeSessionID decodes the region and id from the SessionID.
95-
func SafeDecodeSessionID(session sqlliveness.SessionID) (region, id string, err error) {
84+
// ValidateSessionID validates that the SessionID has the correct format.
85+
func ValidateSessionID(session sqlliveness.SessionID) error {
9686
if len(session) == legacyLen {
97-
return "", "", errors.Newf("unexpected legacy SessionID format")
87+
return errors.Newf("unexpected legacy SessionID format")
9888
}
9989
if len(session) < minimumNonLegacyLen {
10090
// The smallest valid v1 session id is a [version, 1, single_byte_region, uuid...],
10191
// which is three bytes larger than a uuid.
102-
return "", "", errors.New("session id is too short")
92+
return errors.New("session id is too short")
10393
}
104-
10594
// Decode the version.
10695
if session[0] != sessionIDVersion {
107-
return "", "", errors.Newf("invalid session id version: %d", session[0])
96+
return errors.Newf("invalid session id version: %d", session[0])
97+
}
98+
return nil
99+
}
100+
101+
// SafeDecodeSessionID decodes the region and id from the SessionID.
102+
func SafeDecodeSessionID(session sqlliveness.SessionID) (region, id string, err error) {
103+
if err = ValidateSessionID(session); err != nil {
104+
return "", "", err
108105
}
109106
regionLen := int(session[1])
110107
rest := session[2:]
111-
112108
// Decode and validate the length of the region.
113109
if len(rest) != regionLen+uuid.Size {
114110
return "", "", errors.Newf("session id with length %d is the wrong size to include a region with length %d", len(session), regionLen)

pkg/sql/sqlliveness/slstorage/slstorage.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,15 @@ const (
200200
func (s *Storage) isAlive(
201201
ctx context.Context, sid sqlliveness.SessionID, syncOrAsync readType,
202202
) (alive bool, _ error) {
203-
203+
// Confirm the session ID has the correct format, and if it
204+
// doesn't then we can consider it as dead without any extra
205+
// work.
206+
if err := s.keyCodec.validate(sid); err != nil {
207+
// This SessionID may be invalid because of the wrong format
208+
// so consider it as dead.
209+
//nolint:returnerrcheck
210+
return false, nil
211+
}
204212
// If wait is false, alive is set and future is unset.
205213
// If wait is true, alive is unset and future is set.
206214
alive, wait, future, err := func() (bool, bool, singleflight.Future, error) {
@@ -318,6 +326,9 @@ func (s *Storage) deleteOrFetchSession(
318326
ctx = multitenant.WithTenantCostControlExemption(ctx)
319327
livenessProber := regionliveness.NewLivenessProber(s.db, s.codec, nil, s.settings)
320328
k, regionPhysicalRep, err := s.keyCodec.encode(sid)
329+
if err != nil {
330+
return false, hlc.Timestamp{}, err
331+
}
321332
if err := s.txn(ctx, func(ctx context.Context, txn *kv.Txn) error {
322333
// Reset captured variable in case of retry.
323334
deleted, expiration, prevExpiration = false, hlc.Timestamp{}, hlc.Timestamp{}

0 commit comments

Comments
 (0)