プロジェクト

全般

プロフィール

AzurePipelinesのClangエラー対応メモ

2025/03/10記
下記PRでのClang静的解析エラーをChatGPTに聞きながら試したコード
https://github.com/OpenRTM/OpenRTM-aist/pull/1178

  • 現ソースでエラー発生
      void Properties::setDefaults(const char* const defaults[], size_t num)
      {
        for (size_t i = 0; i < num && defaults[i][0] != '\0' ; i += 2)
          {
            setDefault(eraseBothEndsBlank(defaults[i]),
                       eraseBothEndsBlank(defaults[i + 1]));
          }
      }
    

    D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(313,35): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
      313 |     for (size_t i = 0; i < num && defaults[i][0] != '\0' ; i += 2)
          |                                   ^~~~~~~~
    D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(315,39): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
      315 |         setDefault(eraseBothEndsBlank(defaults[i]),
          |                                       ^~~~~~~~
    D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(316,39): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
      316 |                    eraseBothEndsBlank(defaults[i + 1]));
          |                                       ^~~~~~~~
    3 errors generated.
    
  • ChatGPTに聞いて下記に変更
    • 修正ポイント
      • i + 1 < num をループ条件に追加 → 配列の範囲外アクセスを防ぐ
      • defaults[i] と defaults[i + 1] の NULL チェックを追加 → NULL ポインタの参照を防ぐ
      • defaults[i]0 != '\0' のチェックを NULL チェックの後に実施
    • 変更ソース(1)
      void Properties::setDefaults(const char* const defaults[], size_t num)
      {
          for (size_t i = 0; i + 1 < num; i += 2) // i + 1 < num を追加
          {
              if (defaults[i] == nullptr || defaults[i + 1] == nullptr)
              {
                  continue; // NULL の場合はスキップ
              }
      
              if (defaults[i][0] != '\0') // NULL チェック後にアクセス
              {
                  setDefault(eraseBothEndsBlank(defaults[i]),
                             eraseBothEndsBlank(defaults[i + 1]));
              }
          }
      }
      

      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(315,11): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        315 |       if (defaults[i] == nullptr || defaults[i + 1] == nullptr)
            |           ^~~~~~~~
      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(315,37): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        315 |       if (defaults[i] == nullptr || defaults[i + 1] == nullptr)
            |                                     ^~~~~~~~
      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(319,11): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        319 |       if (defaults[i][0] != '\0')
            |           ^~~~~~~~
      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(321,39): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        321 |         setDefault(eraseBothEndsBlank(defaults[i]),
            |                                       ^~~~~~~~
      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(322,34): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        322 |               eraseBothEndsBlank(defaults[i + 1]));
            |                                  ^~~~~~~~
      5 errors generated.
      
  • このエラーをChatGPTに聞く
    • エラー原因は以下とのこと
      1.defaults[i] や defaults[i + 1] が NULL の可能性
        ・defaults は const char* const defaults[] の形なので、NULL の可能性がある。
        ・しかし、defaults[i] == nullptr の判定自体が defaults[i] を参照するため、Clang は「NULL かどうか分からないのにアクセスしている」と判断。
      2.defaults がポインタの配列ではなく nullptr の可能性
        ・defaults の型 const char* const defaults[] は 実際には const char** として扱われる。
        ・もし defaults == nullptr の場合、defaults[i] にアクセスすること自体が 未定義動作 になる。
      
    • 修正ポイント
      • defaults == nullptr のチェックを 最初に実施 して nullptr の可能性を排除
      • defaults[i] のアクセスを const char* key = defaults[i]; に代えて、変数に格納してからチェック
      • key0 != '\0' の条件を nullptr チェックの後にすることで、NULL 参照を防止
    • 変更ソース(2)
      void Properties::setDefaults(const char* const defaults[], size_t num)
      {
          if (defaults == nullptr) { // defaults 自体が nullptr なら即リターン
              return;
          }
      
          for (size_t i = 0; i + 1 < num; i += 2)
          {
              const char* key = defaults[i];
              const char* value = defaults[i + 1];
      
              if (key == nullptr || value == nullptr) {
                  continue; // NULL の場合はスキップ
              }
      
              if (key[0] != '\0') { // NULL チェック後にアクセス
                  setDefault(eraseBothEndsBlank(key), eraseBothEndsBlank(value));
              }
          }
      }
      

      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(319,27): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        319 |         const char* key = defaults[i];
            |                           ^~~~~~~~
      D:\a\1\s\src\lib\coil\common\coil\Properties.cpp(320,27): error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
        320 |         const char* val = defaults[i + 1];
            |                           ^~~~~~~~
      2 errors generated.
      
    • 変更ソース(3)
      • 変更ソース(2)と同時に下記を指摘される
      • std::vector<std::string> を使用する(推奨)
        C++ では const char* 配列ではなく、std::vector<std::string> を使うのが安全です。
        void Properties::setDefaults(const std::vector<std::string>& defaults)
        {
            size_t num = defaults.size();
            for (size_t i = 0; i + 1 < num; i += 2)
            {
                if (!defaults[i].empty()) { // 文字列が空でない場合のみ処理
                    setDefault(eraseBothEndsBlank(defaults[i]), eraseBothEndsBlank(defaults[i + 1]));
                }
            }
        }