Welcome! Log In Create A New Profile

Advanced

Core: Avoid memcpy from NULL

Ben Kallus
December 13, 2023 11:10AM
Nginx executes numerous `memcpy`s from NULL during normal execution.
`memcpy`ing to or from NULL is undefined behavior. Accordingly, some
compilers (gcc -O2) make optimizations that assume `memcpy` arguments
are not NULL. Nginx with UBSan crashes during startup due to this
issue.

Consider the following function:
```C
#include <string.h>

int f(int i) {
char a[] = {'a'};
void *src = i ? a : NULL;
char dst[1];
memcpy(dst, src, 0);
return src == NULL;
}
```
Here's what gcc13.2 -O2 -fno-builtin will do to it:
```asm
f:
sub rsp, 24
xor eax, eax
test edi, edi
lea rsi, [rsp+14]
lea rdi, [rsp+15]
mov BYTE PTR [rsp+14], 97
cmove rsi, rax
xor edx, edx
call memcpy
xor eax, eax
add rsp, 24
ret
```
Note that `f` always returns 0, regardless of the value of `i`.

Feel free to try for yourself at https://gcc.godbolt.org/z/zfvnMMsds

The reasoning here is that since memcpy from NULL is UB, the optimizer
is free to assume that `src` is non-null. You might consider this to
be a problem with the compiler, or the C standard, and I might agree.
Regardless, relying on UB is inherently un-portable, and requires
maintenance to ensure that new compiler releases don't break existing
assumptions about the behavior of undefined operations.

The following patch adds a check to `ngx_memcpy` and `ngx_cpymem` that
makes 0-length memcpy explicitly a noop. Since all memcpying from NULL
in Nginx uses n==0, this should be sufficient to avoid UB.

It would be more efficient to instead add a check to every call to
ngx_memcpy and ngx_cpymem that might be used with src==NULL, but in
the discussion of a previous patch that proposed such a change, a more
straightforward and tidy solution was desired.
It may also be worth considering adding checks for NULL memset,
memmove, etc. I think this is not necessary unless it is demonstrated
that Nginx actually executes such undefined calls.

# HG changeset patch
# User Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
# Date 1702406466 18000
# Tue Dec 12 13:41:06 2023 -0500
# Node ID d270203d4ecf77cc14a2652c727e236afc659f4a
# Parent a6f79f044de58b594563ac03139cd5e2e6a81bdb
Add NULL check to ngx_memcpy and ngx_cpymem to satisfy UBSan.

diff -r a6f79f044de5 -r d270203d4ecf src/core/ngx_string.c
--- a/src/core/ngx_string.c Wed Nov 29 10:58:21 2023 +0400
+++ b/src/core/ngx_string.c Tue Dec 12 13:41:06 2023 -0500
@@ -2098,6 +2098,10 @@
ngx_debug_point();
}

+ if (n == 0) {
+ return dst;
+ }
+
return memcpy(dst, src, n);
}

diff -r a6f79f044de5 -r d270203d4ecf src/core/ngx_string.h
--- a/src/core/ngx_string.h Wed Nov 29 10:58:21 2023 +0400
+++ b/src/core/ngx_string.h Tue Dec 12 13:41:06 2023 -0500
@@ -103,8 +103,9 @@
* gcc3 compiles memcpy(d, s, 4) to the inline "mov"es.
* icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves.
*/
-#define ngx_memcpy(dst, src, n) (void) memcpy(dst, src, n)
-#define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n))
+#define ngx_memcpy(dst, src, n) (void) ((n) == 0 ? (dst) : memcpy(dst, src, n))
+#define ngx_cpymem(dst, src, n) \
+ ((u_char *) ((n) == 0 ? (dst) : memcpy(dst, src, n)) + (n))

#endif

diff -r a6f79f044de5 -r d270203d4ecf src/http/v2/ngx_http_v2.c
--- a/src/http/v2/ngx_http_v2.c Wed Nov 29 10:58:21 2023 +0400
+++ b/src/http/v2/ngx_http_v2.c Tue Dec 12 13:41:06 2023 -0500
@@ -3998,9 +3998,7 @@
n = size;
}

- if (n > 0) {
- rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);
- }
+ rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);

ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 request body recv %uz", n);
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

Core: Avoid memcpy from NULL

Ben Kallus 486 December 13, 2023 11:10AM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 67 December 14, 2023 09:44PM

Re: Core: Avoid memcpy from NULL

Dipl. Ing. Sergey Brester via nginx-devel 69 December 15, 2023 09:48AM

Re: Core: Avoid memcpy from NULL

Ben Kallus 54 December 15, 2023 11:30AM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 53 December 15, 2023 11:18PM

Re: Core: Avoid memcpy from NULL

Ben Kallus 63 December 16, 2023 04:28PM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 57 December 20, 2023 07:36PM

Re: Core: Avoid memcpy from NULL

Ben Kallus 45 December 29, 2023 11:52AM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 58 December 31, 2023 02:54PM

Re: Core: Avoid memcpy from NULL

Ben Kallus 52 January 03, 2024 07:00PM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 55 January 04, 2024 11:12AM

Re: Core: Avoid memcpy from NULL

Ben Kallus 48 January 09, 2024 11:20AM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 49 January 09, 2024 02:26PM

Re: Core: Avoid memcpy from NULL

Ben Kallus 36 January 23, 2024 07:10PM

Re: Core: Avoid memcpy from NULL

Maxim Dounin 40 January 24, 2024 03:58AM

Re: Core: Avoid memcpy from NULL

karton 44 January 24, 2024 10:54PM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 213
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready