Last edited one year ago

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 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. 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 occurs if a patch, that is 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 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 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.

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

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

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

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

4.3. A 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 an application with these two defines, the behavior can be different:

  • "successful" if the application does not deal with time or is 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.

The following sub-chapters present some build failures that may happen.

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

The glibc added a time64 alias for ioctl but some applications can still look for ioctl function while the function as be renammed inoctl_time64 when define XXXX is used during compilation. A patch could be proposed as shown 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 also be defined. But some applications disable this. A quick patch can be proposed as shown below:

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

Sometimes, removing the "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