Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 7 Sep 2022 09:55:39 +0200
From:      Hans Petter Selasky <hps@selasky.org>
To:        Xin Li <delphij@FreeBSD.org>, src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   Re: git: c65e42dbde41 - main - libc: add test case for qsort_b(3)
Message-ID:  <9801581e-ac26-c41b-e987-ee3bb954417d@selasky.org>
In-Reply-To: <dfd11cf5-48e8-7eec-deb6-539a4d2d56c3@FreeBSD.org>
References:  <202209070612.2876C6ko054410@gitrepo.freebsd.org> <ca85bb26-0432-adc8-e8bd-24a6f26890b5@selasky.org> <dfd11cf5-48e8-7eec-deb6-539a4d2d56c3@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
On 9/7/22 09:17, Xin Li wrote:
> 
> 
> On 9/6/22 23:58, Hans Petter Selasky wrote:
>> Hi,
>>
>> Could we also have a test that qsort_b() is not sensitive to the sort 
>> pattern it is given? You are aware about how qsort() works?
> 
> Not sure if I'm following...  The current qsort_b is essentially a block 
> version of qsort_r and uses the same code of qsort, so if qsort is 
> sensitive to certain patterns, it is affected too.  The main purpose for 
> this test case was to verify that qsort_b() actually is sorting and not 
> a performance test (e.g. it doesn't check for catastrophic cases).
> 
> Would you mind elaborating a little bit more about what should be tested?
> 
>> In my opinion, qsort() should be removed from the kernel. It does 
> 
> I'm not sure if removing qsort() interface from the kernel is a good 
> idea, because apparently it's being used in a lot of places.  Note that 
> it doesn't have to be the current implementation, we can always replace 
> it with something better if available.
> 
>> sorting, but is not reliable for all kinds of unsorted data! And can 
>> explode into stack usage ...
> 
> Speaking for stack usage, I _think_ I've fixed the qsort(3) code to 
> perform at most log2(N) levels of recursion in 2017 (svn r318515 / 
> ca1578f0), and before the fix it could potentially recurse N levels, no? 
>   Was this the stack explode that you are referring to, or did you mean 
> some other cases that we haven't considered (in which case, it can 
> potentially be SA worthy).
> 

Hi Xin,

I'm not sure about the current state of qsort(), but I have a test 
program which you may want to try and it looks like there may still be a 
CPU hog issue in there!

Just read the attached code to figure out the meaning of the arguments. 
The test program compares mergesort(), qsort() and my mbin_sort() 
algorithm, and also includes an exhaustive test trying all kinds of 
patters within a certain range.

git clone https://github.com/hselasky/libmbin
cd libmbin
make all install

You need to compile and install my libmbin from github before trying this:

# Ask qsort() in libc to sort the vulnerable pattern:

cc -lmbin1 -lm mergesort.c && time ./a.out 10 2 0

0.241u 0.000s 0:00.24 100.0%	5+170k 0+0io 0pf+0w

# Ask mbin_sort() in libmbin to sort the vulnerable pattern:

cc -lmbin1 -lm mergesort.c && time ./a.out 10 2 2

0.086u 0.000s 0:00.08 100.0%	5+181k 0+0io 0pf+0w

# Ask mergesort() in libc to sort the vulnerable pattern:

cc -lmbin1 -lm mergesort.c && time ./a.out 10 2 3

0.075u 0.007s 0:00.08 87.5%	6+207k 0+0io 0pf+0w

The number 10 means 2 in the power of 10 elements. May be raised. See 
attachment!

--HPS
[-- Attachment #2 --]
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math_bin.h>

uint32_t calls;

static int
hps_compare(const void *pa, const void *pb)
{
uintptr_t a = (uintptr_t)*(void **)pa;
uintptr_t b = (uintptr_t)*(void **)pb;

	if (a > b)
		return (1);
	else if (a < b)
		return (-1);

	return (0);
}

static void
hps_bad(void **v, uint32_t n)
{
	uint32_t i;

	for (i = 0; i < n / 2; i++)
		v[i] = (void *)(uintptr_t)(n / 2 - i);
	v[n / 2] = (void *)(uintptr_t)(n / 2 + 1);
	for (i = n / 2 + 1; i < n; i++)
		v[i] = (void *)(uintptr_t)(n + n / 2 + 1 - i);
}

#define	CMP(t,a,b) ((*a < *b) ? -1 : (*a > *b) ? 1 : 0)

#define	swap(a,b) do {				\
void *tmp = *a;					\
*a = *b;					\
*b = tmp;					\
} while(0)

