From 857f62b502897ce2aa68fb2eb62ae701bc427f6c Mon Sep 17 00:00:00 2001 From: jmestwa-coder Date: Thu, 4 Jun 2026 18:08:14 +0530 Subject: [PATCH] check row buffer bounds in Field_new_decimal::unpack Field_new_decimal::unpack takes the conversion branch when the master's decimal precision or scale differ from the slave's, then calls bin2decimal, which reads my_decimal_get_binary_size(from_precision, from_decimal) bytes from the row buffer. unlike the copy branch right below it, and unlike the other Field::unpack overrides, it never checked that length against from_end, so a truncated row-based binlog event made the read run past row_end (heap out-of-bounds read). move the from_end check above the if so a single test covers both branches. from_pack_len is the number of bytes the conversion branch reads and is always >= len, the size the copy branch reads, so one check up front is sufficient. --- sql/field.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 9f1ec7d318a5d..ada53a8ea51d2 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3799,6 +3799,13 @@ Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end, uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal); uint len= (param_data && (from_pack_len < length)) ? from_pack_len : length; + /* + The conversion branch below reads from_pack_len bytes through bin2decimal + and the copy branch reads len bytes; from_pack_len >= len in every case, + so one check up front covers both. + */ + if (from + from_pack_len > from_end) + return 0; // Wrong data if ((from_pack_len && (from_pack_len < length)) || (from_precision < precision) || (from_decimal < decimals())) @@ -3822,8 +3829,6 @@ Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end, } else { - if (from + len > from_end) - return 0; // Wrong data memcpy(to, from, len); // Sizes are the same, just copy the data. } return from+len;