Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[java-firestore] UserDataConverter throws IndexOutOfBoundsException on an empty __binary__ value

オープン 初心者向け
#14,634 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 2 日以内に返信

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
74/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
google-cloud, java
領域
backend

調査の方向性

java-firestore/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.javaの414~425行目付近にあるdecodeBsonBinaryを読みます。issueでは、未チェックのbytes.byteAt(0)呼び出しが指摘されています。空の__binary__値に対する回帰テストを追加し、関連するFirestoreのユニットテストを実行します。デコード時にIndexOutOfBoundsExceptionではなく、フィールド固有のデコードエラーが報告されれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Issue Details

UserDataConverter.decodeBsonBinary reads the subtype byte with bytes.byteAt(0) without checking that the __binary__ bytes are non-empty, so an empty __binary__ value throws ArrayIndexOutOfBoundsException (an IndexOutOfBoundsException) from DocumentSnapshot.get(...) instead of a decode error naming the field:

https://github.com/googleapis/google-cloud-java/blob/9de611fe5633e9f43c6081754c834cabcd0317bb/java-firestore/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java#L414-L425

Reachability. Writing these forms through the Firestore API is refused with INVALID_ARGUMENT by the emulator (587.0.0-emulators) and by both a Standard-edition and an Enterprise-edition database, so a document written through that API should never carry them (measured 2026-10-03). Writes through the Enterprise edition's MongoDB-compatible API were not tried. Even if the input is unreachable today, the decoder assumes something it does not check, and the consequence is an unchecked runtime exception from a getter, where a FirestoreException naming the malformed value would be expected. A length check before byteAt(0) would fix it.

Found while building an Apache Flink source on google-cloud-firestore (flink-gcp/flink-connector-gcp).

Environment

  • OS Type and Version: macOS 26 (arm64)
  • Java Version and JDK Vendor: Eclipse Temurin 17.0.20

The reproducer is offline: it builds the snapshot from a Document proto with com.google.cloud.firestore.Internal, the way the client does for a server response, and sends nothing.

Dependencies

  • Libraries-Bom: 26.90.0
    • google-cloud-firestore 3.49.0
  • Gax: 2.87.0
  • Auth: 1.54.0

The affected code is identical in 3.49.0 and on main.

Reproducer

package repro;

import com.google.cloud.NoCredentials;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.DocumentSnapshot;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.Internal;
import com.google.cloud.firestore.Query;
import com.google.cloud.firestore.spi.v1.GrpcFirestoreRpc;
import com.google.firestore.v1.Document;
import com.google.firestore.v1.MapValue;
import com.google.firestore.v1.Value;
import com.google.protobuf.ByteString;

/** Offline: builds snapshots from protos as the client would receive them; nothing is sent. */
public class DecodeDefects {
  public static void main(String[] args) throws Exception {
    FirestoreOptions options =
        FirestoreOptions.newBuilder()
            .setProjectId("demo")
            .setCredentials(NoCredentials.getInstance())
            .build();
    Firestore db = options.getService();

    // 1. An __int__ beyond 32 bits is narrowed silently.
    long stored = (1L << 40) + 5;
    DocumentSnapshot wide = snapshot(options, reserved("__int__", Value.newBuilder().setIntegerValue(stored).build()));
    System.out.println("stored __int__: " + stored);
    System.out.println("decoded value:  " + wide.get("k"));
    Query query = db.collection("c").orderBy("k").startAfter(wide);
    System.out.println("cursor value:   " + query.toProto().getStructuredQuery().getStartAt().getValues(0).getMapValue());

    // 2. An empty __binary__ throws instead of failing with a decode error.
    DocumentSnapshot empty = snapshot(options, reserved("__binary__", Value.newBuilder().setBytesValue(ByteString.EMPTY).build()));
    try {
      System.out.println("decoded value:  " + empty.get("k"));
    } catch (RuntimeException e) {
      System.out.println("empty __binary__ threw: " + e);
    }
    System.exit(0);
  }

  static Value reserved(String key, Value value) {
    return Value.newBuilder().setMapValue(MapValue.newBuilder().putFields(key, value)).build();
  }

  static DocumentSnapshot snapshot(FirestoreOptions options, Value value) throws Exception {
    Timestamp now = Timestamp.now();
    Document document =
        Document.newBuilder()
            .setName("projects/demo/databases/(default)/documents/c/x")
            .setCreateTime(now.toProto())
            .setUpdateTime(now.toProto())
            .putFields("k", value)
            .build();
    try (GrpcFirestoreRpc rpc = new GrpcFirestoreRpc(options)) {
      return new Internal(options, rpc).snapshotFromProto(now, document);
    }
  }
}

Only the second case concerns this issue; the first is reported in #14633.

Logs and Stack Trace

stored __int__: 1099511627781
decoded value:  Int32Value{value=5}
cursor value:   fields {
  key: "__int__"
  value {
    integer_value: 5
  }
}

empty __binary__ threw: java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

Behavior

Deterministic: any __binary__ whose bytes are empty.

主要言語
Java
スター
2.1k
フォーク
1.2k
平均マージ
3日 11時間
マージ済み PR(30日)
142

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

googleapis/google-cloud-java のほかの issue

googleapis/google-cloud-java の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。