changeset 320:95183808f549 NGINX_0_6_4

nginx 0.6.4 *) Security: the "msie_refresh" directive allowed XSS. Thanks to Maxim Boguk. *) Change: the "proxy_store" and "fastcgi_store" directives were changed. *) Feature: the "proxy_store_access" and "fastcgi_store_access" directives. *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun Studio. Thanks to Andrei Nigmatulin. *) Workaround: for Sun Studio 12. Thanks to Jiang Hong.
author Igor Sysoev <http://sysoev.ru>
date Tue, 17 Jul 2007 00:00:00 +0400
parents 6ccd0af7f704
children a87830ef6fdd
files CHANGES CHANGES.ru auto/cc/sunc auto/lib/pcre/conf src/core/nginx.h src/core/ngx_file.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_dav_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_ssl_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h 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 22 files changed, 407 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,10 +1,29 @@
 
+Changes with nginx 0.6.4                                         17 Jul 2007
+
+    *) Security: the "msie_refresh" directive allowed XSS.
+
+    *) Change: the "proxy_store" and "fastcgi_store" directives were 
+       changed.
+
+    *) Feature: the "proxy_store_access" and "fastcgi_store_access" 
+       directives.
+
+    *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun 
+       Studio.
+       Thanks to Andrei Nigmatulin.
+
+    *) Workaround: for Sun Studio 12.
+       Thanks to Jiang Hong.
+
+
 Changes with nginx 0.6.3                                         12 Jul 2007
 
     *) Feature: the "proxy_store" and "fastcgi_store" directives.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
        "auth_http_header" directive was used.
+       Thanks to Maxim Dounin.
 
     *) Bugfix: a segmentation fault occurred in worker process if the 
        CRAM-MD5 authentication method was used, but it was not enabled.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,10 +1,28 @@
 
+Изменения в nginx 0.6.4                                           17.07.2007
+
+    *) Безопасность: при использовании директивы msie_refresh был возможен 
+       XSS.
+
+    *) Изменение: директивы proxy_store и fastcgi_store изменены.
+
+    *) Добавление: директивы proxy_store_access и fastcgi_store_access.
+
+    *) Исправление: nginx не работал на Solaris/sparc64, если был собран 
+       Sun Studio.
+       Спасибо Андрею Нигматулину.
+
+    *) Изменение: обход ошибки в Sun Studio 12.
+       Спасибо Jiang Hong.
+
+
 Изменения в nginx 0.6.3                                           12.07.2007
 
     *) Добавление: директивы proxy_store и fastcgi_store.
 
     *) Исправление: при использовании директивы auth_http_header в рабочем 
        процессе мог произойти segmentation fault.
+       Спасибо Максиму Дунину.
 
     *) Исправление: если использовался метод аутентификации CRAM-MD5, но он 
        не был разрешён, то в рабочем процессе происходил 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,8 @@ esac
 
 # optimizations
 
-CFLAGS="$CFLAGS -fast"
+IPO=-xipo
+CFLAGS="$CFLAGS -fast $IPO"
 
 
 case $CPU in
@@ -81,11 +108,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_amd4"
         NGX_AUX=" src/os/unix/ngx_sunpro_amd64.il"
+        NGX_CPU_CACHE_LINE=64
     ;;
 
 esac
@@ -95,15 +140,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.h ]; 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/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.6.3"
+#define NGINX_VERSION      "0.6.4"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -303,11 +303,11 @@ ngx_conf_set_access_slot(ngx_conf_t *cf,
     ngx_uint_t   i, right, shift, *access;
 
     access = (ngx_uint_t *) (confp + cmd->offset);
-    
+
     if (*access != NGX_CONF_UNSET_UINT) {
         return "is duplicate";
     }
-    
+
     value = cf->args->elts;
 
     *access = 0600;
@@ -328,10 +328,6 @@ ngx_conf_set_access_slot(ngx_conf_t *cf,
             shift = 0;
             p += sizeof("all:") - 1;
 
-        } else if (ngx_strncmp(p, "off", sizeof("off") - 1) == 0) {
-            *access = 0;
-            return NGX_CONF_OK;
-
         } else {
             goto invalid;
         }
@@ -348,7 +344,7 @@ ngx_conf_set_access_slot(ngx_conf_t *cf,
 
         *access |= right << shift;
     }
-    
+
     return NGX_CONF_OK;
 
 invalid:
