How to prevent the year 2038 bug

Revision as of 15:08, 13 June 2023 by Registered User
Applicable for STM32MP13x lines, STM32MP15x lines

This article explains the time bug which will happen in 2038 on some Linux® systems. It also gives clues to fix the problem.

1. Introduction[edit source]

The Year 2038 problem is a time formatting bug in computer systems with representing times after 03:14:07 UTC on 19 January 2038. To summarize, this bug will happen on Unix-like system (so on Linux®) because, on 32-bits platforms, the time is coded on a signed 32-bits integer, and at 03:14:07 UTC on 19 January 2038, it will loop and then be understood as 20:45:52 UTC on 13 December 1901.

2. Behavior of OpenSTLinux distribution in 2038[edit source]

On OpenSTLinux distribution, in the event of that year 2038 problem, a watchdog will be fired.

Tue Jan 19 03:14:07 UTC 2038                                                    
[  164.856181] systemd-journald[283]: Assertion 'clock_gettime(map_clock_id(clo.
Thu Jan  1 00:00:01 UTC 1970                                                    
[  165.428314] watchdog: watchdog0: watchdog did not stop!                      
Thu Jan  1 00:00:01 UTC 1970                                                    
Thu Jan  1 00:00:01 UTC 1970                                                    
Thu Jan  1 00:00:01 UTC 1970                                                                                             [  172.837733] systemd-coredump[1036]: Process 283 (systemd-journal) of user 0 .
[  172.844733] systemd-coredump[1036]: Coredump diverted to /var/lib/systemd/coz
Thu Jan  1 00:00:01 UTC 1970
[  183.125664] systemd-coredump[1037]: Failed to log coredump: Connection refusd
Thu Jan  1 00:00:01 UTC 1970                                                    
Thu Jan  1 00:00:01 UTC 1970
INFO:    CPU 0 IT Watchdog 2                                                    
INFO:    CPU : 0       

From this log, we can see the date is "1970" (and not "1901") and does not change anymore. This is caused by the fact that some executables on the system already support the time on a signed 64-bits integer whereas some others still don't support such format.
Such behavior will occur if patch, explained later, is not applied into the software.

3. BSP components[edit source]

OpenSTLinux BSP components (OP-TEE OS, TF-A, U-Boot, Linux® kernel) already support 64-bits coded time instead of 32-bits coded time.

4. User space[edit source]

There is no universal solution to fix that issue. To fix that issue at user space level, the solution is a combination of build options and applications source code modifications including Makefile.

4.1. GNU C Library required version[edit source]

Since GNU C Library version 2.34, the support of 64-bits coded time is already implemented. But, by default, to keep compatibility, all applications built with the GNU C Library, use a 32-bits coded time. To use a 64-bits time format, two "define" options must be set on build command line for applications: -D_TIME_BITS=64 and -D_FILE_OFFSET_BITS=64.

Let's try with an example to see the difference when a binary is built with and without these 2 defines.

4.1.1. get_date.c (an example)[edit source]

Following example displays the size of the structure int, long long int and time_t. It also get the date from the system and then displays it.

// Code based on example found here: https://www.blaess.fr/christophe/2038/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
	time_t now;
	struct tm *tm_now;
	char line[1024];

	printf("sizeof(int): %d\n",sizeof(int));
	printf("sizeof(long long int): %d\n",sizeof(long long int));
	printf("sizeof(time_t): %d\n",sizeof(time_t));

	time(&now);
	tm_now = gmtime(&now);
	strftime(line, 1023, "%x %X", tm_now);
	printf("%s\n", line);

	return 0;
}
4.1.1.1. Cross-compile without required defines[edit source]

It can be compiled without the two defines mentioned above, so it uses the 32-bits coded time.

$CC -Wall get_date.c -o get_date_32b

On that example, the date has been set to 01/19/2038 at 03:14:07. Then the binary is executed on the platform.

 ./get_date_32b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 4
01/19/38 03:14:07

The time is displayed correctly.

After one second, the program is executed again.

 ./get_date_32b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 4
01/01/70 00:00:00

The time is wrong, it's back to 1970.

After one seconds the program is executed again.

 ./get_date_32b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 4
01/01/70 00:00:00

The time is still wrong and does not change anymore.

4.1.1.2. Cross-compile one example with required defines[edit source]

It can be compiled with the two defines mentioned above, so it uses the 64-bits coded time.

$CC -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 -Wall get_date.c -o get_date_64b

On that example, the date has been set to 01/19/2038 at 03:14:07. Then the binary is executed on the platform.

 ./get_date_64b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 8
01/19/38 03:14:07

The time is displayed correctly.

After one second, the program is executed again.

 ./get_date_64b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 8
01/19/38 03:14:08

The time is correct.

After one more second, the program is executed again.

 ./get_date_64b
sizeof(int): 4
sizeof(long long int): 8
sizeof(time_t): 8
01/19/38 03:14:09

The time is correct and continue to increase.

The first executable cannot read correctly the time, whereas the second can.

4.2. Compile all code in Yocto with required defines[edit source]

To define these flags when compiling all code in Yocto, add following line in the file conf/local.conf.

TARGET_CPPFLAGS += "-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64"

4.3. Few examples of known issues during build in user space software[edit source]

Some known issues with using defines _TIME_BITS and _FILE_OFFSET_BITS.

When building application with these two defines, behavior can be different:

  • successful if the application does not deal with time or already ported to support 64-bits time format
  • unsuccessful if the application using time function is not yet ready for a 64-bits coded time

Following sub-chapters will present some build failures that can happen.

4.3.1. Build issue due to ioclt alias[edit source]

The glibc added a time64 alias for ioctl but some application can still look for ioctl function while the function as be renammed inoctl_time64 when define XXXX is used during compilation. An idea of patch could be proposed below:

 libtswrap_la_LDFLAGS = \
-       -export-symbols-regex ^ioctl \
+       -export-symbols-regex ^__ioctl_time64 \
4.3.1.1. _FILE_OFFSET_BITS[edit source]

When the flag _TIME_BITS is defined, the flag _FILE_OFFSET_BITS must be defined too. But some application disable it. A quick patch could be proposed below:

-#    undef _FILE_OFFSET_BITS
+/*#    undef _FILE_OFFSET_BITS*/

But sometimes, to remove that "undef" is not enough and can bring new build issues, as shown below:

| {standard input}: Assembler messages:
| {standard input}:197: Error: symbol `open64' is already defined
| {standard input}:437: Error: symbol `mmap64' is already defined