pkgsrc-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

pkg/60712: net/trickle: Memory fault crash on NetBSD



>Number:         60712
>Category:       pkg
>Synopsis:       net/trickle: Memory fault crash on NetBSD
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    pkg-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Sep 10 23:25:00 +0000 2026
>Originator:     Showta Ishizaki
>Release:        NetBSD 11.0, pkgsrc CVS as of 2026-09-01
>Organization:
>Environment:
System: NetBSD 11.0 amd64
Architecture: x86_64
Machine: amd64
>Description:
	net/trickle segfaults on NetBSD as soon as the program it wraps
	creates a socket:

	  $ printf '#include <sys/socket.h>\nint main(void){return socket(AF_INET,SOCK_STREAM,0)<0;}\n' > s.c
	  $ cc -o s s.c
	  $ trickle -d 10 ./s
	  Segmentation fault (core dumped)

	The crash is in the LD_PRELOAD library trickle-overload.so.  I built
	trickle from source on NetBSD 9.4, 10.1 and 11.0 (all amd64) and it
	crashes on each, both with the pkgsrc binary package and from source.

	pkgsrc ships trickle-1.06; the last upstream release is 1.07 (2004)
	and trickle is otherwise unmaintained.  The crash is in the pkgsrc
	1.06 package; a plain 1.07 does not even build on NetBSD 11 (a
	conflicting poll() declaration), which the patch below also fixes.

	A patch posted to netbsd-users on 2025-02-20 fixes it:

	  https://mail-index.netbsd.org/netbsd-users/2025/02/20/msg032328.html

	On NetBSD the socket() and select() wrappers are exported as
	__socket30 / __select50 (the headers rename them), but the overload
	library resolves the real functions with dlsym("socket") /
	dlsym("select") -- the wrong, plain symbols.  The patch resolves
	__socket30 / __select50 on NetBSD, masks SOCK_NONBLOCK and SOCK_CLOEXEC
	out of the socket type, adds NetBSD to the poll() conditionals, and
	fixes signedness in bwstat_getdelay().  I did not isolate which of
	these is the segfault, but with the whole patch the test above does
	not crash across repeated runs.

	I verified this on NetBSD 11.0: 1.07 plus the patch builds and does
	not crash (5/5 runs, both the socket test above and fetching a file
	with ftp(1) under trickle).  The version bump alone is not enough --
	a plain 1.07 does not build on NetBSD 11, and the patch is written
	against 1.07 and does not apply cleanly to 1.06 -- which is why the
	fix is an update to 1.07 plus the patch.

>How-To-Repeat:
	Build net/trickle (currently trickle-1.06) on NetBSD and run any
	socket-using program under it.  The smallest reproduction:

	  $ printf '#include <sys/socket.h>\nint main(void){return socket(AF_INET,SOCK_STREAM,0)<0;}\n' > s.c
	  $ cc -o s s.c
	  $ trickle -d 10 ./s
	  Segmentation fault (core dumped)

>Fix:
	Update net/trickle to 1.07 and carry the netbsd-users patch as
	patches/patch-bwstat.c and patches/patch-trickle-overload.c.  I built
	the result on NetBSD 11.0: all patches apply, it builds, and the test
	above does not crash (5/5 runs).

	  - Makefile: DISTNAME trickle-1.07, drop PKGREVISION.  The 1.07
	    distfile is on the existing MASTER_SITES
	    (https://monkey.org/~marius/trickle/).
	  - distinfo: 1.07 checksums (BLAKE2s/SHA512/Size) plus SHA1 for the
	    three patches.
	  - patch-aa is dropped (cvs rm): patch-trickle-overload.c already adds
	    NetBSD to the same poll() conditional that patch-aa fixed.
	  - patch-Makefile.in is regenerated against 1.07 (same .WAIT change).

	The full change is below: a two-line Makefile edit, a regenerated
	distinfo, and three patch files (patch-bwstat.c and
	patch-trickle-overload.c are new, patch-Makefile.in replaces the 1.06
	one).  patch-aa is removed.

	  cvs rm patches/patch-aa
	  cvs add patches/patch-bwstat.c patches/patch-trickle-overload.c

=== Makefile (patch -p0) ===
--- Makefile.orig
+++ Makefile
@@ -1,6 +1,5 @@
 # $NetBSD: Makefile,v 1.31 2023/10/24 22:10:36 wiz Exp $
 #
-DISTNAME=	trickle-1.06
-PKGREVISION=	17
+DISTNAME=	trickle-1.07
 CATEGORIES=	net
 MASTER_SITES=	https://monkey.org/~marius/trickle/

=== distinfo (regenerate with "make makesum makepatchsum"; values are) ===
$NetBSD$

BLAKE2s (trickle-1.07.tar.gz) = 8f81357d86379678af87334aaed2db32e594717d7eb4e819c81c25c60167fb69
SHA512 (trickle-1.07.tar.gz) = 08ede3230df849189b6c47e4cbd5be1f3b8e91bd7254219201c00f9ab44cf4c69055a0ca490f55877a28cb5d46554a80cb865390c27b986171300875bcd69824
Size (trickle-1.07.tar.gz) = 308863 bytes
SHA1 (patch-Makefile.in) = e8063def0191699998208b9b1370035a95db3d17
SHA1 (patch-bwstat.c) = 8718635338459d61d1c3ccd12f749ea6d3d205b6
SHA1 (patch-trickle-overload.c) = b9200719970df0f63d1bc02d604a485f54be4bd3

=== patches/patch-Makefile.in (regenerated for 1.07) ===
$NetBSD$

Force the programs to be built before the man pages and data so a
parallel build does not race.

--- Makefile.in.orig
+++ Makefile.in
@@ -547,7 +547,7 @@
 uninstall-am: uninstall-binPROGRAMS uninstall-man uninstall-overloadDATA \
 		uninstall-trickleoverloadDATA
 uninstall: uninstall-am
-all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) config.h
+all-am: Makefile $(PROGRAMS) .WAIT $(MANS) $(DATA) config.h
 all-redirect: all-am
 install-strip:
 	$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install

