Compare commits

...

24 Commits

Author SHA1 Message Date
Maxim Dounin da99067102 release-1.12.2 tag 2017-10-17 16:16:37 +03:00
Maxim Dounin 2e11e8c840 nginx-1.12.2-RELEASE 2017-10-17 16:16:37 +03:00
Maxim Dounin 08acb932e2 Fixed build without IPv6, broken by 874171c3c71a. 2017-10-05 16:50:35 +03:00
Maxim Dounin c260a18d26 Fixed handling of unix sockets in $binary_remote_addr.
Previously, unix sockets were treated as AF_INET ones, and this may
result in buffer overread on Linux, where unbound unix sockets have
2-byte addresses.

Note that it is not correct to use just sun_path as a binary representation
for unix sockets.  This will result in an empty string for unbound unix
sockets, and thus behaviour of limit_req and limit_conn will change when
switching from $remote_addr to $binary_remote_addr.  As such, normal text
representation is used.

Reported by Stephan Dollberg.
2017-10-04 21:19:42 +03:00
Maxim Dounin df2fbd0385 Fixed handling of non-null-terminated unix sockets.
At least FreeBSD, macOS, NetBSD, and OpenBSD can return unix sockets
with non-null-terminated sun_path.  Additionally, the address may become
non-null-terminated if it does not fit into the buffer provided and was
truncated (may happen on macOS, NetBSD, and Solaris, which allow unix socket
addresess larger than struct sockaddr_un).  As such, ngx_sock_ntop() might
overread the sockaddr provided, as it used "%s" format and thus assumed
null-terminated string.

To fix this, the ngx_strnlen() function was introduced, and it is now used
to calculate correct length of sun_path.
2017-10-04 21:19:38 +03:00
Maxim Dounin 2a66bbf13d Fixed buffer overread with unix sockets after accept().
Some OSes (notably macOS, NetBSD, and Solaris) allow unix socket addresses
larger than struct sockaddr_un.  Moreover, some of them (macOS, Solaris)
return socklen of the socket address before it was truncated to fit the
buffer provided.  As such, on these systems socklen must not be used without
additional check that it is within the buffer provided.

Appropriate checks added to ngx_event_accept() (after accept()),
ngx_event_recvmsg() (after recvmsg()), and ngx_set_inherited_sockets()
(after getsockname()).

We also obtain socket addresses via getsockname() in
ngx_connection_local_sockaddr(), but it does not need any checks as
it is only used for INET and INET6 sockets (as there can be no
wildcard unix sockets).
2017-10-04 21:19:33 +03:00
Ruslan Ermilov cf5a456adf Fixed the NGX_UNIX_ADDRSTRLEN macro. 2017-09-25 15:19:24 +03:00
Valentin Bartenev 8d24c2bd3e HTTP/2: enforce writing the sync request body buffer to file.
The sync flag of HTTP/2 request body buffer is used when the size of request
body is unknown or bigger than configured "client_body_buffer_size".  In this
case the buffer points to body data inside the global receive buffer that is
used for reading all HTTP/2 connections in the worker process.  Thus, when the
sync flag is set, the buffer must be flushed to a temporary file, otherwise
the request body data can be overwritten.

