Skip to content

Commit 41889cf

Browse files
committed
Fix errors in numeric binary decoder
The size of the output buffer in numeric decoder is computed incorrectly which may lead to stack corruption or access to unitialized memory. This also fixes incorrect rendering of trailing zeros in some cases.
1 parent 9cdd518 commit 41889cf

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed

codecs/numeric.pyx

+29-17
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,8 @@ cdef numeric_decode_binary_ex(
150150
int64_t abs_exponent
151151
ssize_t exponent_chars
152152
ssize_t front_padding = 0
153-
ssize_t trailing_padding = 0
154153
ssize_t num_fract_digits
155-
ssize_t dscale_left
154+
ssize_t trailing_fract_zeros_adj
156155
char smallbuf[_NUMERIC_DECODER_SMALLBUF_SIZE]
157156
char *charbuf
158157
char *bufptr
@@ -175,14 +174,34 @@ cdef numeric_decode_binary_ex(
175174
elif pgdigit0 < 1000:
176175
front_padding = 1
177176

177+
# The number of fractional decimal digits actually encoded in
178+
# base-DEC_DEIGITS digits sent by Postgres.
179+
num_fract_digits = (num_pgdigits - weight - 1) * DEC_DIGITS
180+
181+
# The trailing zero adjustment necessary to obtain exactly
182+
# dscale number of fractional digits in output. May be negative,
183+
# which indicates that trailing zeros in the last input digit
184+
# should be discarded.
185+
trailing_fract_zeros_adj = dscale - num_fract_digits
186+
178187
# Maximum possible number of decimal digits in base 10.
179-
num_pydigits = num_pgdigits * DEC_DIGITS + dscale
188+
# The actual number might be up to 3 digits smaller due to
189+
# leading zeros in first input digit.
190+
num_pydigits = num_pgdigits * DEC_DIGITS
191+
if trailing_fract_zeros_adj > 0:
192+
num_pydigits += trailing_fract_zeros_adj
193+
180194
# Exponent.
181195
exponent = (weight + 1) * DEC_DIGITS - front_padding
182196
abs_exponent = abs(exponent)
183-
# Number of characters required to render absolute exponent value.
184-
exponent_chars = <ssize_t>log10(<double>abs_exponent) + 1
197+
if abs_exponent != 0:
198+
# Number of characters required to render absolute exponent value
199+
# in decimal.
200+
exponent_chars = <ssize_t>log10(<double>abs_exponent) + 1
201+
else:
202+
exponent_chars = 0
185203

204+
# Output buffer size.
186205
buf_size = (
187206
1 + # sign
188207
1 + # leading zero
@@ -221,21 +240,14 @@ cdef numeric_decode_binary_ex(
221240
bufptr = _unpack_digit(bufptr, pgdigit)
222241

223242
if dscale:
224-
if weight >= 0:
225-
num_fract_digits = num_pgdigits - weight - 1
226-
else:
227-
num_fract_digits = num_pgdigits
228-
229-
# Check how much dscale is left to render (trailing zeros).
230-
dscale_left = dscale - num_fract_digits * DEC_DIGITS
231-
if dscale_left > 0:
232-
for i in range(dscale_left):
243+
if trailing_fract_zeros_adj > 0:
244+
for i in range(trailing_fract_zeros_adj):
233245
bufptr[i] = <char>b'0'
234246

235247
# If display scale is _less_ than the number of rendered digits,
236-
# dscale_left will be negative and this will strip the excess
237-
# trailing zeros.
238-
bufptr += dscale_left
248+
# trailing_fract_zeros_adj will be negative and this will strip
249+
# the excess trailing zeros.
250+
bufptr += trailing_fract_zeros_adj
239251

240252
if trail_fract_zero:
241253
# Check if the number of rendered digits matches the exponent,

0 commit comments

Comments
 (0)