From 45aefd30d7aca5b86f3202272ec7592cdcfae10a Mon Sep 17 00:00:00 2001 From: Alex Gu Date: Tue, 26 May 2026 09:20:33 -0500 Subject: [PATCH] fix: filenum_ctrl SIGABRT (#16233) + PosixIO fork _exit + CI action SHA pin (#16502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: PosixIO::call_command 中 fork 后 child 缺少 _exit 导致 unwind 进 parent 代码 当 execlp 失败时,child 分支走 `return std::nullopt`,该子进程会从 PosixIO::call_command 一路 unwind 回 AdbController::call_command 及其 caller,跟 parent 同时跑: - 在 child 地址空间副本中释放 m_callcmd_mutex(caller 处持有的 unique_lock) - 后续命中 `kill(m_child, SIGTERM)` 时 child 视角 m_child==0 → POSIX `kill(0, SIG)` 给整个进程组发信号 → 把 parent 一并杀掉 - 析构时双 close 同一 fd 号(kernel 已重用给别处)→ 关掉 parent 的 socket / log fd → 后续 read/write 错乱 查看 git 历史发现回归引入于 #7748(perf: update PosixIO::call_command, 2023-12)。 当时 perf 重构把 child 分支结尾的 `::exit(exit_ret)` 替换成 `Log.error(...) + return std::nullopt`。 Co-Authored-By: Claude Opus 4.7 (1M context) * ci: 将第三方 action 钉到 commit SHA,符合 GitHub 安全建议 https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions Co-Authored-By: Claude Opus 4.7 (1M context) * fix: filenum_ctrl 中 std::filesystem 调用缺 error_code 导致 SIGABRT Windows 上 debug 截图被另一进程占用时,std::filesystem 多个调用会抛 filesystem_error。 异常未被 filenum_ctrl / save_debug_image / AbstractTask::run 任一层 捕获,沿任务栈一路冒到 MaaCore 顶层(issue #16233)。 Co-Authored-By: Claude Opus 4.7 (1M context) * chore: 清理 PosixIO 子分支 execlp 死赋值 按 Sourcery review 清掉死代码。 Co-Authored-By: Claude Opus 4.7 (1M context) * chore: restore release package workflow --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .github/workflows/blame-ignore.yml | 2 +- .github/workflows/issue-ai-analysis.yml | 2 +- .github/workflows/optimize-templates.yml | 2 +- .github/workflows/pre-commit-scheduled.yml | 2 +- .github/workflows/res-update-game.yml | 2 +- .github/workflows/update-submodules.yml | 2 +- src/MaaCore/Controller/Platform/PosixIO.cpp | 7 +++- src/MaaCore/Utils/DebugImageHelper.hpp | 41 ++++++++++++++++++--- 8 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.github/workflows/blame-ignore.yml b/.github/workflows/blame-ignore.yml index 0d90e527a3..992daad0dd 100644 --- a/.github/workflows/blame-ignore.yml +++ b/.github/workflows/blame-ignore.yml @@ -41,7 +41,7 @@ jobs: - name: Push changes if: ${{ steps.check_changes.outcome == 'failure' }} - uses: ad-m/github-push-action@master + uses: ad-m/github-push-action@d30dc2d070765d7e509df00c34c5fa2dd636ff74 # master as of 2026-05-06 with: branch: ${{ github.ref }} github_token: ${{ secrets.MAA_RESOURCE_SYNC }} diff --git a/.github/workflows/issue-ai-analysis.yml b/.github/workflows/issue-ai-analysis.yml index 4903271c19..6c854c4c0a 100644 --- a/.github/workflows/issue-ai-analysis.yml +++ b/.github/workflows/issue-ai-analysis.yml @@ -33,7 +33,7 @@ jobs: # - https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/dev-v2/.claude/skills/maa-issue-log-analysis/SKILL.md - name: Analyze issue with AI id: analysis - uses: Misteo/ai-issue-analysis@main + uses: Misteo/ai-issue-analysis@c0b258aa9660a1efd7316c9ed6d9ef81f1ac734a # main as of 2026-05-06 with: github-token: ${{ secrets.MAA_BOT_TOKEN }} copilot-github-token: ${{ secrets.COPILOT_GITHUB_TOKEN }} diff --git a/.github/workflows/optimize-templates.yml b/.github/workflows/optimize-templates.yml index 48638be4a2..dfc52c67b2 100644 --- a/.github/workflows/optimize-templates.yml +++ b/.github/workflows/optimize-templates.yml @@ -104,7 +104,7 @@ jobs: - name: Push changes if: steps.check_push.outputs.is_pr != 'True' && steps.commit_changes.outputs.have_commits == 'True' && github.repository_owner == 'MaaAssistantArknights' - uses: ad-m/github-push-action@master + uses: ad-m/github-push-action@d30dc2d070765d7e509df00c34c5fa2dd636ff74 # master as of 2026-05-06 with: github_token: ${{ secrets.MAA_RESOURCE_SYNC }} branch: ${{ github.ref }} diff --git a/.github/workflows/pre-commit-scheduled.yml b/.github/workflows/pre-commit-scheduled.yml index cc3394aff7..078ab1dc97 100644 --- a/.github/workflows/pre-commit-scheduled.yml +++ b/.github/workflows/pre-commit-scheduled.yml @@ -25,7 +25,7 @@ jobs: - name: Commit and push changes if: steps.pre-commit.outcome == 'failure' && github.repository_owner == 'MaaAssistantArknights' - uses: actions-js/push@master + uses: actions-js/push@5a7cbd780d82c0c937b5977586e641b2fd94acc5 # master as of 2026-05-06 with: github_token: ${{ secrets.GITHUB_TOKEN }} message: "chore: Auto update by pre-commit hooks diff --git a/.github/workflows/res-update-game.yml b/.github/workflows/res-update-game.yml index 437fb21fd2..889bb0dabf 100644 --- a/.github/workflows/res-update-game.yml +++ b/.github/workflows/res-update-game.yml @@ -306,7 +306,7 @@ jobs: - name: Push changes if: steps.add_files.outputs.have_commits == 'True' - uses: ad-m/github-push-action@master + uses: ad-m/github-push-action@d30dc2d070765d7e509df00c34c5fa2dd636ff74 # master as of 2026-05-06 with: branch: ${{ github.ref }} github_token: ${{ secrets.MAA_RESOURCE_SYNC }} diff --git a/.github/workflows/update-submodules.yml b/.github/workflows/update-submodules.yml index 6e3c006092..3c83806d95 100644 --- a/.github/workflows/update-submodules.yml +++ b/.github/workflows/update-submodules.yml @@ -21,7 +21,7 @@ jobs: run: bash ./.github/scripts/sync-optional-submodules.sh --remote src/MAAUnified src/MaaMacGui src/maa-cli - name: Commit and push changes - uses: actions-js/push@master + uses: actions-js/push@5a7cbd780d82c0c937b5977586e641b2fd94acc5 # master as of 2026-05-06 with: github_token: ${{ secrets.GITHUB_TOKEN }} message: "feat: Update Submodules MAAUnified, MaaMacGui, maa-cli diff --git a/src/MaaCore/Controller/Platform/PosixIO.cpp b/src/MaaCore/Controller/Platform/PosixIO.cpp index 09e23785ea..a9264a1498 100644 --- a/src/MaaCore/Controller/Platform/PosixIO.cpp +++ b/src/MaaCore/Controller/Platform/PosixIO.cpp @@ -83,9 +83,12 @@ std::optional asst::PosixIO::call_command( ::close(pipe_out[PIPE_READ]); // TODO: close all other fds - exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr); + execlp("sh", "sh", "-c", cmd.c_str(), nullptr); Log.error("exec failed:", std::strerror(errno)); - return std::nullopt; + // 必须终止子进程:返回会让 child 在 parent 地址空间的副本里继续 unwind, + // 释放 m_callcmd_mutex、双重析构 fd、kill(0, ...) 误杀整个进程组。 + // 退出码沿用 POSIX shell 约定的 127(command not found)。 + _exit(127); } ::close(pipe_in[PIPE_READ]); ::close(pipe_out[PIPE_WRITE]); diff --git a/src/MaaCore/Utils/DebugImageHelper.hpp b/src/MaaCore/Utils/DebugImageHelper.hpp index ade7c5fb61..14c41040a1 100644 --- a/src/MaaCore/Utils/DebugImageHelper.hpp +++ b/src/MaaCore/Utils/DebugImageHelper.hpp @@ -26,18 +26,47 @@ inline size_t filenum_ctrl(const std::filesystem::path& absolute_or_relative_dir absolute_path = absolute_or_relative_dir; } - if (!std::filesystem::exists(absolute_path)) { + std::error_code dir_ec; + if (!std::filesystem::is_directory(absolute_path, dir_ec)) { + if (dir_ec) { + Log.warn(__FUNCTION__, "failed to inspect debug image directory", absolute_path, dir_ec.message()); + } return 0; } size_t file_nums = 0; std::vector> files; - for (auto& file : std::filesystem::directory_iterator(absolute_path)) { - if (file.is_regular_file()) { - ++file_nums; - files.emplace_back(std::filesystem::last_write_time(file.path()), file.path()); + std::error_code iter_ec; + const auto options = std::filesystem::directory_options::skip_permission_denied; + std::filesystem::directory_iterator iter(absolute_path, options, iter_ec); + if (iter_ec) { + Log.warn(__FUNCTION__, "failed to open debug image directory", absolute_path, iter_ec.message()); + return 0; + } + for (const std::filesystem::directory_iterator end; iter != end; iter.increment(iter_ec)) { + if (iter_ec) { + Log.warn(__FUNCTION__, "failed to iterate debug image directory", absolute_path, iter_ec.message()); + break; } + + const auto& file = *iter; + std::error_code entry_ec; + if (!file.is_regular_file(entry_ec)) { + if (entry_ec) { + Log.warn(__FUNCTION__, "failed to inspect debug image entry", file.path(), entry_ec.message()); + } + continue; + } + + const auto write_time = std::filesystem::last_write_time(file.path(), entry_ec); + if (entry_ec) { + Log.warn(__FUNCTION__, "failed to query debug image timestamp", file.path(), entry_ec.message()); + continue; + } + + ++file_nums; + files.emplace_back(write_time, file.path()); } std::sort(files.begin(), files.end(), [](auto& a, auto& b) { @@ -62,7 +91,7 @@ inline size_t filenum_ctrl(const std::filesystem::path& absolute_or_relative_dir ++deleted; } else if (ec) { - LogWarn << "Skip deleting debug image in this round" << files[i].second << ec.value() << ec.message(); + Log.warn(__FUNCTION__, "failed to remove old debug image", files[i].second, ec.message()); } }