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