Small OK ping for this one. Please test when you have some time. ----- Forwarded message from Renaud Allard <[email protected]> -----
> From: Renaud Allard <[email protected]> > To: [email protected] > Subject: tsort(1): heap buffer overread with embedded null bytes in input > Date: Wed, 1 Apr 2026 08:19:51 +0200 > > The word parser in tsort(1) does not treat null bytes as word > delimiters. When input contains embedded null bytes, they become > part of node name strings passed to ohash. > > This causes a heap buffer overread in ohash_lookup_interval() > at lib/libutil/ohash.c line 244: > > strncmp(stored_key, start, end - start) == 0 && > stored_key[end-start] == '\0' > > When a stored key contains an embedded null (e.g., "a\0bc", 4 > bytes), strncmp treats the null as end-of-string and returns 0 > for any lookup key sharing the same prefix (e.g., "a\0xyz", 5 > bytes). The check stored_key[end-start] then reads past the > stored key's allocation. > > Fix: treat null bytes as word delimiters in read_pairs(), the > same way whitespace characters are handled. This ensures no > node name contains embedded nulls, which is consistent with > how ohash uses strncmp for key comparison. > > Found by AFL++ fuzzing. > > Index: usr.bin/tsort/tsort.c > =================================================================== > RCS file: /cvs/src/usr.bin/tsort/tsort.c,v > retrieving revision 1.38 > diff -u -p -r1.38 tsort.c > --- usr.bin/tsort/tsort.c 18 Jan 2024 15:34:29 -0000 1.38 > +++ usr.bin/tsort/tsort.c > @@ -318,12 +318,13 @@ read_pairs(FILE *f, struct ohash *h, int > char *e; > > while (str < sentinel && > - isspace((unsigned char)*str)) > + (isspace((unsigned char)*str) || *str == '\0')) > str++; > if (str == sentinel) > break; > for (e = str; > - e < sentinel && !isspace((unsigned char)*e); e++) > + e < sentinel && !isspace((unsigned char)*e) && > + *e != '\0'; e++) > continue; > if (toggle) { > a = node_lookup(h, str, e); > ----- End forwarded message -----