=== patches/patch-bwstat.c (new) ===
$NetBSD$

bwstat_getdelay() does its pool/npts pool-sharing arithmetic in
unsigned ints, which underflow to huge values; make ncli, pool and
xent signed and stop at xent <= 0.  Also initialise bs->pts in
bwstat_alloc().  Part of a patch posted to netbsd-users on 2025-02-20
that fixes the Memory fault crash on NetBSD.

--- bwstat.c.orig
+++ bwstat.c
@@ -69,6 +69,7 @@
 	if ((bs = calloc(1, sizeof(*bs))) == NULL)
 		return (NULL);
 
+	bs->pts = 1;
 	TAILQ_INSERT_TAIL(&statq, bs, next);
 
 	return (bs);
@@ -143,7 +144,8 @@
 struct timeval *
 bwstat_getdelay(struct bwstat *bs, size_t *len, uint lim, short which)
 {
-	uint rate = 0, ncli = 0, npts = 0, pool = 0, ent, xent;
+	uint rate = 0, npts = 0, ent;
+	int ncli = 0, pool = 0, xent;
 	double delay;
 	static struct timeval tv;
 	struct bwstathead poolq;
@@ -201,7 +203,7 @@
 		if (ncli > 0) {
 			xent = pool / npts;
 
-			if (xent == 0)
+			if (xent <= 0)
 				break;
 
 			TAILQ_FOREACH(xbs, &poolq, qnext)

=== patches/patch-trickle-overload.c (new) ===
$NetBSD$

On NetBSD the socket() and select() interceptors resolved the wrong
libc symbol: the wrappers are exported as __socket30 / __select50
(the headers rename them), but GETADDR looked up the plain names.
Resolve __socket30 / __select50 on NetBSD, mask SOCK_NONBLOCK and
SOCK_CLOEXEC out of the socket type before the AF_INET/SOCK_STREAM
test, add NetBSD to the poll() platform conditionals, and pass the
timeval by pointer in select_shift().  Part of a patch posted to
netbsd-users on 2025-02-20 that fixes the Memory fault crash on NetBSD.

--- trickle-overload.c.orig
+++ trickle-overload.c
@@ -196,7 +196,12 @@
 
 	GETADDR(write);
 
+#ifdef __NetBSD__
+	if ((libc_socket = dlsym(dh, "__socket30")) == NULL)
+		errx(0, "[trickle] Failed to get __socket30() address");
+#else
 	GETADDR(socket);
+#endif
 /*	GETADDR(setsockopt); */
 	GETADDR(close);
 
@@ -213,7 +218,12 @@
 #endif /* !__FreeBSD__ */
 	GETADDR(sendto);
 
+#ifdef __NetBSD__
+	if ((libc_select = dlsym(dh, "__select50")) == NULL)
+		errx(0, "[trickle] Failed to get __select50() address");
+#else
 	GETADDR(select);
+#endif
 //	GETADDR(poll);
 
 	GETADDR(dup);
@@ -302,6 +312,13 @@
 	    domain, type, protocol, sock);
 #endif /* DEBUG */
 
+#ifdef SOCK_NONBLOCK
+	type &= ~SOCK_NONBLOCK;
+#endif
+#ifdef SOCK_CLOEXEC
+	type &= ~SOCK_CLOEXEC;
+#endif
+
 	if (sock != -1 && domain == AF_INET && type == SOCK_STREAM) {
 		if ((sd = calloc(1, sizeof(*sd))) == NULL)
 			return (-1);
@@ -311,7 +328,6 @@
 		}
 
 		/* All sockets are equals. */
-		sd->stat->pts = 1;
 		sd->stat->lsmooth = lsmooth;
 		sd->stat->tsmooth = tsmooth;
 		sd->sock = sock;
@@ -393,18 +409,14 @@
 }
 
 static struct delay *
