The ls(1) (with -l option) and find(1) (with -ls option) utilties segment fault when operating on files with very large modification times. A recent disk corruption set a spurious bit in the mtime field of one of my files to 0x8000000630b0167 (576460753965089127) which is in year 18,266,940,962. I discovered the problem when running fsck_ffs(8) which uses ctime(3) to convert it to a readable format. Ctime cannot fit the year into its four character field, so returns ??? ??? ?? ??:??:?? ???? (typically Thu Nov 24 18:22:48 2021). With the filesystem mounted, I used `ls -l' to see how it would report the modification time and it segment faulted. The find(1) program also segment faulted (see script below). Both these utilities call the localtime(3) function to decode the modification time. Localtime(3) returns a pointer to a struct tm (which breaks things out into its component pieces: year, month, day, hour, minute, second). The ls(1) and find(1) utilities then print out the date based on the appropriate fields in the returned tm structure. Although not documented in the localtime(3) manual page, localtime(3) returns a NULL pointer if the passed in time translates to a year that will not fit in an "int" (which if "int" is 32-bits cannot hold the year 18,266,940,962). Since ls(1) and find(1) do not check for a NULL struct tm * return from localtime(3), they segment fault when they try to dereference it. When localtime(3) returns NULL, the attached patches produce a date string of "bad date val". This string is chosen because it has the same number of characters (12) and white spaces (2) as the usual date string, for example "Sep 3 22:06" or "May 15 2017". The most recent ANSI standard for localtime(3) does say that localtime(3) can return NULL (see https://pubs.opengroup.org/onlinepubs/9699919799/ and enter localtime in the search box). Our localtime(3) man page should be updated to indicate that NULL is a possible return. More importantly, there are over 100 uses of localtime(3) in the FreeBSD source tree (see Differential Revision D36474 for the list). Most do not check for a NULL return from localtime(3). Reported by: Peter Holm Reviewed by: kib, Chuck Silvers, Warner Losh MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D36474
127 lines
3.7 KiB
C
127 lines
3.7 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (c) 1989, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#if 0
|
|
static char sccsid[] = "@(#)ls.c 8.1 (Berkeley) 6/6/93";
|
|
#endif
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fts.h>
|
|
#include <grp.h>
|
|
#include <inttypes.h>
|
|
#include <langinfo.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "find.h"
|
|
|
|
/* Derived from the print routines in the ls(1) source code. */
|
|
|
|
static void printlink(char *);
|
|
static void printtime(time_t);
|
|
|
|
void
|
|
printlong(char *name, char *accpath, struct stat *sb)
|
|
{
|
|
char modep[15];
|
|
|
|
(void)printf("%6ju %8"PRId64" ", (uintmax_t)sb->st_ino, sb->st_blocks);
|
|
(void)strmode(sb->st_mode, modep);
|
|
(void)printf("%s %3ju %-*s %-*s ", modep, (uintmax_t)sb->st_nlink,
|
|
MAXLOGNAME - 1,
|
|
user_from_uid(sb->st_uid, 0), MAXLOGNAME - 1,
|
|
group_from_gid(sb->st_gid, 0));
|
|
|
|
if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode))
|
|
(void)printf("%#8jx ", (uintmax_t)sb->st_rdev);
|
|
else
|
|
(void)printf("%8"PRId64" ", sb->st_size);
|
|
printtime(sb->st_mtime);
|
|
(void)printf("%s", name);
|
|
if (S_ISLNK(sb->st_mode))
|
|
printlink(accpath);
|
|
(void)putchar('\n');
|
|
}
|
|
|
|
static void
|
|
printtime(time_t ftime)
|
|
{
|
|
char longstring[80];
|
|
static time_t lnow;
|
|
const char *format;
|
|
static int d_first = -1;
|
|
struct tm *tm;
|
|
|
|
#ifdef D_MD_ORDER
|
|
if (d_first < 0)
|
|
d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
|
|
#endif
|
|
if (lnow == 0)
|
|
lnow = time(NULL);
|
|
|
|
#define SIXMONTHS ((365 / 2) * 86400)
|
|
if (ftime + SIXMONTHS > lnow && ftime < lnow + SIXMONTHS)
|
|
/* mmm dd hh:mm || dd mmm hh:mm */
|
|
format = d_first ? "%e %b %R " : "%b %e %R ";
|
|
else
|
|
/* mmm dd yyyy || dd mmm yyyy */
|
|
format = d_first ? "%e %b %Y " : "%b %e %Y ";
|
|
if ((tm = localtime(&ftime)) != NULL)
|
|
strftime(longstring, sizeof(longstring), format, tm);
|
|
else
|
|
strlcpy(longstring, "bad date val ", sizeof(longstring));
|
|
fputs(longstring, stdout);
|
|
}
|
|
|
|
static void
|
|
printlink(char *name)
|
|
{
|
|
ssize_t lnklen;
|
|
char path[MAXPATHLEN];
|
|
|
|
if ((lnklen = readlink(name, path, MAXPATHLEN - 1)) == -1) {
|
|
warn("%s", name);
|
|
return;
|
|
}
|
|
path[lnklen] = '\0';
|
|
(void)printf(" -> %s", path);
|
|
}
|