--- 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;
@@ -1025,7 +1025,7 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x800000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */
+        0x000000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1039,18 +1039,30 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
+                    /* " ", """, "'", %00-%1F, %7F-%FF */
 
-    switch (type) {
-    case NGX_ESCAPE_HTML:
-        escape = html;
-        break;
-    case NGX_ESCAPE_ARGS:
-        escape = args;
-        break;
-    default:
-        escape = uri;
-        break;
-    }
+    static uint32_t   refresh[] = {
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+
+                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
+        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
+
+                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+
+                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
+        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+    };
+
+    static uint32_t  *map[] = { uri, args, html, refresh };
+
+
+    escape = map[type];
 
     if (dst == NULL) {
 
--- 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 }
 
@@ -142,6 +153,7 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c
 #define NGX_ESCAPE_URI       0
 #define NGX_ESCAPE_ARGS      1
 #define NGX_ESCAPE_HTML      2
+#define NGX_ESCAPE_REFRESH   3
 
 #define NGX_UNESCAPE_URI     1
 
--- 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_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -57,8 +57,6 @@ static ngx_int_t ngx_http_dav_depth(ngx_
 static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
     ngx_int_t not_found, char *failed, u_char *path);
 static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
-static char *ngx_http_dav_access(ngx_conf_t *cf, ngx_command_t *cmd,
-    void *conf);
 static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -94,9 +92,9 @@ static ngx_command_t  ngx_http_dav_comma
 
     { ngx_string("dav_access"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
-      ngx_http_dav_access,
+      ngx_conf_set_access_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      0,
+      offsetof(ngx_http_dav_loc_conf_t, access),
       NULL },
 
       ngx_null_command
@@ -1106,66 +1104,6 @@ ngx_http_dav_location(ngx_http_request_t
 }
 
 
-static char *
-ngx_http_dav_access(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    ngx_http_dav_loc_conf_t *lcf = conf;
-
-    u_char      *p;
-    ngx_str_t   *value;
-    ngx_uint_t   i, right, shift;
-
-    if (lcf->access != NGX_CONF_UNSET_UINT) {
-        return "is duplicate";
-    }
-
-    value = cf->args->elts;
-
-    lcf->access = 0600;
-
-    for (i = 1; i < cf->args->nelts; i++) {
-
-        p = value[i].data;
-
-        if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
-            shift = 6;
-            p += sizeof("user:") - 1;
-
-        } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
-            shift = 3;
-            p += sizeof("group:") - 1;
-
-        } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
-            shift = 0;
-            p += sizeof("all:") - 1;
-
-        } else {
-            goto invalid;
-        }
-
-        if (ngx_strcmp(p, "rw") == 0) {
-            right = 6;
-
-        } else if (ngx_strcmp(p, "r") == 0) {
-            right = 4;
-
-        } else {
-            goto invalid;
-        }
-
-        lcf->access |= right << shift;
-    }
-
-    return NGX_CONF_OK;
-
-invalid:
-
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "invalid value \"%V\"", &value[i]);
-    return NGX_CONF_ERROR;
-}
-
-
 static void *
 ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
 {
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -124,6 +124,8 @@ static ngx_int_t ngx_http_fastcgi_script
 
 static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
     void *data);
 
