[ruby-oci8-commit] [536] trunk/ruby-oci8: Fix SEGV when a temporary LOB is GCed while another LOB is read.

nobody at rubyforge.org nobody at rubyforge.org
Sun Oct 28 04:51:35 UTC 2012


Revision: 536
Author:   kubo
Date:     2012-10-28 04:51:34 +0000 (Sun, 28 Oct 2012)
Log Message:
-----------
Fix SEGV when a temporary LOB is GCed while another LOB is read.
(github issue #20 reported by techsplicer)

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/lob.c
    trunk/ruby-oci8/ext/oci8/oci8.c
    trunk/ruby-oci8/ext/oci8/oci8.h
    trunk/ruby-oci8/ext/oci8/oci8lib.c

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2012-10-02 09:09:26 UTC (rev 535)
+++ trunk/ruby-oci8/ChangeLog	2012-10-28 04:51:34 UTC (rev 536)
@@ -1,3 +1,8 @@
+2012-10-28  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/lob.c, ext/oci8/oci8.c, ext/oci8/oci8.h, ext/oci8/oci8lib.c:
+	    Fix SEGV when a temporary LOB is GCed while another LOB is read.
+	    (github issue #20 reported by techsplicer)
+
 2012-10-02  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/apiwrap.c.tmpl, ext/oci8/connection_pool.c, ext/oci8/env.c,
 	  ext/oci8/extconf.rb, ext/oci8/oci8.c, ext/oci8/oci8.h, ext/oci8/oci8lib.c,

Modified: trunk/ruby-oci8/ext/oci8/lob.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/lob.c	2012-10-02 09:09:26 UTC (rev 535)
+++ trunk/ruby-oci8/ext/oci8/lob.c	2012-10-28 04:51:34 UTC (rev 536)
@@ -113,8 +113,19 @@
         && OCILobIsTemporary(oci8_envhp, oci8_errhp, lob->base.hp.lob, &is_temporary) == OCI_SUCCESS
         && is_temporary) {
 
+#ifdef HAVE_RB_THREAD_BLOCKING_REGION
+        oci8_svcctx_t *svcctx = oci8_get_svcctx(lob->svc);
+        oci8_temp_lob_t *temp_lob = ALLOC(oci8_temp_lob_t);
+
+        temp_lob->next = svcctx->temp_lobs;
+        temp_lob->lob = lob->base.hp.lob;
+        svcctx->temp_lobs = temp_lob;
+        lob->base.type = 0;
+        lob->base.hp.ptr = NULL;
+#else
         /* FIXME: This may stall the GC. */
         OCILobFreeTemporary(lob->svchp, oci8_errhp, lob->base.hp.lob);
+#endif
     }
     lob->svc = Qnil;
     lob->svchp = NULL;

Modified: trunk/ruby-oci8/ext/oci8/oci8.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.c	2012-10-02 09:09:26 UTC (rev 535)
+++ trunk/ruby-oci8/ext/oci8/oci8.c	2012-10-28 04:51:34 UTC (rev 536)
@@ -101,6 +101,18 @@
 static void oci8_svcctx_free(oci8_base_t *base)
 {
     oci8_svcctx_t *svcctx = (oci8_svcctx_t *)base;
+    oci8_temp_lob_t *lob;
+
+    lob = svcctx->temp_lobs;
+    while (lob != NULL) {
+        oci8_temp_lob_t *lob_next = lob->next;
+
+        OCIDescriptorFree(lob->lob, OCI_DTYPE_LOB);
+        xfree(lob);
+        lob = lob_next;
+    }
+    svcctx->temp_lobs = NULL;
+
     if (svcctx->logoff_strategy != NULL) {
         const oci8_logoff_strategy_t *strategy = svcctx->logoff_strategy;
         void *data = strategy->prepare(svcctx);

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2012-10-02 09:09:26 UTC (rev 535)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2012-10-28 04:51:34 UTC (rev 536)
@@ -334,6 +334,11 @@
 
 typedef struct oci8_logoff_strategy oci8_logoff_strategy_t;
 
+typedef struct oci8_temp_lob {
+    struct oci8_temp_lob *next;
+    OCILobLocator *lob;
+} oci8_temp_lob_t;
+
 typedef struct oci8_svcctx {
     oci8_base_t base;
     volatile VALUE executing_thread;
@@ -347,6 +352,7 @@
     char non_blocking;
 #endif
     VALUE long_read_len;
+    oci8_temp_lob_t *temp_lobs;
 } oci8_svcctx_t;
 
 struct oci8_logoff_strategy {

Modified: trunk/ruby-oci8/ext/oci8/oci8lib.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8lib.c	2012-10-02 09:09:26 UTC (rev 535)
+++ trunk/ruby-oci8/ext/oci8/oci8lib.c	2012-10-28 04:51:34 UTC (rev 536)
@@ -210,15 +210,68 @@
     OCIBreak(svcctx->base.hp.ptr, oci8_errhp);
 }
 
+typedef struct free_temp_lob_arg_t {
+    oci8_svcctx_t *svcctx;
+    OCISvcCtx *svchp;
+    OCIError *errhp;
+    OCILobLocator *lob;
+} free_temp_lob_arg_t;
+
+static void *free_temp_lob(void *user_data)
+{
+    free_temp_lob_arg_t *data = (free_temp_lob_arg_t *)user_data;
+    sword rv = OCILobFreeTemporary(data->svchp, data->errhp, data->lob);
+
+    data->svcctx->executing_thread = Qnil;
+    return (void*)(VALUE)rv;
+}
+
 /* ruby 1.9 */
 sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data)
 {
+    oci8_temp_lob_t *lob;
+    OCIError *errhp = oci8_errhp;
+
+    if (!NIL_P(svcctx->executing_thread)) {
+        rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
+    }
+
+    lob = svcctx->temp_lobs;
+    while (lob != NULL) {
+        oci8_temp_lob_t *lob_next = lob->next;
+
+        if (svcctx->non_blocking) {
+            free_temp_lob_arg_t arg;
+            sword rv;
+
+            arg.svcctx = svcctx;
+            arg.svchp = svcctx->base.hp.svc;
+            arg.errhp = errhp;
+            arg.lob = lob->lob;
+
+            svcctx->executing_thread = rb_thread_current();
+#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+            rv = (sword)(VALUE)rb_thread_call_without_gvl(free_temp_lob, &arg, oci8_unblock_func, svcctx);
+#else
+            rv = (sword)rb_thread_blocking_region(free_temp_lob, &arg, oci8_unblock_func, svcctx);
+#endif
+            if (rv == OCI_ERROR) {
+                if (oci8_get_error_code(errhp) == 1013) {
+                    rb_raise(eOCIBreak, "Canceled by user request.");
+                }
+            }
+        } else {
+            OCILobFreeTemporary(svcctx->base.hp.svc, errhp, lob->lob);
+        }
+        OCIDescriptorFree(lob->lob, OCI_DTYPE_LOB);
+
+        xfree(lob);
+        svcctx->temp_lobs = lob = lob_next;
+    }
+
     if (svcctx->non_blocking) {
         sword rv;
 
-        if (!NIL_P(svcctx->executing_thread)) {
-            rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
-        }
         svcctx->executing_thread = rb_thread_current();
         /* Note: executing_thread is cleard at the end of the blocking function. */
 #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
@@ -227,7 +280,7 @@
         rv = (sword)rb_thread_blocking_region(func, (rb_blocking_function_t)data, oci8_unblock_func, svcctx);
 #endif
         if (rv == OCI_ERROR) {
-            if (oci8_get_error_code(oci8_errhp) == 1013) {
+            if (oci8_get_error_code(errhp) == 1013) {
                 rb_raise(eOCIBreak, "Canceled by user request.");
             }
         }



More information about the ruby-oci8-commit mailing list