-select_shift(struct delayhead *dhead, struct timeval *inittv,
+select_shift(struct delayhead *dhead, struct timeval *difftv,
     struct timeval **delaytv)
 {
-	struct timeval curtv, difftv;
 	struct delay *d;
 	struct sockdesc *sd;
 
-	gettimeofday(&curtv, NULL);
-	timersub(&curtv, inittv, &difftv);
-
 	TAILQ_FOREACH(d, dhead, next) {
-		if (timercmp(&d->delaytv, &difftv, >))
+		if (timercmp(&d->delaytv, difftv, >))
 			break;
 		sd = d->sd;
 
@@ -413,7 +425,7 @@
 	}
 
 	if (d != NULL)
-		timersub(&d->delaytv, &difftv, *delaytv);
+		timersub(&d->delaytv, difftv, *delaytv);
 	else 
 		*delaytv = NULL;
 
@@ -431,8 +443,8 @@
 {
 	struct sockdesc *sd;
 	fd_set *fdsets[] = { wfds, rfds }, *fds;
-	struct timeval *delaytv, *selecttv = NULL, *timeout = NULL, _timeout,
-	    inittv, curtv, difftv;
+	struct timeval *delaytv, _delaytv, *selecttv = NULL, *timeout = NULL,
+	    _timeout, inittv, curtv, difftv;
 	short which;
 	struct delayhead dhead;
 	struct delay *d, *_d;
@@ -462,15 +474,18 @@
 			    FD_ISSET(sd->sock, fds) &&
 			    select_delay(&dhead, sd, which)) {
 				FD_CLR(sd->sock, fds);
-				nfds--;
 			}
 
 	gettimeofday(&inittv, NULL);
 	curtv = inittv;
 	d = TAILQ_FIRST(&dhead);
-	delaytv = d != NULL ? &d->delaytv : NULL;
+	if (d != NULL) {
+		_delaytv = d->delaytv;
+		delaytv = &_delaytv;
+	} else
+		delaytv = NULL;
+	timersub(&curtv, &inittv, &difftv);
  again:
-	timersub(&inittv, &curtv, &difftv);
 	selecttv = NULL;
 
 	if (delaytv != NULL)
@@ -498,15 +513,15 @@
 #endif /* DEBUG */
 
 	if (ret == 0 && delaytv != NULL && selecttv == delaytv) {
-		_d = select_shift(&dhead, &inittv, &delaytv);
+		gettimeofday(&curtv, NULL);
+		timersub(&curtv, &inittv, &difftv);
+		_d = select_shift(&dhead, &difftv, &delaytv);
 		while ((d = TAILQ_FIRST(&dhead)) != _d) {
 			FD_SET(d->sd->sock, fdsets[d->which]);
-			nfds++;
 			TAILQ_REMOVE(&dhead, d, next);
 			free(d);
 		}
 
-		gettimeofday(&curtv, NULL);
 		goto again;
 	}
 
@@ -522,10 +537,10 @@
 #define POLL_WRMASK (POLLOUT | POLLWRNORM | POLLWRBAND)
 #define POLL_RDMASK (POLLIN | /* POLLNORM | */  POLLPRI | POLLRDNORM | POLLRDBAND)
 
-#if defined(__linux__) || (defined(__svr4__) && defined(__sun__)) || defined(__OpenBSD__)
+#if defined(__linux__) || (defined(__svr4__) && defined(__sun__)) || defined(__OpenBSD__) || defined(__NetBSD__)
 int
 poll(struct pollfd *fds, nfds_t nfds, int __timeout)
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
 int
 poll(struct pollfd *fds, unsigned int nfds, int __timeout)
 #else
@@ -994,7 +1009,6 @@
 		}
 
 		sd->sock = ret;
-		sd->stat->pts = 1;
 		sd->stat->lsmooth = lsmooth;
 		sd->stat->tsmooth = tsmooth;
 		TAILQ_INSERT_TAIL(&sdhead, sd, next);




Home | Main Index | Thread Index | Old Index