Opened 2 years ago
Closed 20 months ago
#2416 closed defect (fixed)
Memory overrun due to alignment issues when cross-compiling mingw on linux
Reported by: | Orgad Shaneh | Owned by: | |
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | nginx-core | Version: | 1.22.x |
Keywords: | memory security vulnerability | Cc: | Orgad Shaneh |
uname -a: | MINGW64_NT-10.0-19043 | ||
nginx -V: |
nginx version: nginx/1.22.1
built with OpenSSL 1.1.1s 1 Nov 2022 TLS SNI support enabled configure arguments: --crossbuild=win32 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --without-http_scgi_module --without-http_uwsgi_module --with-openssl=../openssl --with-stream --with-stream_ssl_module --with-http_auth_request_module --with-http_sub_module --without-http_auth_basic_module --with-http_gzip_static_module --conf-path=nginx.conf --with-pcre=pcre --with-zlib=zlib-1.2.13 |
Description (last modified by )
Hi,
When I cross-compile nginx for Windows on linux host using mxe, NGX_ALIGNMENT is set to 16. This is set in auto/os/conf:
case "$NGX_MACHINE" in ... *) have=NGX_ALIGNMENT value=16 . auto/define NGX_MACH_CACHE_LINE=32 ;; esac
But the pool is allocated using plain malloc (on Windows target), which is *not* aligned to 16 bytes (it's aligned to 4), so pool->d.end is 4-byte aligned.
Now, in ngx_palloc_small, the following code causes integer underflow, and the comparison doesn't work as expected:
m = p->d.last; if (align) { m = ngx_align_ptr(m, NGX_ALIGNMENT); } if ((size_t) (p->d.end - m) >= size) { /* <------- This condition */ p->d.last = m + size; return m; }
Since m is aligned, and end is not, end - m can be a negative number. Then it is cast to size_t, which is unsigned, and so it just keeps writing past the memory pool, and overruns the next pool.
A minimal patch that solves this issue is:
-
src/core/ngx_palloc.c
a b ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align) 160 160 m = ngx_align_ptr(m, NGX_ALIGNMENT); 161 161 } 162 162 163 if ( (size_t) (p->d.end - m) >= size) {163 if (m + size <= p->d.end) { 164 164 p->d.last = m + size; 165 165 166 166 return m;
But it would be better to assign the right alignment for Windows, even when cross-building. And also ensure that end is aligned correctly.
I also noticed that the default alignment when not set is sizeof(long), which is wrong for Win64. It should be sizeof(intptr_t).
Build command:
CC=i686-w64-mingw32.static-gcc ./auto/configure --crossbuild=win32 --without-http_rewrite_module --without-http_gzip_module $ grep -1 ALIGN objs/ngx_auto_config.h #ifndef NGX_ALIGNMENT #define NGX_ALIGNMENT 16 #endif
Change History (6)
comment:1 by , 2 years ago
Description: | modified (diff) |
---|
comment:2 by , 2 years ago
Priority: | critical → minor |
---|
comment:3 by , 2 years ago
For the record, I've submitted a patch to address this:
https://mailman.nginx.org/pipermail/nginx-devel/2022-December/QMOITMSRB5XIU5EEYOAIOHW34WEISUJS.html
Review and testing appreciated.
comment:6 by , 20 months ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Fix committed and available in nginx 1.23.4 / 1.24.0, closing this. Thanks to all involved.
Thanks for reporting this.
Crossbuilds are not really supported. The
--crossbuild
configure option was used a while ago for compilation of Windows binaries on Unix systems using Wine, mostly for compile-testing, though it is not really supported, and intentionally not documented neither in./configure --help
nor in the documentation. Nevertheless, this issue probably worth fixing.Note though that the condition in ngx_palloc_small() is perfectly correct, but assumes pointers properly aligned at least to NGX_ALIGNMENT (see #686). Further, the suggested patch looks wrong, as
m + size
is not guaranteed to exist, and might overflow, leading to incorrect results even with correctly aligned pointers.As I see it, there are two problems here:
./configure
does not know the machine architecture and assumes strict alignment requirements.posix_memalign()
(and not evenaligned_alloc()
introduced by C11), so nginx uses plainmalloc()
for supposedly aligned allocations viangx_memalign()
, and too aggressive alignment requirements become important.The most trivial fix would be to explicitly define the expected alignment for the target platform during compilation, such as
./configure --crossbuild=win32 --with-cc-opt="-DNGX_ALIGNMENT=8"
for 32-bit builds (andNGX_ALIGNMENT=16
for 64-bit builds).It might make sense to hardcode these into
src/os/win32/ngx_win32_config.h
to simplify things. Alternatively, assuming i386 or amd64 machine in case of--crossbuild=win32
might make sense, so the default alignment will be used.Not sure if trying to support aligned allocations on win32 make sense. The only interface available seems to be _aligned_malloc(). It requires
_aligned_free()
for deallocation, which is not compatible withfree()
, so we'll have to use aligned allocations everywhere.The default alignment is the default, and does not pretend to be correct for all possible platforms. On Windows, malloc() is expected to provide 8 bytes alignment on 32-bit platforms, and 16 bytes on 64-bit platforms.
On the other hand, given that nginx on Windows does not support platforms with strict alignment requirements, any arbitrary small number will be good enough, including the default
sizeof(unsigned long)
as used now with native compilation.