最近更新: 2010-07-22

The Memory Leak of Java Unix Sockets Library (libmatthew-java)

UPDATE: I have commit this code to Matthew Johnson. This bug is fixed in the version 0.7.3 of libmatthew-java. See Debian Bug report logs - #590331.

Java D-Bus bindings requires libmatthew-java, a small JNI library, to use Unix-Sockets. We find a memory leak in libmatthew-java version 0.7.2. Here is the fixed code.

libmatthew-java-0.7.2.tar.gz: unix-java.c: line 238 - 294

JNIEXPORT jint JNICALL Java_cx_ath_matthew_unix_USOutputStream_native_1send__I_3_3B
  (JNIEnv *env, jobject o, jint sock, jobjectArray bufs)
{
   size_t sblen = 1;
   socklen_t sblen_size = sizeof(sblen);
   getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sblen, &sblen_size);

   struct msghdr msg;
   struct iovec *iov;
   msg.msg_name = NULL;
   msg.msg_namelen = 0;
   msg.msg_control = NULL;
   msg.msg_controllen = 0;
   msg.msg_flags = 0;
   size_t els = (*env)->GetArrayLength(env, bufs);
   iov = (struct iovec*) malloc((els<IOV_MAX?els:IOV_MAX) * sizeof(struct iovec));
   msg.msg_iov = iov;
   jbyteArray *b = (jbyteArray*) malloc(els * sizeof(jbyteArray));
   int rv = 0;

   for (int i = 0, j = 0, s = 0; i <= els; i++, j++) {
      if (i == els) {
         msg.msg_iovlen = j;
         rv = sendmsg(sock, &msg, 0);
         for (int k = i-1, l = j-1; l >= 0; k--, l--)
            (*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
         if (-1 == rv) { handleerrno(env); return -1; }
         break;
      }
      b[i] = (*env)->GetObjectArrayElement(env, bufs, i);
      if (NULL == b[i]) {
         msg.msg_iovlen = j;
         rv = sendmsg(sock, &msg, 0);
         for (int k = i-1, l = j-1; l >= 0; k--, l--)
            (*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
         if (-1 == rv) { handleerrno(env); return -1; }
         break;
      }
      size_t l = (*env)->GetArrayLength(env, b[i]);
      if (s+l > sblen || j == IOV_MAX) {
         msg.msg_iovlen = j;
         rv = sendmsg(sock, &msg, 0);
         s = 0;
         for (int k = i-1, l = j-1; l >= 0; k--, l--)
            (*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
         j = 0;  //FIXED!!

         if (-1 == rv) { handleerrno(env); return -1; }
      }
      iov[j].iov_base = (*env)->GetByteArrayElements(env, b[i], NULL);
      iov[j].iov_len = l;
      s += l;
   }

   free(iov);
   free(b);
   return rv;
}

Problem code

j is 0 and l be initialized as j - 1. It means that l will be -1 and the condition (l >= 0) will never meet. Therefore most of the memories allocated by GetByteArrayElements() would not be freed.

      size_t l = (*env)->GetArrayLength(env, b[i]);
      if (s+l > sblen || j == IOV_MAX) {
         msg.msg_iovlen = j;
         rv = sendmsg(sock, &msg, 0);
         j = 0;  //reset j (wrong place)

         s = 0;
         for (int k = i-1, l = j-1; l >= 0; k--, l--) //PROBLEM!!

            (*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
         if (-1 == rv) { handleerrno(env); return -1; }
      }
      iov[j].iov_base = (*env)->GetByteArrayElements(env, b[i], NULL);
Fixed code

j should be reset after loop.

      size_t l = (*env)->GetArrayLength(env, b[i]);
      if (s+l > sblen || j == IOV_MAX) {
         msg.msg_iovlen = j;
         rv = sendmsg(sock, &msg, 0);
         s = 0;
         for (int k = i-1, l = j-1; l >= 0; k--, l--)
            (*env)->ReleaseByteArrayElements(env, b[k], iov[l].iov_base, 0);
         j = 0;  // reset j should be done after above loop.

         if (-1 == rv) { handleerrno(env); return -1; }
      }
      iov[j].iov_base = (*env)->GetByteArrayElements(env, b[i], NULL);
樂多舊網址: http://blog.roodo.com/rocksaying/archives/13135015.html