changeset 318:3021f899881a NGINX_0_5_29

nginx 0.5.29 *) Feature: $nginx_version variable. Thanks to Nick S. Grechukh. *) Bugfix: if the FastCGI header was split in records, then nginx passed garbage in the header to a client. *) Bugfix: Sun Studio compatibility on Solaris/amd64 and Solaris/sparc64. Thanks to Jiang Hong and Andrei Nigmatulin. *) Bugfix: of minor potential bugs. Thanks to Coverity's Scan.
author Igor Sysoev <http://sysoev.ru>
date Mon, 23 Jul 2007 00:00:00 +0400
parents c012154f05d1
children 10d5a311cc5e
files CHANGES CHANGES.ru auto/cc/sunc auto/lib/pcre/conf conf/fastcgi_params src/core/nginx.c src/core/nginx.h src/core/ngx_file.c src/core/ngx_palloc.c src/core/ngx_string.c src/core/ngx_string.h src/http/modules/ngx_http_charset_filter_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_ssl_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_variables.c src/http/ngx_http_variables.h src/os/unix/ngx_atomic.h src/os/unix/ngx_sunpro_amd64.il src/os/unix/ngx_sunpro_x86.il
diffstat 21 files changed, 318 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,24 @@
 
-Changes with nginx 0.5.28                                        17 Jun 2007
+Changes with nginx 0.5.29                                        23 Jul 2007
+
+    *) Feature: $nginx_version variable.
+       Thanks to Nick S. Grechukh.
+
+    *) Bugfix: if the FastCGI header was split in records, then nginx 
+       passed garbage in the header to a client.
+
+    *) Bugfix: Sun Studio compatibility on Solaris/amd64 and 
+       Solaris/sparc64.
+       Thanks to Jiang Hong and Andrei Nigmatulin.
+
+    *) Bugfix: of minor potential bugs.
+       Thanks to Coverity's Scan.
+
+
+Changes with nginx 0.5.28                                        17 Jul 2007
 
     *) Security: the "msie_refresh" directive allowed XSS.
+       Thanks to Maxim Boguk.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
        "auth_http_header" directive was used.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,8 +1,25 @@
 
-Изменения в nginx 0.5.28                                          17.06.2007
+Изменения в nginx 0.5.29                                          23.07.2007
+
+    *) Добавление: переменная $nginx_version.
+       Спасибо Николаю Гречуху.
+
+    *) Исправление: если заголовок ответа был разделён в FastCGI-записях, 
+       то nginx передавал клиенту мусор в таких заголовках.
+
+    *) Исправление: совместимость с Sun Studio на Solaris/amd64 и 
+       Solaris/sparc64.
+       Спасибо Jiang Hong и Андрею Нигматулину.
+
+    *) Исправление: незначительных потенциальных ошибок.
+       Спасибо Coverity's Scan.
+
+
+Изменения в nginx 0.5.28                                          17.07.2007
 
     *) Безопасность: при использовании директивы msie_refresh был возможен 
        XSS.
+       Спасибо Максиму Богуку.
 
     *) Исправление: при использовании директивы auth_http_header в рабочем 
        процессе мог произойти segmentation fault.
--- a/auto/cc/sunc
+++ b/auto/cc/sunc
@@ -2,8 +2,10 @@
 # Copyright (C) Igor Sysoev
 
 
