2020-11-15 16:09:44 +08:00
|
|
|
/* Self tests of the gmp-utils API.
|
|
|
|
|
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"
|
|
|
|
#include "gdbsupport/selftest.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
namespace selftests {
|
|
|
|
|
|
|
|
/* Perform a series of general tests of gdb_mpz's as_integer method.
|
|
|
|
|
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 function limits itself to values which are in range (out-of-range
|
|
|
|
values will be tested separately). In doing so, it tries to be reasonably
|
|
|
|
exhaustive, by testing the edges, as well as a resonable set of values
|
|
|
|
including negative ones, zero, and positive values. */
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_as_integer ()
|
|
|
|
{
|
|
|
|
/* Test a range of values, both as LONGEST and ULONGEST. */
|
|
|
|
gdb_mpz v;
|
|
|
|
LONGEST l_expected;
|
|
|
|
ULONGEST ul_expected;
|
|
|
|
|
|
|
|
/* Start with the smallest LONGEST */
|
|
|
|
l_expected = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
|
|
|
|
|
|
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
|
|
|
|
mpz_neg (v.val, v.val);
|
|
|
|
|
|
|
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
|
|
|
|
|
|
|
/* Try with a small range of integers including negative, zero,
|
|
|
|
and positive values. */
|
|
|
|
for (int i = -256; i <= 256; i++)
|
|
|
|
{
|
|
|
|
l_expected = (LONGEST) i;
|
|
|
|
mpz_set_si (v.val, i);
|
|
|
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
|
|
|
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
ul_expected = (ULONGEST) i;
|
|
|
|
mpz_set_ui (v.val, i);
|
|
|
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try with LONGEST_MAX. */
|
|
|
|
l_expected = LONGEST_MAX;
|
|
|
|
ul_expected = (ULONGEST) l_expected;
|
|
|
|
|
|
|
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
|
|
|
|
mpz_sub_ui (v.val, v.val, 1);
|
|
|
|
|
|
|
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
|
|
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
|
|
|
|
|
|
|
/* Try with ULONGEST_MAX. */
|
|
|
|
ul_expected = ULONGEST_MAX;
|
|
|
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8);
|
|
|
|
mpz_sub_ui (v.val, v.val, 1);
|
|
|
|
|
|
|
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/* A helper function which calls the given gdb_mpz object's as_integer
|
|
|
|
method with the given type T, and verifies that this triggers
|
|
|
|
an error due to VAL's value being out of range for type T. */
|
|
|
|
|
|
|
|
template<typename T, typename = gdb::Requires<std::is_integral<T>>>
|
|
|
|
static void
|
|
|
|
check_as_integer_raises_out_of_range_error (const gdb_mpz &val)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
val.as_integer<T> ();
|
|
|
|
}
|
|
|
|
catch (const gdb_exception_error &ex)
|
|
|
|
{
|
|
|
|
SELF_CHECK (ex.reason == RETURN_ERROR);
|
|
|
|
SELF_CHECK (ex.error == GENERIC_ERROR);
|
|
|
|
SELF_CHECK (strstr (ex.what (), "Cannot export value") != nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* The expected exception did not get raised. */
|
|
|
|
SELF_CHECK (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform out-of-range tests of gdb_mpz's as_integer method.
|
|
|
|
|
|
|
|
The goal of this function is to verify that gdb_mpz::as_integer
|
|
|
|
handles out-of-range values correctly. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_as_integer_out_of_range ()
|
|
|
|
{
|
|
|
|
gdb_mpz v;
|
|
|
|
|
|
|
|
/* Try LONGEST_MIN minus 1. */
|
|
|
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
|
|
|
|
mpz_neg (v.val, v.val);
|
|
|
|
mpz_sub_ui (v.val, v.val, 1);
|
|
|
|
|
|
|
|
check_as_integer_raises_out_of_range_error<ULONGEST> (v);
|
|
|
|
check_as_integer_raises_out_of_range_error<LONGEST> (v);
|
|
|
|
|
|
|
|
/* Try negative one (-1). */
|
|
|
|
v = -1;
|
|
|
|
|
|
|
|
check_as_integer_raises_out_of_range_error<ULONGEST> (v);
|
|
|
|
SELF_CHECK (v.as_integer<LONGEST> () == (LONGEST) -1);
|
|
|
|
|
|
|
|
/* Try LONGEST_MAX plus 1. */
|
|
|
|
v = LONGEST_MAX;
|
|
|
|
mpz_add_ui (v.val, v.val, 1);
|
|
|
|
|
|
|
|
SELF_CHECK (v.as_integer<ULONGEST> () == (ULONGEST) LONGEST_MAX + 1);
|
|
|
|
check_as_integer_raises_out_of_range_error<LONGEST> (v);
|
|
|
|
|
|
|
|
/* Try ULONGEST_MAX plus 1. */
|
|
|
|
v = ULONGEST_MAX;
|
|
|
|
mpz_add_ui (v.val, v.val, 1);
|
|
|
|
|
|
|
|
check_as_integer_raises_out_of_range_error<ULONGEST> (v);
|
|
|
|
check_as_integer_raises_out_of_range_error<LONGEST> (v);
|
|
|
|
}
|
|
|
|
|
2020-11-15 16:09:44 +08:00
|
|
|
/* A helper function to store the given integer value into a buffer,
|
|
|
|
before reading it back into a gdb_mpz. Sets ACTUAL to the value
|
|
|
|
read back, while at the same time setting EXPECTED as the value
|
|
|
|
we would expect to be read back.
|
|
|
|
|
|
|
|
Note that this function does not perform the comparison between
|
|
|
|
EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK
|
|
|
|
call, allowing the line information shown when the test fails
|
|
|
|
to provide a bit more information about the kind of values
|
|
|
|
that were used when the check failed. This makes the writing
|
|
|
|
of the tests a little more verbose, but the debugging in case
|
|
|
|
of problems should hopefuly be easier. */
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void
|
2020-11-24 10:46:38 +08:00
|
|
|
store_and_read_back (T val, size_t buf_len, enum bfd_endian byte_order,
|
2020-11-15 16:09:44 +08:00
|
|
|
gdb_mpz &expected, gdb_mpz &actual)
|
|
|
|
{
|
|
|
|
gdb_byte *buf;
|
|
|
|
|
|
|
|
expected = val;
|
|
|
|
|
|
|
|
buf = (gdb_byte *) alloca (buf_len);
|
|
|
|
store_integer (buf, buf_len, byte_order, val);
|
|
|
|
|
|
|
|
/* Pre-initialize ACTUAL to something that's not the expected value. */
|
|
|
|
mpz_set (actual.val, expected.val);
|
|
|
|
mpz_sub_ui (actual.val, actual.val, 500);
|
|
|
|
|
2020-11-24 10:46:38 +08:00
|
|
|
actual.read ({buf, buf_len}, byte_order, !std::is_signed<T>::value);
|
2020-11-15 16:09:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the gdb_mpz::read method over a reasonable range of values.
|
|
|
|
|
|
|
|
The testing is done by picking an arbitrary buffer length, after
|
|
|
|
which we test every possible value that this buffer allows, both
|
|
|
|
with signed numbers as well as unsigned ones. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_read_all_from_small ()
|
|
|
|
{
|
|
|
|
/* Start with a type whose size is small enough that we can afford
|
|
|
|
to check the complete range. */
|
|
|
|
|
|
|
|
int buf_len = 1;
|
gdb: fix unittests/gmp-utils-selftests.c build on solaris
When building on solaris (gcc farm machine gcc211), I get:
CXX unittests/gmp-utils-selftests.o
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c: In function 'void selftests::gdb_mpz_read_all_from_small()' :
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:128:43: error: call of overloaded 'pow(int, int)' is ambiguous
LONGEST l_min = -pow (2, buf_len * 8 - 1);
^
In file included from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/math.h:22:0,
from ../gnulib/import/math.h:27,
from /export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:23:
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:210:21: note: candidate: long double std::pow(long double, long double)
inline long double pow(long double __X, long double __Y) { return
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:170:15: note: candidate: float std::pow(float, float)
inline float pow(float __X, float __Y) { return __powf(__X, __Y); }
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:71:15: note: candidate: double std::pow(double, double)
extern double pow __P((double, double));
^
The "pow" function overloads only exist for float-like types, and the
compiler doesn't know which one we want. Change "2" for "2.0", which
makes the compiler choose one alternative (the double one, I believe).
gdb/ChangeLog:
* unittests/gmp-utils-selftests.c (gdb_mpz_read_all_from_small):
Pass 2.0 to pow.
(gdb_mpz_write_all_from_small): Likewise.
Change-Id: Ied2ae0f01494430244a7c94f8a38b07d819f4213
2020-11-21 00:17:33 +08:00
|
|
|
LONGEST l_min = -pow (2.0, buf_len * 8 - 1);
|
|
|
|
LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1;
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
for (LONGEST l = l_min; l <= l_max; l++)
|
|
|
|
{
|
|
|
|
gdb_mpz expected, actual;
|
|
|
|
|
|
|
|
store_and_read_back (l, buf_len, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (l, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the same as above, but with an unsigned type. */
|
|
|
|
ULONGEST ul_min = 0;
|
gdb: fix unittests/gmp-utils-selftests.c build on solaris
When building on solaris (gcc farm machine gcc211), I get:
CXX unittests/gmp-utils-selftests.o
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c: In function 'void selftests::gdb_mpz_read_all_from_small()' :
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:128:43: error: call of overloaded 'pow(int, int)' is ambiguous
LONGEST l_min = -pow (2, buf_len * 8 - 1);
^
In file included from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/math.h:22:0,
from ../gnulib/import/math.h:27,
from /export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:23:
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:210:21: note: candidate: long double std::pow(long double, long double)
inline long double pow(long double __X, long double __Y) { return
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:170:15: note: candidate: float std::pow(float, float)
inline float pow(float __X, float __Y) { return __powf(__X, __Y); }
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:71:15: note: candidate: double std::pow(double, double)
extern double pow __P((double, double));
^
The "pow" function overloads only exist for float-like types, and the
compiler doesn't know which one we want. Change "2" for "2.0", which
makes the compiler choose one alternative (the double one, I believe).
gdb/ChangeLog:
* unittests/gmp-utils-selftests.c (gdb_mpz_read_all_from_small):
Pass 2.0 to pow.
(gdb_mpz_write_all_from_small): Likewise.
Change-Id: Ied2ae0f01494430244a7c94f8a38b07d819f4213
2020-11-21 00:17:33 +08:00
|
|
|
ULONGEST ul_max = pow (2.0, buf_len * 8) - 1;
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
|
|
|
|
{
|
|
|
|
gdb_mpz expected, actual;
|
|
|
|
|
|
|
|
store_and_read_back (ul, buf_len, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (ul, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the gdb_mpz::read the extremes of LONGEST and ULONGEST. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_read_min_max ()
|
|
|
|
{
|
|
|
|
gdb_mpz expected, actual;
|
|
|
|
|
|
|
|
/* Start with the smallest LONGEST. */
|
|
|
|
|
|
|
|
LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
|
|
|
|
|
|
|
store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
/* Same with LONGEST_MAX. */
|
|
|
|
|
|
|
|
LONGEST l_max = LONGEST_MAX;
|
|
|
|
|
|
|
|
store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
/* Same with the smallest ULONGEST. */
|
|
|
|
|
|
|
|
ULONGEST ul_min = 0;
|
|
|
|
|
|
|
|
store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
/* Same with ULONGEST_MAX. */
|
|
|
|
|
|
|
|
ULONGEST ul_max = ULONGEST_MAX;
|
|
|
|
|
|
|
|
store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
|
|
|
|
expected, actual);
|
|
|
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A helper function which creates a gdb_mpz object from the given
|
|
|
|
integer VAL, and then writes it using its gdb_mpz::write method.
|
|
|
|
|
|
|
|
The written value is then extracted from the buffer and returned,
|
|
|
|
for comparison with the original.
|
|
|
|
|
|
|
|
Note that this function does not perform the comparison between
|
|
|
|
VAL and the returned value. The caller will do it inside a SELF_CHECK
|
|
|
|
call, allowing the line information shown when the test fails
|
|
|
|
to provide a bit more information about the kind of values
|
|
|
|
that were used when the check failed. This makes the writing
|
|
|
|
of the tests a little more verbose, but the debugging in case
|
|
|
|
of problems should hopefuly be easier. */
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
T
|
2020-11-24 10:46:38 +08:00
|
|
|
write_and_extract (T val, size_t buf_len, enum bfd_endian byte_order)
|
2020-11-15 16:09:44 +08:00
|
|
|
{
|
|
|
|
gdb_mpz v (val);
|
|
|
|
|
|
|
|
SELF_CHECK (v.as_integer<T> () == val);
|
|
|
|
|
|
|
|
gdb_byte *buf = (gdb_byte *) alloca (buf_len);
|
2020-11-24 10:46:38 +08:00
|
|
|
v.write ({buf, buf_len}, byte_order, !std::is_signed<T>::value);
|
2020-11-15 16:09:44 +08:00
|
|
|
|
2021-10-26 11:29:34 +08:00
|
|
|
return extract_integer<T> ({buf, buf_len}, byte_order);
|
2020-11-15 16:09:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the gdb_mpz::write method over a reasonable range of values.
|
|
|
|
|
|
|
|
The testing is done by picking an arbitrary buffer length, after
|
|
|
|
which we test every possible value that this buffer allows. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_write_all_from_small ()
|
|
|
|
{
|
|
|
|
int buf_len = 1;
|
gdb: fix unittests/gmp-utils-selftests.c build on solaris
When building on solaris (gcc farm machine gcc211), I get:
CXX unittests/gmp-utils-selftests.o
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c: In function 'void selftests::gdb_mpz_read_all_from_small()' :
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:128:43: error: call of overloaded 'pow(int, int)' is ambiguous
LONGEST l_min = -pow (2, buf_len * 8 - 1);
^
In file included from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/math.h:22:0,
from ../gnulib/import/math.h:27,
from /export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:23:
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:210:21: note: candidate: long double std::pow(long double, long double)
inline long double pow(long double __X, long double __Y) { return
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:170:15: note: candidate: float std::pow(float, float)
inline float pow(float __X, float __Y) { return __powf(__X, __Y); }
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:71:15: note: candidate: double std::pow(double, double)
extern double pow __P((double, double));
^
The "pow" function overloads only exist for float-like types, and the
compiler doesn't know which one we want. Change "2" for "2.0", which
makes the compiler choose one alternative (the double one, I believe).
gdb/ChangeLog:
* unittests/gmp-utils-selftests.c (gdb_mpz_read_all_from_small):
Pass 2.0 to pow.
(gdb_mpz_write_all_from_small): Likewise.
Change-Id: Ied2ae0f01494430244a7c94f8a38b07d819f4213
2020-11-21 00:17:33 +08:00
|
|
|
LONGEST l_min = -pow (2.0, buf_len * 8 - 1);
|
|
|
|
LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1;
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
for (LONGEST l = l_min; l <= l_max; l++)
|
|
|
|
{
|
|
|
|
SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_BIG) == l);
|
|
|
|
SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_LITTLE) == l);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the same as above, but with an unsigned type. */
|
|
|
|
ULONGEST ul_min = 0;
|
gdb: fix unittests/gmp-utils-selftests.c build on solaris
When building on solaris (gcc farm machine gcc211), I get:
CXX unittests/gmp-utils-selftests.o
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c: In function 'void selftests::gdb_mpz_read_all_from_small()' :
/export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:128:43: error: call of overloaded 'pow(int, int)' is ambiguous
LONGEST l_min = -pow (2, buf_len * 8 - 1);
^
In file included from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/math.h:22:0,
from ../gnulib/import/math.h:27,
from /export/home/simark/src/binutils-gdb/gdb/unittests/gmp-utils-selftests.c:23:
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:210:21: note: candidate: long double std::pow(long double, long double)
inline long double pow(long double __X, long double __Y) { return
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:170:15: note: candidate: float std::pow(float, float)
inline float pow(float __X, float __Y) { return __powf(__X, __Y); }
^
/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed/iso/math_iso.h:71:15: note: candidate: double std::pow(double, double)
extern double pow __P((double, double));
^
The "pow" function overloads only exist for float-like types, and the
compiler doesn't know which one we want. Change "2" for "2.0", which
makes the compiler choose one alternative (the double one, I believe).
gdb/ChangeLog:
* unittests/gmp-utils-selftests.c (gdb_mpz_read_all_from_small):
Pass 2.0 to pow.
(gdb_mpz_write_all_from_small): Likewise.
Change-Id: Ied2ae0f01494430244a7c94f8a38b07d819f4213
2020-11-21 00:17:33 +08:00
|
|
|
ULONGEST ul_max = pow (2.0, buf_len * 8) - 1;
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
|
|
|
|
{
|
|
|
|
SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_BIG) == ul);
|
|
|
|
SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_LITTLE) == ul);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the gdb_mpz::write the extremes of LONGEST and ULONGEST. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpz_write_min_max ()
|
|
|
|
{
|
|
|
|
/* Start with the smallest LONGEST. */
|
|
|
|
|
|
|
|
LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
|
|
|
SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG)
|
|
|
|
== l_min);
|
|
|
|
SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
|
|
|
|
== l_min);
|
|
|
|
|
|
|
|
/* Same with LONGEST_MAX. */
|
|
|
|
|
|
|
|
LONGEST l_max = LONGEST_MAX;
|
|
|
|
SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG)
|
|
|
|
== l_max);
|
|
|
|
SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
|
|
|
|
== l_max);
|
|
|
|
|
|
|
|
/* Same with the smallest ULONGEST. */
|
|
|
|
|
|
|
|
ULONGEST ul_min = (ULONGEST) 1 << (sizeof (ULONGEST) * 8 - 1);
|
|
|
|
SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG)
|
|
|
|
== ul_min);
|
|
|
|
SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
|
|
|
|
== ul_min);
|
|
|
|
|
|
|
|
/* Same with ULONGEST_MAX. */
|
|
|
|
|
|
|
|
ULONGEST ul_max = ULONGEST_MAX;
|
|
|
|
SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG)
|
|
|
|
== ul_max);
|
|
|
|
SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
|
|
|
|
== ul_max);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A helper function which stores the signed number, the unscaled value
|
|
|
|
of a fixed point object, into a buffer, and then uses gdb_mpq's
|
|
|
|
read_fixed_point to read it as a fixed_point value, with
|
|
|
|
the given parameters.
|
|
|
|
|
|
|
|
EXPECTED is set to the value we expected to get after the call
|
|
|
|
to read_fixed_point. ACTUAL is the value we actually do get.
|
|
|
|
|
|
|
|
Note that this function does not perform the comparison between
|
|
|
|
EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK
|
|
|
|
call, allowing the line information shown when the test fails
|
|
|
|
to provide a bit more information about the kind of values
|
|
|
|
that were used when the check failed. This makes the writing
|
|
|
|
of the tests a little more verbose, but the debugging in case
|
|
|
|
of problems should hopefuly be easier. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_fp_test (int unscaled, const gdb_mpq &scaling_factor,
|
|
|
|
enum bfd_endian byte_order,
|
|
|
|
gdb_mpq &expected, gdb_mpq &actual)
|
|
|
|
{
|
|
|
|
/* For this kind of testing, we'll use a buffer the same size as
|
|
|
|
our unscaled parameter. */
|
2020-11-24 10:46:38 +08:00
|
|
|
const size_t len = sizeof (unscaled);
|
2020-11-15 16:09:44 +08:00
|
|
|
gdb_byte buf[len];
|
|
|
|
store_signed_integer (buf, len, byte_order, unscaled);
|
|
|
|
|
2020-11-24 10:46:38 +08:00
|
|
|
actual.read_fixed_point ({buf, len}, byte_order, 0, scaling_factor);
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
mpq_set_si (expected.val, unscaled, 1);
|
|
|
|
mpq_mul (expected.val, expected.val, scaling_factor.val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform various tests of the gdb_mpq::read_fixed_point method. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpq_read_fixed_point ()
|
|
|
|
{
|
|
|
|
gdb_mpq expected, actual;
|
|
|
|
gdb_mpq scaling_factor;
|
|
|
|
|
|
|
|
/* Pick an arbitrary scaling_factor; this operation is trivial enough
|
|
|
|
thanks to GMP that the value we use isn't really important. */
|
|
|
|
mpq_set_ui (scaling_factor.val, 3, 5);
|
|
|
|
|
|
|
|
/* Try a few values, both negative and positive... */
|
|
|
|
|
|
|
|
read_fp_test (-256, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
read_fp_test (-256, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
read_fp_test (-1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
read_fp_test (-1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
read_fp_test (0, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
read_fp_test (0, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
read_fp_test (1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
read_fp_test (1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
|
|
|
|
read_fp_test (1025, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
read_fp_test (1025, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
|
|
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A helper function which builds a gdb_mpq object from the given
|
|
|
|
NUMERATOR and DENOMINATOR, and then calls gdb_mpq's write_fixed_point
|
|
|
|
method to write it to a buffer.
|
|
|
|
|
|
|
|
The value written into the buffer is then read back as is,
|
|
|
|
and returned. */
|
|
|
|
|
|
|
|
static LONGEST
|
|
|
|
write_fp_test (int numerator, unsigned int denominator,
|
|
|
|
const gdb_mpq &scaling_factor,
|
|
|
|
enum bfd_endian byte_order)
|
|
|
|
{
|
|
|
|
/* For this testing, we'll use a buffer the size of LONGEST.
|
|
|
|
This is really an arbitrary decision, as long as the buffer
|
|
|
|
is long enough to hold the unscaled values that we'll be
|
|
|
|
writing. */
|
2020-11-24 10:46:38 +08:00
|
|
|
const size_t len = sizeof (LONGEST);
|
2020-11-15 16:09:44 +08:00
|
|
|
gdb_byte buf[len];
|
|
|
|
memset (buf, 0, len);
|
|
|
|
|
|
|
|
gdb_mpq v;
|
Fix stack smashing error during gdb_mpq_write_fixed_point selftest
When building GDB using Ubuntu 20.04's system libgmp and compiler,
running the "maintenance selftest" command triggers the following error:
| Running selftest gdb_mpq_write_fixed_point.
| *** stack smashing detected ***: terminated
| [1] 1092790 abort (core dumped) ./gdb gdb
This happens while trying to construct an mpq_t object (a rational)
from two integers representing the numerator and denominator.
In our test, the numerator is -8, and the denominator is 1.
The problem was that the rational was constructed using the wrong
function. This is what we were doing prior to this patch:
mpq_set_ui (v.val, numerator, denominator);
The 'u' in "ui" stands for *unsigned*, which is wrong because
numerator and denominator's type is "int".
As a result of the above, instead of getting a rational value of -8,
we get a rational with a very large positive value (gmp_printf
says "18446744073709551608").
From there, the test performs an operation which is expected to
write this value into a buffer which was not dimensioned to fit
such a number, thus leading GMP into a buffer overflow.
This was verified by applying the formula that GMP's documentation
gives for the required memory buffer size needed during export:
| When an application is allocating space itself the required size can
| be determined with a calculation like the following. Since
| mpz_sizeinbase always returns at least 1, count here will be at
| least one, which avoids any portability problems with malloc(0),
| though if z is zero no space at all is actually needed (or written).
|
| numb = 8*size - nail;
| count = (mpz_sizeinbase (z, 2) + numb-1) / numb;
| p = malloc (count * size);
With the very large number, mpz_sizeinbase returns 66 and thus
the malloc size becomes 16 bytes instead of the 8 we allocated.
This patch fixes the issue by using the correct "set" function.
gdb/ChangeLog:
* unittests/gmp-utils-selftests.c (write_fp_test): Use mpq_set_si
instead of mpq_set_ui to initialize our GMP rational.
2020-11-24 10:34:57 +08:00
|
|
|
mpq_set_si (v.val, numerator, denominator);
|
2020-11-15 16:09:44 +08:00
|
|
|
mpq_canonicalize (v.val);
|
2020-11-24 10:46:38 +08:00
|
|
|
v.write_fixed_point ({buf, len}, byte_order, 0, scaling_factor);
|
2020-11-15 16:09:44 +08:00
|
|
|
|
|
|
|
return extract_unsigned_integer (buf, len, byte_order);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform various tests of the gdb_mpq::write_fixed_point method. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdb_mpq_write_fixed_point ()
|
|
|
|
{
|
|
|
|
/* Pick an arbitrary factor; this operations is sufficiently trivial
|
|
|
|
with the use of GMP that the value of this factor is not really
|
|
|
|
all that important. */
|
|
|
|
gdb_mpq scaling_factor;
|
|
|
|
mpq_set_ui (scaling_factor.val, 1, 3);
|
|
|
|
|
|
|
|
gdb_mpq vq;
|
|
|
|
|
|
|
|
/* Try a few multiples of the scaling factor, both negative,
|
|
|
|
and positive... */
|
|
|
|
|
|
|
|
SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_BIG) == -24);
|
|
|
|
SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_LITTLE) == -24);
|
|
|
|
|
|
|
|
SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_BIG) == -2);
|
|
|
|
SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_LITTLE) == -2);
|
|
|
|
|
|
|
|
SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_BIG) == 0);
|
|
|
|
SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 0);
|
|
|
|
|
|
|
|
SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_BIG) == 5);
|
|
|
|
SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void _initialize_gmp_utils_selftests ();
|
|
|
|
|
|
|
|
void
|
|
|
|
_initialize_gmp_utils_selftests ()
|
|
|
|
{
|
|
|
|
selftests::register_test ("gdb_mpz_as_integer",
|
|
|
|
selftests::gdb_mpz_as_integer);
|
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
|
|
|
selftests::register_test ("gdb_mpz_as_integer_out_of_range",
|
|
|
|
selftests::gdb_mpz_as_integer_out_of_range);
|
2020-11-15 16:09:44 +08:00
|
|
|
selftests::register_test ("gdb_mpz_read_all_from_small",
|
|
|
|
selftests::gdb_mpz_read_all_from_small);
|
|
|
|
selftests::register_test ("gdb_mpz_read_min_max",
|
|
|
|
selftests::gdb_mpz_read_min_max);
|
|
|
|
selftests::register_test ("gdb_mpz_write_all_from_small",
|
|
|
|
selftests::gdb_mpz_write_all_from_small);
|
|
|
|
selftests::register_test ("gdb_mpz_write_min_max",
|
|
|
|
selftests::gdb_mpz_write_min_max);
|
|
|
|
selftests::register_test ("gdb_mpq_read_fixed_point",
|
|
|
|
selftests::gdb_mpq_read_fixed_point);
|
|
|
|
selftests::register_test ("gdb_mpq_write_fixed_point",
|
|
|
|
selftests::gdb_mpq_write_fixed_point);
|
|
|
|
}
|