We are experiencing what appears to be a memory corruption
problem with a kernel device driver running under FreeBSD
2.2.8-STABLE with the CAM patches.

The driver manages a series of memory buffers implemented
using a singly-linked tail list. The number and size of
buffers may be changed via an ioctl call which causes all
of the currently-allocated buffers (one per tail list element)
to be deallocated, and a new set - matching the required buffer
size - to be malloc'ed. The tail queue holding the newly-
allocated buffers is a local copy, and not subject to change
in the event of an interrupt meant for the driver. Given
certain choices for the size and number of buffers, we are observing
corruption of the local copy of the tail queue during and 
after the memory allocation process. The corruption usually takes the
form of a NULL pointer overwriting the linking member of a
queue element somewhere in the list, thus rendering some
protions of the allocated memory to be "lost", or non-free-able.

The particular combination of buffer size and number of buffers
which exhibits the problem is:

        buffer_size = 8k + 8 bytes (8k buffer + link pointer etc)
        num_buffers >= 22. 

If less than 22 buffers are allocated, the list appears to remain
intact. I'm not sure if it's relavant, but I note that
22 * 8k is just over the 256 kb size, while 21 * 8k is less than this
total.

Here is the snipet of code which builds the list ->

   STAILQ_INIT(&buildlist);

   gsio_tossbuffers(sc);           /* dump old buffers */
   i = args->value;

   while(i--) {            /* allocate required buffers */
     gb = malloc(sizeof(struct gsio_buf) + args->which, M_DEVBUF, M_NOWAIT);
     debug(BUF2, "g_freelist +> %p", gb);
                   
     if (gb == NULL) {           /* allocate failed, toss all
                                  * buffers */
       debug(BUF, "failed to allocate buffers, freeing");
       while ((gb = STAILQ_FIRST(&buildlist)) != NULL) {
         STAILQ_REMOVE_HEAD(&buildlist, g_link);
         free(gb, M_DEVBUF);
       }
       error = ENOMEM;
       break;
     } else {
       STAILQ_INSERT_HEAD(&buildlist, gb, g_link);
       debug(BUF, "New buffer at %p length 0x%x, next is %p ,tail is %p",
                gb, sizeof(struct gsio_buf) + args->which,
                gb->g_link.stqe_next,
                buildlist.stqh_last);
     }
   }

---
Daniel O'Connor software and network engineer
for Genesis Software - http://www.gsoft.com.au
"The nice thing about standards is that there
are so many of them to choose from."
  -- Andrew Tanenbaum

Attachment: pgpW4KKiLTChh.pgp
Description: PGP signature

Reply via email to