我剛剛遇到一個令人驚訝的緩衝區溢出,同時嘗試在TCP套接字的recv上使用標誌MSG_TRUNC。帶有MSG_TRUNC的Linux TCP recv() - 寫入緩衝區?
它似乎只發生在gcc(不是鐺聲),只有在編譯優化時纔會發生。
根據該鏈接:http://man7.org/linux/man-pages/man7/tcp.7.html
由於2.4版本,Linux支持在的recv的flags參數使用MSG_TRUNC的(2)(和recvmsg(2))。該標誌會導致接收到的數據字節被丟棄,而不是通過調用者提供的緩衝區傳回。從Linux 2.4.4開始,MSG_PEEK在與MSG_OOB一起使用以接收帶外數據時也具有此效果。
這是否意味着提供的緩衝區不會被寫入?我期待如此,但很驚訝。 如果您傳遞一個緩衝區(非零指針)並且其大小大於緩衝區大小,則當客戶端發送大於緩衝區的內容時,會導致緩衝區溢出。如果消息很小並且適合緩衝區(沒有溢出),它實際上不會將消息寫入緩衝區。 顯然,如果你傳遞一個空指針,問題就會消失。
客戶端是一個簡單的netcat發送大於4個字符的消息。
服務器代碼是基於: http://www.linuxhowtos.org/data/6/server.c
更改的讀出與向MSG_TRUNC RECV,和緩衝區大小爲4(bzero至4以及)。
編譯在Ubuntu 14.04上。這些彙編工作正常(無警告):
的gcc -o server.x server.c
鐺-o server.x server.c
鐺-02 server.x server.c (?)
這是越野車編制,這也給了一個警告,暗示這個問題:
GCC -O 2 -o server.x server.c
無論如何,就像我提到的將指針改爲null可修復問題,但這是一個已知問題嗎?或者我錯過了手冊頁中的某些內容?
UPDATE:
緩衝區溢出也恰好用gcc -O1。 這裏是編譯警告:
在函數「的recv」, 在server.c從「主」內聯:47:14: 的/ usr /包括/ x86_64的-Linux的GNU /比特/ SOCKET2。h:42:2:警告:調用'__recv_chk_warn'聲明屬性警告:recv調用的長度大於目標緩衝區的大小[缺省情況下啓用] return __recv_chk_warn(__fd,__buf,__n,__bos0(__buf),__flags) ;
這裏是緩衝區溢出:
./server.x 10003 *緩衝區溢出檢測*:./server.x終止 =======回溯:= ======== /lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7fcbdc44b38f] /lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c) [0x7fcbdc4e2c9c] /lib/x86_64-linux-gnu/libc.so.6(+0x109b60)[0x7fcbdc4e1b60] /lib/x86_64-linux-gnu/libc.so.6(+0x10a023)[0x7fcbdc4e2023] ./server.x[0x400a6c] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7fcbdc3f9ec5] ./server.x[0x400879] ======= Memory圖:====== 00400000-00401000 R-XP 00000000 08:01 17732> /tmp/server.x ...更多信息這裏 中止(核心轉儲)
和GCC版本:
GCC(Ubuntu的4.8.4-2ubuntu1〜14.04.3)4.8.4
緩衝區和接收電話:
char buffer [4];
n = recv(newsockfd,buffer,255,MSG_TRUNC);
這似乎解決它:
N =的recv(newsockfd,NULL,255,MSG_TRUNC);
這不會產生任何警告或錯誤:
的gcc -Wall -pedantic -Wextra -o server.x server.c
這裏是完整的代碼:
/* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[4];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,4);
n = recv(newsockfd,buffer,255,MSG_TRUNC);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
UPDATE: 也恰好在Ubuntu 16.04,用gcc版本:
GCC(Ubuntu的5.4.0-6ubuntu1〜16.04.2)5.4.0 20160609
*什麼*警告你得到什麼?您是否嘗試過啓用更多警告(例如'-Wall -Wextra -pedantic'或類似的)?並請顯示您的實際'recv'調用以及緩衝區的定義。 –
此外,由於它明顯的作品,它可能不是與內核問題,但與* *編譯器,這樣你就可以請告訴我們您正在使用的GCC的版本?你有沒有嘗試過GCC的更高版本?早些時候?試圖禁用某些特定的優化?用'-O1'測試?使用'-O1'進行測試,然後啓用一個接一個的特定優化選項,直到出現問題(因此您知道哪一個是原因)? –
這個問題不太可能出現在編譯器中。它可能與C庫(這可能是太好的區別),但它更可能與*程序*。爲了有意義地解決這個問題,我們需要能夠重現問題的代碼。如果你想讓我們看看這樣的代碼,那麼就以[mcve]的形式呈現它。 –