openresty / openresty/openresty
[Windows x64] Log timestamps fall back to 1970 after 2038 due to 32-bit `timeval.tv_sec` overflow
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 14k
- Forks
- 1.6k
- PR merge metrics
- No merged PRs in 30d
Description
Summary
On Windows x64, OpenResty log timestamps fall back to a date around 1970 when the Windows system clock is set past 2038-01-19 03:14:07 UTC.
The tested build has a 64-bit time_t, but the Win32 implementation of ngx_gettimeofday() stores Unix seconds through a 32-bit long. At 2038-01-19 03:14:08 UTC, the conversion overflows to a negative value. ngx_gmtime() later clamps that negative value to zero, so the cached log time is formatted as the Unix Epoch.
Environment
- OpenResty: 1.31.1.1
- Bundled nginx core: 1.31.1
- ngx_lua: 0.10.31rc5
- OpenSSL: 3.1.5
- Compiler: GCC 16.1.0, MSYS2 MinGW64
- Build options:
-O2, 64-bit target - Operating system: Windows 25H2 x64, build 26200.9168
sizeof(time_t): 8sizeof(long): 4
Relevant nginx -V output:
nginx version: openresty/1.31.1.1
built by gcc 16.1.0 (Rev6, Built by MSYS2 project)
built with OpenSSL 3.1.5 30 Jan 2024
Minimal configuration
worker_processes 1;
error_log logs/error.log notice;
events {
worker_connections 64;
}
http {
log_format y2038 '$time_iso8601 $msec "$request" $status';
access_log logs/access.log y2038;
server {
listen 18080;
location / {
return 200 "ok\n";
}
}
}
Steps to reproduce
- Stop OpenResty.
- Set the Windows system clock to
2039-01-01or another date after the 2038 boundary. Automatic time synchronization may need to be disabled temporarily. - Start OpenResty with the configuration above.
- Run
curl.exe http://127.0.0.1:18080/. - Inspect
logs/access.logandlogs/error.log. - Restore automatic time synchronization after the test.
Expected behavior
The log entries should contain the configured system date in 2039.
Actual behavior
The log timestamp falls back to a date around 1970. The exact displayed hour depends on the local time zone and log format, but the year is 1970 instead of 2039.
Root cause
The Win32 implementation obtains a correct 64-bit FILETIME, but converts the seconds to long when filling struct timeval:
intervals -= 116444736000000000;
tp->tv_sec = (long) (intervals / 10000000);
tp->tv_usec = (long) ((intervals % 10000000) / 10);
Windows uses the LLP64 data model, so long remains 32 bits in a 64-bit process. Although time_t is 64 bits in this build, both the cast and the Win32 struct timeval.tv_sec field truncate the Unix timestamp to 32 bits.
At the boundary:
2038-01-19 03:14:08 UTC = 2147483648 Unix seconds
the converted value becomes negative. ngx_gmtime() in src/core/ngx_times.c then normalizes it to zero:
if (t < 0) {
t = 0;
}
The observed path is therefore:
GetSystemTimeAsFileTime()
-> correct 64-bit FILETIME
-> ngx_gettimeofday()
-> conversion through 32-bit long
-> negative Unix timestamp
-> ngx_gmtime() clamps the value to zero
-> the cached log time is formatted as a date in 1970
Suggested direction
Avoid storing Unix seconds through long in the Windows implementation. One source-level approach that has been validated locally is to use an nginx-owned timeval type whose seconds field is time_t:
typedef struct {
time_t tv_sec;
long tv_usec;
} ngx_timeval_t;
The Win32 conversion can then assign the seconds to time_t, while Unix keeps its existing ABI internally through an alias:
typedef struct timeval ngx_timeval_t;
This is only a proposed implementation direction. I am happy to adjust the internal API shape based on maintainer feedback, especially if compatibility with third-party modules requires a different approach.
Validation already performed
The source-level fix was tested with the following values:
- Unix Epoch
2038-01-19 03:14:07 UTC2038-01-19 03:14:08 UTC- A timestamp in 2039, including microseconds
2106-02-07 06:28:16 UTC, corresponding to Unix timestamp2^32
The following checks passed on the OpenResty 1.31.1.1 test build:
- Full Windows x64 build
- 8/8 runtime verification cases
- Correct access and error log timestamps after the 2038 boundary
- Offline clean rebuild
- Source archive inventory check
- Object-code inspection at
-O2; the conversion helper was inlined
The implementation adds no system calls, locks, heap allocations, or extra time-update operations.
Upstream nginx tracking
The root cause is in nginx's Win32 platform layer and is tracked upstream:
- nginx Issue: https://github.com/nginx/nginx/issues/1729
- nginx pull request: https://github.com/nginx/nginx/pull/1730
This OpenResty issue records the downstream impact and can track when
the nginx fix is accepted or included in a bundled nginx release.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Inspect the Windows implementation of ngx_gettimeofday() and src/core/ngx_times.c, then compare the proposed internal timeval type with nginx issue 1729 and pull request 1730. Reproduce the post-2038 cases in a Windows x64 build and verify that access and error log timestamps remain correct through the listed boundary and 2039 checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, nginx
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 42/100