使用OCI_THREADED选项在OCI环境设置失败后尝试释放OCIEnv结构时发生分段错误

时间:2013-02-19 10:34:14

标签: oracle oci

摘要

在使用OCI_THREADED选项(由于例如错误配置的NLS_LANG环境变量导致的故障)导致OCI环境设置失败后尝试释放OCIEnv结构时发生分段错误。

当OCIEnvCreate在没有 OCI_THREADED选项的情况下调用时,示例代码不会崩溃,它会按预期工作。

示例代码

#include <oci.h>;
#include <stdio.h>
#include <string.h>

int my_connect(const char *username, const char *password, const char *sid)
{
  OCIEnv *env = NULL;
  OCIError *err = NULL;
  OCISvcCtx *svc = NULL;

  if ( OCIEnvCreate(&env,
                   OCI_THREADED,
                   (dvoid *)0,
                   0,
                   0,
                   0,
                   (size_t)0,
                   (dvoid **)0) )
  {
    fprintf(stderr, "unable to initialize environment\n");
    if ( env )
    {
      printf("env:[%p]\n", env);
      OCIHandleFree(env, OCI_HTYPE_ENV); // segfault.
    }
    return -1;
  }

  printf("env:[%p]\n", env);

  if ( OCIHandleAlloc((dvoid *)env,
                      (dvoid **)&err,
                      OCI_HTYPE_ERROR,
                      (size_t)0,
                      (dvoid **)0) )
  {
    fprintf(stderr, "unable to alloc error handlers\n");
    goto error;
  }

  if ( OCIHandleAlloc((dvoid *) env,
                      (dvoid **) &svc,
                      OCI_HTYPE_SVCCTX,
                      (size_t) 0,
                      (dvoid **)0) )
  {
    fprintf(stderr, "unable to allocate service handlers\n");
    goto error;
  }

  if ( OCILogon(env, 
                err, 
                &svc,
                (CONST OraText *) username,
                strlen(username),
                (CONST OraText *) password,
                strlen(password),
                sid,
                strlen(sid)
                ) )
  {
    fprintf(stderr, "login failed\n");
    goto error;
  }
  printf("logged in\n");
  if ( OCILogoff (svc, err) )
  {
    fprintf(stderr, "logoff failed\n");
    goto error;
  }
  printf("logged out\n");
error:
  if ( err )
    OCIHandleFree(err, OCI_HTYPE_ERROR);
  if ( svc )
    OCIHandleFree(svc, OCI_HTYPE_SVCCTX);
  if ( env )
    OCIHandleFree(env, OCI_HTYPE_ENV);
  return 0;
}

int main()
{
  return my_connect("test_user", "qqq123", "XE");
}

运行前

export NLS_LANG=x

堆栈跟踪

问题是__pthread_mutex_destroy是用NULL指针调用的。

#0    __pthread_mutex_destroy (mutex=0x0) at pthread_mutex_destroy.c:28
#1    0x00007ffff585e6e0 in sltsmxd () from /lib/libclntsh.so.11.1
#2    0x00007ffff56a147c in kpufhndl0 () from /lib/libclntsh.so.11.1
#3    0x00007ffff56a0185 in kpufhndl () from /lib/libclntsh.so.11.1
#4    0x00007ffff567cac1 in OCIHandleFree () from /lib/libclntsh.so.11.1
#5    0x0000000000400a0c in my_connect (username=0x400dd1 "test_user", password=0x400dca       "qqq123", sid=0x400dc7 "XE") at test2.c:24
#6    0x0000000000400c27 in main () at test2.c:84

产品详情

Basic Lite Package Information

Thu Oct  4 13:00:49 UTC 2007

Client Shared Library 64-bit - 11.1.0.6.0

System name:    Linux
Release:        2.6.9-34.0.1.0.11.ELsmp
Version:        #1 SMP Mon Dec 4 22:20:39 UTC 2006
Machine:        x86_64

操作系统详细信息

Linux 3.2.0-37-generic #58-Ubuntu SMP Thu Jan 24 15:28:10 UTC 2013 x86_64 GNU/Linux
Distributor ID:    Ubuntu
Description:       Ubuntu 10.04.4 LTS
Release:           10.04
Codename:          lucid

问题

目前我还没有释放该内存区域,但这不是一个好的解决方案。 您怎么看?什么是好的解决方案?

1 个答案:

答案 0 :(得分:0)

这看起来可能是'Basic Lite'即时客户端中的一个错误,但我看不出任何与MOS有关的内容;但如果是这样,那么在OCIEnvCreate(),而不是OCIHandleFree()正如我认为你建议的那样。

我见过的示例代码都没有尝试在遇到OCIEnv失败时清理OCIEnvCreate();似乎总是退出,包括Oracle's own example code。看起来这个函数正在创建OCIEnv结构,因为你得到了一个指向它的指针,但可能没有分配它的内部结构。由于它处于一个不确定的状态,试图清理它可能是一个吃力不讨好的任务。因此,似乎不应该拨打OCIHandleFree()

我能够在Oracle Enterprise Linux 5.6下使用Linux x86-64的11.2.0.3.0 Basic Lite客户端软件包(来自TechNet downloads)重新创建问题。使用Basic(非Lite)客户端软件包时,没有看到问题 - 没有内存故障,也没有“无法初始化环境”消息,因此OCIEnvCreate()调用成功。然而它确实无法登录,无论如何这可能更合理。

这表明您不应该期望函数因NLS_LANG值而失败 - 该部分看起来像一个bug。如果由于某些其他原因确实失败了,那么不要试图清理。我看不出任何暗示SDK不能与Lite包一起使用的东西,但是你强迫失败然后试图清理超出常规,所以之前可能不会经常看到这种组合。尽管如此,即使没有清理触发内存故障,它看起来就像是在错误的点上失败了。

要通过明显的错误并在正确的位置获取失败,在登录期间,您可能需要切换到Basic(非Lite)客户端软件包。