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])); } } }
- エラー原因は以下とのこと