2

概要

OCI_THREADED オプションを使用した OCI 環境設定の失敗 (NLS_LANG 環境変数の設定ミスなどによる失敗) の後に OCIEnv 構造を解放しようとすると、セグメンテーション違反が発生します。

OCI_THREADED オプションを指定せずに OCIEnvCreateをコールすると、サンプル コードはクラッシュせず、期待どおりに動作します。

サンプルコード

#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

OS詳細

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

質問

現時点では、そのメモリ領域を解放していませんが、これは良い解決策ではありません。あなたはどう思いますか、良い解決策は何ですか?

4

1 に答える 1

0

これは 'Basic Lite' インスタント クライアントのバグのようですが、MOS に関連するものは見当たりません。しかし、もしそうOCIEnvCreate()ならOCIHandleFree()、私があなたが示唆しているとは思わない.

私が見たサンプル コードのどれもOCIEnv、エラーが発生したときにをクリーンアップしようとはしませんでしたOCIEnvCreate()Oracle独自のサンプルコードを含め、常に終了するようです。その関数は、構造体へのポインターを取得して以来、構造体を作成しているように見えOCIEnvますが、おそらくその内部を割り当てていません。不確定な状態にあるため、クリーンアップを試みるのはおそらく報われない作業になるでしょう。したがって、 を呼び出さなくても問題ないように思われますOCIHandleFree()

Linux x86-64 用の 11.2.0.3.0 Basic Lite クライアント パッケージ ( TechNet ダウンロードから) を使用して、Oracle Enterprise Linux 5.6 で問題を再現することができました。Basic (非 Lite) クライアント パッケージを使用した場合、問題は見られませんでした。メモリ障害はなく、「環境を初期化できません」というメッセージも表示されないため、OCIEnvCreate()呼び出しは成功します。ただし、ログインに失敗しますが、これはおそらくより合理的です。

これは、値が原因で関数が失敗することを期待すべきではないことを示唆していNLS_LANGます-その部分はバグのように見えます. 他の理由で失敗した場合は、クリーンアップを試みないでください。SDK が Lite パッケージで動作することが期待されていないことを示唆するものは何も見当たりませんが、失敗を強制してから標準を超えてクリーンアップしようとしているため、この組み合わせは以前はあまり見られなかった可能性があります。ただし、クリーンアップによってメモリ障害が発生しなくても、間違った時点で失敗しているように見えます。

明らかなバグを回避し、適切な時点で障害を発生させるには、ログイン時に Basic (非 Lite) クライアント パッケージに切り替える必要がある場合があります。

于 2013-02-20T10:00:09.457 に答える