[PATCH] stat: fix negative %X/%Y/%Z timestamps after Y2038 on 32-bit builds

Benjamin Robin via busybox <[email protected]>
Newsgroups gmane.linux.busybox
Message-ID <20260822-fix-stat-seconds-since-epoch-v1-1-8446c0e92abe@bootlin.com>
Introduce a utility function to always print `time_t` values using either
`long long` or `unsigned long long`, depending on whether `time_t` is
signed. This fixes the bug reported in [1].

Previously, `time_t` values were cast to `long`, which is a 32-bit value
on 32-bit builds.

[1]: https://lists.busybox.net/pipermail/busybox/2026-August/092413.html

Signed-off-by: Benjamin Robin <[email protected]>
---
 coreutils/stat.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/coreutils/stat.c b/coreutils/stat.c
index 2c2909e7e45b..e504ab1ab7f2 100644
--- a/coreutils/stat.c
+++ b/coreutils/stat.c
@@ -303,12 +303,25 @@ static void FAST_FUNC print_statfs(char *pformat, const char m,
 }
 #endif
 
+static void FAST_FUNC print_time_t(char *pformat, time_t val)
+{
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+	if (TYPE_SIGNED(time_t)) {
+		strcat(pformat, "lld");
+		printf(pformat, (long long) val);
+	}
+	else {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) val);
+	}
+}
+
 /* print stat info */
 static void FAST_FUNC print_stat(char *pformat, const char m,
 		const char *const filename, const void *data
 		IF_SELINUX(, security_context_t scontext))
 {
-#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
 	struct stat *statbuf = (struct stat *) data;
 	struct passwd *pw_ent;
 	struct group *gw_ent;
@@ -382,20 +395,15 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
 	} else if (m == 'x') {
 		printfs(pformat, human_time(&statbuf->st_atim));
 	} else if (m == 'X') {
-		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
-		/* note: (unsigned long) would be wrong:
-		 * imagine (unsigned long64)int32 */
-		printf(pformat, (long) statbuf->st_atime);
+		print_time_t(pformat, statbuf->st_atime);
 	} else if (m == 'y') {
 		printfs(pformat, human_time(&statbuf->st_mtim));
 	} else if (m == 'Y') {
-		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
-		printf(pformat, (long) statbuf->st_mtime);
+		print_time_t(pformat, statbuf->st_mtime);
 	} else if (m == 'z') {
 		printfs(pformat, human_time(&statbuf->st_ctim));
 	} else if (m == 'Z') {
-		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
-		printf(pformat, (long) statbuf->st_ctime);
+		print_time_t(pformat, statbuf->st_ctime);
 # if ENABLE_SELINUX
 	} else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
 		printfs(pformat, scontext);

---
base-commit: 7473045ad3504db9b421427a452fd9b146346306
change-id: 20260822-fix-stat-seconds-since-epoch-b80c5eb17371

Best regards,
--  
Benjamin Robin <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.