原創文章,轉載請注明: 轉載自pagefault
本文鏈接地址: nginx fastcgi模塊的一個bug
上周服務器更新到nginx的0.8.X之后,nginx出現了core dump的情況,而在0.7.X并不會出現,通過察看core dump文件以及nginx 0.8.x和0.7.x的比較,發現core dump是nginx 0.8.40引入下面這個feature才導致的:
*) Feature: a “fastcgi_param” directive with value starting with
“HTTP_” overrides a client request header line.
在nginx 0.8.40之后,如果你的fastcgi_param定義的變量以HTTP_開頭,則傳遞給后端的頭會忽略request header中的這個頭,比如定義了一個 fastcgi_param $HTTP_HOST test, 那么傳遞給后端時,host這個頭的值就是test.
這里的邏輯是這樣子的,當nginx創建一個fastcgi request的時候,會先計算所需要的長度,首先是計算header的長度,在計算之前會先分配一個ignored數組(用來保存將要被忽略的頭),它的大小是配置文件中fastcgi_param定義的以HTTP_開頭的變量的個數. 然后遍歷所有的request header,如果發現header的名字和fastcgi_param中定義的變量的(HTTP_開頭)名字相同(使用hash),則將這個header指針放到ignored數組中,最后在拷貝request header的時候直接在這個數組里面查找,如果有則跳過,否則拷貝頭以及它的值。
看起來沒什么問題,可是這里忽略了request header有可能會有重復的這個情況,此時ignored數組可能就會越界,從而導致core dump.
來看對應的代碼,引起問題的代碼是下面這段(ngx_http_fastcgi_create_request).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // 這里header_params就是fastcgi_param中定義的變量的(HTTP_開頭)個數 if (flcf->header_params) { // 分配內存 ignored = ngx_palloc(r->pool, flcf->header_params * sizeof(void *)); if (ignored == NULL) { return NGX_ERROR; } } part = &r->headers_in.headers.part; header = part->elts; // 開始遍歷 for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (flcf->header_params) { .............................................................. //headers_hash 就是fastcgi_param中定義的變量(HTTP_開頭)名字的hash表 if (ngx_hash_find(&flcf->headers_hash, hash, lowcase_key, n)) { // 可以看到只要找到相同的hash,則header_params就會加一.而如果重復的頭大于fastcgi_param中定義的變量的(HTTP_開頭)的個數,則ignored肯定會越界. ignored[header_params++] = &header[i]; continue; } n += sizeof("HTTP_" ) - 1; } else { n = sizeof("HTTP_" ) - 1 + header[i].key.len; }
|
舉個例子,配置文件里面包含下面的命令:
fastcgi_param HTTP_HOST $http_host;
然后客戶端傳過來的頭中如果包含多個host頭,則nginx就會core dump掉.