-# Sun C 5.7 Patch 117837-04 2005/05/11
-# Sun C 5.8 2005/10/13
+# Sun C 5.7 Patch 117837-04 2005/05/11    Sun Studio 10
+# Sun C 5.8 2005/10/13                    Sun Studio 11
+# Sun C 5.9 SunOS_i386 2007/05/03         Sun Studio 12
+# Sun C 5.9 SunOS_sparc 2007/05/03
 
 NGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \
                           | sed -e 's/^.* Sun C \(.*\)/\1/'`
@@ -13,6 +15,33 @@ echo " + Sun C version: $NGX_SUNC_VER"
 have=NGX_COMPILER value="\"Sun C $NGX_SUNC_VER\"" . auto/define
 
 
+cat << END > $NGX_AUTOTEST.c
+
+int main() { printf("%d", __SUNPRO_C); }
+
+END
+
+eval "$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+    ngx_sunc_ver=`$NGX_AUTOTEST`
+fi
+
+rm $NGX_AUTOTEST*
+
+# 1424 == 0x590, Sun Studio 12
+
+if [ "$ngx_sunc_ver" -ge 1424 ]; then
+    ngx_sparc32="-m32"
+    ngx_sparc64="-m64"
+    ngx_amd64="-m64"
+
+else
+    ngx_sparc32="-xarch=v8plus"
+    ngx_sparc64="-xarch=v9"
+    ngx_amd64="-amd64"
+fi
+
 case "$NGX_MACHINE" in
 
     i86pc)
@@ -35,9 +64,6 @@ case "$NGX_MACHINE" in
     ;;
 
     sun4u | sun4v)
-        # "-xarch=v9" enables the "casa" assembler instruction
-        CFLAGS="$CFLAGS -xarch=v9"
-        CORE_LINK="$CORE_LINK -xarch=v9"
         NGX_AUX=" src/os/unix/ngx_sunpro_sparc64.il"
     ;;
 
@@ -46,7 +72,9 @@ esac
 
 # optimizations
 
-CFLAGS="$CFLAGS -fast"
+IPO=-xipo
+CFLAGS="$CFLAGS -fast $IPO"
+CORE_LINK="$CORE_LINK -fast $IPO"
 
 
 case $CPU in
@@ -81,11 +109,29 @@ case $CPU in
         CPU_OPT="$CPU_OPT -xcache=64/64/2:1024/64/16"
     ;;
 
+    sparc32)
+        # build 32-bit UltraSparc binary
+        CPU_OPT="$ngx_sparc32"
+        CORE_LINK="$CORE_LINK $ngx_sparc32"
+        CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_sparc32"
+        NGX_CPU_CACHE_LINE=64
+    ;;
+
+    sparc64)
+        # build 64-bit UltraSparc binary
+        CPU_OPT="$ngx_sparc64"
+        CORE_LINK="$CORE_LINK $ngx_sparc64"
+        CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_sparc64"
+        NGX_CPU_CACHE_LINE=64
+    ;;
+
     amd64)
         # build 64-bit amd64 binary
-        CPU_OPT="-xarch=amd64"
-        CORE_LINK="$CORE_LINK -xarch=amd64"
+        CPU_OPT="$ngx_amd64"
+        CORE_LINK="$CORE_LINK $ngx_amd64"
+        CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_amd64"
         NGX_AUX=" src/os/unix/ngx_sunpro_amd64.il"
+        NGX_CPU_CACHE_LINE=64
     ;;
 
 esac
@@ -95,15 +141,15 @@ CFLAGS="$CFLAGS $CPU_OPT"
 
 
 if [ ".$PCRE_OPT" = "." ]; then
-    PCRE_OPT="-fast $CPU_OPT"
+    PCRE_OPT="-fast $IPO $CPU_OPT"
 fi
 
 if [ ".$MD5_OPT" = "." ]; then
-    MD5_OPT="-fast $CPU_OPT"
+    MD5_OPT="-fast $IPO $CPU_OPT"
 fi
 
 if [ ".$ZLIB_OPT" = "." ]; then
-    ZLIB_OPT="-fast $CPU_OPT"
+    ZLIB_OPT="-fast $IPO $CPU_OPT"
 fi
 
 
--- a/auto/lib/pcre/conf
+++ b/auto/lib/pcre/conf
@@ -17,7 +17,7 @@ if [ $PCRE != NONE ]; then
             CORE_LIBS="$CORE_LIBS $PCRE/pcre.lib"
         ;;
 
-        icc* | sunc )
+        icc* )
             have=NGX_PCRE . auto/have
             CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
 
@@ -25,13 +25,18 @@ if [ $PCRE != NONE ]; then
 
             echo $ngx_n "checking for PCRE library ...$ngx_c"
 
-            if [ -e $PCRE/pcre.h ]; then
+            if [ -f $PCRE/pcre.h ]; then
                 ngx_pcre_ver=`grep PCRE_MAJOR $PCRE/pcre.h \
                               | sed -e 's/^.*PCRE_MAJOR.* \(.*\)$/\1/'`
 
-            else
+            else if [ -f $PCRE/configure.in ]; then
                 ngx_pcre_ver=`grep PCRE_MAJOR= $PCRE/configure.in \
                               | sed -e 's/^.*=\(.*\)$/\1/'`
+
+            else
+                ngx_pcre_ver=`grep pcre_major, $PCRE/configure.ac \
+                              | sed -e 's/^.*pcre_major,.*\[\(.*\)\].*$/\1/'`
+            fi
             fi
 
             echo " $ngx_pcre_ver major version found"
--- a/conf/fastcgi_params
+++ b/conf/fastcgi_params
@@ -11,7 +11,7 @@ fastcgi_param  DOCUMENT_ROOT      $docum
 fastcgi_param  SERVER_PROTOCOL    $server_protocol;
 
 fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
-fastcgi_param  SERVER_SOFTWARE    nginx;
+fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
 
 fastcgi_param  REMOTE_ADDR        $remote_addr;
 fastcgi_param  REMOTE_PORT        $remote_port;
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -438,6 +438,9 @@ ngx_set_environment(ngx_cycle_t *cycle, 
     }
 
     var = ngx_array_push(&ccf->env);
+    if (var == NULL) {
+        return NULL;
+    }
 
     var->len = 2;
     var->data = (u_char *) "TZ";
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.5.28"
+#define NGINX_VERSION      "0.5.29"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -456,6 +456,9 @@ ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_s
         }
 
         ctx->data = data;
+
+    } else {
+        data = NULL;
     }
 
     for ( ;; ) {
@@ -581,8 +584,8 @@ done:
         ngx_free(buf.data);
     }
 
-    if (ctx->alloc) {
-        ngx_free(ctx->data);
+    if (data) {
+        ngx_free(data);
         ctx->data = prev;
     }
 
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -163,6 +163,7 @@ ngx_palloc(ngx_pool_t *pool, size_t size
 
     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
     if (large == NULL) {
+        ngx_free(p);
         return NULL;
     }
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -105,21 +105,21 @@ ngx_snprintf(u_char *buf, size_t max, co
 u_char *
 ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args)
 {
-    u_char         *p, zero, *last, temp[NGX_INT64_LEN + 1];
+    u_char                *p, zero, *last, temp[NGX_INT64_LEN + 1];
                                     /*
                                      * really we need temp[NGX_INT64_LEN] only,
                                      * but icc issues the warning
                                      */
-    int             d;
-    size_t          len;
-    uint32_t        ui32;
-    int64_t         i64;
-    uint64_t        ui64;
-    ngx_msec_t      ms;
-    ngx_str_t      *s;
-    ngx_uint_t      width, sign, hexadecimal, max_width;
-    static u_char   hex[] = "0123456789abcdef";
-    static u_char   HEX[] = "0123456789ABCDEF";
+    int                    d;
+    size_t                 len;
+    uint32_t               ui32;
+    int64_t                i64;
+    uint64_t               ui64;
+    ngx_msec_t             ms;
+    ngx_uint_t             width, sign, hexadecimal, max_width;
+    ngx_variable_value_t  *v;
+    static u_char          hex[] = "0123456789abcdef";
+    static u_char          HEX[] = "0123456789ABCDEF";
 
     if (max == 0) {
         return buf;
@@ -188,12 +188,12 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             switch (*fmt) {
 
             case 'V':
-                s = va_arg(args, ngx_str_t *);
+                v = va_arg(args, ngx_variable_value_t *);
 
-                len = s->len & 0xffff;
+                len = v->len;
                 len = (buf + len < last) ? len : (size_t) (last - buf);
 
-                buf = ngx_cpymem(buf, s->data, len);
+                buf = ngx_cpymem(buf, v->data, len);
                 fmt++;
 
                 continue;
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -13,17 +13,28 @@
 
 
 typedef struct {
-    size_t     len;
-    u_char    *data;
+    size_t      len;
+    u_char     *data;
 } ngx_str_t;
 
 
 typedef struct {
-    ngx_str_t  key;
-    ngx_str_t  value;
+    ngx_str_t   key;
+    ngx_str_t   value;
 } ngx_keyval_t;
 
 
+typedef struct {
+    unsigned    len:29;
+
+    unsigned    valid:1;
+    unsigned    no_cachable:1;
+    unsigned    not_found:1;
+
+    u_char     *data;
+} ngx_variable_value_t;
+
+
 #define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }
 #define ngx_null_string     { 0, NULL }
 
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -187,7 +187,7 @@ ngx_http_charset_header_filter(ngx_http_
 {
     u_char                        *ct;
     ngx_int_t                      charset, source_charset;
-    ngx_str_t                     *mc, *from, *to;
+    ngx_str_t                     *mc, *from, *to, s;
     ngx_uint_t                     n;
     ngx_http_charset_t            *charsets;
     ngx_http_charset_ctx_t        *ctx;
@@ -256,8 +256,10 @@ ngx_http_charset_header_filter(ngx_http_
                     return NGX_ERROR;
                 }
 
-                charset = ngx_http_charset_get_charset(charsets, n,
-                                                       (ngx_str_t *) vv);
+                s.len = vv->len;
+                s.data = vv->data;
+
+                charset = ngx_http_charset_get_charset(charsets, n, &s);
             }
         }
 
@@ -303,8 +305,10 @@ ngx_http_charset_header_filter(ngx_http_
                 return NGX_ERROR;
             }
 
-            source_charset = ngx_http_charset_get_charset(charsets, n,
-                                                          (ngx_str_t *) vv);
+            s.len = vv->len;
+            s.data = vv->data;
+
+            source_charset = ngx_http_charset_get_charset(charsets, n, &s);
         }
 
         if (charset != NGX_HTTP_NO_CHARSET) {
@@ -373,17 +377,16 @@ static ngx_int_t
 ngx_http_charset_get_charset(ngx_http_charset_t *charsets, ngx_uint_t n,
     ngx_str_t *charset)
 {
-    size_t      len;
     ngx_uint_t  i;
 
-    len = charset->len & 0xffff;
-
     for (i = 0; i < n; i++) {
-        if (charsets[i].name.len != len) {
+        if (charsets[i].name.len != charset->len) {
             continue;
         }
 
-        if (ngx_strncasecmp(charsets[i].name.data, charset->data, len) == 0) {
+        if (ngx_strncasecmp(charsets[i].name.data, charset->data, charset->len)
+            == 0)
+        {
             return i;
         }
     }
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -38,6 +38,12 @@ typedef enum {
 
 
 typedef struct {
+    u_char                        *start;
+    u_char                        *end;
+} ngx_http_fastcgi_split_part_t;
+
+
+typedef struct {
     ngx_http_fastcgi_state_e       state;
     u_char                        *pos;
     u_char                        *last;
@@ -45,7 +51,9 @@ typedef struct {
     size_t                         length;
     size_t                         padding;
 
-    ngx_uint_t                     fastcgi_stdout;
+    ngx_uint_t                     fastcgi_stdout; /* unsigned :1 */
+
+    ngx_array_t                   *split_parts;
 } ngx_http_fastcgi_ctx_t;
 
 
@@ -840,15 +848,18 @@ ngx_http_fastcgi_reinit_request(ngx_http
 static ngx_int_t
 ngx_http_fastcgi_process_header(ngx_http_request_t *r)
 {
-    u_char                         *start, *last;
+    u_char                         *p, *start, *last, *part_start;
+    size_t                          size;
     ngx_str_t                      *status_line, line, *pattern;
     ngx_int_t                       rc, status;
+    ngx_buf_t                       buf;
     ngx_uint_t                      i;
     ngx_table_elt_t                *h;
     ngx_http_upstream_t            *u;
     ngx_http_fastcgi_ctx_t         *f;
     ngx_http_upstream_header_t     *hh;
     ngx_http_fastcgi_loc_conf_t    *flcf;
+    ngx_http_fastcgi_split_part_t  *part;
     ngx_http_upstream_main_conf_t  *umcf;
 
     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
@@ -1017,6 +1028,8 @@ ngx_http_fastcgi_process_header(ngx_http
 
         for ( ;; ) {
 
+            part_start = u->buffer.pos;
+
             rc = ngx_http_parse_header_line(r, &u->buffer);
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1035,24 +1048,72 @@ ngx_http_fastcgi_process_header(ngx_http
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
+                if (f->split_parts && f->split_parts->nelts) {
+
+                    part = f->split_parts->elts;
+                    size = u->buffer.pos - part_start;
+
+                    for (i = 0; i < f->split_parts->nelts; i++) {
+                        size += part[i].end - part[i].start;
+                    }
+
+                    p = ngx_palloc(r->pool, size);
+                    if (p == NULL) {
+                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    }
+
+                    buf.pos = p;
+
+                    for (i = 0; i < f->split_parts->nelts; i++) {
+                        p = ngx_cpymem(p, part[i].start,
+                                       part[i].end - part[i].start);
+                    }
+
+                    p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
+
+                    buf.last = p;
+
+                    f->split_parts->nelts = 0;
+
+                    rc = ngx_http_parse_header_line(r, &buf);
+
+                    h->key.len = r->header_name_end - r->header_name_start;
+                    h->key.data = r->header_name_start;
+                    h->key.data[h->key.len] = '\0';
+
+                    h->value.len = r->header_end - r->header_start;
+                    h->value.data = r->header_start;
+                    h->value.data[h->value.len] = '\0';
+
+                    h->lowcase_key = ngx_palloc(r->pool, h->key.len);
+                    if (h->lowcase_key == NULL) {
+                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    }
+
+                } else {
+
+                    h->key.len = r->header_name_end - r->header_name_start;
+                    h->value.len = r->header_end - r->header_start;
+
+                    h->key.data = ngx_palloc(r->pool,
+                                             h->key.len + 1 + h->value.len + 1
+                                             + h->key.len);
+                    if (h->key.data == NULL) {
+                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                    }
+
+                    h->value.data = h->key.data + h->key.len + 1;
+                    h->lowcase_key = h->key.data + h->key.len + 1
+                                     + h->value.len + 1;
+
+                    ngx_cpystrn(h->key.data, r->header_name_start,
+                                h->key.len + 1);
+                    ngx_cpystrn(h->value.data, r->header_start,
+                                h->value.len + 1);
+                }
+
                 h->hash = r->header_hash;
 
-                h->key.len = r->header_name_end - r->header_name_start;
-                h->value.len = r->header_end - r->header_start;
-
-                h->key.data = ngx_palloc(r->pool,
-                               h->key.len + 1 + h->value.len + 1 + h->key.len);
-                if (h->key.data == NULL) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
-                }
-
-                h->value.data = h->key.data + h->key.len + 1;
-                h->lowcase_key = h->key.data + h->key.len + 1
-                                 + h->value.len + 1;
-
-                ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
-                ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
-
                 if (h->key.len == r->lowcase_index) {
                     ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
 
@@ -1123,7 +1184,6 @@ ngx_http_fastcgi_process_header(ngx_http
                           "upstream sent invalid header");
 
             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
-
         }
 
         if (last) {
@@ -1150,10 +1210,23 @@ ngx_http_fastcgi_process_header(ngx_http
 
         /* rc == NGX_AGAIN */
 
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      "upstream split a header line in FastCGI records");
-
-        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "upstream split a header line in FastCGI records");
+
+        if (f->split_parts == NULL) {
+            f->split_parts = ngx_array_create(r->pool, 1,
+                                        sizeof(ngx_http_fastcgi_split_part_t));
+            if (f->split_parts == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+
+        part = ngx_array_push(f->split_parts);
+
+        part->start = part_start;
+        part->end = u->buffer.last;
+
+        return NGX_AGAIN;
     }
 }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -439,6 +439,8 @@ ngx_http_ssi_body_filter(ngx_http_reques
                 if (rc == NGX_ERROR || rc == NGX_AGAIN) {
                     return rc;
                 }
+
+                break;
             }
         }
 
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -197,11 +197,14 @@ ngx_http_ssl_static_variable(ngx_http_re
 {
     ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;
 
-    size_t  len;
+    size_t     len;
+    ngx_str_t  s;
 
     if (r->connection->ssl) {
 
-        (void) handler(r->connection, NULL, (ngx_str_t *) v);
+        (void) handler(r->connection, NULL, &s);
+
+        v->data = s.data;
 
         for (len = 0; v->data[len]; len++) { /* void */ }
 
@@ -225,11 +228,17 @@ ngx_http_ssl_variable(ngx_http_request_t
 {
     ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;
 
+    ngx_str_t  s;
+
     if (r->connection->ssl) {
-        if (handler(r->connection, r->pool, (ngx_str_t *) v) != NGX_OK) {
+
+        if (handler(r->connection, r->pool, &s) != NGX_OK) {
             return NGX_ERROR;
         }
 
+        v->len = s.len;
+        v->data = s.data;
+
         if (v->len) {
             v->valid = 1;
             v->no_cachable = 0;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.5.28';
+our $VERSION = '0.5.29';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -8,6 +8,7 @@
 #include <ngx_core.h>
 #include <ngx_event.h>
 #include <ngx_http.h>
+#include <nginx.h>
 
 
 static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,
@@ -66,6 +67,8 @@ static ngx_int_t ngx_http_variable_sent_
 static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
+static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 /*
  * TODO:
@@ -205,6 +208,9 @@ static ngx_http_variable_t  ngx_http_cor
       offsetof(ngx_http_request_t, limit_rate),
       NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 },
 
+    { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
+      0, 0, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -501,7 +507,7 @@ ngx_http_variable_request_set_size(ngx_h
     ssize_t    s, *sp;
     ngx_str_t  val;
 
-    val.len = v->len & 0xffff;
+    val.len = v->len;
     val.data = v->data;
 
     s = ngx_parse_size(&val);
@@ -1205,6 +1211,20 @@ ngx_http_variable_request_body_file(ngx_
 }
 
 
+static ngx_int_t
+ngx_http_variable_nginx_version(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    v->len = sizeof(NGINX_VERSION) - 1;
+    v->valid = 1;
+    v->no_cachable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) NGINX_VERSION;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -14,15 +14,7 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    unsigned                     len:29;
-
-    unsigned                     valid:1;
-    unsigned                     no_cachable:1;
-    unsigned                     not_found:1;
-
-    u_char                      *data;
-} ngx_http_variable_value_t;
+typedef ngx_variable_value_t  ngx_http_variable_value_t;
 
 #define ngx_http_variable(v)     { sizeof(v) - 1, 1, 0, 0, (u_char *) v }
 
--- a/src/os/unix/ngx_atomic.h
+++ b/src/os/unix/ngx_atomic.h
@@ -87,10 +87,17 @@ ngx_atomic_cmp_set(ngx_atomic_t *lock, n
 ngx_atomic_int_t
 ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
 
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il
+ */
+
+void
+ngx_cpu_pause(void);
+
 /* the code in src/os/unix/ngx_sunpro_x86.il */
 
 #define ngx_memory_barrier()        __asm (".volatile"); __asm (".nonvolatile")
-#define ngx_cpu_pause()             __asm ("pause")
 
 
 #else /* ( __GNUC__ || __INTEL_COMPILER ) */
@@ -121,10 +128,17 @@ ngx_atomic_cmp_set(ngx_atomic_t *lock, n
 ngx_atomic_int_t
 ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
 
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il
+ */
+
+void
+ngx_cpu_pause(void);
+
 /* the code in src/os/unix/ngx_sunpro_amd64.il */
 
 #define ngx_memory_barrier()        __asm (".volatile"); __asm (".nonvolatile")
-#define ngx_cpu_pause()             __asm ("pause")
 
 
 #else /* ( __GNUC__ || __INTEL_COMPILER ) */
@@ -136,7 +150,7 @@ ngx_atomic_fetch_add(ngx_atomic_t *value
 #endif
 
 
-#elif ( __sparc__ || __sparcv9 )
+#elif ( __sparc__ || __sparc || __sparcv9 )
 
 #if (NGX_PTR_SIZE == 8)
 
--- a/src/os/unix/ngx_sunpro_amd64.il
+++ b/src/os/unix/ngx_sunpro_amd64.il
@@ -28,3 +28,10 @@
         lock
         xaddq     %rax, (%rdi)
         .end
+
+
+/ ngx_cpu_pause()
+
+       .inline ngx_cpu_pause,0
+       pause
+       .end
--- a/src/os/unix/ngx_sunpro_x86.il
+++ b/src/os/unix/ngx_sunpro_x86.il
@@ -29,3 +29,10 @@
         lock
         xaddl     %eax, (%ecx)
         .end
+
+
+/ ngx_cpu_pause()
+
+       .inline ngx_cpu_pause,0
+       pause
+       .end