Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 12 Jan 2022 03:00:31 -0800
From:      Mark Millard <marklmi@yahoo.com>
To:        =?utf-8?Q?Jan_Kokem=C3=BCller?= <jan.kokemueller@gmail.com>
Cc:        Stefan Esser <se@FreeBSD.org>, bugs@openbsd.org, freebsd-current <freebsd-current@freebsd.org>, Baptiste Daroussin <bapt@FreeBSD.org>
Subject:   Re: UBSAN report for main [so: 14] /usr/bin/whatis: non-zero (48) and zero offsets from null pointer in qsort.c
Message-ID:  <58A5D64F-1157-410B-90A2-3F5F0D31980D@yahoo.com>
In-Reply-To: <80e1f514-c0b3-cf79-ea6f-8c62cb1db386@gmail.com>
References:  <A4577E70-AB32-450F-A3F6-A2B42B09A1B3.ref@yahoo.com> <A4577E70-AB32-450F-A3F6-A2B42B09A1B3@yahoo.com> <35333abc-9d4a-4b78-586d-1e869df4f9d4@FreeBSD.org> <BEFB4665-F32B-4AA0-BE4A-5ABB8B973012@yahoo.com> <7babd754-6dab-223a-7bfd-ff06f10c71e2@FreeBSD.org> <80e1f514-c0b3-cf79-ea6f-8c62cb1db386@gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On 2022-Jan-11, at 23:50, Jan Kokem=C3=BCller =
<jan.kokemueller@gmail.com> wrote:

> On 11.01.22 22:08, Stefan Esser wrote:
>> diff --git a/lib/libc/stdlib/qsort.c b/lib/libc/stdlib/qsort.c
>> index 5016fff7895f..51c41e802330 100644
>> --- a/lib/libc/stdlib/qsort.c
>> +++ b/lib/libc/stdlib/qsort.c
>> @@ -108,6 +108,8 @@ local_qsort(void *a, size_t n, size_t es, cmp_t =
*cmp, void
>> *thunk)
>> 	int cmp_result;
>> 	int swap_cnt;
>>=20
>> +	if (__predict_false(a =3D=3D NULL))
>> +		return;
>> loop:
>> 	swap_cnt =3D 0;
>> 	if (n < 7) {
>>=20
>> This would also work to prevent the NULL pointer arithmetik for
>> ports that might also path a =3D=3D NULL and n =3D=3D 0 in certain =
cases.
>=20
> The UB happens in this line, when "a =3D=3D NULL" and "n =3D=3D 0", =
right?
>=20
>    for (pm =3D (char *)a + es; pm < (char *)a + n * es; pm +=3D es)
>=20
> This is arithmetic on a pointer (the NULL pointer) which is not part =
of an
> array, which is UB.
>=20
> Then, wouldn't "if (__predict_false(n =3D=3D 0))" be more appropriate =
than checking
> for "a =3D=3D NULL" here? Testing for "a =3D=3D NULL" might suppress =
UBSAN warnings of
> valid bugs, i.e. when "qsort" is called with "a =3D=3D NULL" and "n !=3D=
 0". In that
> case UBSAN _should_ trigger.
>=20
> UBSAN should not trigger when n =3D=3D 0, though. At least, when "a" =
does point to
> a valid array. But what about the case of "a =3D=3D NULL && n =3D=3D =
0"? Is that deemed
> UB? It looks like at least FreeBSD's "qsort_s" implementation says =
it's legal.
>=20
> a !=3D NULL (pointing to valid array), n !=3D 0  ->  "normal" case, no =
UB
> a !=3D NULL (pointing to valid array), n =3D=3D 0  ->  should not =
trigger UB, and
>                                                 doesn't in the current
>                                                 implementation
> a =3D=3D NULL, n =3D=3D 0                            ->  should not =
trigger UB?
>                                                 (debatable)
>=20
> So if "a =3D=3D NULL && n =3D=3D 0" was deemed legal, then there would =
be no bug in
> "mansearch.c", right?
>=20

ISO/IEC 9899:2011 (E) is not explicit about such things for
qsort, nor is POSIX as I remember: POSIX states that in cases
of disagreement it defers to a C standard, if I remember right.

But ISO/IEC 9899:2011 (E) is somewhat explicit for qsort_s:
(parameters: base, nmemb, size, and compar in that order)

QUOTE
If nmemb is not equal to zero,  then nether base nor compar
shall be a null pointer.
END QUOTE

But there are no words about nmemb=3D=3D0 relative to either of:

base vs. NULL
compar vs. NULL

So far as I can tell, the implementation is free to treat
nmemb=3D=3D0 && (base=3D=3DNULL||compar=3D=3DNULL) as a =
"runtime-constraint
violation" for qsort_s and to return a non-zero value --or to
not do so and return zero.

As qsort does not return a value, any rejection of such a
combination for qsort would be in a more drastic form, making
such an unlikely choice. (qsort is not documented to assign
errno either.)

So I would expect qsort to avoid involving undefined behavior
when nmemb=3D=3D0 && (base=3D=3DNULL||compar=3D=3DNULL) but to not =
reject
the condition. I do not take doing a well-defined "no-op" as
a rejection for my wording here.


=3D=3D=3D
Mark Millard
marklmi at yahoo.com




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?58A5D64F-1157-410B-90A2-3F5F0D31980D>