static void
hps_test()
{
	uintptr_t a, b, c, d, e, f, g, h;
	void *array[8];
	void *copy[8];

	for (a = 0; a != 8; a++) {
		for (b = 0; b != 8; b++) {
			for (c = 0; c != 8; c++) {
				for (d = 0; d != 8; d++) {
					for (e = 0; e != 8; e++) {
						for (f = 0; f != 8; f++) {
							for (g = 0; g != 8; g++) {
								for (h = 0; h != 8; h++) {
									array[0] = (void *)(uintptr_t)a;
									array[1] = (void *)(uintptr_t)b;
									array[2] = (void *)(uintptr_t)c;
									array[3] = (void *)(uintptr_t)d;
									array[4] = (void *)(uintptr_t)e;
									array[5] = (void *)(uintptr_t)f;
									array[6] = (void *)(uintptr_t)g;
									array[7] = (void *)(uintptr_t)h;

									memcpy(copy, array, sizeof(copy));

									mbin_sort(array, 8, sizeof(void *), &hps_compare);
									qsort(copy, 8, sizeof(void *), &hps_compare);

									if (memcmp(array, copy, sizeof(array)) != 0)
										printf("%zd %zd %zd %zd %zd %zd %zd %zd\n",
										    (uintptr_t)array[0], (uintptr_t)array[1], (uintptr_t)array[2], (uintptr_t)array[3],
										    (uintptr_t)array[4], (uintptr_t)array[5], (uintptr_t)array[6], (uintptr_t)array[7]);
								}
							}
						}
					}
				}
			}
		}
	}
}

int
main(int argc, char **argv)
{
#ifdef HPS_TEST
	hps_test();
	return (0);
#endif

	if (argc < 4)
		return (0);

	uint32_t LBASE = atoi(argv[1]);
	uint32_t BASE = (1 << LBASE);
	uint32_t algo = atoi(argv[2]);
	uint32_t data = atoi(argv[3]);

	void *array[BASE + BASE];
	unsigned x;
	unsigned y;
	unsigned z;

	for (z = 0; z != 1024; z++) {

		if (data == 0) {
			for (x = 0; x != BASE; x++) {
				if (x > 4 && (random() & 1)) {
					array[BASE + x] = array[BASE + x - 4];
				} else
					array[BASE + x] = (void *)(uintptr_t)((uint32_t)(random() /* % BASE */ ));

				*(short *)&array[BASE + x] = x;
			}
		} else if (data == 1) {
			for (x = 0; x != BASE; x++) {
				array[BASE + x] = (void *)(uintptr_t)(random() % BASE);
			}
		} else {
			hps_bad(array + BASE, BASE);
		}

		if (algo == 0)
			qsort(array + BASE, BASE, sizeof(void *), hps_compare);
		else if (algo == 1)
			qsort(array + BASE, BASE, sizeof(void *), hps_compare);
		else if (algo == 2)
			mbin_sort(array + BASE, BASE, sizeof(void *), hps_compare);
		else
			mergesort(array + BASE, BASE, sizeof(void *), hps_compare);

#ifdef HPS_VERIFY
		memcpy(array, array + BASE, sizeof(array[0]) * BASE);
		for (x = 0; x != BASE; x++) {
			if (x + 1 < BASE &&
			    (uintptr_t)array[BASE + x] > (uintptr_t)array[BASE + x + 1]) {
				printf("WRAP\n");
			}
		}
		for (x = 0; x != BASE; x++) {
			for (y = x + 1; y != BASE; y++) {
				if (array[x] == array[y]) {
					x++;
					if (x != y) {
						printf("SORT ERROR %d %d\n", x, y);
						return (0);
					}
				}
			}
		}
#endif
	}
	return (0);
}

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?9801581e-ac26-c41b-e987-ee3bb954417d>