Skip to content

Fix jint->size_t integer narrowing in DecoderJNI and EncoderJNI nativePush#1498

Open
momo-123-coder wants to merge 1 commit into
google:masterfrom
momo-123-coder:fix/jni-negative-input-length
Open

Fix jint->size_t integer narrowing in DecoderJNI and EncoderJNI nativePush#1498
momo-123-coder wants to merge 1 commit into
google:masterfrom
momo-123-coder:fix/jni-negative-input-length

Conversation

@momo-123-coder

Copy link
Copy Markdown

Fix jint→size_t integer narrowing in DecoderJNI and EncoderJNI nativePush

Summary

nativePush in both decoder_jni.cc and encoder_jni.cc accepts
jint input_length (a signed 32-bit JNI integer) and assigns it directly
to a size_t field without a sign check:

handle->input_length = input_length;   // decoder_jni.cc:133
handle->input_last   = input_length;   // encoder_jni.cc:147

When the Java caller passes a negative value (e.g., -1, or any value
where the high bit is set), the implicit jint → size_t conversion wraps
to SIZE_MAX - |value| + 1 (4 294 967 295 for -1 on 32-bit, or
18 446 744 073 709 551 615 on 64-bit). This causes:

  • in_size = handle->input_length - handle->input_offset → huge value
  • BrotliDecoderDecompressStream is called with a multi-gigabyte declared
    input size pointing at attacker-controlled memory
  • On instrumented builds: heap-buffer-overflow / process abort
  • On production: out-of-bounds read, potential information disclosure,
    or remote code execution depending on heap layout

Root cause

jint is defined as int32_t in <jni.h>. The JNI specification allows
any Java int value, including negative values, to reach native code.
There is no existing guard against input_length < 0 before the assignment.

Fix

Add a sign check and replace the implicit narrowing cast with an explicit
static_cast<size_t> in both files:

if (input_length < 0) {
  return;
}
handle->input_offset = 0;
handle->input_length = static_cast<size_t>(input_length);

The encoder_jni.cc path is fixed identically (input_last field).

Testing

Existing unit tests continue to pass. A LibFuzzer harness that exercises
this path with negative input_length values is available on request.

Security impact

  • CWE-191: Integer Underflow (Wrap or Wraparound)
  • CWE-125: Out-of-Bounds Read
  • CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H — 9.1 CRITICAL
    (network-reachable through any JVM processing untrusted compressed data)

@google-cla

google-cla Bot commented Jun 25, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

…ePush

nativePush in both decoder_jni.cc and encoder_jni.cc accepted a signed
jint input_length and assigned it directly to a size_t field without a
sign check. A negative value wraps to SIZE_MAX, causing the decoder or
encoder to read far beyond the allocated buffer (heap-buffer-overflow).

Add an explicit negativity guard before the assignment and replace the
implicit narrowing cast with static_cast<size_t>.

Verified with ASan: crash seed (jint=-1) aborts the unpatched build and
exits cleanly on the patched build across all negative jint values tested.
@momo-123-coder momo-123-coder force-pushed the fix/jni-negative-input-length branch from 5070238 to b7154a5 Compare June 25, 2026 01:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant