2022-01-01 22:56:03 +08:00
|
|
|
/* Copyright (C) 2019-2022 Free Software Foundation, Inc.
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
This file is part of GDB.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include "gmp-utils.h"
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
2020-11-24 10:45:35 +08:00
|
|
|
std::string
|
|
|
|
gmp_string_printf (const char *fmt, ...)
|
2020-11-15 16:09:44 +08:00
|
|
|
{
|
|
|
|
va_list vp;
|
|
|
|
|
|
|
|
va_start (vp, fmt);
|
2020-11-24 10:45:35 +08:00
|
|
|
int size = gmp_vsnprintf (NULL, 0, fmt, vp);
|
2020-11-15 16:09:44 +08:00
|
|
|
va_end (vp);
|
|
|
|
|
2020-11-24 10:45:35 +08:00
|
|
|
std::string str (size, '\0');
|
|
|
|
|
|
|
|
/* C++11 and later guarantee std::string uses contiguous memory and
|
|
|
|
always includes the terminating '\0'. */
|
|
|
|
va_start (vp, fmt);
|
|
|
|
gmp_vsprintf (&str[0], fmt, vp);
|
|
|
|
va_end (vp);
|
|
|
|
|
|
|
|
return str;
|
2020-11-15 16:09:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
void
|
2020-11-24 10:46:38 +08:00
|
|
|
gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
|
2020-11-15 16:09:44 +08:00
|
|
|
bool unsigned_p)
|
|
|
|
{
|
2020-11-24 10:46:38 +08:00
|
|
|
mpz_import (val, 1 /* count */, -1 /* order */, buf.size () /* size */,
|
2020-11-15 16:09:44 +08:00
|
|
|
byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
|
2020-11-24 10:46:38 +08:00
|
|
|
0 /* nails */, buf.data () /* op */);
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
if (!unsigned_p)
|
|
|
|
{
|
|
|
|
/* The value was imported as if it was a positive value,
|
|
|
|
as mpz_import does not handle signs. If the original value
|
|
|
|
was in fact negative, we need to adjust VAL accordingly. */
|
|
|
|
gdb_mpz max;
|
|
|
|
|
2020-12-05 16:03:48 +08:00
|
|
|
mpz_ui_pow_ui (max.val, 2, buf.size () * HOST_CHAR_BIT - 1);
|
2020-11-15 16:09:44 +08:00
|
|
|
if (mpz_cmp (val, max.val) >= 0)
|
|
|
|
mpz_submul_ui (val, max.val, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
void
|
2020-11-24 10:46:38 +08:00
|
|
|
gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
|
2020-11-15 16:09:44 +08:00
|
|
|
bool unsigned_p) const
|
|
|
|
{
|
gmp-utils: protect gdb_mpz exports against out-of-range values
The gdb_mpz class currently provides a couple of methods which
essentially export an mpz_t value into either a buffer, or an integral
type. The export is based on using the mpz_export function which
we discovered can be a bit treacherous if used without caution.
In particular, the initial motivation for this patch was to catch
situations where the mpz_t value was so large that it would not fit
in the destination area. mpz_export does not know the size of
the buffer, and therefore can happily write past the end of our buffer.
While designing a solution to the above problem, I also discovered
that we also needed to be careful when exporting signed numbers.
In particular, numbers which are larger than the maximum value
for a given signed type size, but no so large as to fit in the
*unsigned* version with the same size, would end up being exported
incorrectly. This is related to the fact that mpz_export ignores
the sign of the value being exportd, and assumes an unsigned export.
Thus, for such large values, the appears as if mpz_export is able
to fit our value into our buffer, but in fact, it does not.
Also, I noticed that gdb_mpz::write wasn't taking its unsigned_p
parameter, which was a hole.
For all these reasons, a new low-level private method called
"safe_export" has been added to class gdb_mpz, whose goal is
to perform all necessary checks and manipulations for a safe
and correct export. As a bonus, this method allows us to factorize
the handling of negative value exports.
The gdb_mpz::as_integer and gdb_mpz::write methods are then simplified
to take advantage of this new safe_export method.
gdb/ChangeLog:
* gmp-utils.h (gdb_mpz::safe_export): New private method.
(gdb_mpz::as_integer): Reimplement using gdb_mpz::safe_export.
* gmp-utils.c (gdb_mpz::write): Rewrite using gdb_mpz::safe_export.
(gdb_mpz::safe_export): New method.
* unittests/gmp-utils-selftests .c (gdb_mpz_as_integer):
Update function description.
(check_as_integer_raises_out_of_range_error): New function.
(gdb_mpz_as_integer_out_of_range): New function.
(_initialize_gmp_utils_selftests): Register
gdb_mpz_as_integer_out_of_range as a selftest.
2020-12-06 12:56:59 +08:00
|
|
|
this->safe_export
|
|
|
|
(buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
void
|
|
|
|
gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
|
|
|
|
int endian, bool unsigned_p) const
|
|
|
|
{
|
|
|
|
gdb_assert (buf.size () > 0);
|
|
|
|
|
|
|
|
if (mpz_sgn (val) == 0)
|
|
|
|
{
|
|
|
|
/* Our value is zero, so no need to call mpz_export to do the work,
|
|
|
|
especially since mpz_export's documentation explicitly says
|
|
|
|
that the function is a noop in this case. Just write zero to
|
|
|
|
BUF ourselves. */
|
|
|
|
memset (buf.data (), 0, buf.size ());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the maximum range of values that our buffer can hold,
|
|
|
|
and verify that VAL is within that range. */
|
|
|
|
|
|
|
|
gdb_mpz lo, hi;
|
|
|
|
const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
|
|
|
|
if (unsigned_p)
|
|
|
|
{
|
|
|
|
lo = 0;
|
|
|
|
|
|
|
|
mpz_ui_pow_ui (hi.val, 2, max_usable_bits);
|
|
|
|
mpz_sub_ui (hi.val, hi.val, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mpz_ui_pow_ui (lo.val, 2, max_usable_bits - 1);
|
|
|
|
mpz_neg (lo.val, lo.val);
|
|
|
|
|
|
|
|
mpz_ui_pow_ui (hi.val, 2, max_usable_bits - 1);
|
|
|
|
mpz_sub_ui (hi.val, hi.val, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpz_cmp (val, lo.val) < 0 || mpz_cmp (val, hi.val) > 0)
|
|
|
|
error (_("Cannot export value %s as %zu-bits %s integer"
|
|
|
|
" (must be between %s and %s)"),
|
|
|
|
this->str ().c_str (),
|
|
|
|
max_usable_bits,
|
|
|
|
unsigned_p ? _("unsigned") : _("signed"),
|
|
|
|
lo.str ().c_str (),
|
|
|
|
hi.str ().c_str ());
|
|
|
|
|
2020-11-15 16:09:44 +08:00
|
|
|
gdb_mpz exported_val (val);
|
|
|
|
|
gmp-utils: protect gdb_mpz exports against out-of-range values
The gdb_mpz class currently provides a couple of methods which
essentially export an mpz_t value into either a buffer, or an integral
type. The export is based on using the mpz_export function which
we discovered can be a bit treacherous if used without caution.
In particular, the initial motivation for this patch was to catch
situations where the mpz_t value was so large that it would not fit
in the destination area. mpz_export does not know the size of
the buffer, and therefore can happily write past the end of our buffer.
While designing a solution to the above problem, I also discovered
that we also needed to be careful when exporting signed numbers.
In particular, numbers which are larger than the maximum value
for a given signed type size, but no so large as to fit in the
*unsigned* version with the same size, would end up being exported
incorrectly. This is related to the fact that mpz_export ignores
the sign of the value being exportd, and assumes an unsigned export.
Thus, for such large values, the appears as if mpz_export is able
to fit our value into our buffer, but in fact, it does not.
Also, I noticed that gdb_mpz::write wasn't taking its unsigned_p
parameter, which was a hole.
For all these reasons, a new low-level private method called
"safe_export" has been added to class gdb_mpz, whose goal is
to perform all necessary checks and manipulations for a safe
and correct export. As a bonus, this method allows us to factorize
the handling of negative value exports.
The gdb_mpz::as_integer and gdb_mpz::write methods are then simplified
to take advantage of this new safe_export method.
gdb/ChangeLog:
* gmp-utils.h (gdb_mpz::safe_export): New private method.
(gdb_mpz::as_integer): Reimplement using gdb_mpz::safe_export.
* gmp-utils.c (gdb_mpz::write): Rewrite using gdb_mpz::safe_export.
(gdb_mpz::safe_export): New method.
* unittests/gmp-utils-selftests .c (gdb_mpz_as_integer):
Update function description.
(check_as_integer_raises_out_of_range_error): New function.
(gdb_mpz_as_integer_out_of_range): New function.
(_initialize_gmp_utils_selftests): Register
gdb_mpz_as_integer_out_of_range as a selftest.
2020-12-06 12:56:59 +08:00
|
|
|
if (mpz_cmp_ui (exported_val.val, 0) < 0)
|
2020-11-15 16:09:44 +08:00
|
|
|
{
|
|
|
|
/* mpz_export does not handle signed values, so create a positive
|
|
|
|
value whose bit representation as an unsigned of the same length
|
|
|
|
would be the same as our negative value. */
|
|
|
|
gdb_mpz neg_offset;
|
|
|
|
|
2020-12-05 16:03:48 +08:00
|
|
|
mpz_ui_pow_ui (neg_offset.val, 2, buf.size () * HOST_CHAR_BIT);
|
2020-11-15 16:09:44 +08:00
|
|
|
mpz_add (exported_val.val, exported_val.val, neg_offset.val);
|
|
|
|
}
|
|
|
|
|
gmp-utils: protect gdb_mpz exports against out-of-range values
The gdb_mpz class currently provides a couple of methods which
essentially export an mpz_t value into either a buffer, or an integral
type. The export is based on using the mpz_export function which
we discovered can be a bit treacherous if used without caution.
In particular, the initial motivation for this patch was to catch
situations where the mpz_t value was so large that it would not fit
in the destination area. mpz_export does not know the size of
the buffer, and therefore can happily write past the end of our buffer.
While designing a solution to the above problem, I also discovered
that we also needed to be careful when exporting signed numbers.
In particular, numbers which are larger than the maximum value
for a given signed type size, but no so large as to fit in the
*unsigned* version with the same size, would end up being exported
incorrectly. This is related to the fact that mpz_export ignores
the sign of the value being exportd, and assumes an unsigned export.
Thus, for such large values, the appears as if mpz_export is able
to fit our value into our buffer, but in fact, it does not.
Also, I noticed that gdb_mpz::write wasn't taking its unsigned_p
parameter, which was a hole.
For all these reasons, a new low-level private method called
"safe_export" has been added to class gdb_mpz, whose goal is
to perform all necessary checks and manipulations for a safe
and correct export. As a bonus, this method allows us to factorize
the handling of negative value exports.
The gdb_mpz::as_integer and gdb_mpz::write methods are then simplified
to take advantage of this new safe_export method.
gdb/ChangeLog:
* gmp-utils.h (gdb_mpz::safe_export): New private method.
(gdb_mpz::as_integer): Reimplement using gdb_mpz::safe_export.
* gmp-utils.c (gdb_mpz::write): Rewrite using gdb_mpz::safe_export.
(gdb_mpz::safe_export): New method.
* unittests/gmp-utils-selftests .c (gdb_mpz_as_integer):
Update function description.
(check_as_integer_raises_out_of_range_error): New function.
(gdb_mpz_as_integer_out_of_range): New function.
(_initialize_gmp_utils_selftests): Register
gdb_mpz_as_integer_out_of_range as a selftest.
2020-12-06 12:56:59 +08:00
|
|
|
/* Do the export into a buffer allocated by GMP itself; that way,
|
|
|
|
we can detect cases where BUF is not large enough to export
|
|
|
|
our value, and thus avoid a buffer overlow. Normally, this should
|
|
|
|
never happen, since we verified earlier that the buffer is large
|
|
|
|
enough to accomodate our value, but doing this allows us to be
|
|
|
|
extra safe with the export.
|
|
|
|
|
|
|
|
After verification that the export behaved as expected, we will
|
|
|
|
copy the data over to BUF. */
|
|
|
|
|
|
|
|
size_t word_countp;
|
|
|
|
gdb::unique_xmalloc_ptr<void> exported
|
|
|
|
(mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */,
|
|
|
|
endian, 0 /* nails */, exported_val.val));
|
|
|
|
|
|
|
|
gdb_assert (word_countp == 1);
|
|
|
|
|
|
|
|
memcpy (buf.data (), exported.get (), buf.size ());
|
2020-11-15 16:09:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
gdb_mpz
|
|
|
|
gdb_mpq::get_rounded () const
|
|
|
|
{
|
|
|
|
/* Work with a positive number so as to make the "floor" rounding
|
|
|
|
always round towards zero. */
|
|
|
|
|
|
|
|
gdb_mpq abs_val (val);
|
|
|
|
mpq_abs (abs_val.val, abs_val.val);
|
|
|
|
|
|
|
|
/* Convert our rational number into a quotient and remainder,
|
|
|
|
with "floor" rounding, which in our case means rounding
|
|
|
|
towards zero. */
|
|
|
|
|
|
|
|
gdb_mpz quotient, remainder;
|
|
|
|
mpz_fdiv_qr (quotient.val, remainder.val,
|
|
|
|
mpq_numref (abs_val.val), mpq_denref (abs_val.val));
|
|
|
|
|
|
|
|
/* Multiply the remainder by 2, and see if it is greater or equal
|
|
|
|
to abs_val's denominator. If yes, round to the next integer. */
|
|
|
|
|
|
|
|
mpz_mul_ui (remainder.val, remainder.val, 2);
|
|
|
|
if (mpz_cmp (remainder.val, mpq_denref (abs_val.val)) >= 0)
|
|
|
|
mpz_add_ui (quotient.val, quotient.val, 1);
|
|
|
|
|
|
|
|
/* Re-apply the sign if needed. */
|
|
|
|
if (mpq_sgn (val) < 0)
|
|
|
|
mpz_neg (quotient.val, quotient.val);
|
|
|
|
|
|
|
|
return quotient;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
void
|
2020-11-24 10:46:38 +08:00
|
|
|
gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf,
|
2020-11-15 16:09:44 +08:00
|
|
|
enum bfd_endian byte_order, bool unsigned_p,
|
|
|
|
const gdb_mpq &scaling_factor)
|
|
|
|
{
|
|
|
|
gdb_mpz vz;
|
2020-11-24 10:46:38 +08:00
|
|
|
vz.read (buf, byte_order, unsigned_p);
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
mpq_set_z (val, vz.val);
|
|
|
|
mpq_mul (val, val, scaling_factor.val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See gmp-utils.h. */
|
|
|
|
|
|
|
|
void
|
2020-11-24 10:46:38 +08:00
|
|
|
gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf,
|
2020-11-15 16:09:44 +08:00
|
|
|
enum bfd_endian byte_order, bool unsigned_p,
|
|
|
|
const gdb_mpq &scaling_factor) const
|
|
|
|
{
|
|
|
|
gdb_mpq unscaled (val);
|
|
|
|
|
|
|
|
mpq_div (unscaled.val, unscaled.val, scaling_factor.val);
|
|
|
|
|
|
|
|
gdb_mpz unscaled_z = unscaled.get_rounded ();
|
2020-11-24 10:46:38 +08:00
|
|
|
unscaled_z.write (buf, byte_order, unsigned_p);
|
2020-11-15 16:09:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* A wrapper around xrealloc that we can then register with GMP
|
|
|
|
as the "realloc" function. */
|
|
|
|
|
|
|
|
static void *
|
|
|
|
xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size)
|
|
|
|
{
|
|
|
|
return xrealloc (ptr, new_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A wrapper around xfree that we can then register with GMP
|
|
|
|
as the "free" function. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
xfree_for_gmp (void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
xfree (ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _initialize_gmp_utils ();
|
|
|
|
|
|
|
|
void
|
|
|
|
_initialize_gmp_utils ()
|
|
|
|
{
|
|
|
|
/* Tell GMP to use GDB's memory management routines. */
|
|
|
|
mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
|
|
|
|
}
|