@@ -201,10 +203,17 @@ static ngx_command_t  ngx_http_fastcgi_c
       NULL },
 
     { ngx_string("fastcgi_store"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_fastcgi_store,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("fastcgi_store_access"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
       ngx_conf_set_access_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store),
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
       NULL },
 
     { ngx_string("fastcgi_ignore_client_abort"),
@@ -1635,12 +1644,15 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
+     *     conf->upstream.store_lengths = NULL;
+     *     conf->upstream.store_values = NULL;
      *
      *     conf->index.len = 0;
      *     conf->index.data = NULL;
      */
 
-    conf->upstream.store = NGX_CONF_UNSET_UINT;
+    conf->upstream.store = NGX_CONF_UNSET;
+    conf->upstream.store_access = NGX_CONF_UNSET_UINT;
     conf->upstream.buffering = NGX_CONF_UNSET;
     conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
 
@@ -1685,8 +1697,18 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
-    ngx_conf_merge_uint_value(conf->upstream.store,
-                              prev->upstream.store, 0);
+    if (conf->upstream.store != 0) {
+        ngx_conf_merge_value(conf->upstream.store,
+                                  prev->upstream.store, 0);
+
+        if (conf->upstream.store_lengths == NULL) {
+            conf->upstream.store_lengths = prev->upstream.store_lengths;
+            conf->upstream.store_values = prev->upstream.store_values;
+        }
+    }
+
+    ngx_conf_merge_uint_value(conf->upstream.store_access,
+                              prev->upstream.store_access, 0600);
 
     ngx_conf_merge_value(conf->upstream.buffering,
                               prev->upstream.buffering, 1);
@@ -2159,6 +2181,52 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng
 
 
 static char *
+ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_http_script_compile_t   sc;
+
+    if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths)
+    {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "on") == 0) {
+        flcf->upstream.store = 1;
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        flcf->upstream.store = 0;
+        return NGX_CONF_OK;
+    }
+
+    /* include the terminating '\0' into script */
+    value[1].len++;
+
+    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+    sc.cf = cf;
+    sc.source = &value[1];
+    sc.lengths = &flcf->upstream.store_lengths;
+    sc.values = &flcf->upstream.store_values;
+    sc.variables = ngx_http_script_variables_count(&value[1]);;
+    sc.complete_lengths = 1;
+    sc.complete_values = 1;
+
+    if (ngx_http_script_compile(&sc) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
 #if (NGX_FREEBSD)
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -105,6 +105,8 @@ static char *ngx_http_proxy_pass(ngx_con
     void *conf);
 static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
 
@@ -155,10 +157,17 @@ static ngx_command_t  ngx_http_proxy_com
       NULL },
 
     { ngx_string("proxy_store"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_proxy_store,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("proxy_store_access"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
       ngx_conf_set_access_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.store),
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access),
       NULL },
 
     { ngx_string("proxy_buffering"),
@@ -1497,6 +1506,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
+     *     conf->upstream.store_lengths = NULL;
+     *     conf->upstream.store_values = NULL;
      *
      *     conf->method = NULL;
      *     conf->headers_source = NULL;
@@ -1509,7 +1520,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *     conf->rewrite_locations = NULL;
      */
 
-    conf->upstream.store = NGX_CONF_UNSET_UINT;
+    conf->upstream.store = NGX_CONF_UNSET;
+    conf->upstream.store_access = NGX_CONF_UNSET_UINT;
     conf->upstream.buffering = NGX_CONF_UNSET;
     conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
 
@@ -1561,8 +1573,18 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
-    ngx_conf_merge_uint_value(conf->upstream.store,
-                              prev->upstream.store, 0);
+    if (conf->upstream.store != 0) {
+        ngx_conf_merge_value(conf->upstream.store,
+                                  prev->upstream.store, 0);
+
+        if (conf->upstream.store_lengths == NULL) {
+            conf->upstream.store_lengths = prev->upstream.store_lengths;
+            conf->upstream.store_values = prev->upstream.store_values;
+        }
+    }
+
+    ngx_conf_merge_uint_value(conf->upstream.store_access,
+                              prev->upstream.store_access, 0600);
 
     ngx_conf_merge_value(conf->upstream.buffering,
                               prev->upstream.buffering, 1);
@@ -2371,6 +2393,52 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, 
 
 
 static char *
+ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_proxy_loc_conf_t *plcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_http_script_compile_t   sc;
+
+    if (plcf->upstream.store != NGX_CONF_UNSET || plcf->upstream.store_lengths)
+    {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "on") == 0) {
+        plcf->upstream.store = 1;
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        plcf->upstream.store = 0;
+        return NGX_CONF_OK;
+    }
+
+    /* include the terminating '\0' into script */
+    value[1].len++;
+
+    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+    sc.cf = cf;
+    sc.source = &value[1];
+    sc.lengths = &plcf->upstream.store_lengths;
+    sc.values = &plcf->upstream.store_values;
+    sc.variables = ngx_http_script_variables_count(&value[1]);;
+    sc.complete_lengths = 1;
+    sc.complete_values = 1;
+
+    if (ngx_http_script_compile(&sc) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
 #if (NGX_FREEBSD)
--- 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.6.3';
+our $VERSION = '0.6.4';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -315,6 +315,7 @@ ngx_http_special_response_handler(ngx_ht
 {
     u_char                    *p;
     size_t                     msie_refresh;
+    uintptr_t                  escape;
     ngx_int_t                  rc;
     ngx_buf_t                 *b;
     ngx_str_t                 *uri, *location;
@@ -496,17 +497,19 @@ ngx_http_special_response_handler(ngx_ht
         r->headers_out.content_length = NULL;
     }
 
-    msie_refresh = 0;
-    location = NULL;
-
     if (clcf->msie_refresh
         && r->headers_in.msie
         && (error == NGX_HTTP_MOVED_PERMANENTLY
             || error == NGX_HTTP_MOVED_TEMPORARILY))
     {
+
         location = &r->headers_out.location->value;
+
+        escape = 2 * ngx_escape_uri(NULL, location->data, location->len,
+                                    NGX_ESCAPE_REFRESH);
+
         msie_refresh = sizeof(ngx_http_msie_refresh_head) - 1
-                       + location->len
+                       + escape + location->len
                        + sizeof(ngx_http_msie_refresh_tail) - 1;
 
         r->err_status = NGX_HTTP_OK;
@@ -514,6 +517,11 @@ ngx_http_special_response_handler(ngx_ht
         r->headers_out.content_length_n = msie_refresh;
         r->headers_out.location->hash = 0;
         r->headers_out.location = NULL;
+
+    } else {
+        location = NULL;
+        escape = 0;
+        msie_refresh = 0;
     }
 
     ngx_http_clear_accept_ranges(r);
@@ -595,7 +603,13 @@ ngx_http_special_response_handler(ngx_ht
         p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
                        sizeof(ngx_http_msie_refresh_head) - 1);
 
-        p = ngx_cpymem(p, location->data, location->len);
+        if (escape == 0) {
+            p = ngx_cpymem(p, location->data, location->len);
+
+        } else {
+            p = (u_char *) ngx_escape_uri(p, location->data, location->len,
+                                          NGX_ESCAPE_REFRESH);
+        }
 
         b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
                              sizeof(ngx_http_msie_refresh_tail) - 1);
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -372,7 +372,7 @@ ngx_http_upstream_init(ngx_http_request_
     cln->data = r;
     u->cleanup = &cln->handler;
 
-    u->store = (u->conf->store != 0);
+    u->store = (u->conf->store || u->conf->store_lengths);
 
     ngx_http_upstream_connect(r, u);
 }
@@ -2029,7 +2029,9 @@ ngx_http_upstream_store(ngx_http_request
 
 #if !(NGX_WIN32)
 
-    if (ngx_change_file_access(temp->data, u->conf->store) == NGX_FILE_ERROR) {
+    if (ngx_change_file_access(temp->data, u->conf->store_access)
+        == NGX_FILE_ERROR)
+    {
         err = ngx_errno;
         failed = ngx_change_file_access_n;
         name = temp->data;
@@ -2052,13 +2054,24 @@ ngx_http_upstream_store(ngx_http_request
                 err = ngx_errno;
                 failed = ngx_set_file_time_n;
                 name = temp->data;
-    
+
                 goto failed;
             }
         }
     }
 
-    ngx_http_map_uri_to_path(r, &path, &root, 0);
+    if (u->conf->store_lengths == NULL) {
+
+        ngx_http_map_uri_to_path(r, &path, &root, 0);
+
+    } else {
+        if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,
+                                u->conf->store_values->elts)
+            == NULL)
+        {
+            return;
+        }
+    }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "upstream stores \"%s\" to \"%s\"", temp->data, path.data);
@@ -2074,8 +2087,8 @@ ngx_http_upstream_store(ngx_http_request
 
     if (err == NGX_ENOENT) {
 
-        err = ngx_create_full_path(path.data, ngx_dir_access(u->conf->store));
-
+        err = ngx_create_full_path(path.data,
+                                   ngx_dir_access(u->conf->store_access));
         if (err == 0) {
             if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
                 return;
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -118,7 +118,7 @@ typedef struct {
     size_t                          temp_file_write_size_conf;
 
     ngx_uint_t                      next_upstream;
-    ngx_uint_t                      store;
+    ngx_uint_t                      store_access;
 
     ngx_bufs_t                      bufs;
 
@@ -141,6 +141,10 @@ typedef struct {
     ngx_str_t                       location;
     ngx_str_t                       url;  /* used in proxy_rewrite_location */
 
+    ngx_array_t                    *store_lengths;
+    ngx_array_t                    *store_values;
+
+    signed                          store:2;
     unsigned                        intercept_404:1;
     unsigned                        change_buffering:1;
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -501,7 +501,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);
--- 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