How to prevent the year 2038 bug

Revision as of 09:27, 4 April 2023 by Registered User (→‎get_date.c (an example))
Renaming.png This page is a candidate for renaming (move).
The requested new name is: How to prevent the Year 2038 problem .
The supplied reason is: The name could be better for the understanding .
-- Registered User (-) 14:13, 3 April 2023 (CEST).
Wiki maintainers: remember to update the pages that link this page before renaming (moving) it.
Applicable for STM32MP13x lines, STM32MP15x lines

This article explains the time problem on some Linux system which will happen in 2038. It will also give some 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]

3.1. OP-TEE OS[edit source]

OP-TEE OS already supports time on 64 bits instead of 32bits.

3.2. TF-A[edit source]

TF-A already supports time on 64 bits instead of 32bits.

3.3. U-Boot[edit source]

U-Boot already supports time on 64 bits instead of 32bits.

3.4. Linux® kernel[edit source]

The Linux kernel already supports time on 64 bits instead of 32 bits.

4. User space[edit source]

There is no universal solution to fix that issue. To specify two build option will be the first part of the solution and to fix source code or Makefile will be the second part of the solution.


4.1. Gnu LibC required version[edit source]

Since the version 2.34 of GnuLibC, the support of the time on 64-bits is already implemented. But, by default, to keep compatibility, all applications, built with the Gnu LibC, use a time coded on 32-bits. To use a time format on 64 bits, two "defines" must be set on build line for application: -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 without and, with these 2 defines.

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


Following example will display the size of the structure int, long long int and time_t. It will also get the date from the system and will display 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-compiled without the defines[edit source]

It can be compiled without the two defines mentioned above, so it will use the time coded on 32 bits.

 $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. The time is displayed correctly.

 ./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-compiled with the defines[edit source]

It can be compiled with the two defines mentioned above, so it will use the time coded on 64 bits.

 $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. The time is displayed correctly.

 ./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. Few examples of known issues during build in user space software[edit source]

Some known issues with 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 time format on 64-bits
  • unsuccessful if the application using time function is not yet ready for a time coded on 64-bits.

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

4.2.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 something like that

 libtswrap_la_LDFLAGS = \
-       -export-symbols-regex ^ioctl \
+       -export-symbols-regex ^__ioctl_time64 \
4.2.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 something like that

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

But sometimes, to remove that undef is not enough and can bring new build issues, like these ones.

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