2013-05-02 54 views
1

看來,我在Windows中發現了一個錯誤... 好吧,別這麼可悲。我試圖對UDP進行通用的sendto()操作,偶爾發現WinXP(32位,SP3,在真實和虛擬機上檢查)返回WSAGetLastError()發出的「-1」字節,錯誤10014(又名WSAEFAULT)。僅發生在IPv4地址上(與IPv6目的地相同的代碼完美地工作)。重現的主要條件是在全局範圍聲明的「const struct sockaddr_in」的使用。下面是VS2010的純C代碼(我也試過用Eclipse + MinGW的,得到了​​相同的結果):WinXP:如果目標地址是常量限定,IPv4特定的,則sendto()失敗,並返回10014(WSAEFAULT)

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <winsock2.h> 
#include <stdint.h> 

#pragma comment(lib, "Ws2_32.lib") 

#define INADDR_UPNP_V4 0xEFFFFFFA 
#define htons(x) ((((uint16_t)(x) & 0xFF00) >> 8) | (((uint16_t)(x) & 0x00FF) << 8)) 
#define htonl(x) ((((uint32_t)(x) & 0xFF000000) >> 24) | (((uint32_t)(x) & 0x00FF0000) >> 8) | (((uint32_t)(x) & 0x0000FF00) << 8) | (((uint32_t)(x) & 0x000000FF) << 24)) 

// Magic "const" qualifier, causes run-time error 
const struct sockaddr_in addr_global = { 
    AF_INET, 
    htons(1900), 
    { 
      htonl(INADDR_UPNP_V4) 
    }, 
    {0}, 
}; 

int main(int argc, char** argv) 
{ 
#define CR_LF "\r\n" 

    // these two lines to un-buffer console window output at Win32, see URL below for details 
    // http://wiki.eclipse.org/CDT/User/FAQ#Eclipse_console_does_not_show_output_on_Windows 
    setvbuf(stdout, NULL, _IONBF, 0); 
    setvbuf(stderr, NULL, _IONBF, 0); 

    printf("Started\n"); 

    const struct sockaddr_in addr_local = { 
      AF_INET, 
      htons(1900), 
      { 
        htonl(INADDR_UPNP_V4) 
      }, 
      {0}, 
    }; 

    const char *MSEARCH_REQUEST_V4 = "M-SEARCH * HTTP/1.1"CR_LF 
    "Host:239.255.255.250:1900"CR_LF 
    "MAN:\"ssdp:discover\""CR_LF 
    "ST:ssdp:all"CR_LF 
    "MX:3"CR_LF 
    CR_LF; 

    const int MSEARCH_LEN = strlen(MSEARCH_REQUEST_V4); 

    WSADATA wsaData; 
    int res = WSAStartup(MAKEWORD(2, 2), &wsaData); 

    int af = AF_INET; 
    int sock_id = socket(af, SOCK_DGRAM, IPPROTO_UDP); 
    if (-1 == sock_id) { 
     printf("%s: socket() failed with error %i/%i\n", __FUNCTION__, 
       errno, WSAGetLastError()); 
     return 1; 
    } 

    int data_sent = 0; 

    printf("1st sendto()\n"); 
    data_sent = sendto(sock_id, MSEARCH_REQUEST_V4, 
      MSEARCH_LEN, 0, 
      (const struct sockaddr * const)&addr_local, 
      sizeof(struct sockaddr_in)); 
    if (data_sent < 0) { 
     printf("%s: sendto(local) failed with error %i/%i\n", __FUNCTION__, 
       errno, WSAGetLastError()); 
    } 

    printf("2nd sendto(), will fail on WinXP SP3 (32 bit)\n"); 
    data_sent = sendto(sock_id, MSEARCH_REQUEST_V4, 
      MSEARCH_LEN, 0, 
      (const struct sockaddr * const)&addr_global, 
      sizeof(struct sockaddr_in)); 
    if (data_sent < 0) { 
     printf("%s: sendto(global) failed with error %i/%i\n", __FUNCTION__, 
       errno, WSAGetLastError()); 
    } 

    closesocket(sock_id); 

    res = WSACleanup(); 
    printf("Finished\n"); 
    return 0; 
} 

所以,如果你運行在Win7的這個代碼,例如,這將是絕對OK。但是,如果WinXP配備了「const」限定符(參見上面的「魔術」註釋),則它將失敗addr_global的使用。此外,「輸出」窗口說:在SendtoBugXP.exe在0x71a912f4

第一次機會異常:0000005: 訪問衝突寫入位置0x00415744。

在「Autos」窗口的幫助下,很容易發現0x00415744位置是addr_global.sin_zero字段的地址。看來,WinXP在那裏寫入零並且違反了內存訪問標誌。或者這只是愚蠢的我,試圖走錯門?

非常感謝您的意見。提前致謝。

回答

0

總結來自其他論壇的結果:是的,這是Windows缺陷,在「桌面」上存在WinXP,在「服務器」細分中存在Win2003。 WinSock代碼確實試圖強制使用零填充「sin_zero」字段。而「const」全局作用域會導致內存訪問衝突。堆棧跟蹤是關於這樣的:

主題[1] 0在0x71a912f4
0x71a52f9f
WSAConnect()(暫停:信號:SIGSEGV段錯誤)
WSHTCPIP WSHGetSockaddrType()在0x71ab2fd7
主! ()at tests_main.c:77 0x401584

其他人在bind()上觀察到同樣的行爲。

1

是的,你發現了一個錯誤。 sendto()將該參數聲明爲const,但仍然寫入它。祝你好運,但它固定。提示:它可能在您的防病毒或防火牆中。

+0

不,不是關於防病毒/防火牆(我在虛擬機上打開/關閉所有設置,所以Windows警告我「處於危險中」,如果關閉,圖片仍然相同)。 – 2013-05-02 20:54:56

+1

那麼它畢竟是Windows XP。 – Joshua 2013-05-02 21:00:08

相關問題