You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2026/10/23 14:12:02 UTC

(kudu) 01/02: [client] Enhance client side checks for invalid response

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git

commit 38522df9c2e7a4874c8ce79ff332d24d112ab6a1
Author: Ashwani <as...@apache.org>
AuthorDate: Wed Oct 14 15:55:19 2026 +0530

    [client] Enhance client side checks for invalid response
    
    The patch fixes three unverified accesses that could cause crash on
    client side:
    - In Batcher::ProcessWriteResponse, the per_row_errors loop uses a
      server-supplied row_index as a direct index into rpc.ops() and
      afterwards std::moves write_op out of the referenced in-flight RPCs.
      No guard exists for the case where write_op is already null - either
      due to invalid response from a malicious server or any other reason,
      which could cause undefined behaviour at the op->ToString() call
      (null dereference). A null check on write_op is inserted immediately
      after the existing bounds check, before std::move.
    - Response entry's rpc_addresses are accessed directly, with no check
      that the optional registration sub-message exists or that it
      contains any RPC addresses. Sequential guards are added before the
      access and an appropriate error message (Status::Corruption) is sent
      back to the caller.
    - Master-supplied ts info index with no bounds check could result in
      undefined behaviour / crash in a release build if the index is out of
      range. A bounds check is added before the access that returns
      Status::Corruption with the bad index and the actual count. This
      mirrors the identical guard already present in meta_cache.cc for the
      same field.
    
    Change-Id: I2034b1686d2d8dac1e21b38103303e8c1005e16a
    Partially-Generated-By: Cursor <cu...@cursor.com>
    Reviewed-on: http://gerrit.cloudera.org:8080/25148
    Tested-by: Ashwani Raina <ar...@gmail.com>
    Reviewed-by: Alexey Serbin <al...@apache.org>
---
 src/kudu/client/batcher.cc |  7 +++++++
 src/kudu/client/batcher.h  |  1 -
 src/kudu/client/client.cc  | 26 +++++++++++++++++++++++---
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/kudu/client/batcher.cc b/src/kudu/client/batcher.cc
index b4490f4d5..62e591387 100644
--- a/src/kudu/client/batcher.cc
+++ b/src/kudu/client/batcher.cc
@@ -968,6 +968,13 @@ void Batcher::ProcessWriteResponse(const WriteRpc& rpc,
                  << SecureDebugString(rpc.resp());
       continue;
     }
+    if (!rpc.ops()[err_pb.row_index()]->write_op) {
+      LOG(ERROR) << "Received a per_row_error for op index " << err_pb.row_index()
+                 << " from tablet " << rpc.tablet_id()
+                 << " but write operation is missing: "
+                 << SecureShortDebugString(err_pb);
+      continue;
+    }
     unique_ptr<KuduWriteOperation> op = std::move(rpc.ops()[err_pb.row_index()]->write_op);
     VLOG(2) << "Error on op " << op->ToString() << ": "
             << SecureShortDebugString(err_pb.error());
diff --git a/src/kudu/client/batcher.h b/src/kudu/client/batcher.h
index be08e9c6e..ad16a78c1 100644
--- a/src/kudu/client/batcher.h
+++ b/src/kudu/client/batcher.h
@@ -29,7 +29,6 @@
 #include "kudu/client/write_op.h"
 #include "kudu/common/txn_id.h"
 #include "kudu/gutil/macros.h"
-#include "kudu/gutil/port.h"
 #include "kudu/gutil/ref_counted.h"
 #include "kudu/util/locks.h"
 #include "kudu/util/memory/arena.h"
diff --git a/src/kudu/client/client.cc b/src/kudu/client/client.cc
index 511218210..7f6549490 100644
--- a/src/kudu/client/client.cc
+++ b/src/kudu/client/client.cc
@@ -632,6 +632,21 @@ Status KuduClient::ListTabletServers(vector<KuduTabletServer*>* tablet_servers)
   RETURN_NOT_OK(data_->ListTabletServers(this, deadline, req, &resp));
   for (int i = 0; i < resp.servers_size(); i++) {
     const ListTabletServersResponsePB_Entry& e = resp.servers(i);
+    DCHECK(e.has_registration()) << "tserver " << e.instance_id().permanent_uuid()
+                                 << " has no registration";
+    if (PREDICT_FALSE(!e.has_registration())) {
+      return Status::Corruption(Substitute(
+          "invalid response from master: tserver $0 has no registration",
+          e.instance_id().permanent_uuid()));
+    }
+    DCHECK_GT(e.registration().rpc_addresses_size(), 0)
+        << "tserver " << e.instance_id().permanent_uuid()
+        << " has no RPC addresses";
+    if (PREDICT_FALSE(e.registration().rpc_addresses_size() == 0)) {
+      return Status::Corruption(Substitute(
+          "invalid response from master: tserver $0 has no RPC addresses",
+          e.instance_id().permanent_uuid()));
+    }
     HostPort hp = HostPortFromPB(e.registration().rpc_addresses(0));
     unique_ptr<KuduTabletServer> ts(new KuduTabletServer);
     ts->data_ = new KuduTabletServer::Data(e.instance_id().permanent_uuid(), hp, e.location());
@@ -731,9 +746,9 @@ Status KuduClient::GetTablet(const string& tablet_id, KuduTablet** tablet) {
   auto add_replica_func = [](const TSInfoPB& ts_info,
                              const RaftPeerPB::Role role,
                              vector<const KuduReplica*>* replicas) {
-    if (ts_info.rpc_addresses_size() == 0) {
-      return Status::IllegalState(Substitute(
-          "No RPC addresses found for tserver $0",
+    if (PREDICT_FALSE(ts_info.rpc_addresses_size() == 0)) {
+      return Status::Corruption(Substitute(
+          "invalid response from master: tserver $0 has no RPC addresses",
           ts_info.permanent_uuid()));
     }
     HostPort hp = HostPortFromPB(ts_info.rpc_addresses(0));
@@ -756,6 +771,11 @@ Status KuduClient::GetTablet(const string& tablet_id, KuduTablet** tablet) {
   }
   // Handle interned replicas.
   for (const auto& r : t.interned_replicas()) {
+    if (r.ts_info_idx() >= static_cast<uint32_t>(resp.ts_infos_size())) {
+      return Status::Corruption(Substitute(
+          "invalid response from master: referenced ts_info idx $0 but only $1 present",
+          r.ts_info_idx(), resp.ts_infos_size()));
+    }
     RETURN_NOT_OK(add_replica_func(resp.ts_infos(r.ts_info_idx()), r.role(), &replicas));
   }