cockroachdb / cockroachdb/cockroach

changefeedccl/avro: VARBIT memo reuse panic when bit-array length grows between rows

Open
#166,628 1 comment 0 reactions 0 assignees View on GitHub
branch-master C-bug O-agent T-testeng
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

## Summary

The `BitFamily` encoder in `typeToSchema` (`pkg/ccl/changefeedccl/avro/avro.go:346-364`) panics with an index-out-of-range error when encoding consecutive VARBIT values where the second value requires more 64-bit words than the first. The memo-reuse logic at line 353 only handles truncation (memo longer than needed) but not growth (memo shorter than needed), causing a direct index write past the end of the reused slice. This crashes the changefeed job.

## Affected code

- `pkg/ccl/changefeedccl/avro/avro.go:353` -- Guard condition `len(signedLongs) > len(uints)+1` should also handle the `<` case
- `pkg/ccl/changefeedccl/avro/avro.go:362` -- Panic site: `signedLongs[idx+1] = int64(word)` with `idx+1 >= len(signedLongs)`

Note: Fixed-width `BIT(N)` columns are unaffected since all values produce the same number of encoding words. Only `VARBIT` columns trigger this bug.

## Reproduction

```sql
DROP TABLE IF EXISTS varbit_repro;

CREATE TABLE varbit_repro (
id INT PRIMARY KEY,
bits VARBIT
);

-- Row 1: short bit array (2 bits = 1 uint64 word, signedLongs length 2)
INSERT INTO varbit_repro VALUES (1, B'10');

-- Row 2: long bit array (70 bits = 2 uint64 words, signedLongs length 3)
INSERT INTO varbit_repro VALUES (2, B'1010101010101010101010101010101010101010101010101010101010101010101010');

-- Create a changefeed with Avro format to trigger the panic:
CREATE CHANGEFEED FOR varbit_repro
INTO 'null://'
WITH format = avro, confluent_schema_registry = 'http://localhost:8081';

-- Expected: changefeed processes both rows successfully
-- Actual: changefeed panics with "runtime error: index out of range [2] with length 2"
-- when encoding the second row's VARBIT value

DROP TABLE varbit_repro;
```

## Suggested fix direction

Replace the guard at line 353 to handle both truncation and growth:

```go
if memo != nil {
signedLongs = memo.([]interface{})
if len(signedLongs) >= len(uints)+1 {
signedLongs = signedLongs[:len(uints)+1]
} else {
signedLongs = make([]interface{}, len(uints)+1)
}
}
```

This mirrors the safe pattern used by the `ArrayFamily` encoder (line 735-786), which uses append-based growth and `i < len(avroArr)` guards to handle memo size differences correctly.

_This issue was found via automated deep static analysis._

Jira issue: CRDB-62038

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.