VirtualBox

Changeset 55856 in vbox


Ignore:
Timestamp:
May 13, 2015 9:01:06 PM (9 years ago)
Author:
vboxsync
Message:

NAT: centralize allocation/deletion of icmp_msg so that book-keeping
is properly maintained. Fixes double-free and potnetial inifinite
loop in (what used to be) icmp_cache_clean().

This change aims to preserve existing behavior. The ping proxy part
should really be divorced from the error proxy part and rewritten.

Location:
trunk/src/VBox/Devices/Network/slirp
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/slirp/ip_icmp.c

    r53776 r55856  
    9090};
    9191
    92 static void icmp_cache_clean(PNATState pData, int iEntries);
    9392
    9493int
     
    9998
    10099#ifndef RT_OS_WINDOWS
     100    TAILQ_INIT(&pData->icmp_msg_head);
     101
    101102    if (iIcmpCacheLimit < 0)
    102103    {
     
    129130    fd_nonblock(pData->icmp_socket.s);
    130131    NSOCK_INC();
    131 
    132     LIST_INIT(&pData->icmp_msg_head);
    133 
    134132#else /* RT_OS_WINDOWS */
    135133    if (icmpwin_init(pData) != 0)
     
    149147    icmpwin_finit(pData);
    150148#else
    151     icmp_cache_clean(pData, -1);
     149    while (!TAILQ_EMPTY(&pData->icmp_msg_head))
     150    {
     151        struct icmp_msg *icm = TAILQ_FIRST(&pData->icmp_msg_head);
     152        icmp_msg_delete(pData, icm);
     153    }
    152154    closesocket(pData->icmp_socket.s);
    153155#endif
    154156}
    155157
     158
    156159#if !defined(RT_OS_WINDOWS)
     160static struct icmp_msg *
     161icmp_msg_alloc(PNATState pData)
     162{
     163    struct icmp_msg *icm;
     164
     165#ifdef DEBUG
     166    {
     167        int iTally = 0;
     168        TAILQ_FOREACH(icm, &pData->icmp_msg_head, im_queue)
     169            ++iTally;
     170        Assert(pData->cIcmpCacheSize == iTally);
     171    }
     172#endif
     173
     174    if (pData->cIcmpCacheSize >= pData->iIcmpCacheLimit)
     175    {
     176        int cTargetCacheSize = pData->iIcmpCacheLimit/2;
     177
     178        while (pData->cIcmpCacheSize > cTargetCacheSize)
     179        {
     180            icm = TAILQ_FIRST(&pData->icmp_msg_head);
     181            icmp_msg_delete(pData, icm);
     182        }
     183    }
     184
     185    icm = RTMemAlloc(sizeof(struct icmp_msg));
     186    if (RT_UNLIKELY(icm == NULL))
     187        return NULL;
     188
     189    TAILQ_INSERT_TAIL(&pData->icmp_msg_head, icm, im_queue);
     190    pData->cIcmpCacheSize++;
     191
     192    return icm;
     193}
     194
     195
     196static void
     197icmp_attach(PNATState pData, struct mbuf *m)
     198{
     199    struct icmp_msg *icm;
     200
     201#ifdef DEBUG
     202    {
     203        /* only used for ping */
     204        struct ip *ip = mtod(m, struct ip *);
     205        Assert(ip->ip_p == IPPROTO_ICMP);
     206    }
     207#endif
     208
     209    icm = icmp_msg_alloc(pData);
     210    if (RT_UNLIKELY(icm == NULL))
     211        return;
     212
     213    icm->im_so = &pData->icmp_socket;
     214    icm->im_m = m;
     215}
     216
     217
     218void
     219icmp_msg_delete(PNATState pData, struct icmp_msg *icm)
     220{
     221    if (RT_UNLIKELY(icm == NULL))
     222        return;
     223
     224#ifdef DEBUG
     225    {
     226        struct icmp_msg *existing;
     227        int iTally = 0;
     228
     229        TAILQ_FOREACH(existing, &pData->icmp_msg_head, im_queue)
     230            ++iTally;
     231        Assert(pData->cIcmpCacheSize == iTally);
     232
     233        Assert(pData->cIcmpCacheSize > 0);
     234        TAILQ_FOREACH(existing, &pData->icmp_msg_head, im_queue)
     235        {
     236            if (existing == icm)
     237                break;
     238        }
     239        Assert(existing != NULL);
     240    }
     241#endif
     242
     243    TAILQ_REMOVE(&pData->icmp_msg_head, icm, im_queue);
     244    pData->cIcmpCacheSize--;
     245
     246    icm->im_so->so_m = NULL;
     247    if (icm->im_m != NULL)
     248        m_freem(pData, icm->im_m);
     249
     250    RTMemFree(icm);
     251}
     252
     253
    157254/*
    158255 * ip here is ip header + 64bytes readed from ICMP packet
     
    185282        case IPPROTO_ICMP:
    186283            icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
    187             LIST_FOREACH(icm, &pData->icmp_msg_head, im_list)
     284            TAILQ_FOREACH(icm, &pData->icmp_msg_head, im_queue)
    188285            {
    189286                m0 = icm->im_m;
     
    250347                found = 1;
    251348                so = last_socket;
    252                 goto sofound;
     349                break;
    253350            }
    254351            for (so = head_socket->so_prev; so != head_socket; so = so->so_prev)
     
    269366            Log(("NAT:ICMP: unsupported protocol(%d)\n", ip->ip_p));
    270367    }
    271     sofound:
    272     if (found == 1 && icm == NULL)
    273     {
     368
     369#ifdef DEBUG
     370    if (found)
     371        Assert((icm != NULL) ^ (so != NULL));
     372#endif
     373
     374    if (found && icm == NULL)
     375    {
     376        /*
     377         * XXX: Implies this is not a pong, found socket.  This is, of
     378         * course, wasteful since the caller will delete icmp_msg
     379         * immediately after processing, so there's not much reason to
     380         * clutter up the queue with it.
     381         */
     382        AssertReturn(so != NULL, NULL);
     383
     384        /*
     385         * XXX: FIXME: If the very first send(2) fails, the socket is
     386         * still in SS_NOFDREF and so we will not report this too.
     387         */
    274388        if (so->so_state == SS_NOFDREF)
    275389        {
    276             /* socket is shutdowning we've already sent ICMP on it.*/
    277             Log(("NAT: Received icmp on shutdowning socket (probably corresponding ICMP socket has been already sent)\n"));
     390            /* socket is shutting down we've already sent ICMP on it. */
     391            Log(("NAT:ICMP: disconnected %R[natsock]\n", so));
     392            LogFlowFunc(("LEAVE: icm:NULL\n"));
    278393            return NULL;
    279394        }
    280         icm = RTMemAlloc(sizeof(struct icmp_msg));
     395
     396        if (so->so_m == NULL)
     397        {
     398            Log(("NAT:ICMP: no saved mbuf for %R[natsock]\n", so));
     399            LogFlowFunc(("LEAVE: icm:NULL\n"));
     400            return NULL;
     401        }
     402
     403        icm = icmp_msg_alloc(pData);
     404        if (RT_UNLIKELY(icm == NULL))
     405        {
     406            LogFlowFunc(("LEAVE: icm:NULL\n"));
     407            return NULL;
     408        }
     409
     410        Log(("NAT:ICMP: for %R[natsock]\n", so));
     411        icm->im_so = so;
    281412        icm->im_m = so->so_m;
    282         icm->im_so = so;
    283         found = 1;
    284         Log(("hit:%R[natsock]\n", so));
    285         /*XXX: this storage not very long,
    286          * better add flag if it should removed from lis
    287          */
    288         LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
    289         pData->cIcmpCacheSize++;
    290         if (pData->cIcmpCacheSize > pData->iIcmpCacheLimit)
    291             icmp_cache_clean(pData, pData->iIcmpCacheLimit/2);
    292         LogFlowFunc(("LEAVE: icm:%p\n", icm));
    293         return (icm);
    294     }
    295     if (found == 1)
    296     {
    297         LogFlowFunc(("LEAVE: icm:%p\n", icm));
    298         return icm;
    299     }
    300 
    301     LogFlowFunc(("LEAVE: NULL\n"));
    302     return NULL;
    303 }
    304 
    305 /**
    306  * iEntries how many entries to leave, if iEntries < 0, clean all
    307  */
    308 static void icmp_cache_clean(PNATState pData, int iEntries)
    309 {
    310     int iIcmpCount = 0;
    311     struct icmp_msg *icm = NULL;
    312     LogFlowFunc(("iEntries:%d\n", iEntries));
    313     if (iEntries > pData->cIcmpCacheSize)
    314     {
    315         LogFlowFuncLeave();
    316         return;
    317     }
    318     while(!LIST_EMPTY(&pData->icmp_msg_head))
    319     {
    320         icm = LIST_FIRST(&pData->icmp_msg_head);
    321         if (    iEntries > 0
    322             &&  iIcmpCount < iEntries)
    323         {
    324             iIcmpCount++;
    325             continue;
    326         }
    327 
    328         LIST_REMOVE(icm, im_list);
    329         if (icm->im_m)
    330         {
    331             pData->cIcmpCacheSize--;
    332             m_freem(pData, icm->im_m);
    333         }
    334         RTMemFree(icm);
    335     }
    336     LogFlowFuncLeave();
    337 }
    338 
    339 static int
    340 icmp_attach(PNATState pData, struct mbuf *m)
    341 {
    342     struct icmp_msg *icm;
    343     struct ip *ip;
    344     ip = mtod(m, struct ip *);
    345     Assert(ip->ip_p == IPPROTO_ICMP);
    346     icm = RTMemAlloc(sizeof(struct icmp_msg));
    347     icm->im_m = m;
    348     icm->im_so = m->m_so;
    349     LIST_INSERT_HEAD(&pData->icmp_msg_head, icm, im_list);
    350     pData->cIcmpCacheSize++;
    351     if (pData->cIcmpCacheSize > pData->iIcmpCacheLimit)
    352         icmp_cache_clean(pData, pData->iIcmpCacheLimit/2);
    353     return 0;
     413    }
     414    LogFlowFunc(("LEAVE: icm:%p\n", icm));
     415    return icm;
    354416}
    355417#endif /* !RT_OS_WINDOWS */
     418
    356419
    357420/*
     
    480543                    if (rc >= 0)
    481544                    {
    482                         m->m_so = &pData->icmp_socket;
    483545                        icmp_attach(pData, m);
    484546                        /* don't let m_freem at the end free atached buffer */
  • trunk/src/VBox/Devices/Network/slirp/ip_icmp.h

    r53399 r55856  
    192192struct icmp_msg
    193193{
    194     LIST_ENTRY(icmp_msg) im_list;
     194    TAILQ_ENTRY(icmp_msg) im_queue;
    195195    struct mbuf *im_m;
    196196    struct socket *im_so;
    197197};
    198198
    199 LIST_HEAD(icmp_storage, icmp_msg);
     199TAILQ_HEAD(icmp_storage, icmp_msg);
    200200
    201201int icmp_init (PNATState , int);
    202202void icmp_finit (PNATState );
    203203struct icmp_msg * icmp_find_original_mbuf (PNATState , struct ip *);
     204void icmp_msg_delete(PNATState, struct icmp_msg *);
    204205
    205206#ifdef RT_OS_WINDOWS
  • trunk/src/VBox/Devices/Network/slirp/socket.c

    r55741 r55856  
    12281228    {
    12291229        LogFunc(("%R[natsock] hasn't stored it's mbuf on sent\n", icm->im_so));
    1230         LIST_REMOVE(icm, im_list);
    1231         RTMemFree(icm);
    1232         return;
     1230        goto done;
    12331231    }
    12341232
     
    12411239        {
    12421240            Log(("NAT: we haven't found echo for this reply\n"));
    1243             return;
     1241            goto done;
    12441242        }
    12451243        /*
     
    12541252            Log(("NAT: ECHO(%d) lenght doesn't match ECHOREPLY(%d)\n",
    12551253                (ip->ip_len - hlen), (ip0->ip_len - (ip0->ip_hl << 2))));
    1256             return;
     1254            goto done;
    12571255        }
    12581256    }
     
    12971295    /* m was freed */
    12981296    icm->im_m = NULL;
    1299     icm->im_so->so_m = NULL;
    1300     LIST_REMOVE(icm, im_list);
    1301     pData->cIcmpCacheSize--;
    1302     RTMemFree(icm);
     1297
     1298  done:
     1299    icmp_msg_delete(pData, icm);
    13031300}
    13041301
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette