How to prevent the year 2038 bug

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]

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. 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]

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 occurs if a patch, that is explained later, is not applied into the software.

3 BSP components[edit]

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]

There is no universal solution to fix this issue. To fix this 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]

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.

Here is 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]

The following example displays the size of the structure int, long long int and time_t. It also gets 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]

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 this example, the date is set to 01/19/2038 at 03:14:07. The binary is then 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 is back to 1970.

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 still wrong and does not change anymore.

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

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 this example, the date is set to 01/19/2038 at 03:14:07. The binary is then 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 additional 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 continues to run.

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

4.2 Compile all the codes in Yocto with required defines[edit]

To define these flags when compiling all the codes in Yocto, add the following line in the file layers/meta-st/meta-st-openstlinux/conf/distro/include/openstlinux.inc.

require conf/distro/include/time64.inc