Previously, the sync buffer wasn't flushed to a temporary file if the whole
body was received in one DATA frame with the END_STREAM flag and wasn't
copied into the HTTP/2 body preread buffer.  As a result, the request body
might be corrupted (ticket #1384).

Now, setting r->request_body_in_file_only enforces writing the sync buffer
to a temporary file in all cases.
2017-10-04 21:15:15 +03:00
Roman Arutyunyan 67e5cef2bc Stream: relaxed next upstream condition (ticket #1317).
When switching to a next upstream, some buffers could be stuck in the middle
of the filter chain.  A condition existed that raised an error when this
happened.  As it turned out, this condition prevented switching to a next
upstream if ssl preread was used with the TCP protocol (see the ticket).

In fact, the condition does not make sense for TCP, since after successful
connection to an upstream switching to another upstream never happens.  As for
UDP, the issue with stuck buffers is unlikely to happen, but is still possible.
Specifically, if a filter delays sending data to upstream.

The condition can be relaxed to only check the "buffered" bitmask of the
upstream connection.  The new condition is simpler and fixes the ticket issue
as well.  Additionally, the upstream_out chain is now reset for UDP prior to
connecting to a new upstream to prevent repeating the client data twice.
2017-09-11 15:32:31 +03:00
Maxim Dounin 21c6c9c459 Range filter: changed type for total length to off_t.
Total length of a response with multiple ranges can be larger than a size_t
variable can hold, so type changed to off_t.  Previously, an incorrect
Content-Length was returned when requesting more than 4G of ranges from
a large enough file on a 32-bit system.

An additional size_t variable introduced to calculate size of the boundary
header buffer, as off_t is not needed here and will require type casts on
win32.

Reported by Shuxin Yang,
http://mailman.nginx.org/pipermail/nginx/2017-July/054384.html.
2017-08-10 22:21:23 +03:00
Maxim Dounin a15c219ae5 Updated PCRE used for win32 builds. 2017-08-08 15:21:10 +03:00
Maxim Dounin f742aa020d Core: fixed error message on setsockopt(SO_REUSEPORT) failure.
The error is fatal when configuring a new socket, so the ", ignored" part
is not appropriate and was removed.
2017-07-11 20:06:52 +03:00
Maxim Dounin 358b873075 Core: disabled SO_REUSEPORT when testing config (ticket #1300).
When closing a socket with SO_REUSEPORT, Linux drops all connections waiting
in this socket's listen queue.  Previously, it was believed to only result
in connection resets when reconfiguring nginx to use smaller number of worker
processes.  It also results in connection resets during configuration
testing though.

Workaround is to avoid using SO_REUSEPORT when testing configuration.  It
should prevent listening sockets from being created if a conflicting socket
already exists, while still preserving detection of other possible errors.
It should also cover UDP sockets.

The only downside of this approach seems to be that a configuration testing
won't be able to properly report the case when nginx was compiled with
SO_REUSEPORT, but the kernel is not able to set it.  Such errors will be
reported on a real start instead.
2017-07-11 19:59:56 +03:00
Roman Arutyunyan cc68c10d40 Fixed deferred accept with EPOLLRDHUP enabled (ticket #1278).
Previously, the read event of the accepted connection was marked ready, but not
available.  This made EPOLLRDHUP-related code (for example, in ngx_unix_recv())
expect more data from the socket, leading to unexpected behavior.

For example, if SSL, PROXY protocol and deferred accept were enabled on a listen
socket, the client connection was aborted due to unexpected return value of
c->recv().
2017-05-24 13:17:08 +03:00
Maxim Dounin f13f641b67 Added missing "fall through" comments (ticket #1259).
Found by gcc7 (-Wimplicit-fallthrough).
2017-04-27 16:57:18 +03:00
Maxim Dounin 4b74e5492d Version bump. 2017-10-13 20:23:57 +03:00
Maxim Dounin 275252e283 release-1.12.1 tag 2017-07-11 16:24:05 +03:00
Maxim Dounin 6a367b8a32 nginx-1.12.1-RELEASE 2017-07-11 16:24:04 +03:00
Maxim Dounin 40b16153df Range filter: protect from total size overflows.
The overflow can be used to circumvent the restriction on total size of
ranges introduced in c2a91088b0c0 (1.1.2).  Additionally, overflow
allows producing ranges with negative start (such ranges can be created
by using a suffix, "bytes=-100"; normally this results in 200 due to
the total size check).  These can result in the following errors in logs:

[crit] ... pread() ... failed (22: Invalid argument)
[alert] ... sendfile() failed (22: Invalid argument)

When using cache, it can be also used to reveal cache file header.
It is believed that there are no other negative effects, at least with
standard nginx modules.

In theory, this can also result in memory disclosure and/or segmentation
faults if multiple ranges are allowed, and the response is returned in a
single in-memory buffer.  This never happens with standard nginx modules
though, as well as known 3rd party modules.

Fix is to properly protect from possible overflow when incrementing size.
2017-07-11 16:06:23 +03:00
Maxim Dounin d06fe800f7 Updated OpenSSL used for win32 builds. 2017-05-30 17:14:00 +03:00
Maxim Dounin 97beb12711 Version bump. 2017-07-11 16:19:05 +03:00
Maxim Dounin 2ddc95b81a release-1.12.0 tag 2017-04-12 17:46:00 +03:00
Maxim Dounin 56525381cf nginx-1.12.0-RELEASE 2017-04-12 17:46:00 +03:00
Maxim Dounin d488f5504b Stable branch. 2017-04-12 16:42:30 +03:00
17 changed files with 200 additions and 26 deletions

View File

@ -5,6 +5,109 @@
<change_log title="nginx">
<changes ver="1.12.2" date="2017-10-17">
<change type="bugfix">
<para lang="ru">
клиентские SSL-соединения сразу закрывались, если использовался
отложенный accept и параметр proxy_protocol директивы listen.
</para>
<para lang="en">
client SSL connections were immediately closed if deferred accept
and the "proxy_protocol" parameter of the "listen" directive were used.
</para>
</change>
<change type="bugfix">
<para lang="ru">
клиентские соединения могли сбрасываться при тестировании конфигурации,
если использовался параметр reuseport директивы listen на Linux.
</para>
<para lang="en">
client connections might be dropped during configuration testing
when using the "reuseport" parameter of the "listen" directive on Linux.
</para>
</change>
<change type="bugfix">
<para lang="ru">
на 32-битных платформах
при запросе более 4 гигабайт с помощью нескольких диапазонов
возвращалась некорректная длина ответа.
</para>
<para lang="en">
incorrect response length was returned
on 32-bit platforms when requesting more than 4 gigabytes
with multiple ranges.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании директивы ssl_preread
в модуле stream не работало переключение на следующий бэкенд.
</para>
<para lang="en">
switching to the next upstream server in the stream module did not work
when using the "ssl_preread" directive.
</para>
</change>
<change type="bugfix">
<para lang="ru">
при использовании HTTP/2 тело запроса могло быть повреждено.
</para>
<para lang="en">
when using HTTP/2 client request body might be corrupted.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в обработке адресов клиентов при использовании unix domain сокетов.
</para>
<para lang="en">
in handling of client addresses when using unix domain sockets.
</para>
</change>
</changes>
<changes ver="1.12.1" date="2017-07-11">
<change type="security">
<para lang="ru">
специально созданный запрос мог вызвать целочисленное переполнение
в range-фильтре и последующую некорректную обработку запрошенных диапазонов,
что потенциально могло привести к утечке конфиденциальной информации
(CVE-2017-7529).
</para>
<para lang="en">
a specially crafted request might result in an integer overflow
and incorrect processing of ranges in the range filter,
potentially resulting in sensitive information leak
(CVE-2017-7529).
</para>
</change>
</changes>
<changes ver="1.12.0" date="2017-04-12">
<change>
<para lang="ru">
Стабильная ветка 1.12.x.
</para>
<para lang="en">
1.12.x stable branch.
</para>
</change>
</changes>
<changes ver="1.11.13" date="2017-04-04">
<change type="feature">

View File

@ -6,9 +6,9 @@ TEMP = tmp
CC = cl
OBJS = objs.msvc8
OPENSSL = openssl-1.0.2k
OPENSSL = openssl-1.0.2l
ZLIB = zlib-1.2.11
PCRE = pcre-8.40
PCRE = pcre-8.41
release: export

View File

@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
#define nginx_version 1011013
#define NGINX_VERSION "1.11.13"
#define nginx_version 1012002
#define NGINX_VERSION "1.12.2"
#define NGINX_VER "nginx/" NGINX_VERSION
#ifdef NGX_BUILD

View File

@ -165,6 +165,10 @@ ngx_set_inherited_sockets(ngx_cycle_t *cycle)
continue;
}
if (ls[i].socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
ls[i].socklen = sizeof(ngx_sockaddr_t);
}
switch (ls[i].sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
@ -473,7 +477,7 @@ ngx_open_listening_sockets(ngx_cycle_t *cycle)
#if (NGX_HAVE_REUSEPORT)
if (ls[i].reuseport) {
if (ls[i].reuseport && !ngx_test_config) {
int reuseport;
reuseport = 1;
@ -483,7 +487,7 @@ ngx_open_listening_sockets(ngx_cycle_t *cycle)
== -1)
{
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
"setsockopt(SO_REUSEPORT) %V failed, ignored",
"setsockopt(SO_REUSEPORT) %V failed",
&ls[i].addr_text);
if (ngx_close_socket(s) == -1) {

View File

@ -182,9 +182,11 @@ ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len,
ngx_uint_t port)
{
u_char *p;
#if (NGX_HAVE_INET6 || NGX_HAVE_UNIX_DOMAIN)
size_t n;
#endif
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
size_t n;
struct sockaddr_in6 *sin6;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
@ -241,7 +243,9 @@ ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len,
p = ngx_snprintf(text, len, "unix:%Z");
} else {
p = ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path);
n = ngx_strnlen((u_char *) saun->sun_path,
socklen - offsetof(struct sockaddr_un, sun_path));
p = ngx_snprintf(text, len, "unix:%*s%Z", n, saun->sun_path);
}
/* we do not include trailing zero in address length */

View File

@ -17,10 +17,11 @@
#define NGX_INET6_ADDRSTRLEN \
(sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1)
#define NGX_UNIX_ADDRSTRLEN \
(sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))
(sizeof("unix:") - 1 + \
sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))
#if (NGX_HAVE_UNIX_DOMAIN)
#define NGX_SOCKADDR_STRLEN (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN)
#define NGX_SOCKADDR_STRLEN NGX_UNIX_ADDRSTRLEN
#elif (NGX_HAVE_INET6)
#define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1)
#else

View File

@ -35,8 +35,10 @@ ngx_murmur_hash2(u_char *data, size_t len)
switch (len) {
case 3:
h ^= data[2] << 16;
/* fall through */
case 2:
h ^= data[1] << 8;
/* fall through */
case 1:
h ^= data[0];
h *= 0x5bd1e995;

View File

@ -29,6 +29,22 @@ ngx_strlow(u_char *dst, u_char *src, size_t n)
}
size_t
ngx_strnlen(u_char *p, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
if (p[i] == '\0') {
return i;
}
}
return n;
}
u_char *
ngx_cpystrn(u_char *dst, u_char *src, size_t n)
{

View File

@ -60,6 +60,8 @@ void ngx_strlow(u_char *dst, u_char *src, size_t n);
#define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2)
#define ngx_strlen(s) strlen((const char *) s)
size_t ngx_strnlen(u_char *p, size_t n);
#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c)
static ngx_inline u_char *

View File

@ -164,6 +164,10 @@ ngx_event_accept(ngx_event_t *ev)
return;
}
if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
socklen = sizeof(ngx_sockaddr_t);
}
c->sockaddr = ngx_palloc(c->pool, socklen);
if (c->sockaddr == NULL) {
ngx_close_accepted_connection(c);
@ -238,7 +242,7 @@ ngx_event_accept(ngx_event_t *ev)
if (ev->deferred_accept) {
rev->ready = 1;
#if (NGX_HAVE_KQUEUE)
#if (NGX_HAVE_KQUEUE || NGX_HAVE_EPOLLRDHUP)
rev->available = 1;
#endif
}
@ -440,6 +444,10 @@ ngx_event_recvmsg(ngx_event_t *ev)
c->type = SOCK_DGRAM;
c->socklen = msg.msg_namelen;
if (c->socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
c->socklen = sizeof(ngx_sockaddr_t);
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif

View File

@ -377,6 +377,10 @@ ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
range->start = start;
range->end = end;
if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
size += end - start;
if (ranges-- == 0) {
@ -454,23 +458,24 @@ static ngx_int_t
ngx_http_range_multipart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx)
{
size_t len;
off_t len;
size_t size;
ngx_uint_t i;
ngx_http_range_t *range;
ngx_atomic_uint_t boundary;
len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ sizeof(CRLF "Content-Type: ") - 1
+ r->headers_out.content_type.len
+ sizeof(CRLF "Content-Range: bytes ") - 1;
size = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ sizeof(CRLF "Content-Type: ") - 1
+ r->headers_out.content_type.len
+ sizeof(CRLF "Content-Range: bytes ") - 1;
if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len)
{
len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
size += sizeof("; charset=") - 1 + r->headers_out.charset.len;
}
ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
ctx->boundary_header.data = ngx_pnalloc(r->pool, size);
if (ctx->boundary_header.data == NULL) {
return NGX_ERROR;
}
@ -560,7 +565,7 @@ ngx_http_range_multipart_header(ngx_http_request_t *r,
- range[i].content_range.data;
len += ctx->boundary_header.len + range[i].content_range.len
+ (size_t) (range[i].end - range[i].start);
+ (range[i].end - range[i].start);
}
r->headers_out.content_length_n = len;

View File

@ -1390,6 +1390,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
goto done;
case '+':
r->plus_in_uri = 1;
/* fall through */
default:
state = sw_usual;
*u++ = ch;
@ -1431,6 +1432,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
goto done;
case '+':
r->plus_in_uri = 1;
/* fall through */
default:
state = sw_usual;
*u++ = ch;
@ -1478,6 +1480,7 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
goto done;
case '+':
r->plus_in_uri = 1;
/* fall through */
default:
state = sw_usual;
*u++ = ch;

View File

@ -1225,6 +1225,18 @@ ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) r->connection->sockaddr;

View File

@ -3557,11 +3557,6 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r,
rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
} else {
if (stream->preread) {
/* enforce writing preread buffer to file */
r->request_body_in_file_only = 1;
}
rb->buf = ngx_calloc_buf(r->pool);
if (rb->buf != NULL) {
@ -3660,6 +3655,8 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
buf->pos = buf->start = pos;
buf->last = buf->end = pos + size;
r->request_body_in_file_only = 1;
} else {
if (size > (size_t) (buf->end - buf->last)) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,

View File

@ -405,6 +405,7 @@ ngx_signal_handler(int signo)
break;
}
ngx_debug_quit = 1;
/* fall through */
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ngx_quit = 1;
action = ", shutting down";

View File

@ -1677,13 +1677,17 @@ ngx_stream_proxy_next_upstream(ngx_stream_session_t *s)
u = s->upstream;
pc = u->peer.connection;
if (u->upstream_out || u->upstream_busy || (pc && pc->buffered)) {
if (pc && pc->buffered) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"pending buffers on next upstream");
"buffered data on next upstream");
ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
return;
}
if (s->connection->type == SOCK_DGRAM) {
u->upstream_out = NULL;
}
if (u->peer.sockaddr) {
u->peer.free(&u->peer, u->peer.data, NGX_PEER_FAILED);
u->peer.sockaddr = NULL;

View File

@ -481,6 +481,18 @@ ngx_stream_variable_binary_remote_addr(ngx_stream_session_t *s,
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
v->len = s->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = s->connection->addr_text.data;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) s->connection->sockaddr;