原創(chuàng)文章,轉(zhuǎn)載請注明: 轉(zhuǎn)載自pagefault
本文鏈接地址: nginx fastcgi模塊的一個(gè)bug
上周服務(wù)器更新到nginx的0.8.X之后,nginx出現(xiàn)了core dump的情況,而在0.7.X并不會出現(xiàn),通過察看core dump文件以及nginx 0.8.x和0.7.x的比較,發(fā)現(xiàn)core dump是nginx 0.8.40引入下面這個(gè)feature才導(dǎo)致的:
*) 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中的這個(gè)頭,比如定義了一個(gè) fastcgi_param $HTTP_HOST test, 那么傳遞給后端時(shí),host這個(gè)頭的值就是test.
這里的邏輯是這樣子的,當(dāng)nginx創(chuàng)建一個(gè)fastcgi request的時(shí)候,會先計(jì)算所需要的長度,首先是計(jì)算header的長度,在計(jì)算之前會先分配一個(gè)ignored數(shù)組(用來保存將要被忽略的頭),它的大小是配置文件中fastcgi_param定義的以HTTP_開頭的變量的個(gè)數(shù). 然后遍歷所有的request header,如果發(fā)現(xiàn)header的名字和fastcgi_param中定義的變量的(HTTP_開頭)名字相同(使用hash),則將這個(gè)header指針放到ignored數(shù)組中,最后在拷貝request header的時(shí)候直接在這個(gè)數(shù)組里面查找,如果有則跳過,否則拷貝頭以及它的值。
看起來沒什么問題,可是這里忽略了request header有可能會有重復(fù)的這個(gè)情況,此時(shí)ignored數(shù)組可能就會越界,從而導(dǎo)致core dump.
來看對應(yīng)的代碼,引起問題的代碼是下面這段(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_開頭)個(gè)數(shù) if (flcf->header_params) { // 分配內(nèi)存 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就會加一.而如果重復(fù)的頭大于fastcgi_param中定義的變量的(HTTP_開頭)的個(gè)數(shù),則ignored肯定會越界. ignored[header_params++] = &header[i]; continue; } n += sizeof("HTTP_" ) - 1; } else { n = sizeof("HTTP_" ) - 1 + header[i].key.len; }
|
舉個(gè)例子,配置文件里面包含下面的命令:
fastcgi_param HTTP_HOST $http_host;
然后客戶端傳過來的頭中如果包含多個(gè)host頭,則nginx就會core dump掉.