From: Mark Kettenis Subject: Re: pax(1): localtime(3) can fail To: Florian Obser Cc: tech@openbsd.org Date: Sat, 27 Apr 2024 20:45:56 +0200 > From: Florian Obser > Date: Sat, 27 Apr 2024 16:18:18 +0200 > > A time_t very far in the future (and past) is not representable by > struct tm and localtime(3) returns NULL, leading to a segfault > > Core was generated by `pax'. > Program terminated with signal SIGSEGV, Segmentation fault. > #0 _fmt (format=0x88eb4dfbd4a "b %e %Y", t=0x0, pt=0x761f8811dd40 "\001", > ptlim=0x761f8811dd80 "drwxr-xr-x ", warnp=0x761f8811dd04) > at /usr/src/lib/libc/time/strftime.c:159 > 159 pt = _add((t->tm_mon < 0 || > (gdb) bt > #0 _fmt (format=0x88eb4dfbd4a "b %e %Y", t=0x0, pt=0x761f8811dd40 "\001", > ptlim=0x761f8811dd80 "drwxr-xr-x ", warnp=0x761f8811dd04) > at /usr/src/lib/libc/time/strftime.c:159 > #1 0x0000089112ad9f2a in _libc_strftime (s=0x761f8811dd40 "\001", maxsize=64, > format=, t=0x0) at /usr/src/lib/libc/time/strftime.c:118 > #2 0x0000088eb4e11c6f in ls_list (arcn=0x761f8811ddf0, now=1714210446, > fp=0x89112b7cdc8 <__sF+152>) at /usr/src/bin/pax/gen_subs.c:106 > #3 0x0000088eb4e04010 in list () at /usr/src/bin/pax/ar_subs.c:127 > #4 0x0000088eb4e1cb18 in main (argc=3, argv=0x761f8811f7c8) > at /usr/src/bin/pax/pax.c:326 > (gdb) frame 2 13:09:54 [117/446] > #2 0x0000088eb4e11c6f in ls_list (arcn=0x761f8811ddf0, now=1714210446, > fp=0x89112b7cdc8 <__sF+152>) at /usr/src/bin/pax/gen_subs.c:106 > 106 if (strftime(f_date, sizeof(f_date), TIMEFMT(sbp->st_mtime, now) > , > (gdb) p sbp->st_mtime > value has been optimized out > (gdb) p arcn->sb > $2 = {st_mode = 16877, st_dev = 0, st_ino = 0, st_nlink = 2, st_uid = 1000, > st_gid = 1000, st_rdev = 0, st_atim = {tv_sec = 1714200723, > tv_nsec = 879795667}, st_mtim = {tv_sec = 1714200723888079566, > tv_nsec = 0}, st_ctim = {tv_sec = 1714200723, tv_nsec = 880795660}, > st_size = 0, st_blocks = 0, st_blksize = 0, st_flags = 0, st_gen = 0, > __st_birthtim = {tv_sec = 0, tv_nsec = 0}} > > I found this with afl using the gnu tar test suite as input, I'll attach > a broken test file that triggers the segfault with "pax -vf pax_broken" > > OK? > > diff --git gen_subs.c gen_subs.c > index 42c70804fb7..f3a8cb775a1 100644 > --- gen_subs.c > +++ gen_subs.c > @@ -75,6 +75,7 @@ void > ls_list(ARCHD *arcn, time_t now, FILE *fp) > { > struct stat *sbp; > + struct tm * tm; No space after '*'. > char f_mode[MODELEN]; > char f_date[DATELEN]; > int term; > @@ -103,8 +104,10 @@ ls_list(ARCHD *arcn, time_t now, FILE *fp) > /* > * print file mode, link count, uid, gid and time > */ > - if (strftime(f_date, sizeof(f_date), TIMEFMT(sbp->st_mtime, now), > - localtime(&(sbp->st_mtime))) == 0) > + if ((tm = localtime(&(sbp->st_mtime))) == NULL) > + f_date[0] = '\0'; > + else if (strftime(f_date, sizeof(f_date), TIMEFMT(sbp->st_mtime, now), > + tm) == 0) > f_date[0] = '\0'; > (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, sbp->st_nlink, > NAME_WIDTH, UT_NAMESIZE, user_from_uid(sbp->st_uid, 0), > @@ -146,6 +149,7 @@ ls_list(ARCHD *arcn, time_t now, FILE *fp) > void > ls_tty(ARCHD *arcn) > { > + struct tm * tm; Likewise. > char f_date[DATELEN]; > char f_mode[MODELEN]; > time_t now = time(NULL); > @@ -153,8 +157,10 @@ ls_tty(ARCHD *arcn) > /* > * convert time to string, and print > */ > - if (strftime(f_date, DATELEN, TIMEFMT(arcn->sb.st_mtime, now), > - localtime(&(arcn->sb.st_mtime))) == 0) > + if ((tm = localtime(&(arcn->sb.st_mtime))) == NULL) > + f_date[0] = '\0'; > + else if (strftime(f_date, DATELEN, TIMEFMT(arcn->sb.st_mtime, now), > + tm) == 0) > f_date[0] = '\0'; > strmode(arcn->sb.st_mode, f_mode); > tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name); > > > [2:application/octet-stream Show Save:pax_broken (10kB)] > > > [3:text/plain Hide] > > > -- > In my defence, I have been left unsupervised.