binutils-gdb/gdb/gmp-utils.c

261 lines
7.3 KiB
C
Raw Permalink Normal View History

/* Copyright (C) 2019-2024 Free Software Foundation, Inc.
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. */
std::string
gmp_string_printf (const char *fmt, ...)
{
va_list vp;
va_start (vp, fmt);
int size = gmp_vsnprintf (NULL, 0, fmt, vp);
va_end (vp);
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;
}
/* See gmp-utils.h. */
void
gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
bool unsigned_p)
{
mpz_import (m_val, 1 /* count */, -1 /* order */, buf.size () /* size */,
byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
0 /* nails */, buf.data () /* op */);
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;
mpz_ui_pow_ui (max.m_val, 2, buf.size () * HOST_CHAR_BIT - 1);
if (mpz_cmp (m_val, max.m_val) >= 0)
mpz_submul_ui (m_val, max.m_val, 2);
}
}
/* See gmp-utils.h. */
void
gdb_mpz::export_bits (gdb::array_view<gdb_byte> buf, int endian, bool unsigned_p,
bool safe) 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
{
int sign = mpz_sgn (m_val);
if (sign == 0)
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
{
/* 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, if it is non-empty. In some languages, a
zero-bit type can exist and this is also fine. */
if (buf.size () > 0)
memset (buf.data (), 0, buf.size ());
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
return;
}
gdb_assert (buf.size () > 0);
if (safe)
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
{
/* 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.m_val, 2, max_usable_bits);
mpz_sub_ui (hi.m_val, hi.m_val, 1);
}
else
{
mpz_ui_pow_ui (lo.m_val, 2, max_usable_bits - 1);
mpz_neg (lo.m_val, lo.m_val);
mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits - 1);
mpz_sub_ui (hi.m_val, hi.m_val, 1);
}
if (mpz_cmp (m_val, lo.m_val) < 0 || mpz_cmp (m_val, hi.m_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 ());
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
}
const gdb_mpz *exported_val = this;
gdb_mpz un_signed;
if (sign < 0)
{
/* 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 = gdb_mpz::pow (2, buf.size () * HOST_CHAR_BIT);
un_signed = *exported_val + neg_offset;
exported_val = &un_signed;
}
/* If the value is too large, truncate it. */
if (!safe
&& mpz_sizeinbase (exported_val->m_val, 2) > buf.size () * HOST_CHAR_BIT)
{
/* If we don't already have a copy, make it now. */
if (exported_val != &un_signed)
{
un_signed = *exported_val;
exported_val = &un_signed;
}
un_signed.mask (buf.size () * HOST_CHAR_BIT);
}
/* It's possible that one of the above results in zero, which has to
be handled specially. */
if (exported_val->sgn () == 0)
{
memset (buf.data (), 0, buf.size ());
return;
}
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
[gdb] Fix typos Fix a few typos: - implemention -> implementation - convertion(s) -> conversion(s) - backlashes -> backslashes - signoring -> ignoring - (un)ambigious -> (un)ambiguous - occured -> occurred - hidding -> hiding - temporarilly -> temporarily - immediatelly -> immediately - sillyness -> silliness - similiar -> similar - porkuser -> pokeuser - thats -> that - alway -> always - supercede -> supersede - accomodate -> accommodate - aquire -> acquire - priveleged -> privileged - priviliged -> privileged - priviledges -> privileges - privilige -> privilege - recieve -> receive - (p)refered -> (p)referred - succesfully -> successfully - successfuly -> successfully - responsability -> responsibility - wether -> whether - wich -> which - disasbleable -> disableable - descriminant -> discriminant - construcstor -> constructor - underlaying -> underlying - underyling -> underlying - structureal -> structural - appearences -> appearances - terciarily -> tertiarily - resgisters -> registers - reacheable -> reachable - likelyhood -> likelihood - intepreter -> interpreter - disassemly -> disassembly - covnersion -> conversion - conviently -> conveniently - atttribute -> attribute - struction -> struct - resonable -> reasonable - popupated -> populated - namespaxe -> namespace - intialize -> initialize - identifer(s) -> identifier(s) - expection -> exception - exectuted -> executed - dungerous -> dangerous - dissapear -> disappear - completly -> completely - (inter)changable -> (inter)changeable - beakpoint -> breakpoint - automativ -> automatic - alocating -> allocating - agressive -> aggressive - writting -> writing - reguires -> requires - registed -> registered - recuding -> reducing - opeartor -> operator - ommitted -> omitted - modifing -> modifying - intances -> instances - imbedded -> embedded - gdbaarch -> gdbarch - exection -> execution - direcive -> directive - demanged -> demangled - decidely -> decidedly - argments -> arguments - agrument -> argument - amespace -> namespace - targtet -> target - supress(ed) -> suppress(ed) - startum -> stratum - squence -> sequence - prompty -> prompt - overlow -> overflow - memember -> member - languge -> language - geneate -> generate - funcion -> function - exising -> existing - dinking -> syncing - destroh -> destroy - clenaed -> cleaned - changep -> changedp (name of variable) - arround -> around - aproach -> approach - whould -> would - symobl -> symbol - recuse -> recurse - outter -> outer - freeds -> frees - contex -> context Tested on x86_64-linux. Reviewed-By: Tom Tromey <tom@tromey.com>
2023-06-04 04:43:57 +08:00
our value, and thus avoid a buffer overflow. Normally, this should
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
never happen, since we verified earlier that the buffer is large
[gdb] Fix typos Fix a few typos: - implemention -> implementation - convertion(s) -> conversion(s) - backlashes -> backslashes - signoring -> ignoring - (un)ambigious -> (un)ambiguous - occured -> occurred - hidding -> hiding - temporarilly -> temporarily - immediatelly -> immediately - sillyness -> silliness - similiar -> similar - porkuser -> pokeuser - thats -> that - alway -> always - supercede -> supersede - accomodate -> accommodate - aquire -> acquire - priveleged -> privileged - priviliged -> privileged - priviledges -> privileges - privilige -> privilege - recieve -> receive - (p)refered -> (p)referred - succesfully -> successfully - successfuly -> successfully - responsability -> responsibility - wether -> whether - wich -> which - disasbleable -> disableable - descriminant -> discriminant - construcstor -> constructor - underlaying -> underlying - underyling -> underlying - structureal -> structural - appearences -> appearances - terciarily -> tertiarily - resgisters -> registers - reacheable -> reachable - likelyhood -> likelihood - intepreter -> interpreter - disassemly -> disassembly - covnersion -> conversion - conviently -> conveniently - atttribute -> attribute - struction -> struct - resonable -> reasonable - popupated -> populated - namespaxe -> namespace - intialize -> initialize - identifer(s) -> identifier(s) - expection -> exception - exectuted -> executed - dungerous -> dangerous - dissapear -> disappear - completly -> completely - (inter)changable -> (inter)changeable - beakpoint -> breakpoint - automativ -> automatic - alocating -> allocating - agressive -> aggressive - writting -> writing - reguires -> requires - registed -> registered - recuding -> reducing - opeartor -> operator - ommitted -> omitted - modifing -> modifying - intances -> instances - imbedded -> embedded - gdbaarch -> gdbarch - exection -> execution - direcive -> directive - demanged -> demangled - decidely -> decidedly - argments -> arguments - agrument -> argument - amespace -> namespace - targtet -> target - supress(ed) -> suppress(ed) - startum -> stratum - squence -> sequence - prompty -> prompt - overlow -> overflow - memember -> member - languge -> language - geneate -> generate - funcion -> function - exising -> existing - dinking -> syncing - destroh -> destroy - clenaed -> cleaned - changep -> changedp (name of variable) - arround -> around - aproach -> approach - whould -> would - symobl -> symbol - recuse -> recurse - outter -> outer - freeds -> frees - contex -> context Tested on x86_64-linux. Reviewed-By: Tom Tromey <tom@tromey.com>
2023-06-04 04:43:57 +08:00
enough to accommodate our value, but doing this allows us to be
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
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->m_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
gdb_assert (word_countp == 1);
memcpy (buf.data (), exported.get (), buf.size ());
}
/* 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 (m_val);
mpq_abs (abs_val.m_val, abs_val.m_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.m_val, remainder.m_val,
mpq_numref (abs_val.m_val), mpq_denref (abs_val.m_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.m_val, remainder.m_val, 2);
if (mpz_cmp (remainder.m_val, mpq_denref (abs_val.m_val)) >= 0)
mpz_add_ui (quotient.m_val, quotient.m_val, 1);
/* Re-apply the sign if needed. */
if (mpq_sgn (m_val) < 0)
mpz_neg (quotient.m_val, quotient.m_val);
return quotient;
}
/* See gmp-utils.h. */
void
gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf,
enum bfd_endian byte_order, bool unsigned_p,
const gdb_mpq &scaling_factor)
{
gdb_mpz vz;
vz.read (buf, byte_order, unsigned_p);
mpq_set_z (m_val, vz.m_val);
mpq_mul (m_val, m_val, scaling_factor.m_val);
}
/* See gmp-utils.h. */
void
gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf,
enum bfd_endian byte_order, bool unsigned_p,
const gdb_mpq &scaling_factor) const
{
gdb_mpq unscaled (m_val);
mpq_div (unscaled.m_val, unscaled.m_val, scaling_factor.m_val);
gdb_mpz unscaled_z = unscaled.get_rounded ();
unscaled_z.write (buf, byte_order, unsigned_p);
}
/* 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);
}